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 }