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             if (ce instanceof Op op && op.result() != null && op.result().uses().size() != 1) {
125                 l.add(op.result());
126             } else if (ce instanceof CoreOp.InvokeOp invokeOp && (invokeOp.invokeDescriptor().equals(M_BLOCK_BUILDER_OP)
127                    || invokeOp.invokeDescriptor().equals(M_BLOCK_BUILDER_PARAM))) {
128                l.add(invokeOp.result());
129             }
130             return l;
131         });
132 
133         var stats = new ListBuffer<JCTree.JCStatement>();
134         for (Value root : rootValues) {
135             JCTree tree = opToTree(root);
136             if (tree instanceof JCExpression e) {
137                 var vs = new Symbol.VarSymbol(LocalVarFlags | SYNTHETIC, names.fromString("_$" + localVarCount++), tree.type, ms);
138                 tree = treeMaker.VarDef(vs, e);
139                 valueToTree.put(root, tree);
140             }
141             stats.add((JCTree.JCStatement) tree);
142         }
143         var mb = treeMaker.Block(0, stats.toList());
144 
145         return treeMaker.MethodDef(ms, mb);
146     }
147 
148     private JCTree opToTree(Value v) {
149         if (valueToTree.containsKey(v)) {
150             return valueToTree.get(v);
151         }
152         Op op = ((Op.Result) v).op();
153         JCTree tree = switch (op) {
154             case CoreOp.ConstantOp constantOp when constantOp.value() == null ->
155                     treeMaker.Literal(TypeTag.BOT, null).setType(syms.botType);
156             case CoreOp.ConstantOp constantOp -> treeMaker.Literal(constantOp.value());
157             case CoreOp.InvokeOp invokeOp -> invokeOpToJCMethodInvocation(invokeOp);
158             case CoreOp.NewOp newOp when newOp.resultType() instanceof ArrayType at -> {
159                 var elemType = treeMaker.Ident(typeElementToType(at.componentType()).tsym);
160                 var dims = new ListBuffer<JCTree.JCExpression>();
161                 for (int d = 0; d < at.dimensions(); d++) {
162                     dims.add(toExpr(opToTree(newOp.operands().get(d))));
163                 }
164                 var na = treeMaker.NewArray(elemType, dims.toList(), null);
165                 na.type = typeElementToType(at);
166                 yield na;
167             }
168             case CoreOp.NewOp newOp -> {
169                 var ownerType = typeElementToType(newOp.constructorDescriptor().refType());
170                 var clazz = treeMaker.Ident(ownerType.tsym);
171                 var args = new ListBuffer<JCTree.JCExpression>();
172                 for (Value operand : newOp.operands()) {
173                     args.add(toExpr(opToTree(operand)));
174                 }
175                 var nc = treeMaker.NewClass(null, null, clazz, args.toList(), null);
176                 if (newOp.isVarargs()) {
177                     setVarargs(nc, newOp.constructorDescriptor().type());
178                 }
179                 nc.type = ownerType;
180                 nc.constructor = constructorDescriptorToSymbol(newOp.constructorDescriptor());
181                 nc.constructorType = nc.constructor.type;
182                 yield nc;
183             }
184             case CoreOp.ReturnOp returnOp ->
185                     treeMaker.Return(toExpr(opToTree(returnOp.returnValue())));
186             case CoreOp.FieldAccessOp.FieldLoadOp fieldLoadOp -> {
187                 var sym = fieldDescriptorToSymbol(fieldLoadOp.fieldDescriptor());
188                 Assert.check(sym.isStatic());
189                 yield treeMaker.Select(treeMaker.Ident(sym.owner), sym);
190             }
191             case CoreOp.ArrayAccessOp.ArrayStoreOp arrayStoreOp -> {
192                 var array = arrayStoreOp.operands().get(0);
193                 var val = arrayStoreOp.operands().get(1);
194                 var index = arrayStoreOp.operands().get(2);
195                 var as = treeMaker.Assign(
196                         treeMaker.Indexed(
197                                 toExpr(opToTree(array)), toExpr(opToTree(index))), toExpr(opToTree(val))
198                 );
199                 as.type = typeElementToType(((ArrayType) array.type()).componentType());
200                 yield as;
201             }
202             default -> throw new IllegalStateException("Op -> JCTree not supported for :" + op.getClass().getName());
203         };
204         valueToTree.put(v, tree);
205         return tree;
206     }
207 
208     VarSymbol fieldDescriptorToSymbol(FieldRef fieldRef) {
209         Name name = names.fromString(fieldRef.name());
210         Type site = typeElementToType(fieldRef.refType());
211         return resolve.resolveInternalField(attrEnv.enclClass, attrEnv, site, name);
212     }
213 
214     MethodSymbol methodDescriptorToSymbol(MethodRef methodRef) {
215         Name name = names.fromString(methodRef.name());
216         Type site = typeElementToType(methodRef.refType());
217         List<Type> argtypes = methodRef.type().parameterTypes().stream()
218                 .map(this::typeElementToType).collect(List.collector());
219         return resolve.resolveInternalMethod(attrEnv.enclClass, attrEnv, site, name, argtypes, List.nil());
220     }
221 
222     MethodSymbol constructorDescriptorToSymbol(ConstructorRef constructorRef) {
223         Type site = typeElementToType(constructorRef.refType());
224         List<Type> argtypes = constructorRef.type().parameterTypes().stream()
225                 .map(this::typeElementToType).collect(List.collector());
226         return resolve.resolveInternalConstructor(attrEnv.enclClass, attrEnv, site, argtypes, List.nil());
227     }
228 
229     // TODO: generate AST in SSA form
230     // TODO: drop addVarsWhenNecessary
231     // TODO: maybe move back into ReflectMethods
232 }