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 }