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