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 }