1 package jdk.incubator.code.internal;
  2 
  3 import com.sun.tools.javac.code.*;
  4 import com.sun.tools.javac.code.Symbol.MethodSymbol;
  5 import com.sun.tools.javac.code.Symbol.VarSymbol;
  6 import com.sun.tools.javac.comp.AttrContext;
  7 import com.sun.tools.javac.comp.Env;
  8 import com.sun.tools.javac.comp.Resolve;
  9 import com.sun.tools.javac.tree.JCTree;
 10 import com.sun.tools.javac.tree.JCTree.JCExpression;
 11 import com.sun.tools.javac.tree.TreeInfo;
 12 import com.sun.tools.javac.tree.TreeMaker;
 13 import com.sun.tools.javac.util.*;
 14 import com.sun.tools.javac.util.List;
 15 import jdk.incubator.code.*;
 16 import jdk.incubator.code.op.CoreOp;
 17 import jdk.incubator.code.type.*;
 18 
 19 import java.util.*;
 20 
 21 import static com.sun.tools.javac.code.Flags.*;
 22 
 23 public class CodeModelToAST {
 24 
 25     private final TreeMaker treeMaker;
 26     private final Names names;
 27     private final Symtab syms;
 28     private final Env<AttrContext> attrEnv;
 29     private final Resolve resolve;
 30     private final Types types;
 31     private final Symbol.ClassSymbol currClassSym;
 32     private final CodeReflectionSymbols crSym;
 33     private Symbol.MethodSymbol ms;
 34     private int localVarCount = 0; // used to name variables we introduce in the AST
 35     private final Map<Value, JCTree> valueToTree = new HashMap<>();
 36     private static final MethodRef M_BLOCK_BUILDER_OP = MethodRef.method(Block.Builder.class, "op",
 37             Op.Result.class, Op.class);
 38     private static final MethodRef M_BLOCK_BUILDER_PARAM = MethodRef.method(Block.Builder.class, "parameter",
 39             Block.Parameter.class, TypeElement.class);
 40 
 41     public CodeModelToAST(TreeMaker treeMaker, Names names, Symtab syms, Resolve resolve,
 42                           Types types, Env<AttrContext> attrEnv, CodeReflectionSymbols crSym) {
 43         this.treeMaker = treeMaker;
 44         this.names = names;
 45         this.syms = syms;
 46         this.resolve = resolve;
 47         this.types = types;
 48         this.attrEnv = attrEnv;
 49         this.currClassSym = attrEnv.enclClass.sym;
 50         this.crSym = crSym;
 51     }
 52 
 53     private Type typeElementToType(TypeElement jt) {
 54         return switch (jt) {
 55             case PrimitiveType pt when pt == JavaType.BOOLEAN -> syms.booleanType;
 56             case PrimitiveType pt when pt == JavaType.BYTE -> syms.byteType;
 57             case PrimitiveType pt when pt == JavaType.CHAR -> syms.charType;
 58             case PrimitiveType pt when pt == JavaType.INT -> syms.intType;
 59             case PrimitiveType pt when pt == JavaType.LONG -> syms.longType;
 60             case PrimitiveType pt when pt == JavaType.FLOAT -> syms.floatType;
 61             case PrimitiveType pt when pt == JavaType.DOUBLE -> syms.doubleType;
 62             case ClassType ct when ct.hasTypeArguments() -> {
 63                 Type enclosing = ct.enclosingType().map(this::typeElementToType).orElse(Type.noType);
 64                 List<Type> typeArgs = List.from(ct.typeArguments()).map(this::typeElementToType);
 65                 yield new Type.ClassType(enclosing, typeArgs, typeElementToType(ct.rawType()).tsym);
 66             }
 67             case ClassType ct -> types.erasure(syms.enterClass(attrEnv.toplevel.modle, ct.toClassName()));
 68             case ArrayType at -> new Type.ArrayType(typeElementToType(at.componentType()), syms.arrayClass);
 69             default -> throw new IllegalStateException("Unsupported type: " + jt);
 70         };
 71     }
 72 
 73     private JCExpression toExpr(JCTree t) {
 74         return switch (t) {
 75             case JCExpression e -> e;
 76             case JCTree.JCVariableDecl vd -> treeMaker.Ident(vd);
 77             case null, default -> throw new IllegalArgumentException();
 78         };
 79     }
 80 
 81     private JCTree invokeOpToJCMethodInvocation(CoreOp.InvokeOp invokeOp) {
 82         Value receiver = (invokeOp.invokeKind() == CoreOp.InvokeOp.InvokeKind.INSTANCE) ?
 83                 invokeOp.operands().get(0) : null;
 84         List<Value> arguments = invokeOp.operands().stream()
 85                 .skip(receiver == null ? 0 : 1)
 86                 .collect(List.collector());
 87         var methodSym = methodDescriptorToSymbol(invokeOp.invokeDescriptor());
 88         var meth = (receiver == null) ?
 89                 treeMaker.Ident(methodSym) :
 90                 treeMaker.Select(toExpr(opToTree(receiver)), methodSym);
 91         var args = new ListBuffer<JCTree.JCExpression>();
 92         for (Value operand : arguments) {
 93             args.add(toExpr(opToTree(operand)));
 94         }
 95         var methodInvocation = treeMaker.App(meth, args.toList());
 96         if (invokeOp.isVarArgs()) {
 97             setVarargs(methodInvocation, invokeOp.invokeDescriptor().type());
 98         }
 99         return methodInvocation;
100     }
101 
102     void setVarargs(JCExpression tree, FunctionType type) {
103         var lastParam = type.parameterTypes().getLast();
104         if (lastParam instanceof ArrayType varargType) {
105             TreeInfo.setVarargsElement(tree, typeElementToType(varargType.componentType()));
106         } else {
107             Assert.error("Expected trailing array type: " + type);
108         }
109     }
110 
111     public JCTree.JCMethodDecl transformFuncOpToAST(CoreOp.FuncOp funcOp, Name methodName) {
112         Assert.check(funcOp.body().blocks().size() == 1);
113 
114         var paramTypes = List.of(crSym.opFactoryType, crSym.typeElementFactoryType);
115         var mt = new Type.MethodType(paramTypes, crSym.opType, List.nil(), syms.methodClass);
116         ms = new Symbol.MethodSymbol(PUBLIC | STATIC | SYNTHETIC, methodName, mt, currClassSym);
117         currClassSym.members().enter(ms);
118 
119         for (int i = 0; i < funcOp.parameters().size(); i++) {
120             valueToTree.put(funcOp.parameters().get(i), treeMaker.Ident(ms.params().get(i)));
121         }
122 
123         java.util.List<Value> rootValues = funcOp.traverse(new ArrayList<>(), (l, ce) -> {
124             boolean isRoot = switch (ce) {
125                 case CoreOp.InvokeOp invokeOp when invokeOp.invokeDescriptor().equals(M_BLOCK_BUILDER_OP)
126                         || invokeOp.invokeDescriptor().equals(M_BLOCK_BUILDER_PARAM) -> true;
127                 case CoreOp.ReturnOp _, CoreOp.ArrayAccessOp.ArrayStoreOp _ -> true;
128                 case Op op when op.result() != null && op.result().uses().size() > 1 -> true;
129                 default -> false;
130             };
131             if (isRoot) {
132                 l.add(((Op)ce).result());
133             }
134             return l;
135         });
136 
137         var stats = new ListBuffer<JCTree.JCStatement>();
138         for (Value root : rootValues) {
139             JCTree tree = opToTree(root);
140             if (tree instanceof JCExpression e) {
141                 if (!root.uses().isEmpty()) {
142                     var vs = new Symbol.VarSymbol(LocalVarFlags | SYNTHETIC, names.fromString("_$" + localVarCount++), tree.type, ms);
143                     tree = treeMaker.VarDef(vs, e);
144                     valueToTree.put(root, tree);
145                 } else {
146                     tree = treeMaker.Exec(e);
147                 }
148             }
149             stats.add((JCTree.JCStatement) tree);
150         }
151         var mb = treeMaker.Block(0, stats.toList());
152 
153         return treeMaker.MethodDef(ms, mb);
154     }
155 
156     private JCTree opToTree(Value v) {
157         if (valueToTree.containsKey(v)) {
158             return valueToTree.get(v);
159         }
160         Op op = ((Op.Result) v).op();
161         JCTree tree = switch (op) {
162             case CoreOp.ConstantOp constantOp when constantOp.value() == null ->
163                     treeMaker.Literal(TypeTag.BOT, null).setType(syms.botType);
164             case CoreOp.ConstantOp constantOp -> treeMaker.Literal(constantOp.value());
165             case CoreOp.InvokeOp invokeOp -> invokeOpToJCMethodInvocation(invokeOp);
166             case CoreOp.NewOp newOp when newOp.resultType() instanceof ArrayType at -> {
167                 var elemType = treeMaker.Ident(typeElementToType(at.componentType()).tsym);
168                 var dims = new ListBuffer<JCTree.JCExpression>();
169                 for (int d = 0; d < at.dimensions(); d++) {
170                     dims.add(toExpr(opToTree(newOp.operands().get(d))));
171                 }
172                 var na = treeMaker.NewArray(elemType, dims.toList(), null);
173                 na.type = typeElementToType(at);
174                 yield na;
175             }
176             case CoreOp.NewOp newOp -> {
177                 var ownerType = typeElementToType(newOp.constructorDescriptor().refType());
178                 var clazz = treeMaker.Ident(ownerType.tsym);
179                 var args = new ListBuffer<JCTree.JCExpression>();
180                 for (Value operand : newOp.operands()) {
181                     args.add(toExpr(opToTree(operand)));
182                 }
183                 var nc = treeMaker.NewClass(null, null, clazz, args.toList(), null);
184                 if (newOp.isVarargs()) {
185                     setVarargs(nc, newOp.constructorDescriptor().type());
186                 }
187                 nc.type = ownerType;
188                 nc.constructor = constructorDescriptorToSymbol(newOp.constructorDescriptor());
189                 nc.constructorType = nc.constructor.type;
190                 yield nc;
191             }
192             case CoreOp.ReturnOp returnOp ->
193                     treeMaker.Return(toExpr(opToTree(returnOp.returnValue())));
194             case CoreOp.FieldAccessOp.FieldLoadOp fieldLoadOp -> {
195                 var sym = fieldDescriptorToSymbol(fieldLoadOp.fieldDescriptor());
196                 Assert.check(sym.isStatic());
197                 yield treeMaker.Select(treeMaker.Ident(sym.owner), sym);
198             }
199             case CoreOp.ArrayAccessOp.ArrayStoreOp arrayStoreOp -> {
200                 var array = arrayStoreOp.operands().get(0);
201                 var index = arrayStoreOp.operands().get(1);
202                 var val = arrayStoreOp.operands().get(2);
203                 var as = treeMaker.Assign(
204                         treeMaker.Indexed(
205                                 toExpr(opToTree(array)), toExpr(opToTree(index))), toExpr(opToTree(val))
206                 );
207                 as.type = typeElementToType(((ArrayType) array.type()).componentType());
208                 yield as;
209             }
210             default -> throw new IllegalStateException("Op -> JCTree not supported for :" + op.getClass().getName());
211         };
212         valueToTree.put(v, tree);
213         return tree;
214     }
215 
216     VarSymbol fieldDescriptorToSymbol(FieldRef fieldRef) {
217         Name name = names.fromString(fieldRef.name());
218         Type site = typeElementToType(fieldRef.refType());
219         return resolve.resolveInternalField(attrEnv.enclClass, attrEnv, site, name);
220     }
221 
222     MethodSymbol methodDescriptorToSymbol(MethodRef methodRef) {
223         Name name = names.fromString(methodRef.name());
224         Type site = typeElementToType(methodRef.refType());
225         List<Type> argtypes = methodRef.type().parameterTypes().stream()
226                 .map(this::typeElementToType).collect(List.collector());
227         return resolve.resolveInternalMethod(attrEnv.enclClass, attrEnv, site, name, argtypes, List.nil());
228     }
229 
230     MethodSymbol constructorDescriptorToSymbol(ConstructorRef constructorRef) {
231         Type site = typeElementToType(constructorRef.refType());
232         List<Type> argtypes = constructorRef.type().parameterTypes().stream()
233                 .map(this::typeElementToType).collect(List.collector());
234         return resolve.resolveInternalConstructor(attrEnv.enclClass, attrEnv, site, argtypes, List.nil());
235     }
236 
237     // TODO: generate AST in SSA form
238     // TODO: drop addVarsWhenNecessary
239     // TODO: maybe move back into ReflectMethods
240 }