1 /* 2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.incubator.code.internal; 27 28 import com.sun.source.tree.LambdaExpressionTree; 29 import com.sun.source.tree.MemberReferenceTree.ReferenceMode; 30 import com.sun.tools.javac.code.Kinds.Kind; 31 import com.sun.tools.javac.code.Symbol; 32 import com.sun.tools.javac.code.Symbol.ClassSymbol; 33 import com.sun.tools.javac.code.Symbol.MethodSymbol; 34 import com.sun.tools.javac.code.Symbol.VarSymbol; 35 import com.sun.tools.javac.code.Symtab; 36 import com.sun.tools.javac.code.Type; 37 import com.sun.tools.javac.code.Type.ArrayType; 38 import com.sun.tools.javac.code.Type.MethodType; 39 import com.sun.tools.javac.code.TypeTag; 40 import com.sun.tools.javac.code.Types; 41 import com.sun.tools.javac.comp.AttrContext; 42 import com.sun.tools.javac.comp.CaptureScanner; 43 import com.sun.tools.javac.comp.DeferredAttr.FilterScanner; 44 import com.sun.tools.javac.comp.Env; 45 import com.sun.tools.javac.comp.Flow; 46 import com.sun.tools.javac.comp.Lower; 47 import com.sun.tools.javac.comp.CodeReflectionTransformer; 48 import com.sun.tools.javac.comp.Resolve; 49 import com.sun.tools.javac.comp.TypeEnvs; 50 import com.sun.tools.javac.jvm.ByteCodes; 51 import com.sun.tools.javac.jvm.Gen; 52 import com.sun.tools.javac.tree.JCTree; 53 import com.sun.tools.javac.tree.JCTree.JCAnnotation; 54 import com.sun.tools.javac.tree.JCTree.JCArrayAccess; 55 import com.sun.tools.javac.tree.JCTree.JCAssign; 56 import com.sun.tools.javac.tree.JCTree.JCBinary; 57 import com.sun.tools.javac.tree.JCTree.JCBlock; 58 import com.sun.tools.javac.tree.JCTree.JCClassDecl; 59 import com.sun.tools.javac.tree.JCTree.JCExpression; 60 import com.sun.tools.javac.tree.JCTree.JCFieldAccess; 61 import com.sun.tools.javac.tree.JCTree.JCFunctionalExpression; 62 import com.sun.tools.javac.tree.JCTree.JCIdent; 63 import com.sun.tools.javac.tree.JCTree.JCLambda; 64 import com.sun.tools.javac.tree.JCTree.JCLiteral; 65 import com.sun.tools.javac.tree.JCTree.JCMemberReference; 66 import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind; 67 import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 68 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; 69 import com.sun.tools.javac.tree.JCTree.JCModuleDecl; 70 import com.sun.tools.javac.tree.JCTree.JCNewArray; 71 import com.sun.tools.javac.tree.JCTree.JCNewClass; 72 import com.sun.tools.javac.tree.JCTree.JCReturn; 73 import com.sun.tools.javac.tree.JCTree.JCVariableDecl; 74 import com.sun.tools.javac.tree.JCTree.JCAssert; 75 import com.sun.tools.javac.tree.JCTree.Tag; 76 import com.sun.tools.javac.tree.TreeInfo; 77 import com.sun.tools.javac.tree.TreeMaker; 78 import com.sun.tools.javac.tree.TreeScanner; 79 import com.sun.tools.javac.tree.TreeTranslator; 80 import com.sun.tools.javac.util.Assert; 81 import com.sun.tools.javac.util.Context; 82 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 83 import com.sun.tools.javac.util.ListBuffer; 84 import com.sun.tools.javac.util.Log; 85 import com.sun.tools.javac.util.Name; 86 import com.sun.tools.javac.util.Names; 87 import com.sun.tools.javac.util.Options; 88 import jdk.incubator.code.*; 89 import jdk.incubator.code.extern.DialectFactory; 90 import jdk.incubator.code.dialect.core.*; 91 import jdk.incubator.code.dialect.java.*; 92 import jdk.incubator.code.dialect.java.WildcardType.BoundKind; 93 94 import javax.lang.model.element.Modifier; 95 import javax.tools.JavaFileObject; 96 import java.lang.constant.ClassDesc; 97 import java.util.*; 98 import java.util.function.Function; 99 import java.util.function.Supplier; 100 101 import static com.sun.tools.javac.code.Flags.*; 102 import static com.sun.tools.javac.code.Kinds.Kind.MTH; 103 import static com.sun.tools.javac.code.Kinds.Kind.TYP; 104 import static com.sun.tools.javac.code.Kinds.Kind.VAR; 105 import static com.sun.tools.javac.code.TypeTag.BOT; 106 import static com.sun.tools.javac.code.TypeTag.CLASS; 107 import static com.sun.tools.javac.code.TypeTag.METHOD; 108 import static com.sun.tools.javac.code.TypeTag.NONE; 109 import static com.sun.tools.javac.main.Option.G_CUSTOM; 110 111 import static com.sun.tools.javac.resources.CompilerProperties.Errors.*; 112 import static com.sun.tools.javac.resources.CompilerProperties.Notes.*; 113 114 /** 115 * This a tree translator that adds the code model to all method declaration marked 116 * with the {@code CodeReflection} annotation. The model is expressed using the code 117 * reflection API (see jdk.internal.java.lang.reflect.code). 118 */ 119 public class ReflectMethods extends TreeTranslator { 120 protected static final Context.Key<ReflectMethods> reflectMethodsKey = new Context.Key<>(); 121 122 public static ReflectMethods instance(Context context) { 123 ReflectMethods instance = context.get(reflectMethodsKey); 124 if (instance == null) 125 instance = new ReflectMethods(context); 126 return instance; 127 } 128 129 private final Types types; 130 private final Names names; 131 private final Symtab syms; 132 private final Resolve resolve; 133 private final Gen gen; 134 private final Log log; 135 private final Lower lower; 136 private final TypeEnvs typeEnvs; 137 private final Flow flow; 138 private final CodeReflectionSymbols crSyms; 139 private final boolean dumpIR; 140 private final boolean lineDebugInfo; 141 private final CodeModelStorageOption codeModelStorageOption; 142 143 private TreeMaker make; 144 private ListBuffer<JCTree> classOps; 145 private Symbol.ClassSymbol currentClassSym; 146 private int lambdaCount; 147 148 @SuppressWarnings("this-escape") 149 protected ReflectMethods(Context context) { 150 context.put(reflectMethodsKey, this); 151 Options options = Options.instance(context); 152 dumpIR = options.isSet("dumpIR"); 153 lineDebugInfo = 154 options.isUnset(G_CUSTOM) || 155 options.isSet(G_CUSTOM, "lines"); 156 codeModelStorageOption = CodeModelStorageOption.parse(options.get("codeModelStorageOption")); 157 names = Names.instance(context); 158 syms = Symtab.instance(context); 159 resolve = Resolve.instance(context); 160 types = Types.instance(context); 161 gen = Gen.instance(context); 162 log = Log.instance(context); 163 lower = Lower.instance(context); 164 typeEnvs = TypeEnvs.instance(context); 165 flow = Flow.instance(context); 166 crSyms = new CodeReflectionSymbols(context); 167 } 168 169 @Override 170 public void visitMethodDef(JCMethodDecl tree) { 171 if (tree.sym.attribute(crSyms.codeReflectionType.tsym) != null) { 172 if (currentClassSym.type.getEnclosingType().hasTag(CLASS)) { 173 // Reflectable methods in inner classes are not supported 174 log.error(tree, QuotedMethodInnerClass(currentClassSym.enclClass())); 175 } else { 176 // if the method is annotated, scan it 177 BodyScanner bodyScanner = new BodyScanner(tree); 178 CoreOp.FuncOp funcOp = bodyScanner.scanMethod(); 179 if (dumpIR) { 180 // dump the method IR if requested 181 log.note(MethodIrDump(tree.sym.enclClass(), tree.sym, funcOp.toText())); 182 } 183 // create a static method that returns the op 184 classOps.add(opMethodDecl(methodName(symbolToMethodRef(tree.sym)), funcOp, codeModelStorageOption)); 185 } 186 } 187 super.visitMethodDef(tree); 188 } 189 190 @Override 191 public void visitModuleDef(JCModuleDecl that) { 192 // do nothing 193 } 194 195 @Override 196 public void visitClassDef(JCClassDecl tree) { 197 ListBuffer<JCTree> prevClassOps = classOps; 198 Symbol.ClassSymbol prevClassSym = currentClassSym; 199 int prevLambdaCount = lambdaCount; 200 JavaFileObject prev = log.useSource(tree.sym.sourcefile); 201 try { 202 lambdaCount = 0; 203 currentClassSym = tree.sym; 204 classOps = new ListBuffer<>(); 205 super.visitClassDef(tree); 206 tree.defs = tree.defs.prependList(classOps.toList()); 207 } finally { 208 lambdaCount = prevLambdaCount; 209 classOps = prevClassOps; 210 currentClassSym = prevClassSym; 211 result = tree; 212 log.useSource(prev); 213 } 214 } 215 216 @Override 217 public void visitLambda(JCLambda tree) { 218 FunctionalExpressionKind kind = functionalKind(tree); 219 if (kind.isQuoted) { 220 if (currentClassSym.type.getEnclosingType().hasTag(CLASS)) { 221 // Quotable lambdas in inner classes are not supported 222 log.error(tree, QuotedLambdaInnerClass(currentClassSym.enclClass())); 223 result = tree; 224 return; 225 } 226 227 // quoted lambda - scan it 228 BodyScanner bodyScanner = new BodyScanner(tree, kind); 229 CoreOp.FuncOp funcOp = bodyScanner.scanLambda(); 230 if (dumpIR) { 231 // dump the method IR if requested 232 log.note(QuotedIrDump(funcOp.toText())); 233 } 234 // create a static method that returns the FuncOp representing the lambda 235 JCMethodDecl opMethod = opMethodDecl(lambdaName(), funcOp, codeModelStorageOption); 236 classOps.add(opMethod); 237 238 switch (kind) { 239 case QUOTED_STRUCTURAL -> { 240 // @@@ Consider replacing with invokedynamic to quoted bootstrap method 241 // Thereby we avoid certain dependencies and hide specific details 242 ListBuffer<JCExpression> args = new ListBuffer<>(); 243 // Get the func operation 244 JCIdent opMethodId = make.Ident(opMethod.sym); 245 JCExpression op = make.TypeCast(crSyms.funcOpType, make.App(opMethodId)); 246 args.add(op); 247 // Append captured vars 248 ListBuffer<JCExpression> capturedArgs = quotedCapturedArgs(tree, bodyScanner); 249 args.appendList(capturedArgs.toList()); 250 // Get the quoted instance by calling Quoted::quotedOp 251 JCMethodInvocation quotedInvoke = make.App(make.Ident(crSyms.quotedExtractOp), args.toList()); 252 quotedInvoke.varargsElement = syms.objectType; 253 super.visitLambda(tree); 254 result = quotedInvoke; 255 } 256 case QUOTABLE -> { 257 // leave the lambda in place, but also leave a trail for LambdaToMethod 258 tree.codeModel = opMethod.sym; 259 super.visitLambda(tree); 260 } 261 } 262 } else { 263 super.visitLambda(tree); 264 } 265 } 266 267 @Override 268 public void visitReference(JCMemberReference tree) { 269 FunctionalExpressionKind kind = functionalKind(tree); 270 Assert.check(kind != FunctionalExpressionKind.QUOTED_STRUCTURAL, 271 "structural quoting not supported for method references"); 272 MemberReferenceToLambda memberReferenceToLambda = new MemberReferenceToLambda(tree, currentClassSym); 273 JCVariableDecl recvDecl = memberReferenceToLambda.receiverVar(); 274 JCLambda lambdaTree = memberReferenceToLambda.lambda(); 275 276 if (kind.isQuoted) { 277 if (currentClassSym.type.getEnclosingType().hasTag(CLASS)) { 278 // Quotable lambdas in inner classes are not supported 279 log.error(tree, QuotedMrefInnerClass(currentClassSym.enclClass())); 280 result = tree; 281 return; 282 } 283 284 // quoted lambda - scan it 285 BodyScanner bodyScanner = new BodyScanner(lambdaTree, kind); 286 CoreOp.FuncOp funcOp = bodyScanner.scanLambda(); 287 if (dumpIR) { 288 // dump the method IR if requested 289 log.note(QuotedIrDump(funcOp.toText())); 290 } 291 // create a method that returns the FuncOp representing the lambda 292 JCMethodDecl opMethod = opMethodDecl(lambdaName(), funcOp, codeModelStorageOption); 293 classOps.add(opMethod); 294 tree.codeModel = opMethod.sym; 295 super.visitReference(tree); 296 if (recvDecl != null) { 297 result = copyReferenceWithReceiverVar(tree, recvDecl); 298 } 299 } else { 300 super.visitReference(tree); 301 } 302 } 303 304 // @@@: Only used for quoted lambda, not quotable ones. Remove? 305 ListBuffer<JCExpression> quotedCapturedArgs(DiagnosticPosition pos, BodyScanner bodyScanner) { 306 ListBuffer<JCExpression> capturedArgs = new ListBuffer<>(); 307 for (Symbol capturedSym : bodyScanner.stack.localToOp.keySet()) { 308 if (capturedSym.kind == Kind.VAR) { 309 // captured var 310 VarSymbol var = (VarSymbol)capturedSym; 311 if (var.getConstValue() == null) { 312 capturedArgs.add(make.at(pos).Ident(capturedSym)); 313 } 314 } else { 315 throw new AssertionError("Unexpected captured symbol: " + capturedSym); 316 } 317 } 318 if (capturedArgs.size() < bodyScanner.top.body.entryBlock().parameters().size()) { 319 // needs to capture 'this' 320 capturedArgs.prepend(make.at(pos).This(currentClassSym.type)); 321 } 322 return capturedArgs; 323 } 324 325 /* 326 * Creates a let expression of the kind: 327 * let $recv in $recv::memberRef 328 * 329 * This is required to make sure that LambdaToMethod doesn't end up emitting the 330 * code for capturing the bound method reference receiver twice. 331 */ 332 JCExpression copyReferenceWithReceiverVar(JCMemberReference ref, JCVariableDecl recvDecl) { 333 JCMemberReference newRef = make.at(ref).Reference(ref.mode, ref.name, make.Ident(recvDecl.sym), ref.typeargs); 334 newRef.type = ref.type; 335 newRef.target = ref.target; 336 newRef.refPolyKind = ref.refPolyKind; 337 newRef.referentType = ref.referentType; 338 newRef.kind = ref.kind; 339 newRef.varargsElement = ref.varargsElement; 340 newRef.ownerAccessible = ref.ownerAccessible; 341 newRef.sym = ref.sym; 342 newRef.codeModel = ref.codeModel; 343 return make.at(ref).LetExpr(recvDecl, newRef).setType(newRef.type); 344 } 345 346 Name lambdaName() { 347 return names.fromString("lambda").append('$', names.fromString(String.valueOf(lambdaCount++))); 348 } 349 350 Name methodName(MethodRef method) { 351 char[] sigCh = method.toString().toCharArray(); 352 for (int i = 0; i < sigCh.length; i++) { 353 switch (sigCh[i]) { 354 case '.', ';', '[', '/' -> sigCh[i] = '$'; 355 } 356 } 357 return names.fromChars(sigCh, 0, sigCh.length); 358 } 359 360 // @@@ Retain enum for when we might add another storage to test 361 // and compare 362 private enum CodeModelStorageOption { 363 CODE_BUILDER; 364 365 public static CodeModelStorageOption parse(String s) { 366 if (s == null) { 367 return CodeModelStorageOption.CODE_BUILDER; 368 } 369 return CodeModelStorageOption.valueOf(s); 370 } 371 } 372 373 private JCMethodDecl opMethodDecl(Name methodName, CoreOp.FuncOp op, CodeModelStorageOption codeModelStorageOption) { 374 // Create the method that constructs the code model stored in the class file 375 var mt = new MethodType(com.sun.tools.javac.util.List.nil(), crSyms.opType, 376 com.sun.tools.javac.util.List.nil(), syms.methodClass); 377 var ms = new MethodSymbol(PRIVATE | STATIC | SYNTHETIC, methodName, mt, currentClassSym); 378 currentClassSym.members().enter(ms); 379 380 // Create the method body 381 // Code model is stored as code that builds the code model 382 // using the builder API and public APIs 383 var opBuilder = OpBuilder.createBuilderFunction(op, 384 b -> b.op(JavaOp.fieldLoad( 385 FieldRef.field(JavaOp.class, "JAVA_DIALECT_FACTORY", DialectFactory.class)))); 386 var codeModelTranslator = new CodeModelTranslator(); 387 var body = codeModelTranslator.translateFuncOp(opBuilder, ms); 388 389 var md = make.MethodDef(ms, make.Block(0, com.sun.tools.javac.util.List.of(body))); 390 return md; 391 } 392 393 public JCTree translateTopLevelClass(JCTree cdef, TreeMaker make) { 394 // note that this method does NOT support recursion. 395 this.make = make; 396 return translate(cdef); 397 } 398 399 public CoreOp.FuncOp getMethodBody(Symbol.ClassSymbol classSym, JCMethodDecl methodDecl, JCBlock attributedBody, TreeMaker make) { 400 // if the method is annotated, scan it 401 // Called from JavacElements::getBody 402 try { 403 this.make = make; 404 currentClassSym = classSym; 405 BodyScanner bodyScanner = new BodyScanner(methodDecl, attributedBody); 406 return bodyScanner.scanMethod(); 407 } finally { 408 currentClassSym = null; 409 this.make = null; 410 } 411 } 412 413 static class BodyStack { 414 final BodyStack parent; 415 416 // Tree associated with body 417 final JCTree tree; 418 419 // Body to add blocks 420 final Body.Builder body; 421 // Current block to add operations 422 Block.Builder block; 423 424 // Map of symbols (method arguments and local variables) to varOp values 425 final Map<Symbol, Value> localToOp; 426 427 // Label 428 Map.Entry<String, Op.Result> label; 429 430 BodyStack(BodyStack parent, JCTree tree, FunctionType bodyType) { 431 this.parent = parent; 432 433 this.tree = tree; 434 435 this.body = Body.Builder.of(parent != null ? parent.body : null, bodyType); 436 this.block = body.entryBlock(); 437 438 this.localToOp = new LinkedHashMap<>(); // order is important for captured values 439 } 440 441 public void setLabel(String labelName, Op.Result labelValue) { 442 if (label != null) { 443 throw new IllegalStateException("Label already defined: " + labelName); 444 } 445 label = Map.entry(labelName, labelValue); 446 } 447 } 448 449 class BodyScanner extends TreeScanner { 450 private final JCTree body; 451 private final Name name; 452 private final BodyStack top; 453 private BodyStack stack; 454 private Op lastOp; 455 private Value result; 456 private Type pt = Type.noType; 457 private final boolean isQuoted; 458 private Type bodyTarget; 459 private JCTree currentNode; 460 private final Map<Symbol, List<Symbol>> localCaptures = new HashMap<>(); 461 462 BodyScanner(JCMethodDecl tree) { 463 this(tree, tree.body); 464 } 465 466 BodyScanner(JCMethodDecl tree, JCBlock body) { 467 this.currentNode = tree; 468 this.body = body; 469 this.name = tree.name; 470 this.isQuoted = false; 471 472 List<TypeElement> parameters = new ArrayList<>(); 473 int blockArgOffset = 0; 474 // Instance methods model "this" as an additional argument occurring 475 // before all other arguments. 476 // @@@ Inner classes. 477 // We need to capture all "this", in nested order, as arguments. 478 if (!tree.getModifiers().getFlags().contains(Modifier.STATIC)) { 479 parameters.add(typeToTypeElement(tree.sym.owner.type)); 480 blockArgOffset++; 481 } 482 tree.sym.type.getParameterTypes().stream().map(ReflectMethods.this::typeToTypeElement).forEach(parameters::add); 483 484 FunctionType bodyType = CoreType.functionType( 485 typeToTypeElement(tree.sym.type.getReturnType()), parameters); 486 487 this.stack = this.top = new BodyStack(null, tree.body, bodyType); 488 489 // @@@ this as local variable? (it can never be stored to) 490 for (int i = 0 ; i < tree.params.size() ; i++) { 491 Op.Result paramOp = append(CoreOp.var( 492 tree.params.get(i).name.toString(), 493 top.block.parameters().get(blockArgOffset + i))); 494 top.localToOp.put(tree.params.get(i).sym, paramOp); 495 } 496 497 bodyTarget = tree.sym.type.getReturnType(); 498 } 499 500 BodyScanner(JCLambda tree, FunctionalExpressionKind kind) { 501 Assert.check(kind != FunctionalExpressionKind.NOT_QUOTED); 502 503 this.currentNode = tree; 504 this.body = tree; 505 this.name = names.fromString("quotedLambda"); 506 this.isQuoted = true; 507 508 QuotableLambdaCaptureScanner lambdaCaptureScanner = 509 new QuotableLambdaCaptureScanner(tree); 510 511 List<VarSymbol> capturedSymbols = lambdaCaptureScanner.analyzeCaptures(); 512 int blockParamOffset = 0; 513 514 ListBuffer<Type> capturedTypes = new ListBuffer<>(); 515 if (lambdaCaptureScanner.capturesThis) { 516 capturedTypes.add(currentClassSym.type); 517 blockParamOffset++; 518 } 519 for (Symbol s : capturedSymbols) { 520 capturedTypes.add(s.type); 521 } 522 523 MethodType mtype = new MethodType(capturedTypes.toList(), crSyms.quotedType, 524 com.sun.tools.javac.util.List.nil(), syms.methodClass); 525 FunctionType mtDesc = CoreType.functionType(typeToTypeElement(mtype.restype), 526 mtype.getParameterTypes().map(ReflectMethods.this::typeToTypeElement)); 527 528 this.stack = this.top = new BodyStack(null, tree.body, mtDesc); 529 530 // add captured variables mappings 531 for (int i = 0 ; i < capturedSymbols.size() ; i++) { 532 Symbol capturedSymbol = capturedSymbols.get(i); 533 var capturedArg = top.block.parameters().get(blockParamOffset + i); 534 top.localToOp.put(capturedSymbol, 535 append(CoreOp.var(capturedSymbol.name.toString(), capturedArg))); 536 } 537 538 // add captured constant mappings 539 for (Map.Entry<Symbol, Object> constantCapture : lambdaCaptureScanner.constantCaptures.entrySet()) { 540 Symbol capturedSymbol = constantCapture.getKey(); 541 var capturedArg = append(CoreOp.constant(typeToTypeElement(capturedSymbol.type), 542 constantCapture.getValue())); 543 top.localToOp.put(capturedSymbol, 544 append(CoreOp.var(capturedSymbol.name.toString(), capturedArg))); 545 } 546 547 bodyTarget = tree.target.getReturnType(); 548 } 549 550 /** 551 * Compute the set of local variables captured by a quotable lambda expression. 552 * Inspired from LambdaToMethod's LambdaCaptureScanner. 553 */ 554 class QuotableLambdaCaptureScanner extends CaptureScanner { 555 boolean capturesThis; 556 Set<ClassSymbol> seenClasses = new HashSet<>(); 557 Map<Symbol, Object> constantCaptures = new HashMap<>(); 558 559 QuotableLambdaCaptureScanner(JCLambda ownerTree) { 560 super(ownerTree); 561 } 562 563 @Override 564 public void visitClassDef(JCClassDecl tree) { 565 seenClasses.add(tree.sym); 566 super.visitClassDef(tree); 567 } 568 569 @Override 570 public void visitIdent(JCIdent tree) { 571 if (!tree.sym.isStatic() && 572 tree.sym.owner.kind == TYP && 573 (tree.sym.kind == VAR || tree.sym.kind == MTH) && 574 !seenClasses.contains(tree.sym.owner)) { 575 // a reference to an enclosing field or method, we need to capture 'this' 576 capturesThis = true; 577 } else if (tree.sym.kind == VAR && ((VarSymbol)tree.sym).getConstValue() != null) { 578 // record the constant value associated with this 579 constantCaptures.put(tree.sym, ((VarSymbol)tree.sym).getConstValue()); 580 } else { 581 // might be a local capture 582 super.visitIdent(tree); 583 } 584 } 585 586 @Override 587 public void visitSelect(JCFieldAccess tree) { 588 if (tree.sym.kind == VAR && 589 (tree.sym.name == names._this || 590 tree.sym.name == names._super) && 591 !seenClasses.contains(tree.sym.type.tsym)) { 592 capturesThis = true; 593 } 594 super.visitSelect(tree); 595 } 596 597 @Override 598 public void visitNewClass(JCNewClass tree) { 599 if (tree.type.tsym.owner.kind == MTH && 600 !seenClasses.contains(tree.type.tsym)) { 601 throw unsupported(tree); 602 } 603 super.visitNewClass(tree); 604 } 605 606 @Override 607 public void visitAnnotation(JCAnnotation tree) { 608 // do nothing (annotation values look like captured instance fields) 609 } 610 } 611 612 @Override 613 public void scan(JCTree tree) { 614 JCTree prev = currentNode; 615 currentNode = tree; 616 try { 617 super.scan(tree); 618 } finally { 619 currentNode = prev; 620 } 621 } 622 623 void pushBody(JCTree tree, FunctionType bodyType) { 624 stack = new BodyStack(stack, tree, bodyType); 625 lastOp = null; // reset 626 } 627 628 void popBody() { 629 stack = stack.parent; 630 } 631 632 Value varOpValue(Symbol sym) { 633 BodyStack s = stack; 634 while (s != null) { 635 Value v = s.localToOp.get(sym); 636 if (v != null) { 637 return v; 638 } 639 s = s.parent; 640 } 641 throw new NoSuchElementException(sym.toString()); 642 } 643 644 Value thisValue() { // @@@: outer this? 645 return top.block.parameters().get(0); 646 } 647 648 Value getLabel(String labelName) { 649 BodyStack s = stack; 650 while (s != null) { 651 if (s.label != null && s.label.getKey().equals(labelName)) { 652 return s.label.getValue(); 653 } 654 s = s.parent; 655 } 656 throw new NoSuchElementException(labelName); 657 } 658 659 private Op.Result append(Op op) { 660 return append(op, generateLocation(currentNode, false), stack); 661 } 662 663 private Op.Result append(Op op, Location l) { 664 return append(op, l, stack); 665 } 666 667 private Op.Result append(Op op, Location l, BodyStack stack) { 668 lastOp = op; 669 op.setLocation(l); 670 return stack.block.op(op); 671 } 672 673 Location generateLocation(JCTree node, boolean includeSourceReference) { 674 if (!lineDebugInfo) { 675 return Location.NO_LOCATION; 676 } 677 678 int pos = node.getStartPosition(); 679 int line = log.currentSource().getLineNumber(pos); 680 int col = log.currentSource().getColumnNumber(pos, false); 681 String path; 682 if (includeSourceReference) { 683 path = log.currentSource().getFile().toUri().toString(); 684 } else { 685 path = null; 686 } 687 return new Location(path, line, col); 688 } 689 690 private void appendReturnOrUnreachable(JCTree body) { 691 // Append only if an existing terminating operation is not present 692 if (lastOp == null || !(lastOp instanceof Op.Terminating)) { 693 // If control can continue after the body append return. 694 // Otherwise, append unreachable. 695 if (isAliveAfter(body)) { 696 append(CoreOp.return_()); 697 } else { 698 append(CoreOp.unreachable()); 699 } 700 } 701 } 702 703 private boolean isAliveAfter(JCTree node) { 704 return flow.aliveAfter(typeEnvs.get(currentClassSym), node, make); 705 } 706 707 private <O extends Op & Op.Terminating> void appendTerminating(Supplier<O> sop) { 708 // Append only if an existing terminating operation is not present 709 if (lastOp == null || !(lastOp instanceof Op.Terminating)) { 710 append(sop.get()); 711 } 712 } 713 714 public Value toValue(JCExpression expression, Type targetType) { 715 result = null; // reset 716 Type prevPt = pt; 717 try { 718 pt = targetType; 719 scan(expression); 720 return (result == null || targetType.hasTag(TypeTag.VOID) || targetType.hasTag(NONE)) ? 721 result : coerce(result, expression.type, targetType); 722 } finally { 723 pt = prevPt; 724 } 725 } 726 727 public Value toValue(JCExpression expression) { 728 return toValue(expression, Type.noType); 729 } 730 731 public Value toValue(JCTree.JCStatement statement) { 732 result = null; // reset 733 scan(statement); 734 return result; 735 } 736 737 Value coerce(Value sourceValue, Type sourceType, Type targetType) { 738 if (sourceType.isReference() && targetType.isReference() && 739 !types.isSubtype(types.erasure(sourceType), types.erasure(targetType))) { 740 return append(JavaOp.cast(typeToTypeElement(targetType), sourceValue)); 741 } 742 return convert(sourceValue, targetType); 743 } 744 745 Value boxIfNeeded(Value exprVal) { 746 Type source = typeElementToType(exprVal.type()); 747 return source.hasTag(NONE) ? 748 exprVal : convert(exprVal, types.boxedTypeOrType(source)); 749 } 750 751 Value unboxIfNeeded(Value exprVal) { 752 Type source = typeElementToType(exprVal.type()); 753 return source.hasTag(NONE) ? 754 exprVal : convert(exprVal, types.unboxedTypeOrType(source)); 755 } 756 757 Value convert(Value exprVal, Type target) { 758 Type source = typeElementToType(exprVal.type()); 759 boolean sourcePrimitive = source.isPrimitive(); 760 boolean targetPrimitive = target.isPrimitive(); 761 if (target.hasTag(NONE)) { 762 return exprVal; 763 } else if (sourcePrimitive == targetPrimitive) { 764 if (!sourcePrimitive || types.isSameType(source, target)) { 765 return exprVal; 766 } else { 767 // implicit primitive conversion 768 return append(JavaOp.conv(typeToTypeElement(target), exprVal)); 769 } 770 } else if (sourcePrimitive) { 771 // we need to box 772 Type unboxedTarget = types.unboxedType(target); 773 if (!unboxedTarget.hasTag(NONE)) { 774 // non-Object target 775 if (!types.isConvertible(source, unboxedTarget)) { 776 exprVal = convert(exprVal, unboxedTarget); 777 } 778 return box(exprVal, target); 779 } else { 780 // Object target 781 return box(exprVal, types.boxedClass(source).type); 782 } 783 } else { 784 // we need to unbox 785 return unbox(exprVal, source, target, types.unboxedType(source)); 786 } 787 } 788 789 Value box(Value valueExpr, Type box) { 790 // Boxing is a static method e.g., java.lang.Integer::valueOf(int)java.lang.Integer 791 MethodRef boxMethod = MethodRef.method(typeToTypeElement(box), names.valueOf.toString(), 792 CoreType.functionType(typeToTypeElement(box), typeToTypeElement(types.unboxedType(box)))); 793 return append(JavaOp.invoke(boxMethod, valueExpr)); 794 } 795 796 Value unbox(Value valueExpr, Type box, Type primitive, Type unboxedType) { 797 if (unboxedType.hasTag(NONE)) { 798 // Object target, first downcast to correct wrapper type 799 unboxedType = primitive; 800 box = types.boxedClass(unboxedType).type; 801 valueExpr = append(JavaOp.cast(typeToTypeElement(box), valueExpr)); 802 } 803 // Unboxing is a virtual method e.g., java.lang.Integer::intValue()int 804 MethodRef unboxMethod = MethodRef.method(typeToTypeElement(box), 805 unboxedType.tsym.name.append(names.Value).toString(), 806 CoreType.functionType(typeToTypeElement(unboxedType))); 807 return append(JavaOp.invoke(unboxMethod, valueExpr)); 808 } 809 810 @Override 811 public void visitVarDef(JCVariableDecl tree) { 812 JavaType javaType = typeToTypeElement(tree.type); 813 if (tree.init != null) { 814 Value initOp = toValue(tree.init, tree.type); 815 result = append(CoreOp.var(tree.name.toString(), javaType, initOp)); 816 } else { 817 // Uninitialized 818 result = append(CoreOp.var(tree.name.toString(), javaType)); 819 } 820 stack.localToOp.put(tree.sym, result); 821 } 822 823 @Override 824 public void visitAssign(JCAssign tree) { 825 // Consume top node that applies to write access 826 JCTree lhs = TreeInfo.skipParens(tree.lhs); 827 Type target = tree.lhs.type; 828 switch (lhs.getTag()) { 829 case IDENT: { 830 JCIdent assign = (JCIdent) lhs; 831 832 // Scan the rhs, the assign expression result is its input 833 result = toValue(tree.rhs, target); 834 835 Symbol sym = assign.sym; 836 switch (sym.getKind()) { 837 case LOCAL_VARIABLE, PARAMETER -> { 838 Value varOp = varOpValue(sym); 839 append(CoreOp.varStore(varOp, result)); 840 } 841 case FIELD -> { 842 FieldRef fd = symbolToFieldRef(sym, symbolSiteType(sym)); 843 if (sym.isStatic()) { 844 append(JavaOp.fieldStore(fd, result)); 845 } else { 846 append(JavaOp.fieldStore(fd, thisValue(), result)); 847 } 848 } 849 default -> { 850 // @@@ Cannot reach here? 851 throw unsupported(tree); 852 } 853 } 854 break; 855 } 856 case SELECT: { 857 JCFieldAccess assign = (JCFieldAccess) lhs; 858 859 Value receiver = toValue(assign.selected); 860 861 // Scan the rhs, the assign expression result is its input 862 result = toValue(tree.rhs, target); 863 864 Symbol sym = assign.sym; 865 FieldRef fr = symbolToFieldRef(sym, assign.selected.type); 866 if (sym.isStatic()) { 867 append(JavaOp.fieldStore(fr, result)); 868 } else { 869 append(JavaOp.fieldStore(fr, receiver, result)); 870 } 871 break; 872 } 873 case INDEXED: { 874 JCArrayAccess assign = (JCArrayAccess) lhs; 875 876 Value array = toValue(assign.indexed); 877 Value index = toValue(assign.index); 878 879 // Scan the rhs, the assign expression result is its input 880 result = toValue(tree.rhs, target); 881 882 append(JavaOp.arrayStoreOp(array, index, result)); 883 break; 884 } 885 default: 886 throw unsupported(tree); 887 } 888 } 889 890 @Override 891 public void visitAssignop(JCTree.JCAssignOp tree) { 892 // Capture applying rhs and operation 893 Function<Value, Value> scanRhs = (lhs) -> { 894 Type unboxedType = types.unboxedTypeOrType(tree.type); 895 Value rhs; 896 if (tree.operator.opcode == ByteCodes.string_add && tree.rhs.type.isPrimitive()) { 897 rhs = toValue(tree.rhs); 898 } else { 899 rhs = toValue(tree.rhs, unboxedType); 900 } 901 lhs = unboxIfNeeded(lhs); 902 903 Value assignOpResult = switch (tree.getTag()) { 904 905 // Arithmetic operations 906 case PLUS_ASG -> { 907 if (tree.operator.opcode == ByteCodes.string_add) { 908 yield append(JavaOp.concat(lhs, rhs)); 909 } else { 910 yield append(JavaOp.add(lhs, rhs)); 911 } 912 } 913 case MINUS_ASG -> append(JavaOp.sub(lhs, rhs)); 914 case MUL_ASG -> append(JavaOp.mul(lhs, rhs)); 915 case DIV_ASG -> append(JavaOp.div(lhs, rhs)); 916 case MOD_ASG -> append(JavaOp.mod(lhs, rhs)); 917 918 // Bitwise operations (including their boolean variants) 919 case BITOR_ASG -> append(JavaOp.or(lhs, rhs)); 920 case BITAND_ASG -> append(JavaOp.and(lhs, rhs)); 921 case BITXOR_ASG -> append(JavaOp.xor(lhs, rhs)); 922 923 // Shift operations 924 case SL_ASG -> append(JavaOp.lshl(lhs, rhs)); 925 case SR_ASG -> append(JavaOp.ashr(lhs, rhs)); 926 case USR_ASG -> append(JavaOp.lshr(lhs, rhs)); 927 928 929 default -> throw unsupported(tree); 930 }; 931 return result = convert(assignOpResult, tree.type); 932 }; 933 934 applyCompoundAssign(tree.lhs, scanRhs); 935 } 936 937 void applyCompoundAssign(JCTree.JCExpression lhs, Function<Value, Value> scanRhs) { 938 // Consume top node that applies to access 939 lhs = TreeInfo.skipParens(lhs); 940 switch (lhs.getTag()) { 941 case IDENT -> { 942 JCIdent assign = (JCIdent) lhs; 943 944 Symbol sym = assign.sym; 945 switch (sym.getKind()) { 946 case LOCAL_VARIABLE, PARAMETER -> { 947 Value varOp = varOpValue(sym); 948 949 Op.Result lhsOpValue = append(CoreOp.varLoad(varOp)); 950 // Scan the rhs 951 Value r = scanRhs.apply(lhsOpValue); 952 953 append(CoreOp.varStore(varOp, r)); 954 } 955 case FIELD -> { 956 FieldRef fr = symbolToFieldRef(sym, symbolSiteType(sym)); 957 958 Op.Result lhsOpValue; 959 TypeElement resultType = typeToTypeElement(sym.type); 960 if (sym.isStatic()) { 961 lhsOpValue = append(JavaOp.fieldLoad(resultType, fr)); 962 } else { 963 lhsOpValue = append(JavaOp.fieldLoad(resultType, fr, thisValue())); 964 } 965 // Scan the rhs 966 Value r = scanRhs.apply(lhsOpValue); 967 968 if (sym.isStatic()) { 969 append(JavaOp.fieldStore(fr, r)); 970 } else { 971 append(JavaOp.fieldStore(fr, thisValue(), r)); 972 } 973 } 974 default -> { 975 // @@@ Cannot reach here? 976 throw unsupported(lhs); 977 } 978 } 979 } 980 case SELECT -> { 981 JCFieldAccess assign = (JCFieldAccess) lhs; 982 983 Value receiver = toValue(assign.selected); 984 985 Symbol sym = assign.sym; 986 FieldRef fr = symbolToFieldRef(sym, assign.selected.type); 987 988 Op.Result lhsOpValue; 989 TypeElement resultType = typeToTypeElement(sym.type); 990 if (sym.isStatic()) { 991 lhsOpValue = append(JavaOp.fieldLoad(resultType, fr)); 992 } else { 993 lhsOpValue = append(JavaOp.fieldLoad(resultType, fr, receiver)); 994 } 995 // Scan the rhs 996 Value r = scanRhs.apply(lhsOpValue); 997 998 if (sym.isStatic()) { 999 append(JavaOp.fieldStore(fr, r)); 1000 } else { 1001 append(JavaOp.fieldStore(fr, receiver, r)); 1002 } 1003 } 1004 case INDEXED -> { 1005 JCArrayAccess assign = (JCArrayAccess) lhs; 1006 1007 Value array = toValue(assign.indexed); 1008 Value index = toValue(assign.index); 1009 1010 Op.Result lhsOpValue = append(JavaOp.arrayLoadOp(array, index)); 1011 // Scan the rhs 1012 Value r = scanRhs.apply(lhsOpValue); 1013 1014 append(JavaOp.arrayStoreOp(array, index, r)); 1015 } 1016 default -> throw unsupported(lhs); 1017 } 1018 } 1019 1020 @Override 1021 public void visitIdent(JCIdent tree) { 1022 // Visited only for read access 1023 1024 Symbol sym = tree.sym; 1025 switch (sym.getKind()) { 1026 case LOCAL_VARIABLE, RESOURCE_VARIABLE, BINDING_VARIABLE, PARAMETER, EXCEPTION_PARAMETER -> 1027 result = loadVar(sym); 1028 case FIELD, ENUM_CONSTANT -> { 1029 if (sym.name.equals(names._this) || sym.name.equals(names._super)) { 1030 result = thisValue(); 1031 } else if (top.localToOp.containsKey(sym)) { 1032 // if field symbol is a key in top.localToOp 1033 // we expect that we're producing the model of a lambda 1034 // we also expect that the field is a constant capture and sym was mapped to VarOp result 1035 Assert.check(isQuoted); 1036 Assert.check(sym.isStatic()); 1037 Assert.check(sym.isFinal()); 1038 result = loadVar(sym); 1039 } else { 1040 FieldRef fr = symbolToFieldRef(sym, symbolSiteType(sym)); 1041 TypeElement resultType = typeToTypeElement(sym.type); 1042 if (sym.isStatic()) { 1043 result = append(JavaOp.fieldLoad(resultType, fr)); 1044 } else { 1045 result = append(JavaOp.fieldLoad(resultType, fr, thisValue())); 1046 } 1047 } 1048 } 1049 case INTERFACE, CLASS, ENUM -> { 1050 result = null; 1051 } 1052 default -> { 1053 // @@@ Cannot reach here? 1054 throw unsupported(tree); 1055 } 1056 } 1057 } 1058 1059 private Value loadVar(Symbol sym) { 1060 Value varOp = varOpValue(sym); 1061 Assert.check(varOp.type() instanceof VarType); 1062 return append(CoreOp.varLoad(varOp)); 1063 } 1064 1065 @Override 1066 public void visitTypeIdent(JCTree.JCPrimitiveTypeTree tree) { 1067 result = null; 1068 } 1069 1070 @Override 1071 public void visitTypeArray(JCTree.JCArrayTypeTree tree) { 1072 result = null; // MyType[].class is handled in visitSelect just as MyType.class 1073 } 1074 1075 @Override 1076 public void visitSelect(JCFieldAccess tree) { 1077 // Visited only for read access 1078 1079 Type qualifierTarget = qualifierTarget(tree); 1080 // @@@: might cause redundant load if accessed symbol is static but the qualifier is not a type 1081 Value receiver = toValue(tree.selected); 1082 1083 if (tree.name.equals(names._class)) { 1084 result = append(CoreOp.constant(JavaType.J_L_CLASS, typeToTypeElement(tree.selected.type))); 1085 } else if (types.isArray(tree.selected.type)) { 1086 if (tree.sym.equals(syms.lengthVar)) { 1087 result = append(JavaOp.arrayLength(receiver)); 1088 } else { 1089 // Should not reach here 1090 throw unsupported(tree); 1091 } 1092 } else { 1093 Symbol sym = tree.sym; 1094 switch (sym.getKind()) { 1095 case FIELD, ENUM_CONSTANT -> { 1096 if (sym.name.equals(names._this) || sym.name.equals(names._super)) { 1097 result = thisValue(); 1098 } else { 1099 FieldRef fr = symbolToFieldRef(sym, qualifierTarget.hasTag(NONE) ? 1100 tree.selected.type : qualifierTarget); 1101 TypeElement resultType = typeToTypeElement(types.memberType(tree.selected.type, sym)); 1102 if (sym.isStatic()) { 1103 result = append(JavaOp.fieldLoad(resultType, fr)); 1104 } else { 1105 result = append(JavaOp.fieldLoad(resultType, fr, receiver)); 1106 } 1107 } 1108 } 1109 case INTERFACE, CLASS, ENUM -> { 1110 result = null; 1111 } 1112 default -> { 1113 // @@@ Cannot reach here? 1114 throw unsupported(tree); 1115 } 1116 } 1117 } 1118 } 1119 1120 @Override 1121 public void visitIndexed(JCArrayAccess tree) { 1122 // Visited only for read access 1123 1124 Value array = toValue(tree.indexed); 1125 1126 Value index = toValue(tree.index, typeElementToType(JavaType.INT)); 1127 1128 result = append(JavaOp.arrayLoadOp(array, index)); 1129 } 1130 1131 @Override 1132 public void visitApply(JCTree.JCMethodInvocation tree) { 1133 // @@@ Symbol.externalType, for use with inner classes 1134 1135 // @@@ this.xyz(...) calls in a constructor 1136 1137 JCTree meth = TreeInfo.skipParens(tree.meth); 1138 switch (meth.getTag()) { 1139 case IDENT: { 1140 JCIdent access = (JCIdent) meth; 1141 1142 Symbol sym = access.sym; 1143 List<Value> args = new ArrayList<>(); 1144 JavaOp.InvokeOp.InvokeKind ik; 1145 if (!sym.isStatic()) { 1146 ik = JavaOp.InvokeOp.InvokeKind.INSTANCE; 1147 args.add(thisValue()); 1148 } else { 1149 ik = JavaOp.InvokeOp.InvokeKind.STATIC; 1150 } 1151 1152 args.addAll(scanMethodArguments(tree.args, tree.meth.type, tree.varargsElement)); 1153 1154 MethodRef mr = symbolToMethodRef(sym, symbolSiteType(sym)); 1155 Value res = append(JavaOp.invoke(ik, tree.varargsElement != null, 1156 typeToTypeElement(meth.type.getReturnType()), mr, args)); 1157 if (sym.type.getReturnType().getTag() != TypeTag.VOID) { 1158 result = res; 1159 } 1160 break; 1161 } 1162 case SELECT: { 1163 JCFieldAccess access = (JCFieldAccess) meth; 1164 1165 Type qualifierTarget = qualifierTarget(access); 1166 Value receiver = toValue(access.selected, qualifierTarget); 1167 1168 Symbol sym = access.sym; 1169 List<Value> args = new ArrayList<>(); 1170 JavaOp.InvokeOp.InvokeKind ik; 1171 if (!sym.isStatic()) { 1172 args.add(receiver); 1173 // @@@ expr.super(...) for inner class super constructor calls 1174 ik = switch (access.selected) { 1175 case JCIdent i when i.sym.name.equals(names._super) -> JavaOp.InvokeOp.InvokeKind.SUPER; 1176 case JCFieldAccess fa when fa.sym.name.equals(names._super) -> JavaOp.InvokeOp.InvokeKind.SUPER; 1177 default -> JavaOp.InvokeOp.InvokeKind.INSTANCE; 1178 }; 1179 } else { 1180 ik = JavaOp.InvokeOp.InvokeKind.STATIC; 1181 } 1182 1183 args.addAll(scanMethodArguments(tree.args, tree.meth.type, tree.varargsElement)); 1184 1185 MethodRef mr = symbolToMethodRef(sym, qualifierTarget.hasTag(NONE) ? 1186 access.selected.type : qualifierTarget); 1187 JavaType returnType = typeToTypeElement(meth.type.getReturnType()); 1188 JavaOp.InvokeOp iop = JavaOp.invoke(ik, tree.varargsElement != null, 1189 returnType, mr, args); 1190 Value res = append(iop); 1191 if (sym.type.getReturnType().getTag() != TypeTag.VOID) { 1192 result = res; 1193 } 1194 break; 1195 } 1196 default: 1197 unsupported(meth); 1198 } 1199 } 1200 1201 List<Value> scanMethodArguments(List<JCExpression> args, Type methodType, Type varargsElement) { 1202 ListBuffer<Value> argValues = new ListBuffer<>(); 1203 com.sun.tools.javac.util.List<Type> targetTypes = methodType.getParameterTypes(); 1204 if (varargsElement != null) { 1205 targetTypes = targetTypes.reverse().tail; 1206 for (int i = 0 ; i < args.size() - (methodType.getParameterTypes().size() - 1) ; i++) { 1207 targetTypes = targetTypes.prepend(varargsElement); 1208 } 1209 targetTypes = targetTypes.reverse(); 1210 } 1211 1212 for (JCTree.JCExpression arg : args) { 1213 argValues.add(toValue(arg, targetTypes.head)); 1214 targetTypes = targetTypes.tail; 1215 } 1216 return argValues.toList(); 1217 } 1218 1219 @Override 1220 public void visitReference(JCTree.JCMemberReference tree) { 1221 MemberReferenceToLambda memberReferenceToLambda = new MemberReferenceToLambda(tree, currentClassSym); 1222 JCVariableDecl recv = memberReferenceToLambda.receiverVar(); 1223 if (recv != null) { 1224 scan(recv); 1225 } 1226 scan(memberReferenceToLambda.lambda()); 1227 } 1228 1229 Type qualifierTarget(JCFieldAccess tree) { 1230 Type selectedType = types.skipTypeVars(tree.selected.type, true); 1231 return selectedType.isCompound() ? 1232 tree.sym.owner.type : 1233 Type.noType; 1234 } 1235 1236 @Override 1237 public void visitTypeCast(JCTree.JCTypeCast tree) { 1238 Value v = toValue(tree.expr); 1239 1240 Type expressionType = tree.expr.type; 1241 Type type = tree.type; 1242 if (expressionType.isPrimitive() && type.isPrimitive()) { 1243 if (expressionType.equals(type)) { 1244 // Redundant cast 1245 result = v; 1246 } else { 1247 result = append(JavaOp.conv(typeToTypeElement(type), v)); 1248 } 1249 } else if (expressionType.isPrimitive() || type.isPrimitive()) { 1250 result = convert(v, tree.type); 1251 } else if (!expressionType.hasTag(BOT) && 1252 types.isAssignable(expressionType, type)) { 1253 // Redundant cast 1254 result = v; 1255 } else { 1256 // Reference cast 1257 JavaType jt = typeToTypeElement(types.erasure(type)); 1258 result = append(JavaOp.cast(typeToTypeElement(type), jt, v)); 1259 } 1260 } 1261 1262 @Override 1263 public void visitTypeTest(JCTree.JCInstanceOf tree) { 1264 Value target = toValue(tree.expr); 1265 1266 if (tree.pattern.getTag() != Tag.IDENT) { 1267 result = scanPattern(tree.getPattern(), target); 1268 } else { 1269 result = append(JavaOp.instanceOf(typeToTypeElement(tree.pattern.type), target)); 1270 } 1271 } 1272 1273 Value scanPattern(JCTree.JCPattern pattern, Value target) { 1274 // Type of pattern 1275 JavaType patternType; 1276 if (pattern instanceof JCTree.JCBindingPattern p) { 1277 patternType = JavaOp.Pattern.bindingType(typeToTypeElement(p.type)); 1278 } else if (pattern instanceof JCTree.JCRecordPattern p) { 1279 patternType = JavaOp.Pattern.recordType(typeToTypeElement(p.record.type)); 1280 } else { 1281 throw unsupported(pattern); 1282 } 1283 1284 // Push pattern body 1285 pushBody(pattern, CoreType.functionType(patternType)); 1286 1287 // @@@ Assumes just pattern nodes, likely will change when method patterns are supported 1288 // that have expressions for any arguments (which perhaps in turn may have pattern expressions) 1289 List<JCVariableDecl> variables = new ArrayList<>(); 1290 class PatternScanner extends FilterScanner { 1291 1292 private Value result; 1293 1294 public PatternScanner() { 1295 super(Set.of(Tag.BINDINGPATTERN, Tag.RECORDPATTERN, Tag.ANYPATTERN)); 1296 } 1297 1298 @Override 1299 public void visitBindingPattern(JCTree.JCBindingPattern binding) { 1300 JCVariableDecl var = binding.var; 1301 variables.add(var); 1302 boolean unnamedPatternVariable = var.name.isEmpty(); 1303 String bindingName = unnamedPatternVariable ? null : var.name.toString(); 1304 result = append(JavaOp.typePattern(typeToTypeElement(var.type), bindingName)); 1305 } 1306 1307 @Override 1308 public void visitRecordPattern(JCTree.JCRecordPattern record) { 1309 // @@@ Is always Identifier to record? 1310 // scan(record.deconstructor); 1311 1312 List<Value> nestedValues = new ArrayList<>(); 1313 for (JCTree.JCPattern jcPattern : record.nested) { 1314 // @@@ when we support ANYPATTERN, we must add result of toValue only if it's non-null 1315 // because passing null to recordPattern methods will cause an error 1316 nestedValues.add(toValue(jcPattern)); 1317 } 1318 1319 result = append(JavaOp.recordPattern(symbolToRecordTypeRef(record.record), nestedValues)); 1320 } 1321 1322 @Override 1323 public void visitAnyPattern(JCTree.JCAnyPattern anyPattern) { 1324 result = append(JavaOp.matchAllPattern()); 1325 } 1326 1327 Value toValue(JCTree tree) { 1328 result = null; 1329 scan(tree); 1330 return result; 1331 } 1332 } 1333 // Scan pattern 1334 Value patternValue = new PatternScanner().toValue(pattern); 1335 append(CoreOp.core_yield(patternValue)); 1336 Body.Builder patternBody = stack.body; 1337 1338 // Pop body 1339 popBody(); 1340 1341 // Find nearest ancestor body stack element associated with a statement tree 1342 // @@@ Strengthen check of tree? 1343 BodyStack _variablesStack = stack; 1344 while (!(_variablesStack.tree instanceof JCTree.JCStatement)) { 1345 _variablesStack = _variablesStack.parent; 1346 } 1347 BodyStack variablesStack = _variablesStack; 1348 1349 // Create pattern var ops for pattern variables using the 1350 // builder associated with the nearest statement tree 1351 for (JCVariableDecl jcVar : variables) { 1352 // @@@ use uninitialized variable 1353 Value defaultValue = variablesStack.block.op(defaultValue(jcVar.type)); 1354 Value init = convert(defaultValue, jcVar.type); 1355 Op.Result op = variablesStack.block.op(CoreOp.var(jcVar.name.toString(), typeToTypeElement(jcVar.type), init)); 1356 variablesStack.localToOp.put(jcVar.sym, op); 1357 } 1358 1359 // Create pattern descriptor 1360 List<JavaType> patternDescParams = variables.stream().map(var -> typeToTypeElement(var.type)).toList(); 1361 FunctionType matchFuncType = CoreType.functionType(JavaType.VOID, patternDescParams); 1362 1363 // Create the match body, assigning pattern values to pattern variables 1364 Body.Builder matchBody = Body.Builder.of(patternBody.ancestorBody(), matchFuncType); 1365 Block.Builder matchBuilder = matchBody.entryBlock(); 1366 for (int i = 0; i < variables.size(); i++) { 1367 Value v = matchBuilder.parameters().get(i); 1368 Value var = variablesStack.localToOp.get(variables.get(i).sym); 1369 matchBuilder.op(CoreOp.varStore(var, v)); 1370 } 1371 matchBuilder.op(CoreOp.core_yield()); 1372 1373 // Create the match operation 1374 return append(JavaOp.match(target, patternBody, matchBody)); 1375 } 1376 1377 @Override 1378 public void visitNewClass(JCTree.JCNewClass tree) { 1379 if (tree.def != null) { 1380 scan(tree.def); 1381 } 1382 1383 // @@@ Support local classes in pre-construction contexts 1384 if (tree.type.tsym.isDirectlyOrIndirectlyLocal() && (tree.type.tsym.flags() & NOOUTERTHIS) != 0) { 1385 throw unsupported(tree); 1386 } 1387 1388 List<TypeElement> argtypes = new ArrayList<>(); 1389 Type type = tree.type; 1390 Type outer = type.getEnclosingType(); 1391 List<Value> args = new ArrayList<>(); 1392 if (!outer.hasTag(TypeTag.NONE)) { 1393 // Obtain outer value for inner class, and add as first argument 1394 JCTree.JCExpression encl = tree.encl; 1395 Value outerInstance; 1396 if (encl == null) { 1397 outerInstance = thisValue(); 1398 } else { 1399 outerInstance = toValue(tree.encl); 1400 } 1401 args.add(outerInstance); 1402 argtypes.add(outerInstance.type()); 1403 } 1404 if (tree.type.tsym.isDirectlyOrIndirectlyLocal()) { 1405 for (Symbol c : localCaptures.get(tree.type.tsym)) { 1406 args.add(loadVar(c)); 1407 argtypes.add(symbolToErasedDesc(c)); 1408 } 1409 } 1410 1411 // Create erased method type reference for constructor, where 1412 // the return type declares the class to instantiate 1413 // We need to manually construct the constructor reference, 1414 // as the signature of the constructor symbol is not augmented 1415 // with enclosing this and captured params. 1416 MethodRef methodRef = symbolToMethodRef(tree.constructor); 1417 argtypes.addAll(methodRef.type().parameterTypes()); 1418 FunctionType constructorType = CoreType.functionType( 1419 symbolToErasedDesc(tree.constructor.owner), 1420 argtypes); 1421 ConstructorRef constructorRef = ConstructorRef.constructor(constructorType); 1422 1423 args.addAll(scanMethodArguments(tree.args, tree.constructorType, tree.varargsElement)); 1424 1425 result = append(JavaOp.new_(tree.varargsElement != null, typeToTypeElement(type), constructorRef, args)); 1426 } 1427 1428 @Override 1429 public void visitNewArray(JCTree.JCNewArray tree) { 1430 if (tree.elems != null) { 1431 int length = tree.elems.size(); 1432 Op.Result a = append(JavaOp.newArray( 1433 typeToTypeElement(tree.type), 1434 append(CoreOp.constant(JavaType.INT, length)))); 1435 int i = 0; 1436 for (JCExpression elem : tree.elems) { 1437 Value element = toValue(elem, types.elemtype(tree.type)); 1438 append(JavaOp.arrayStoreOp( 1439 a, 1440 append(CoreOp.constant(JavaType.INT, i)), 1441 element)); 1442 i++; 1443 } 1444 1445 result = a; 1446 } else { 1447 List<Value> indexes = new ArrayList<>(); 1448 for (JCTree.JCExpression dim : tree.dims) { 1449 indexes.add(toValue(dim)); 1450 } 1451 1452 JavaType arrayType = typeToTypeElement(tree.type); 1453 ConstructorRef constructorRef = ConstructorRef.constructor(arrayType, 1454 indexes.stream().map(Value::type).toList()); 1455 result = append(JavaOp.new_(constructorRef, indexes)); 1456 } 1457 } 1458 1459 @Override 1460 public void visitLambda(JCTree.JCLambda tree) { 1461 FunctionalExpressionKind kind = functionalKind(tree); 1462 final FunctionType lambdaType = switch (kind) { 1463 case QUOTED_STRUCTURAL -> typeToFunctionType(tree.target); 1464 default -> typeToFunctionType(types.findDescriptorType(tree.target)); 1465 }; 1466 1467 // Push quoted body 1468 // We can either be explicitly quoted or a structural quoted expression 1469 // within some larger reflected code 1470 1471 // a lambda targeted to Quoted is always going to have its model wrapped in QuotedOp, regardless of whether 1472 // we are producing the model of the method that contain it or we are producing the model of the lambda itself 1473 // on the other hand, a lambda targeted to a subtype of Quotable is going to have its model wrapped in QuotedOp 1474 // only when we are producing the model of the lambda, thus the condition (isQuoted ...) 1475 // also, a lambda contained in a quotable lambda, will not have its model wrapped in QuotedOp, 1476 // thus the condition (... body == tree) 1477 // @@@ better name for isQuoted ? 1478 boolean toQuote = (isQuoted && body == tree) || kind == FunctionalExpressionKind.QUOTED_STRUCTURAL; 1479 if (toQuote) { 1480 pushBody(tree.body, CoreType.FUNCTION_TYPE_VOID); 1481 } 1482 1483 // Push lambda body 1484 pushBody(tree.body, lambdaType); 1485 1486 // Map lambda parameters to varOp values 1487 for (int i = 0; i < tree.params.size(); i++) { 1488 JCVariableDecl p = tree.params.get(i); 1489 Op.Result paramOp = append(CoreOp.var( 1490 p.name.toString(), 1491 stack.block.parameters().get(i))); 1492 stack.localToOp.put(p.sym, paramOp); 1493 } 1494 1495 // Scan the lambda body 1496 Type lambdaReturnType = tree.getDescriptorType(types).getReturnType(); 1497 if (tree.getBodyKind() == LambdaExpressionTree.BodyKind.EXPRESSION) { 1498 Value exprVal = toValue(((JCExpression) tree.body), lambdaReturnType); 1499 if (!lambdaReturnType.hasTag(TypeTag.VOID)) { 1500 append(CoreOp.return_(exprVal)); 1501 } else { 1502 appendTerminating(CoreOp::return_); 1503 } 1504 } else { 1505 Type prevBodyTarget = bodyTarget; 1506 try { 1507 bodyTarget = lambdaReturnType; 1508 toValue(((JCTree.JCStatement) tree.body)); 1509 appendReturnOrUnreachable(tree.body); 1510 } finally { 1511 bodyTarget = prevBodyTarget; 1512 } 1513 } 1514 1515 Op lambdaOp = switch (kind) { 1516 case QUOTED_STRUCTURAL -> { 1517 yield CoreOp.closure(stack.body); 1518 } 1519 case QUOTABLE, NOT_QUOTED -> { 1520 // Get the functional interface type 1521 JavaType fiType = typeToTypeElement(tree.target); 1522 // build functional lambda 1523 yield JavaOp.lambda(fiType, stack.body); 1524 } 1525 }; 1526 1527 // Pop lambda body 1528 popBody(); 1529 1530 Value lambdaResult; 1531 if (toQuote) { 1532 lambdaResult = append(lambdaOp, generateLocation(tree, true)); 1533 } else { 1534 lambdaResult = append(lambdaOp); 1535 } 1536 1537 if (toQuote) { 1538 append(CoreOp.core_yield(lambdaResult)); 1539 CoreOp.QuotedOp quotedOp = CoreOp.quoted(stack.body); 1540 1541 // Pop quoted body 1542 popBody(); 1543 1544 lambdaResult = append(quotedOp); 1545 } 1546 1547 result = lambdaResult; 1548 } 1549 1550 @Override 1551 public void visitIf(JCTree.JCIf tree) { 1552 List<Body.Builder> bodies = new ArrayList<>(); 1553 1554 while (tree != null) { 1555 JCTree.JCExpression cond = TreeInfo.skipParens(tree.cond); 1556 1557 // Push if condition 1558 pushBody(cond, 1559 CoreType.functionType(JavaType.BOOLEAN)); 1560 Value last = toValue(cond); 1561 last = convert(last, typeElementToType(JavaType.BOOLEAN)); 1562 // Yield the boolean result of the condition 1563 append(CoreOp.core_yield(last)); 1564 bodies.add(stack.body); 1565 1566 // Pop if condition 1567 popBody(); 1568 1569 // Push if body 1570 pushBody(tree.thenpart, CoreType.FUNCTION_TYPE_VOID); 1571 1572 scan(tree.thenpart); 1573 appendTerminating(CoreOp::core_yield); 1574 bodies.add(stack.body); 1575 1576 // Pop if body 1577 popBody(); 1578 1579 JCTree.JCStatement elsepart = tree.elsepart; 1580 if (elsepart == null) { 1581 tree = null; 1582 } else if (elsepart.getTag() == Tag.IF) { 1583 tree = (JCTree.JCIf) elsepart; 1584 } else { 1585 // Push else body 1586 pushBody(elsepart, CoreType.FUNCTION_TYPE_VOID); 1587 1588 scan(elsepart); 1589 appendTerminating(CoreOp::core_yield); 1590 bodies.add(stack.body); 1591 1592 // Pop else body 1593 popBody(); 1594 1595 tree = null; 1596 } 1597 } 1598 1599 append(JavaOp.if_(bodies)); 1600 result = null; 1601 } 1602 1603 @Override 1604 public void visitSwitchExpression(JCTree.JCSwitchExpression tree) { 1605 Value target = toValue(tree.selector); 1606 1607 Type switchType = adaptBottom(tree.type); 1608 FunctionType caseBodyType = CoreType.functionType(typeToTypeElement(switchType)); 1609 1610 List<Body.Builder> bodies = visitSwitchStatAndExpr(tree, tree.selector, target, tree.cases, caseBodyType, 1611 !tree.hasUnconditionalPattern); 1612 1613 result = append(JavaOp.switchExpression(caseBodyType.returnType(), target, bodies)); 1614 } 1615 1616 @Override 1617 public void visitSwitch(JCTree.JCSwitch tree) { 1618 Value target = toValue(tree.selector); 1619 1620 FunctionType actionType = CoreType.FUNCTION_TYPE_VOID; 1621 1622 List<Body.Builder> bodies = visitSwitchStatAndExpr(tree, tree.selector, target, tree.cases, actionType, 1623 tree.patternSwitch && !tree.hasUnconditionalPattern); 1624 1625 result = append(JavaOp.switchStatement(target, bodies)); 1626 } 1627 1628 private List<Body.Builder> visitSwitchStatAndExpr(JCTree tree, JCExpression selector, Value target, 1629 List<JCTree.JCCase> cases, FunctionType caseBodyType, 1630 boolean isDefaultCaseNeeded) { 1631 List<Body.Builder> bodies = new ArrayList<>(); 1632 Body.Builder defaultLabel = null; 1633 Body.Builder defaultBody = null; 1634 1635 for (JCTree.JCCase c : cases) { 1636 Body.Builder caseLabel = visitCaseLabel(tree, selector, target, c); 1637 Body.Builder caseBody = visitCaseBody(tree, c, caseBodyType); 1638 1639 if (c.labels.head instanceof JCTree.JCDefaultCaseLabel) { 1640 defaultLabel = caseLabel; 1641 defaultBody = caseBody; 1642 } else { 1643 bodies.add(caseLabel); 1644 bodies.add(caseBody); 1645 } 1646 } 1647 1648 if (defaultLabel != null) { 1649 bodies.add(defaultLabel); 1650 bodies.add(defaultBody); 1651 } else if (isDefaultCaseNeeded) { 1652 // label 1653 pushBody(tree, CoreType.functionType(JavaType.BOOLEAN)); 1654 append(CoreOp.core_yield(append(CoreOp.constant(JavaType.BOOLEAN, true)))); 1655 bodies.add(stack.body); 1656 popBody(); 1657 1658 // body 1659 pushBody(tree, caseBodyType); 1660 append(JavaOp.throw_( 1661 append(JavaOp.new_(ConstructorRef.constructor(MatchException.class))) 1662 )); 1663 bodies.add(stack.body); 1664 popBody(); 1665 } 1666 1667 return bodies; 1668 } 1669 1670 private Body.Builder visitCaseLabel(JCTree tree, JCExpression selector, Value target, JCTree.JCCase c) { 1671 Body.Builder body; 1672 FunctionType caseLabelType = CoreType.functionType(JavaType.BOOLEAN, target.type()); 1673 1674 JCTree.JCCaseLabel headCl = c.labels.head; 1675 if (headCl instanceof JCTree.JCPatternCaseLabel pcl) { 1676 if (c.labels.size() > 1) { 1677 throw unsupported(c); 1678 } 1679 1680 pushBody(pcl, caseLabelType); 1681 1682 Value localTarget = stack.block.parameters().get(0); 1683 final Value localResult; 1684 if (c.guard != null) { 1685 List<Body.Builder> clBodies = new ArrayList<>(); 1686 1687 pushBody(pcl.pat, CoreType.functionType(JavaType.BOOLEAN)); 1688 Value patVal = scanPattern(pcl.pat, localTarget); 1689 append(CoreOp.core_yield(patVal)); 1690 clBodies.add(stack.body); 1691 popBody(); 1692 1693 pushBody(c.guard, CoreType.functionType(JavaType.BOOLEAN)); 1694 append(CoreOp.core_yield(toValue(c.guard))); 1695 clBodies.add(stack.body); 1696 popBody(); 1697 1698 localResult = append(JavaOp.conditionalAnd(clBodies)); 1699 } else { 1700 localResult = scanPattern(pcl.pat, localTarget); 1701 } 1702 // Yield the boolean result of the condition 1703 append(CoreOp.core_yield(localResult)); 1704 body = stack.body; 1705 1706 // Pop label 1707 popBody(); 1708 } else if (headCl instanceof JCTree.JCConstantCaseLabel ccl) { 1709 pushBody(headCl, caseLabelType); 1710 1711 Value localTarget = stack.block.parameters().get(0); 1712 final Value localResult; 1713 if (c.labels.size() == 1) { 1714 Value expr = toValue(ccl.expr); 1715 // per java spec, constant type is compatible with the type of the selector expression 1716 // so, we convert constant to the type of the selector expression 1717 expr = convert(expr, selector.type); 1718 if (selector.type.isPrimitive()) { 1719 localResult = append(JavaOp.eq(localTarget, expr)); 1720 } else { 1721 localResult = append(JavaOp.invoke( 1722 MethodRef.method(Objects.class, "equals", boolean.class, Object.class, Object.class), 1723 localTarget, expr)); 1724 } 1725 } else { 1726 List<Body.Builder> clBodies = new ArrayList<>(); 1727 for (JCTree.JCCaseLabel cl : c.labels) { 1728 ccl = (JCTree.JCConstantCaseLabel) cl; 1729 pushBody(ccl, CoreType.functionType(JavaType.BOOLEAN)); 1730 1731 Value expr = toValue(ccl.expr); 1732 expr = convert(expr, selector.type); 1733 final Value labelResult; 1734 if (selector.type.isPrimitive()) { 1735 labelResult = append(JavaOp.eq(localTarget, expr)); 1736 } else { 1737 labelResult = append(JavaOp.invoke( 1738 MethodRef.method(Objects.class, "equals", boolean.class, Object.class, Object.class), 1739 localTarget, expr)); 1740 } 1741 1742 append(CoreOp.core_yield(labelResult)); 1743 clBodies.add(stack.body); 1744 1745 // Pop label 1746 popBody(); 1747 } 1748 1749 localResult = append(JavaOp.conditionalOr(clBodies)); 1750 } 1751 1752 append(CoreOp.core_yield(localResult)); 1753 body = stack.body; 1754 1755 // Pop labels 1756 popBody(); 1757 } else if (headCl instanceof JCTree.JCDefaultCaseLabel) { 1758 // @@@ Do we need to model the default label body? 1759 pushBody(headCl, CoreType.functionType(JavaType.BOOLEAN)); 1760 1761 append(CoreOp.core_yield(append(CoreOp.constant(JavaType.BOOLEAN, true)))); 1762 body = stack.body; 1763 1764 // Pop label 1765 popBody(); 1766 } else { 1767 throw unsupported(tree); 1768 } 1769 1770 return body; 1771 } 1772 1773 private Body.Builder visitCaseBody(JCTree tree, JCTree.JCCase c, FunctionType caseBodyType) { 1774 Body.Builder body = null; 1775 Type yieldType = tree.type != null ? adaptBottom(tree.type) : Type.noType; 1776 1777 JCTree.JCCaseLabel headCl = c.labels.head; 1778 switch (c.caseKind) { 1779 case RULE -> { 1780 pushBody(c.body, caseBodyType); 1781 1782 if (c.body instanceof JCTree.JCExpression e) { 1783 Value bodyVal = toValue(e, yieldType); 1784 append(CoreOp.core_yield(bodyVal)); 1785 } else if (c.body instanceof JCTree.JCStatement s){ // this includes Block 1786 // Otherwise there is a yield statement 1787 Type prevBodyTarget = bodyTarget; 1788 try { 1789 bodyTarget = yieldType; 1790 toValue(s); 1791 } finally { 1792 bodyTarget = prevBodyTarget; 1793 } 1794 appendTerminating(c.completesNormally ? CoreOp::core_yield : CoreOp::unreachable); 1795 } 1796 body = stack.body; 1797 1798 // Pop block 1799 popBody(); 1800 } 1801 case STATEMENT -> { 1802 // @@@ Avoid nesting for a single block? Goes against "say what you see" 1803 // boolean oneBlock = c.stats.size() == 1 && c.stats.head instanceof JCBlock; 1804 pushBody(c, caseBodyType); 1805 1806 scan(c.stats); 1807 1808 appendTerminating(c.completesNormally ? 1809 headCl instanceof JCTree.JCDefaultCaseLabel ? CoreOp::core_yield : JavaOp::switchFallthroughOp 1810 : CoreOp::unreachable); 1811 1812 body = stack.body; 1813 1814 // Pop block 1815 popBody(); 1816 } 1817 } 1818 return body; 1819 } 1820 1821 @Override 1822 public void visitYield(JCTree.JCYield tree) { 1823 Value retVal = toValue(tree.value, bodyTarget); 1824 if (retVal == null) { 1825 result = append(JavaOp.java_yield()); 1826 } else { 1827 result = append(JavaOp.java_yield(retVal)); 1828 } 1829 } 1830 1831 @Override 1832 public void visitWhileLoop(JCTree.JCWhileLoop tree) { 1833 // @@@ Patterns 1834 JCTree.JCExpression cond = TreeInfo.skipParens(tree.cond); 1835 1836 // Push while condition 1837 pushBody(cond, CoreType.functionType(JavaType.BOOLEAN)); 1838 Value last = toValue(cond); 1839 // Yield the boolean result of the condition 1840 last = convert(last, typeElementToType(JavaType.BOOLEAN)); 1841 append(CoreOp.core_yield(last)); 1842 Body.Builder condition = stack.body; 1843 1844 // Pop while condition 1845 popBody(); 1846 1847 // Push while body 1848 pushBody(tree.body, CoreType.FUNCTION_TYPE_VOID); 1849 scan(tree.body); 1850 appendTerminating(JavaOp::continue_); 1851 Body.Builder body = stack.body; 1852 1853 // Pop while body 1854 popBody(); 1855 1856 append(JavaOp.while_(condition, body)); 1857 result = null; 1858 } 1859 1860 @Override 1861 public void visitDoLoop(JCTree.JCDoWhileLoop tree) { 1862 // @@@ Patterns 1863 JCTree.JCExpression cond = TreeInfo.skipParens(tree.cond); 1864 1865 // Push while body 1866 pushBody(tree.body, CoreType.FUNCTION_TYPE_VOID); 1867 scan(tree.body); 1868 appendTerminating(JavaOp::continue_); 1869 Body.Builder body = stack.body; 1870 1871 // Pop while body 1872 popBody(); 1873 1874 // Push while condition 1875 pushBody(cond, CoreType.functionType(JavaType.BOOLEAN)); 1876 Value last = toValue(cond); 1877 last = convert(last, typeElementToType(JavaType.BOOLEAN)); 1878 // Yield the boolean result of the condition 1879 append(CoreOp.core_yield(last)); 1880 Body.Builder condition = stack.body; 1881 1882 // Pop while condition 1883 popBody(); 1884 1885 append(JavaOp.doWhile(body, condition)); 1886 result = null; 1887 } 1888 1889 @Override 1890 public void visitForeachLoop(JCTree.JCEnhancedForLoop tree) { 1891 // Push expression 1892 pushBody(tree.expr, CoreType.functionType(typeToTypeElement(tree.expr.type))); 1893 Value last = toValue(tree.expr); 1894 // Yield the Iterable result of the expression 1895 append(CoreOp.core_yield(last)); 1896 Body.Builder expression = stack.body; 1897 1898 // Pop expression 1899 popBody(); 1900 1901 JCVariableDecl var = tree.getVariable(); 1902 JavaType eType = typeToTypeElement(var.type); 1903 VarType varEType = CoreType.varType(typeToTypeElement(var.type)); 1904 1905 // Push init 1906 // @@@ When lhs assignment is a pattern we embed the pattern match into the init body and 1907 // return the bound variables 1908 pushBody(var, CoreType.functionType(varEType, eType)); 1909 Op.Result varEResult = append(CoreOp.var(var.name.toString(), stack.block.parameters().get(0))); 1910 append(CoreOp.core_yield(varEResult)); 1911 Body.Builder init = stack.body; 1912 // Pop init 1913 popBody(); 1914 1915 // Push body 1916 pushBody(tree.body, CoreType.functionType(JavaType.VOID, varEType)); 1917 stack.localToOp.put(var.sym, stack.block.parameters().get(0)); 1918 1919 scan(tree.body); 1920 appendTerminating(JavaOp::continue_); 1921 Body.Builder body = stack.body; 1922 // Pop body 1923 popBody(); 1924 1925 append(JavaOp.enhancedFor(expression, init, body)); 1926 result = null; 1927 } 1928 1929 @Override 1930 public void visitForLoop(JCTree.JCForLoop tree) { 1931 class VarDefScanner extends FilterScanner { 1932 final List<JCVariableDecl> decls; 1933 1934 public VarDefScanner() { 1935 super(Set.of(Tag.VARDEF)); 1936 this.decls = new ArrayList<>(); 1937 } 1938 1939 @Override 1940 public void visitVarDef(JCVariableDecl tree) { 1941 decls.add(tree); 1942 } 1943 1944 void mapVarsToBlockArguments() { 1945 for (int i = 0; i < decls.size(); i++) { 1946 stack.localToOp.put(decls.get(i).sym, stack.block.parameters().get(i)); 1947 } 1948 } 1949 1950 List<VarType> varTypes() { 1951 return decls.stream() 1952 .map(t -> CoreType.varType(typeToTypeElement(t.type))) 1953 .toList(); 1954 } 1955 1956 List<Value> varValues() { 1957 return decls.stream() 1958 .map(t -> stack.localToOp.get(t.sym)) 1959 .toList(); 1960 } 1961 } 1962 1963 // Scan local variable declarations 1964 VarDefScanner vds = new VarDefScanner(); 1965 vds.scan(tree.init); 1966 List<VarType> varTypes = vds.varTypes(); 1967 1968 // Push init 1969 if (varTypes.size() > 1) { 1970 pushBody(null, CoreType.functionType(CoreType.tupleType(varTypes))); 1971 scan(tree.init); 1972 1973 // Capture all local variable declarations in tuple 1974 append(CoreOp.core_yield(append(CoreOp.tuple(vds.varValues())))); 1975 } else if (varTypes.size() == 1) { 1976 pushBody(null, CoreType.functionType(varTypes.get(0))); 1977 scan(tree.init); 1978 1979 append(CoreOp.core_yield(vds.varValues().get(0))); 1980 } else { 1981 pushBody(null, CoreType.FUNCTION_TYPE_VOID); 1982 scan(tree.init); 1983 1984 append(CoreOp.core_yield()); 1985 } 1986 Body.Builder init = stack.body; 1987 1988 // Pop init 1989 popBody(); 1990 1991 // Push cond 1992 pushBody(tree.cond, CoreType.functionType(JavaType.BOOLEAN, varTypes)); 1993 if (tree.cond != null) { 1994 vds.mapVarsToBlockArguments(); 1995 1996 Value last = toValue(tree.cond); 1997 // Yield the boolean result of the condition 1998 append(CoreOp.core_yield(last)); 1999 } else { 2000 append(CoreOp.core_yield(append(CoreOp.constant(JavaType.BOOLEAN, true)))); 2001 } 2002 Body.Builder cond = stack.body; 2003 2004 // Pop cond 2005 popBody(); 2006 2007 // Push update 2008 // @@@ tree.step is a List<JCStatement> 2009 pushBody(null, CoreType.functionType(JavaType.VOID, varTypes)); 2010 if (!tree.step.isEmpty()) { 2011 vds.mapVarsToBlockArguments(); 2012 2013 scan(tree.step); 2014 } 2015 append(CoreOp.core_yield()); 2016 Body.Builder update = stack.body; 2017 2018 // Pop update 2019 popBody(); 2020 2021 // Push body 2022 pushBody(tree.body, CoreType.functionType(JavaType.VOID, varTypes)); 2023 if (tree.body != null) { 2024 vds.mapVarsToBlockArguments(); 2025 2026 scan(tree.body); 2027 } 2028 appendTerminating(JavaOp::continue_); 2029 Body.Builder body = stack.body; 2030 2031 // Pop update 2032 popBody(); 2033 2034 append(JavaOp.for_(init, cond, update, body)); 2035 result = null; 2036 } 2037 2038 @Override 2039 public void visitConditional(JCTree.JCConditional tree) { 2040 List<Body.Builder> bodies = new ArrayList<>(); 2041 2042 JCTree.JCExpression cond = TreeInfo.skipParens(tree.cond); 2043 2044 // Push condition 2045 pushBody(cond, 2046 CoreType.functionType(JavaType.BOOLEAN)); 2047 Value condVal = toValue(cond); 2048 // Yield the boolean result of the condition 2049 append(CoreOp.core_yield(condVal)); 2050 bodies.add(stack.body); 2051 2052 // Pop condition 2053 popBody(); 2054 2055 JCTree.JCExpression truepart = TreeInfo.skipParens(tree.truepart); 2056 2057 Type condType = adaptBottom(tree.type); 2058 2059 // Push true body 2060 pushBody(truepart, 2061 CoreType.functionType(typeToTypeElement(condType))); 2062 2063 Value trueVal = toValue(truepart, condType); 2064 // Yield the result 2065 append(CoreOp.core_yield(trueVal)); 2066 bodies.add(stack.body); 2067 2068 // Pop true body 2069 popBody(); 2070 2071 JCTree.JCExpression falsepart = TreeInfo.skipParens(tree.falsepart); 2072 2073 // Push false body 2074 pushBody(falsepart, 2075 CoreType.functionType(typeToTypeElement(condType))); 2076 2077 Value falseVal = toValue(falsepart, condType); 2078 // Yield the result 2079 append(CoreOp.core_yield(falseVal)); 2080 bodies.add(stack.body); 2081 2082 // Pop false body 2083 popBody(); 2084 2085 result = append(JavaOp.conditionalExpression(typeToTypeElement(condType), bodies)); 2086 } 2087 2088 private Type condType(JCExpression tree, Type type) { 2089 if (type.hasTag(BOT)) { 2090 return adaptBottom(tree.type); 2091 } else { 2092 return type; 2093 } 2094 } 2095 2096 private Type adaptBottom(Type type) { 2097 return type.hasTag(BOT) ? 2098 (pt.hasTag(NONE) ? syms.objectType : pt) : 2099 type; 2100 } 2101 2102 @Override 2103 public void visitAssert(JCAssert tree) { 2104 // assert <cond:body1> [detail:body2] 2105 2106 List<Body.Builder> bodies = new ArrayList<>(); 2107 JCTree.JCExpression cond = TreeInfo.skipParens(tree.cond); 2108 2109 // Push condition 2110 pushBody(cond, 2111 CoreType.functionType(JavaType.BOOLEAN)); 2112 Value condVal = toValue(cond); 2113 2114 // Yield the boolean result of the condition 2115 append(CoreOp.core_yield(condVal)); 2116 bodies.add(stack.body); 2117 2118 // Pop condition 2119 popBody(); 2120 2121 if (tree.detail != null) { 2122 JCTree.JCExpression detail = TreeInfo.skipParens(tree.detail); 2123 2124 pushBody(detail, 2125 CoreType.functionType(typeToTypeElement(tree.detail.type))); 2126 Value detailVal = toValue(detail); 2127 2128 append(CoreOp.core_yield(detailVal)); 2129 bodies.add(stack.body); 2130 2131 //Pop detail 2132 popBody(); 2133 } 2134 2135 result = append(JavaOp.assert_(bodies)); 2136 2137 } 2138 2139 @Override 2140 public void visitBlock(JCTree.JCBlock tree) { 2141 if (stack.tree == tree) { 2142 // Block is associated with the visit of a parent structure 2143 scan(tree.stats); 2144 } else { 2145 // Otherwise, independent block structure 2146 // Push block 2147 pushBody(tree, CoreType.FUNCTION_TYPE_VOID); 2148 scan(tree.stats); 2149 appendTerminating(CoreOp::core_yield); 2150 Body.Builder body = stack.body; 2151 2152 // Pop block 2153 popBody(); 2154 2155 append(JavaOp.block(body)); 2156 } 2157 result = null; 2158 } 2159 2160 @Override 2161 public void visitSynchronized(JCTree.JCSynchronized tree) { 2162 // Push expr 2163 pushBody(tree.lock, CoreType.functionType(typeToTypeElement(tree.lock.type))); 2164 Value last = toValue(tree.lock); 2165 append(CoreOp.core_yield(last)); 2166 Body.Builder expr = stack.body; 2167 2168 // Pop expr 2169 popBody(); 2170 2171 // Push body block 2172 pushBody(tree.body, CoreType.FUNCTION_TYPE_VOID); 2173 // Scan body block statements 2174 scan(tree.body.stats); 2175 appendTerminating(CoreOp::core_yield); 2176 Body.Builder blockBody = stack.body; 2177 2178 // Pop body block 2179 popBody(); 2180 2181 append(JavaOp.synchronized_(expr, blockBody)); 2182 } 2183 2184 @Override 2185 public void visitLabelled(JCTree.JCLabeledStatement tree) { 2186 // Push block 2187 pushBody(tree, CoreType.FUNCTION_TYPE_VOID); 2188 // Create constant for label 2189 String labelName = tree.label.toString(); 2190 Op.Result label = append(CoreOp.constant(JavaType.J_L_STRING, labelName)); 2191 // Set label on body stack 2192 stack.setLabel(labelName, label); 2193 scan(tree.body); 2194 appendTerminating(CoreOp::core_yield); 2195 Body.Builder body = stack.body; 2196 2197 // Pop block 2198 popBody(); 2199 2200 result = append(JavaOp.labeled(body)); 2201 } 2202 2203 @Override 2204 public void visitTry(JCTree.JCTry tree) { 2205 List<JCVariableDecl> rVariableDecls = new ArrayList<>(); 2206 List<TypeElement> rTypes = new ArrayList<>(); 2207 Body.Builder resources; 2208 if (!tree.resources.isEmpty()) { 2209 // Resources body returns a tuple that contains the resource variables/values 2210 // in order of declaration 2211 for (JCTree resource : tree.resources) { 2212 if (resource instanceof JCVariableDecl vdecl) { 2213 rVariableDecls.add(vdecl); 2214 rTypes.add(CoreType.varType(typeToTypeElement(vdecl.type))); 2215 } else { 2216 rTypes.add(typeToTypeElement(resource.type)); 2217 } 2218 } 2219 2220 // Push resources body 2221 pushBody(null, CoreType.functionType(CoreType.tupleType(rTypes))); 2222 2223 List<Value> rValues = new ArrayList<>(); 2224 for (JCTree resource : tree.resources) { 2225 if (resource instanceof JCTree.JCExpression e) { 2226 rValues.add(toValue(e)); 2227 } else if (resource instanceof JCTree.JCStatement s) { 2228 rValues.add(toValue(s)); 2229 } 2230 } 2231 2232 append(CoreOp.core_yield(append(CoreOp.tuple(rValues)))); 2233 resources = stack.body; 2234 2235 // Pop resources body 2236 popBody(); 2237 } else { 2238 resources = null; 2239 } 2240 2241 // Push body 2242 // Try body accepts the resource variables (in order of declaration). 2243 List<VarType> rVarTypes = rTypes.stream().<VarType>mapMulti((t, c) -> { 2244 if (t instanceof VarType vt) { 2245 c.accept(vt); 2246 } 2247 }).toList(); 2248 pushBody(tree.body, CoreType.functionType(JavaType.VOID, rVarTypes)); 2249 for (int i = 0; i < rVariableDecls.size(); i++) { 2250 stack.localToOp.put(rVariableDecls.get(i).sym, stack.block.parameters().get(i)); 2251 } 2252 scan(tree.body); 2253 appendTerminating(CoreOp::core_yield); 2254 Body.Builder body = stack.body; 2255 2256 // Pop block 2257 popBody(); 2258 2259 List<Body.Builder> catchers = new ArrayList<>(); 2260 for (JCTree.JCCatch catcher : tree.catchers) { 2261 // Push body 2262 pushBody(catcher.body, CoreType.functionType(JavaType.VOID, typeToTypeElement(catcher.param.type))); 2263 Op.Result exVariable = append(CoreOp.var( 2264 catcher.param.name.toString(), 2265 stack.block.parameters().get(0))); 2266 stack.localToOp.put(catcher.param.sym, exVariable); 2267 scan(catcher.body); 2268 appendTerminating(CoreOp::core_yield); 2269 catchers.add(stack.body); 2270 2271 // Pop block 2272 popBody(); 2273 } 2274 2275 Body.Builder finalizer; 2276 if (tree.finalizer != null) { 2277 // Push body 2278 pushBody(tree.finalizer, CoreType.FUNCTION_TYPE_VOID); 2279 scan(tree.finalizer); 2280 appendTerminating(CoreOp::core_yield); 2281 finalizer = stack.body; 2282 2283 // Pop block 2284 popBody(); 2285 } 2286 else { 2287 finalizer = null; 2288 } 2289 2290 result = append(JavaOp.try_(resources, body, catchers, finalizer)); 2291 } 2292 2293 @Override 2294 public void visitUnary(JCTree.JCUnary tree) { 2295 Tag tag = tree.getTag(); 2296 switch (tag) { 2297 case POSTINC, POSTDEC, PREINC, PREDEC -> { 2298 // Capture applying rhs and operation 2299 Function<Value, Value> scanRhs = (lhs) -> { 2300 Type unboxedType = types.unboxedTypeOrType(tree.type); 2301 Value one = convert(append(numericOneValue(unboxedType)), unboxedType); 2302 Value unboxedLhs = unboxIfNeeded(lhs); 2303 2304 Value unboxedLhsPlusOne = switch (tree.getTag()) { 2305 // Arithmetic operations 2306 case POSTINC, PREINC -> append(JavaOp.add(unboxedLhs, one)); 2307 case POSTDEC, PREDEC -> append(JavaOp.sub(unboxedLhs, one)); 2308 2309 default -> throw unsupported(tree); 2310 }; 2311 Value lhsPlusOne = convert(unboxedLhsPlusOne, tree.type); 2312 2313 // Assign expression result 2314 result = switch (tree.getTag()) { 2315 case POSTINC, POSTDEC -> lhs; 2316 case PREINC, PREDEC -> lhsPlusOne; 2317 2318 default -> throw unsupported(tree); 2319 }; 2320 return lhsPlusOne; 2321 }; 2322 2323 applyCompoundAssign(tree.arg, scanRhs); 2324 } 2325 case NEG -> { 2326 Value rhs = toValue(tree.arg, tree.type); 2327 result = append(JavaOp.neg(rhs)); 2328 } 2329 case NOT -> { 2330 Value rhs = toValue(tree.arg, tree.type); 2331 result = append(JavaOp.not(rhs)); 2332 } 2333 case COMPL -> { 2334 Value rhs = toValue(tree.arg, tree.type); 2335 result = append(JavaOp.compl(rhs)); 2336 } 2337 case POS -> { 2338 // Result is value of the operand 2339 result = toValue(tree.arg, tree.type); 2340 } 2341 default -> throw unsupported(tree); 2342 } 2343 } 2344 2345 @Override 2346 public void visitBinary(JCBinary tree) { 2347 Tag tag = tree.getTag(); 2348 if (tag == Tag.AND || tag == Tag.OR) { 2349 // Logical operations 2350 // @@@ Flatten nested sequences 2351 2352 // Push lhs 2353 pushBody(tree.lhs, CoreType.functionType(JavaType.BOOLEAN)); 2354 Value lhs = toValue(tree.lhs); 2355 // Yield the boolean result of the condition 2356 append(CoreOp.core_yield(lhs)); 2357 Body.Builder bodyLhs = stack.body; 2358 2359 // Pop lhs 2360 popBody(); 2361 2362 // Push rhs 2363 pushBody(tree.rhs, CoreType.functionType(JavaType.BOOLEAN)); 2364 Value rhs = toValue(tree.rhs); 2365 // Yield the boolean result of the condition 2366 append(CoreOp.core_yield(rhs)); 2367 Body.Builder bodyRhs = stack.body; 2368 2369 // Pop lhs 2370 popBody(); 2371 2372 List<Body.Builder> bodies = List.of(bodyLhs, bodyRhs); 2373 result = append(tag == Tag.AND 2374 ? JavaOp.conditionalAnd(bodies) 2375 : JavaOp.conditionalOr(bodies)); 2376 } else if (tag == Tag.PLUS && tree.operator.opcode == ByteCodes.string_add) { 2377 //Ignore the operator and query both subexpressions for their type with concats 2378 Type lhsType = tree.lhs.type; 2379 Type rhsType = tree.rhs.type; 2380 2381 Value lhs = toValue(tree.lhs, lhsType); 2382 Value rhs = toValue(tree.rhs, rhsType); 2383 2384 result = append(JavaOp.concat(lhs, rhs)); 2385 } 2386 else { 2387 Type opType = tree.operator.type.getParameterTypes().getFirst(); 2388 // @@@ potentially handle shift input conversion like other binary ops 2389 boolean isShift = tag == Tag.SL || tag == Tag.SR || tag == Tag.USR; 2390 Value lhs = toValue(tree.lhs, opType); 2391 Value rhs = toValue(tree.rhs, isShift ? tree.operator.type.getParameterTypes().getLast() : opType); 2392 2393 result = switch (tag) { 2394 // Arithmetic operations 2395 case PLUS -> append(JavaOp.add(lhs, rhs)); 2396 case MINUS -> append(JavaOp.sub(lhs, rhs)); 2397 case MUL -> append(JavaOp.mul(lhs, rhs)); 2398 case DIV -> append(JavaOp.div(lhs, rhs)); 2399 case MOD -> append(JavaOp.mod(lhs, rhs)); 2400 2401 // Test operations 2402 case EQ -> append(JavaOp.eq(lhs, rhs)); 2403 case NE -> append(JavaOp.neq(lhs, rhs)); 2404 // 2405 case LT -> append(JavaOp.lt(lhs, rhs)); 2406 case LE -> append(JavaOp.le(lhs, rhs)); 2407 case GT -> append(JavaOp.gt(lhs, rhs)); 2408 case GE -> append(JavaOp.ge(lhs, rhs)); 2409 2410 // Bitwise operations (including their boolean variants) 2411 case BITOR -> append(JavaOp.or(lhs, rhs)); 2412 case BITAND -> append(JavaOp.and(lhs, rhs)); 2413 case BITXOR -> append(JavaOp.xor(lhs, rhs)); 2414 2415 // Shift operations 2416 case SL -> append(JavaOp.lshl(lhs, rhs)); 2417 case SR -> append(JavaOp.ashr(lhs, rhs)); 2418 case USR -> append(JavaOp.lshr(lhs, rhs)); 2419 2420 default -> throw unsupported(tree); 2421 }; 2422 } 2423 } 2424 2425 @Override 2426 public void visitLiteral(JCLiteral tree) { 2427 Object value = switch (tree.type.getTag()) { 2428 case BOOLEAN -> tree.value instanceof Integer i && i == 1; 2429 case CHAR -> (char) (int) tree.value; 2430 default -> tree.value; 2431 }; 2432 Type constantType = adaptBottom(tree.type); 2433 result = append(CoreOp.constant(typeToTypeElement(constantType), value)); 2434 } 2435 2436 @Override 2437 public void visitReturn(JCReturn tree) { 2438 Value retVal = toValue(tree.expr, bodyTarget); 2439 if (retVal == null) { 2440 result = append(CoreOp.return_()); 2441 } else { 2442 result = append(CoreOp.return_(retVal)); 2443 } 2444 } 2445 2446 @Override 2447 public void visitThrow(JCTree.JCThrow tree) { 2448 Value throwVal = toValue(tree.expr); 2449 result = append(JavaOp.throw_(throwVal)); 2450 } 2451 2452 @Override 2453 public void visitBreak(JCTree.JCBreak tree) { 2454 Value label = tree.label != null 2455 ? getLabel(tree.label.toString()) 2456 : null; 2457 result = append(JavaOp.break_(label)); 2458 } 2459 2460 @Override 2461 public void visitContinue(JCTree.JCContinue tree) { 2462 Value label = tree.label != null 2463 ? getLabel(tree.label.toString()) 2464 : null; 2465 result = append(JavaOp.continue_(label)); 2466 } 2467 2468 @Override 2469 public void visitClassDef(JCClassDecl tree) { 2470 if (tree.sym.isDirectlyOrIndirectlyLocal()) { 2471 // we need to keep track of captured locals using same strategy as Lower 2472 class FreeVarScanner extends Lower.FreeVarCollector { 2473 FreeVarScanner() { 2474 lower.super(tree); 2475 } 2476 2477 @Override 2478 protected void addFreeVars(ClassSymbol c) { 2479 localCaptures.getOrDefault(c, List.of()) 2480 .forEach(s -> addFreeVar((VarSymbol)s)); 2481 } 2482 } 2483 FreeVarScanner fvs = new FreeVarScanner(); 2484 localCaptures.put(tree.sym, List.copyOf(fvs.analyzeCaptures())); 2485 } 2486 } 2487 2488 UnsupportedASTException unsupported(JCTree tree) { 2489 return new UnsupportedASTException(tree); 2490 } 2491 2492 CoreOp.FuncOp scanMethod() { 2493 scan(body); 2494 appendReturnOrUnreachable(body); 2495 CoreOp.FuncOp func = CoreOp.func(name.toString(), stack.body); 2496 func.setLocation(generateLocation(currentNode, true)); 2497 return func; 2498 } 2499 2500 CoreOp.FuncOp scanLambda() { 2501 scan(body); 2502 // Return the quoted result 2503 append(CoreOp.return_(result)); 2504 return CoreOp.func(name.toString(), stack.body); 2505 } 2506 2507 Op defaultValue(Type t) { 2508 return switch (t.getTag()) { 2509 case BYTE, SHORT, INT -> CoreOp.constant(JavaType.INT, 0); 2510 case CHAR -> CoreOp.constant(typeToTypeElement(t), (char)0); 2511 case BOOLEAN -> CoreOp.constant(typeToTypeElement(t), false); 2512 case FLOAT -> CoreOp.constant(typeToTypeElement(t), 0f); 2513 case LONG -> CoreOp.constant(typeToTypeElement(t), 0L); 2514 case DOUBLE -> CoreOp.constant(typeToTypeElement(t), 0d); 2515 default -> CoreOp.constant(typeToTypeElement(t), null); 2516 }; 2517 } 2518 2519 Op numericOneValue(Type t) { 2520 return switch (t.getTag()) { 2521 case BYTE, SHORT, INT -> CoreOp.constant(JavaType.INT, 1); 2522 case CHAR -> CoreOp.constant(typeToTypeElement(t), (char)1); 2523 case FLOAT -> CoreOp.constant(typeToTypeElement(t), 1f); 2524 case LONG -> CoreOp.constant(typeToTypeElement(t), 1L); 2525 case DOUBLE -> CoreOp.constant(typeToTypeElement(t), 1d); 2526 default -> throw new UnsupportedOperationException(t.toString()); 2527 }; 2528 } 2529 } 2530 2531 /** 2532 * An exception thrown when an unsupported AST node is found when building a method IR. 2533 */ 2534 static class UnsupportedASTException extends RuntimeException { 2535 2536 private static final long serialVersionUID = 0; 2537 transient final JCTree tree; 2538 2539 public UnsupportedASTException(JCTree tree) { 2540 this.tree = tree; 2541 } 2542 } 2543 2544 enum FunctionalExpressionKind { 2545 QUOTED_STRUCTURAL(true), // this is transitional 2546 QUOTABLE(true), 2547 NOT_QUOTED(false); 2548 2549 final boolean isQuoted; 2550 2551 FunctionalExpressionKind(boolean isQuoted) { 2552 this.isQuoted = isQuoted; 2553 } 2554 } 2555 2556 FunctionalExpressionKind functionalKind(JCFunctionalExpression functionalExpression) { 2557 if (functionalExpression.target.hasTag(TypeTag.METHOD)) { 2558 return FunctionalExpressionKind.QUOTED_STRUCTURAL; 2559 } else if (types.asSuper(functionalExpression.target, crSyms.quotableType.tsym) != null) { 2560 return FunctionalExpressionKind.QUOTABLE; 2561 } else { 2562 return FunctionalExpressionKind.NOT_QUOTED; 2563 } 2564 } 2565 2566 /* 2567 * Converts a method reference which cannot be used directly into a lambda. 2568 * This code has been derived from LambdaToMethod::MemberReferenceToLambda. The main 2569 * difference is that, while that code concerns with translation strategy, boxing 2570 * conversion and type erasure, this version does not and, as such, can remain 2571 * at a higher level. Note that this code needs to create a synthetic variable 2572 * declaration in case of a bounded method reference whose receiver expression 2573 * is other than 'this'/'super' (this is done to prevent the receiver expression 2574 * from being computed twice). 2575 */ 2576 private class MemberReferenceToLambda { 2577 2578 private final JCMemberReference tree; 2579 private final Symbol owner; 2580 private final ListBuffer<JCExpression> args = new ListBuffer<>(); 2581 private final ListBuffer<JCVariableDecl> params = new ListBuffer<>(); 2582 private JCVariableDecl receiverVar = null; 2583 2584 MemberReferenceToLambda(JCMemberReference tree, Symbol currentClass) { 2585 this.tree = tree; 2586 this.owner = new MethodSymbol(0, names.lambda, tree.target, currentClass); 2587 if (tree.kind == ReferenceKind.BOUND && !isThisOrSuper(tree.getQualifierExpression())) { 2588 // true bound method reference, hoist receiver expression out 2589 Type recvType = types.asSuper(tree.getQualifierExpression().type, tree.sym.owner); 2590 VarSymbol vsym = makeSyntheticVar("rec$", recvType); 2591 receiverVar = make.VarDef(vsym, tree.getQualifierExpression()); 2592 } 2593 } 2594 2595 JCVariableDecl receiverVar() { 2596 return receiverVar; 2597 } 2598 2599 JCLambda lambda() { 2600 int prevPos = make.pos; 2601 try { 2602 make.at(tree); 2603 2604 //body generation - this can be either a method call or a 2605 //new instance creation expression, depending on the member reference kind 2606 VarSymbol rcvr = addParametersReturnReceiver(); 2607 JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE) 2608 ? expressionInvoke(rcvr) 2609 : expressionNew(); 2610 2611 JCLambda slam = make.Lambda(params.toList(), expr); 2612 slam.target = tree.target; 2613 slam.type = tree.type; 2614 slam.pos = tree.pos; 2615 return slam; 2616 } finally { 2617 make.at(prevPos); 2618 } 2619 } 2620 2621 /** 2622 * Generate the parameter list for the converted member reference. 2623 * 2624 * @return The receiver variable symbol, if any 2625 */ 2626 VarSymbol addParametersReturnReceiver() { 2627 com.sun.tools.javac.util.List<Type> descPTypes = tree.getDescriptorType(types).getParameterTypes(); 2628 VarSymbol receiverParam = null; 2629 switch (tree.kind) { 2630 case BOUND: 2631 if (receiverVar != null) { 2632 receiverParam = receiverVar.sym; 2633 } 2634 break; 2635 case UNBOUND: 2636 // The receiver is the first parameter, extract it and 2637 // adjust the SAM and unerased type lists accordingly 2638 receiverParam = addParameter("rec$", descPTypes.head, false); 2639 descPTypes = descPTypes.tail; 2640 break; 2641 } 2642 for (int i = 0; descPTypes.nonEmpty(); ++i) { 2643 // By default use the implementation method parameter type 2644 Type parmType = descPTypes.head; 2645 addParameter("x$" + i, parmType, true); 2646 2647 // Advance to the next parameter 2648 descPTypes = descPTypes.tail; 2649 } 2650 2651 return receiverParam; 2652 } 2653 2654 /** 2655 * determine the receiver of the method call - the receiver can 2656 * be a type qualifier, the synthetic receiver parameter or 'super'. 2657 */ 2658 private JCExpression expressionInvoke(VarSymbol receiverParam) { 2659 JCExpression qualifier = receiverParam != null ? 2660 make.at(tree.pos).Ident(receiverParam) : 2661 tree.getQualifierExpression(); 2662 2663 //create the qualifier expression 2664 JCFieldAccess select = make.Select(qualifier, tree.sym.name); 2665 select.sym = tree.sym; 2666 select.type = tree.referentType; 2667 2668 //create the method call expression 2669 JCMethodInvocation apply = make.Apply(com.sun.tools.javac.util.List.nil(), select, args.toList()). 2670 setType(tree.referentType.getReturnType()); 2671 2672 apply.varargsElement = tree.varargsElement; 2673 return apply; 2674 } 2675 2676 /** 2677 * Lambda body to use for a 'new'. 2678 */ 2679 private JCExpression expressionNew() { 2680 Type expectedType = tree.referentType.getReturnType().hasTag(TypeTag.VOID) ? 2681 tree.expr.type : tree.referentType.getReturnType(); 2682 if (tree.kind == ReferenceKind.ARRAY_CTOR) { 2683 //create the array creation expression 2684 JCNewArray newArr = make.NewArray( 2685 make.Type(types.elemtype(expectedType)), 2686 com.sun.tools.javac.util.List.of(make.Ident(params.first())), 2687 null); 2688 newArr.type = tree.getQualifierExpression().type; 2689 return newArr; 2690 } else { 2691 //create the instance creation expression 2692 //note that method reference syntax does not allow an explicit 2693 //enclosing class (so the enclosing class is null) 2694 // but this may need to be patched up later with the proxy for the outer this 2695 JCExpression newType = make.Type(types.erasure(expectedType)); 2696 if (expectedType.tsym.type.getTypeArguments().nonEmpty()) { 2697 newType = make.TypeApply(newType, com.sun.tools.javac.util.List.nil()); 2698 } 2699 JCNewClass newClass = make.NewClass(null, 2700 com.sun.tools.javac.util.List.nil(), 2701 newType, 2702 args.toList(), 2703 null); 2704 newClass.constructor = tree.sym; 2705 newClass.constructorType = tree.referentType; 2706 newClass.type = expectedType; 2707 newClass.varargsElement = tree.varargsElement; 2708 return newClass; 2709 } 2710 } 2711 2712 private VarSymbol makeSyntheticVar(String name, Type type) { 2713 VarSymbol vsym = new VarSymbol(PARAMETER | SYNTHETIC, names.fromString(name), type, owner); 2714 vsym.pos = tree.pos; 2715 return vsym; 2716 } 2717 2718 private VarSymbol addParameter(String name, Type type, boolean genArg) { 2719 VarSymbol vsym = makeSyntheticVar(name, type); 2720 params.append(make.VarDef(vsym, null)); 2721 if (genArg) { 2722 args.append(make.Ident(vsym)); 2723 } 2724 return vsym; 2725 } 2726 2727 boolean isThisOrSuper(JCExpression expression) { 2728 return TreeInfo.isThisQualifier(expression) || TreeInfo.isSuperQualifier(tree); 2729 } 2730 } 2731 2732 public static class Provider implements CodeReflectionTransformer { 2733 @Override 2734 public JCTree translateTopLevelClass(Context context, JCTree tree, TreeMaker make) { 2735 return ReflectMethods.instance(context).translateTopLevelClass(tree, make); 2736 } 2737 } 2738 2739 /** 2740 * Translate a code model (a function op) into the corresponding AST. 2741 * The input function op is assumed to be generated by {@code OpBuilder}. 2742 */ 2743 class CodeModelTranslator { 2744 private static final MethodRef M_BLOCK_BUILDER_OP = MethodRef.method(Block.Builder.class, "op", 2745 Op.Result.class, Op.class); 2746 private static final MethodRef M_BLOCK_BUILDER_PARAM = MethodRef.method(Block.Builder.class, "parameter", 2747 Block.Parameter.class, TypeElement.class); 2748 private static final MethodRef M_OP_SEAL = MethodRef.method(Op.class, "seal", void.class); 2749 2750 private final Map<Value, JCTree> valueToTree = new HashMap<>(); 2751 private int localVarCount = 0; // used to name variables we introduce in the AST 2752 2753 private JCExpression toExpr(JCTree t) { 2754 return switch (t) { 2755 case JCExpression e -> e; 2756 case JCTree.JCVariableDecl vd -> make.Ident(vd); 2757 case null, default -> throw new IllegalArgumentException(); 2758 }; 2759 } 2760 2761 private JCTree translateInvokeOp(JavaOp.InvokeOp invokeOp) { 2762 Value receiver = (invokeOp.invokeKind() == JavaOp.InvokeOp.InvokeKind.INSTANCE) ? 2763 invokeOp.operands().get(0) : null; 2764 com.sun.tools.javac.util.List<Value> arguments = invokeOp.operands().stream() 2765 .skip(receiver == null ? 0 : 1) 2766 .collect(com.sun.tools.javac.util.List.collector()); 2767 var methodSym = methodDescriptorToSymbol(invokeOp.invokeDescriptor()); 2768 var meth = (receiver == null) ? 2769 make.Ident(methodSym) : 2770 make.Select(toExpr(translateOp(receiver)), methodSym); 2771 var args = new ListBuffer<JCTree.JCExpression>(); 2772 for (Value operand : arguments) { 2773 args.add(toExpr(translateOp(operand))); 2774 } 2775 var methodInvocation = make.App(meth, args.toList()); 2776 if (invokeOp.isVarArgs()) { 2777 setVarargs(methodInvocation, invokeOp.invokeDescriptor().type()); 2778 } 2779 return methodInvocation; 2780 } 2781 2782 private void setVarargs(JCExpression tree, FunctionType type) { 2783 var lastParam = type.parameterTypes().getLast(); 2784 if (lastParam instanceof jdk.incubator.code.dialect.java.ArrayType varargType) { 2785 TreeInfo.setVarargsElement(tree, typeElementToType(varargType.componentType())); 2786 } else { 2787 Assert.error("Expected trailing array type: " + type); 2788 } 2789 } 2790 2791 private static final Set<MethodRef> mRefs = Set.of(M_BLOCK_BUILDER_OP, M_BLOCK_BUILDER_PARAM, M_OP_SEAL); 2792 public JCTree.JCStatement translateFuncOp(CoreOp.FuncOp funcOp, MethodSymbol ms) { 2793 Assert.check(funcOp.parameters().isEmpty()); 2794 Assert.check(funcOp.body().blocks().size() == 1); 2795 2796 java.util.List<Value> rootValues = funcOp.traverse(new ArrayList<>(), (l, ce) -> { 2797 boolean isRoot = switch (ce) { 2798 case JavaOp.InvokeOp invokeOp when mRefs.contains(invokeOp.invokeDescriptor()) -> true; 2799 case CoreOp.ReturnOp _, JavaOp.ArrayAccessOp.ArrayStoreOp _ -> true; 2800 case Op op when op.result() != null && op.result().uses().size() > 1 -> true; 2801 default -> false; 2802 }; 2803 if (isRoot) { 2804 l.add(((Op) ce).result()); 2805 } 2806 return l; 2807 }); 2808 2809 var stats = new ListBuffer<JCTree.JCStatement>(); 2810 for (Value root : rootValues) { 2811 JCTree tree = translateOp(root); 2812 if (tree instanceof JCExpression e) { 2813 if (!root.uses().isEmpty()) { 2814 var vs = new Symbol.VarSymbol(LocalVarFlags | SYNTHETIC, names.fromString("_$" + localVarCount++), tree.type, ms); 2815 tree = make.VarDef(vs, e); 2816 valueToTree.put(root, tree); 2817 } else { 2818 tree = make.Exec(e); 2819 } 2820 } 2821 stats.add((JCTree.JCStatement) tree); 2822 } 2823 var mb = make.Block(0, stats.toList()); 2824 2825 return mb; 2826 } 2827 2828 private JCTree translateOp(Value v) { 2829 if (valueToTree.containsKey(v)) { 2830 return valueToTree.get(v); 2831 } 2832 Op op = ((Op.Result) v).op(); 2833 JCTree tree = switch (op) { 2834 case CoreOp.ConstantOp constantOp when constantOp.value() == null -> 2835 make.Literal(TypeTag.BOT, null).setType(syms.botType); 2836 case CoreOp.ConstantOp constantOp -> make.Literal(constantOp.value()); 2837 case JavaOp.InvokeOp invokeOp -> translateInvokeOp(invokeOp); 2838 case JavaOp.NewOp newOp when newOp.resultType() instanceof jdk.incubator.code.dialect.java.ArrayType at -> { 2839 var elemType = make.Ident(typeElementToType(at.componentType()).tsym); 2840 var dims = new ListBuffer<JCTree.JCExpression>(); 2841 for (int d = 0; d < at.dimensions(); d++) { 2842 dims.add(toExpr(translateOp(newOp.operands().get(d)))); 2843 } 2844 var na = make.NewArray(elemType, dims.toList(), null); 2845 na.type = typeElementToType(at); 2846 yield na; 2847 } 2848 case JavaOp.NewOp newOp -> { 2849 var ownerType = typeElementToType(newOp.constructorDescriptor().refType()); 2850 var clazz = make.Ident(ownerType.tsym); 2851 var args = new ListBuffer<JCTree.JCExpression>(); 2852 for (Value operand : newOp.operands()) { 2853 args.add(toExpr(translateOp(operand))); 2854 } 2855 var nc = make.NewClass(null, null, clazz, args.toList(), null); 2856 if (newOp.isVarargs()) { 2857 setVarargs(nc, newOp.constructorDescriptor().type()); 2858 } 2859 nc.type = ownerType; 2860 nc.constructor = constructorDescriptorToSymbol(newOp.constructorDescriptor()); 2861 nc.constructorType = nc.constructor.type; 2862 yield nc; 2863 } 2864 case CoreOp.ReturnOp returnOp -> make.Return(toExpr(translateOp(returnOp.returnValue()))); 2865 case JavaOp.FieldAccessOp.FieldLoadOp fieldLoadOp -> { 2866 var sym = fieldDescriptorToSymbol(fieldLoadOp.fieldDescriptor()); 2867 Assert.check(sym.isStatic()); 2868 yield make.Select(make.Ident(sym.owner), sym); 2869 } 2870 case JavaOp.ArrayAccessOp.ArrayStoreOp arrayStoreOp -> { 2871 var array = arrayStoreOp.operands().get(0); 2872 var index = arrayStoreOp.operands().get(1); 2873 var val = arrayStoreOp.operands().get(2); 2874 var as = make.Assign( 2875 make.Indexed( 2876 toExpr(translateOp(array)), toExpr(translateOp(index))), toExpr(translateOp(val)) 2877 ); 2878 as.type = typeElementToType(((jdk.incubator.code.dialect.java.ArrayType) array.type()).componentType()); 2879 yield as; 2880 } 2881 default -> 2882 throw new IllegalStateException("Op -> JCTree not supported for :" + op.getClass().getName()); 2883 }; 2884 valueToTree.put(v, tree); 2885 return tree; 2886 } 2887 } 2888 2889 // type and ref conversion utils 2890 2891 JavaType symbolToErasedDesc(Symbol s) { 2892 return typeToTypeElement(s.erasure(types)); 2893 } 2894 2895 JavaType typeToTypeElement(Type t) { 2896 Assert.check(!t.hasTag(METHOD)); 2897 t = types.upward(t, false, types.captures(t)); 2898 return switch (t.getTag()) { 2899 case VOID -> JavaType.VOID; 2900 case CHAR -> JavaType.CHAR; 2901 case BOOLEAN -> JavaType.BOOLEAN; 2902 case BYTE -> JavaType.BYTE; 2903 case SHORT -> JavaType.SHORT; 2904 case INT -> JavaType.INT; 2905 case FLOAT -> JavaType.FLOAT; 2906 case LONG -> JavaType.LONG; 2907 case DOUBLE -> JavaType.DOUBLE; 2908 case ARRAY -> { 2909 Type et = ((ArrayType)t).elemtype; 2910 yield JavaType.array(typeToTypeElement(et)); 2911 } 2912 case WILDCARD -> { 2913 Type.WildcardType wt = (Type.WildcardType)t; 2914 yield wt.isUnbound() ? 2915 JavaType.wildcard() : 2916 JavaType.wildcard(wt.isExtendsBound() ? BoundKind.EXTENDS : BoundKind.SUPER, typeToTypeElement(wt.type)); 2917 } 2918 case TYPEVAR -> t.tsym.owner.kind == Kind.MTH ? 2919 JavaType.typeVarRef(t.tsym.name.toString(), symbolToMethodRef(t.tsym.owner), 2920 typeToTypeElement(t.getUpperBound())) : 2921 JavaType.typeVarRef(t.tsym.name.toString(), 2922 (jdk.incubator.code.dialect.java.ClassType)symbolToErasedDesc(t.tsym.owner), 2923 typeToTypeElement(t.getUpperBound())); 2924 case CLASS -> { 2925 Assert.check(!t.isIntersection() && !t.isUnion()); 2926 JavaType typ; 2927 if (t.getEnclosingType() != Type.noType) { 2928 Name innerName = t.tsym.flatName().subName(t.getEnclosingType().tsym.flatName().length() + 1); 2929 typ = JavaType.qualified(typeToTypeElement(t.getEnclosingType()), innerName.toString()); 2930 } else { 2931 typ = JavaType.type(ClassDesc.of(t.tsym.flatName().toString())); 2932 } 2933 2934 List<JavaType> typeArguments; 2935 if (t.getTypeArguments().nonEmpty()) { 2936 typeArguments = new ArrayList<>(); 2937 for (Type ta : t.getTypeArguments()) { 2938 typeArguments.add(typeToTypeElement(ta)); 2939 } 2940 } else { 2941 typeArguments = List.of(); 2942 } 2943 2944 // Use flat name to ensure demarcation of nested classes 2945 yield JavaType.parameterized(typ, typeArguments); 2946 } 2947 default -> throw new UnsupportedOperationException("Unsupported type: kind=" + t.getKind() + " type=" + t); 2948 }; 2949 } 2950 2951 Type typeElementToType(TypeElement jt) { 2952 return switch (jt) { 2953 case PrimitiveType pt when pt == JavaType.BOOLEAN -> syms.booleanType; 2954 case PrimitiveType pt when pt == JavaType.CHAR -> syms.charType; 2955 case PrimitiveType pt when pt == JavaType.BYTE -> syms.byteType; 2956 case PrimitiveType pt when pt == JavaType.SHORT -> syms.shortType; 2957 case PrimitiveType pt when pt == JavaType.INT -> syms.intType; 2958 case PrimitiveType pt when pt == JavaType.LONG -> syms.longType; 2959 case PrimitiveType pt when pt == JavaType.FLOAT -> syms.floatType; 2960 case PrimitiveType pt when pt == JavaType.DOUBLE -> syms.doubleType; 2961 case ClassType ct when ct.hasTypeArguments() -> { 2962 Type enclosing = ct.enclosingType().map(this::typeElementToType).orElse(Type.noType); 2963 com.sun.tools.javac.util.List<Type> typeArgs = com.sun.tools.javac.util.List.from(ct.typeArguments()).map(this::typeElementToType); 2964 yield new Type.ClassType(enclosing, typeArgs, typeElementToType(ct.rawType()).tsym); 2965 } 2966 case ClassType ct -> types.erasure(syms.enterClass(attrEnv().toplevel.modle, ct.toClassName())); 2967 case jdk.incubator.code.dialect.java.ArrayType at -> new Type.ArrayType(typeElementToType(at.componentType()), syms.arrayClass); 2968 default -> Type.noType; 2969 }; 2970 } 2971 2972 Type symbolSiteType(Symbol s) { 2973 boolean isMember = s.owner == syms.predefClass || 2974 s.isMemberOf(currentClassSym, types); 2975 return isMember ? currentClassSym.type : s.owner.type; 2976 } 2977 2978 FieldRef symbolToFieldRef(Symbol s, Type site) { 2979 // @@@ Made Gen::binaryQualifier public, duplicate logic? 2980 // Ensure correct qualifying class is used in the reference, see JLS 13.1 2981 // https://docs.oracle.com/javase/specs/jls/se20/html/jls-13.html#jls-13.1 2982 return symbolFieldRef(gen.binaryQualifier(s, types.erasure(site))); 2983 } 2984 2985 FieldRef symbolFieldRef(Symbol s) { 2986 Type erasedType = s.erasure(types); 2987 return FieldRef.field( 2988 typeToTypeElement(s.owner.erasure(types)), 2989 s.name.toString(), 2990 typeToTypeElement(erasedType)); 2991 } 2992 2993 MethodRef symbolToMethodRef(Symbol s, Type site) { 2994 // @@@ Made Gen::binaryQualifier public, duplicate logic? 2995 // Ensure correct qualifying class is used in the reference, see JLS 13.1 2996 // https://docs.oracle.com/javase/specs/jls/se20/html/jls-13.html#jls-13.1 2997 return symbolToMethodRef(gen.binaryQualifier(s, types.erasure(site))); 2998 } 2999 3000 MethodRef symbolToMethodRef(Symbol s) { 3001 Type erasedType = s.erasure(types); 3002 return MethodRef.method( 3003 typeToTypeElement(s.owner.erasure(types)), 3004 s.name.toString(), 3005 typeToTypeElement(erasedType.getReturnType()), 3006 erasedType.getParameterTypes().stream().map(this::typeToTypeElement).toArray(TypeElement[]::new)); 3007 } 3008 3009 FunctionType typeToFunctionType(Type t) { 3010 return CoreType.functionType( 3011 typeToTypeElement(t.getReturnType()), 3012 t.getParameterTypes().stream().map(this::typeToTypeElement).toArray(TypeElement[]::new)); 3013 } 3014 3015 RecordTypeRef symbolToRecordTypeRef(Symbol.ClassSymbol s) { 3016 TypeElement recordType = typeToTypeElement(s.type); 3017 List<RecordTypeRef.ComponentRef> components = s.getRecordComponents().stream() 3018 .map(rc -> new RecordTypeRef.ComponentRef(typeToTypeElement(rc.type), rc.name.toString())) 3019 .toList(); 3020 return RecordTypeRef.recordType(recordType, components); 3021 } 3022 3023 Env<AttrContext> attrEnv() { 3024 return typeEnvs.get(currentClassSym); 3025 } 3026 3027 VarSymbol fieldDescriptorToSymbol(FieldRef fieldRef) { 3028 Name name = names.fromString(fieldRef.name()); 3029 Type site = typeElementToType(fieldRef.refType()); 3030 return resolve.resolveInternalField(attrEnv().enclClass, attrEnv(), site, name); 3031 } 3032 3033 MethodSymbol methodDescriptorToSymbol(MethodRef methodRef) { 3034 Name name = names.fromString(methodRef.name()); 3035 Type site = typeElementToType(methodRef.refType()); 3036 com.sun.tools.javac.util.List<Type> argtypes = methodRef.type().parameterTypes().stream() 3037 .map(this::typeElementToType).collect(com.sun.tools.javac.util.List.collector()); 3038 return resolve.resolveInternalMethod(attrEnv().enclClass, attrEnv(), site, name, argtypes, com.sun.tools.javac.util.List.nil()); 3039 } 3040 3041 MethodSymbol constructorDescriptorToSymbol(ConstructorRef constructorRef) { 3042 Type site = typeElementToType(constructorRef.refType()); 3043 com.sun.tools.javac.util.List<Type> argtypes = constructorRef.type().parameterTypes().stream() 3044 .map(this::typeElementToType).collect(com.sun.tools.javac.util.List.collector()); 3045 return resolve.resolveInternalConstructor(attrEnv().enclClass, attrEnv(), site, argtypes, com.sun.tools.javac.util.List.nil()); 3046 } 3047 }