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.quotedQuotedOp), 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 ? 721 coerce(result, expression.type, targetType) : 722 null; 723 } finally { 724 pt = prevPt; 725 } 726 } 727 728 public Value toValue(JCExpression expression) { 729 return toValue(expression, Type.noType); 730 } 731 732 public Value toValue(JCTree.JCStatement statement) { 733 result = null; // reset 734 scan(statement); 735 return result; 736 } 737 738 Value coerce(Value sourceValue, Type sourceType, Type targetType) { 739 if (sourceType.isReference() && targetType.isReference() && 740 !types.isSubtype(types.erasure(sourceType), types.erasure(targetType))) { 741 return append(JavaOp.cast(typeToTypeElement(targetType), sourceValue)); 742 } else { 743 return convert(sourceValue, targetType); 744 } 745 } 746 747 Value boxIfNeeded(Value exprVal) { 748 Type source = typeElementToType(exprVal.type()); 749 return source.hasTag(NONE) ? 750 exprVal : convert(exprVal, types.boxedTypeOrType(source)); 751 } 752 753 Value unboxIfNeeded(Value exprVal) { 754 Type source = typeElementToType(exprVal.type()); 755 return source.hasTag(NONE) ? 756 exprVal : convert(exprVal, types.unboxedTypeOrType(source)); 757 } 758 759 Value convert(Value exprVal, Type target) { 760 Type source = typeElementToType(exprVal.type()); 761 boolean sourcePrimitive = source.isPrimitive(); 762 boolean targetPrimitive = target.isPrimitive(); 763 if (target.hasTag(NONE)) { 764 return exprVal; 765 } else if (sourcePrimitive == targetPrimitive) { 766 if (!sourcePrimitive || types.isSameType(source, target)) { 767 return exprVal; 768 } else { 769 // implicit primitive conversion 770 return append(JavaOp.conv(typeToTypeElement(target), exprVal)); 771 } 772 } else if (sourcePrimitive) { 773 // we need to box 774 Type unboxedTarget = types.unboxedType(target); 775 if (!unboxedTarget.hasTag(NONE)) { 776 // non-Object target 777 if (!types.isConvertible(source, unboxedTarget)) { 778 exprVal = convert(exprVal, unboxedTarget); 779 } 780 return box(exprVal, target); 781 } else { 782 // Object target 783 return box(exprVal, types.boxedClass(source).type); 784 } 785 } else { 786 // we need to unbox 787 return unbox(exprVal, source, target, types.unboxedType(source)); 788 } 789 } 790 791 Value box(Value valueExpr, Type box) { 792 // Boxing is a static method e.g., java.lang.Integer::valueOf(int)java.lang.Integer 793 MethodRef boxMethod = MethodRef.method(typeToTypeElement(box), names.valueOf.toString(), 794 CoreType.functionType(typeToTypeElement(box), typeToTypeElement(types.unboxedType(box)))); 795 return append(JavaOp.invoke(boxMethod, valueExpr)); 796 } 797 798 Value unbox(Value valueExpr, Type box, Type primitive, Type unboxedType) { 799 if (unboxedType.hasTag(NONE)) { 800 // Object target, first downcast to correct wrapper type 801 unboxedType = primitive; 802 box = types.boxedClass(unboxedType).type; 803 valueExpr = append(JavaOp.cast(typeToTypeElement(box), valueExpr)); 804 } 805 // Unboxing is a virtual method e.g., java.lang.Integer::intValue()int 806 MethodRef unboxMethod = MethodRef.method(typeToTypeElement(box), 807 unboxedType.tsym.name.append(names.Value).toString(), 808 CoreType.functionType(typeToTypeElement(unboxedType))); 809 return append(JavaOp.invoke(unboxMethod, valueExpr)); 810 } 811 812 @Override 813 public void visitVarDef(JCVariableDecl tree) { 814 JavaType javaType = typeToTypeElement(tree.type); 815 if (tree.init != null) { 816 Value initOp = toValue(tree.init, tree.type); 817 result = append(CoreOp.var(tree.name.toString(), javaType, initOp)); 818 } else { 819 // Uninitialized 820 result = append(CoreOp.var(tree.name.toString(), javaType)); 821 } 822 stack.localToOp.put(tree.sym, result); 823 } 824 825 @Override 826 public void visitAssign(JCAssign tree) { 827 // Consume top node that applies to write access 828 JCTree lhs = TreeInfo.skipParens(tree.lhs); 829 Type target = tree.lhs.type; 830 switch (lhs.getTag()) { 831 case IDENT: { 832 JCIdent assign = (JCIdent) lhs; 833 834 // Scan the rhs, the assign expression result is its input 835 result = toValue(tree.rhs, target); 836 837 Symbol sym = assign.sym; 838 switch (sym.getKind()) { 839 case LOCAL_VARIABLE, PARAMETER -> { 840 Value varOp = varOpValue(sym); 841 append(CoreOp.varStore(varOp, result)); 842 } 843 case FIELD -> { 844 FieldRef fd = symbolToFieldRef(sym, symbolSiteType(sym)); 845 if (sym.isStatic()) { 846 append(JavaOp.fieldStore(fd, result)); 847 } else { 848 append(JavaOp.fieldStore(fd, thisValue(), result)); 849 } 850 } 851 default -> { 852 // @@@ Cannot reach here? 853 throw unsupported(tree); 854 } 855 } 856 break; 857 } 858 case SELECT: { 859 JCFieldAccess assign = (JCFieldAccess) lhs; 860 861 Value receiver = toValue(assign.selected); 862 863 // Scan the rhs, the assign expression result is its input 864 result = toValue(tree.rhs, target); 865 866 Symbol sym = assign.sym; 867 FieldRef fr = symbolToFieldRef(sym, assign.selected.type); 868 if (sym.isStatic()) { 869 append(JavaOp.fieldStore(fr, result)); 870 } else { 871 append(JavaOp.fieldStore(fr, receiver, result)); 872 } 873 break; 874 } 875 case INDEXED: { 876 JCArrayAccess assign = (JCArrayAccess) lhs; 877 878 Value array = toValue(assign.indexed); 879 Value index = toValue(assign.index); 880 881 // Scan the rhs, the assign expression result is its input 882 result = toValue(tree.rhs, target); 883 884 append(JavaOp.arrayStoreOp(array, index, result)); 885 break; 886 } 887 default: 888 throw unsupported(tree); 889 } 890 } 891 892 @Override 893 public void visitAssignop(JCTree.JCAssignOp tree) { 894 // Capture applying rhs and operation 895 Function<Value, Value> scanRhs = (lhs) -> { 896 Type unboxedType = types.unboxedTypeOrType(tree.type); 897 Value rhs; 898 if (tree.operator.opcode == ByteCodes.string_add && tree.rhs.type.isPrimitive()) { 899 rhs = toValue(tree.rhs); 900 } else { 901 rhs = toValue(tree.rhs, unboxedType); 902 } 903 lhs = unboxIfNeeded(lhs); 904 905 Value assignOpResult = switch (tree.getTag()) { 906 907 // Arithmetic operations 908 case PLUS_ASG -> { 909 if (tree.operator.opcode == ByteCodes.string_add) { 910 yield append(JavaOp.concat(lhs, rhs)); 911 } else { 912 yield append(JavaOp.add(lhs, rhs)); 913 } 914 } 915 case MINUS_ASG -> append(JavaOp.sub(lhs, rhs)); 916 case MUL_ASG -> append(JavaOp.mul(lhs, rhs)); 917 case DIV_ASG -> append(JavaOp.div(lhs, rhs)); 918 case MOD_ASG -> append(JavaOp.mod(lhs, rhs)); 919 920 // Bitwise operations (including their boolean variants) 921 case BITOR_ASG -> append(JavaOp.or(lhs, rhs)); 922 case BITAND_ASG -> append(JavaOp.and(lhs, rhs)); 923 case BITXOR_ASG -> append(JavaOp.xor(lhs, rhs)); 924 925 // Shift operations 926 case SL_ASG -> append(JavaOp.lshl(lhs, rhs)); 927 case SR_ASG -> append(JavaOp.ashr(lhs, rhs)); 928 case USR_ASG -> append(JavaOp.lshr(lhs, rhs)); 929 930 931 default -> throw unsupported(tree); 932 }; 933 return result = convert(assignOpResult, tree.type); 934 }; 935 936 applyCompoundAssign(tree.lhs, scanRhs); 937 } 938 939 void applyCompoundAssign(JCTree.JCExpression lhs, Function<Value, Value> scanRhs) { 940 // Consume top node that applies to access 941 lhs = TreeInfo.skipParens(lhs); 942 switch (lhs.getTag()) { 943 case IDENT -> { 944 JCIdent assign = (JCIdent) lhs; 945 946 Symbol sym = assign.sym; 947 switch (sym.getKind()) { 948 case LOCAL_VARIABLE, PARAMETER -> { 949 Value varOp = varOpValue(sym); 950 951 Op.Result lhsOpValue = append(CoreOp.varLoad(varOp)); 952 // Scan the rhs 953 Value r = scanRhs.apply(lhsOpValue); 954 955 append(CoreOp.varStore(varOp, r)); 956 } 957 case FIELD -> { 958 FieldRef fr = symbolToFieldRef(sym, symbolSiteType(sym)); 959 960 Op.Result lhsOpValue; 961 TypeElement resultType = typeToTypeElement(sym.type); 962 if (sym.isStatic()) { 963 lhsOpValue = append(JavaOp.fieldLoad(resultType, fr)); 964 } else { 965 lhsOpValue = append(JavaOp.fieldLoad(resultType, fr, thisValue())); 966 } 967 // Scan the rhs 968 Value r = scanRhs.apply(lhsOpValue); 969 970 if (sym.isStatic()) { 971 append(JavaOp.fieldStore(fr, r)); 972 } else { 973 append(JavaOp.fieldStore(fr, thisValue(), r)); 974 } 975 } 976 default -> { 977 // @@@ Cannot reach here? 978 throw unsupported(lhs); 979 } 980 } 981 } 982 case SELECT -> { 983 JCFieldAccess assign = (JCFieldAccess) lhs; 984 985 Value receiver = toValue(assign.selected); 986 987 Symbol sym = assign.sym; 988 FieldRef fr = symbolToFieldRef(sym, assign.selected.type); 989 990 Op.Result lhsOpValue; 991 TypeElement resultType = typeToTypeElement(sym.type); 992 if (sym.isStatic()) { 993 lhsOpValue = append(JavaOp.fieldLoad(resultType, fr)); 994 } else { 995 lhsOpValue = append(JavaOp.fieldLoad(resultType, fr, receiver)); 996 } 997 // Scan the rhs 998 Value r = scanRhs.apply(lhsOpValue); 999 1000 if (sym.isStatic()) { 1001 append(JavaOp.fieldStore(fr, r)); 1002 } else { 1003 append(JavaOp.fieldStore(fr, receiver, r)); 1004 } 1005 } 1006 case INDEXED -> { 1007 JCArrayAccess assign = (JCArrayAccess) lhs; 1008 1009 Value array = toValue(assign.indexed); 1010 Value index = toValue(assign.index); 1011 1012 Op.Result lhsOpValue = append(JavaOp.arrayLoadOp(array, index)); 1013 // Scan the rhs 1014 Value r = scanRhs.apply(lhsOpValue); 1015 1016 append(JavaOp.arrayStoreOp(array, index, r)); 1017 } 1018 default -> throw unsupported(lhs); 1019 } 1020 } 1021 1022 @Override 1023 public void visitIdent(JCIdent tree) { 1024 // Visited only for read access 1025 1026 Symbol sym = tree.sym; 1027 switch (sym.getKind()) { 1028 case LOCAL_VARIABLE, RESOURCE_VARIABLE, BINDING_VARIABLE, PARAMETER, EXCEPTION_PARAMETER -> 1029 result = loadVar(sym); 1030 case FIELD, ENUM_CONSTANT -> { 1031 if (sym.name.equals(names._this) || sym.name.equals(names._super)) { 1032 result = thisValue(); 1033 } else { 1034 FieldRef fr = symbolToFieldRef(sym, symbolSiteType(sym)); 1035 TypeElement resultType = typeToTypeElement(sym.type); 1036 if (sym.isStatic()) { 1037 result = append(JavaOp.fieldLoad(resultType, fr)); 1038 } else { 1039 result = append(JavaOp.fieldLoad(resultType, fr, thisValue())); 1040 } 1041 } 1042 } 1043 case INTERFACE, CLASS, ENUM -> { 1044 result = null; 1045 } 1046 default -> { 1047 // @@@ Cannot reach here? 1048 throw unsupported(tree); 1049 } 1050 } 1051 } 1052 1053 private Value loadVar(Symbol sym) { 1054 Value varOp = varOpValue(sym); 1055 Assert.check(varOp.type() instanceof VarType); 1056 return append(CoreOp.varLoad(varOp)); 1057 } 1058 1059 @Override 1060 public void visitTypeIdent(JCTree.JCPrimitiveTypeTree tree) { 1061 result = null; 1062 } 1063 1064 @Override 1065 public void visitTypeArray(JCTree.JCArrayTypeTree tree) { 1066 result = null; // MyType[].class is handled in visitSelect just as MyType.class 1067 } 1068 1069 @Override 1070 public void visitSelect(JCFieldAccess tree) { 1071 // Visited only for read access 1072 1073 Type qualifierTarget = qualifierTarget(tree); 1074 // @@@: might cause redundant load if accessed symbol is static but the qualifier is not a type 1075 Value receiver = toValue(tree.selected); 1076 1077 if (tree.name.equals(names._class)) { 1078 result = append(CoreOp.constant(JavaType.J_L_CLASS, typeToTypeElement(tree.selected.type))); 1079 } else if (types.isArray(tree.selected.type)) { 1080 if (tree.sym.equals(syms.lengthVar)) { 1081 result = append(JavaOp.arrayLength(receiver)); 1082 } else { 1083 // Should not reach here 1084 throw unsupported(tree); 1085 } 1086 } else { 1087 Symbol sym = tree.sym; 1088 switch (sym.getKind()) { 1089 case FIELD, ENUM_CONSTANT -> { 1090 if (sym.name.equals(names._this) || sym.name.equals(names._super)) { 1091 result = thisValue(); 1092 } else { 1093 FieldRef fr = symbolToFieldRef(sym, qualifierTarget.hasTag(NONE) ? 1094 tree.selected.type : qualifierTarget); 1095 TypeElement resultType = typeToTypeElement(types.memberType(tree.selected.type, sym)); 1096 if (sym.isStatic()) { 1097 result = append(JavaOp.fieldLoad(resultType, fr)); 1098 } else { 1099 result = append(JavaOp.fieldLoad(resultType, fr, receiver)); 1100 } 1101 } 1102 } 1103 case INTERFACE, CLASS, ENUM -> { 1104 result = null; 1105 } 1106 default -> { 1107 // @@@ Cannot reach here? 1108 throw unsupported(tree); 1109 } 1110 } 1111 } 1112 } 1113 1114 @Override 1115 public void visitIndexed(JCArrayAccess tree) { 1116 // Visited only for read access 1117 1118 Value array = toValue(tree.indexed); 1119 1120 Value index = toValue(tree.index, typeElementToType(JavaType.INT)); 1121 1122 result = append(JavaOp.arrayLoadOp(array, index)); 1123 } 1124 1125 @Override 1126 public void visitApply(JCTree.JCMethodInvocation tree) { 1127 // @@@ Symbol.externalType, for use with inner classes 1128 1129 // @@@ this.xyz(...) calls in a constructor 1130 1131 JCTree meth = TreeInfo.skipParens(tree.meth); 1132 switch (meth.getTag()) { 1133 case IDENT: { 1134 JCIdent access = (JCIdent) meth; 1135 1136 Symbol sym = access.sym; 1137 List<Value> args = new ArrayList<>(); 1138 JavaOp.InvokeOp.InvokeKind ik; 1139 if (!sym.isStatic()) { 1140 ik = JavaOp.InvokeOp.InvokeKind.INSTANCE; 1141 args.add(thisValue()); 1142 } else { 1143 ik = JavaOp.InvokeOp.InvokeKind.STATIC; 1144 } 1145 1146 args.addAll(scanMethodArguments(tree.args, tree.meth.type, tree.varargsElement)); 1147 1148 MethodRef mr = symbolToMethodRef(sym, symbolSiteType(sym)); 1149 Value res = append(JavaOp.invoke(ik, tree.varargsElement != null, 1150 typeToTypeElement(meth.type.getReturnType()), mr, args)); 1151 if (sym.type.getReturnType().getTag() != TypeTag.VOID) { 1152 result = res; 1153 } 1154 break; 1155 } 1156 case SELECT: { 1157 JCFieldAccess access = (JCFieldAccess) meth; 1158 1159 Type qualifierTarget = qualifierTarget(access); 1160 Value receiver = toValue(access.selected, qualifierTarget); 1161 1162 Symbol sym = access.sym; 1163 List<Value> args = new ArrayList<>(); 1164 JavaOp.InvokeOp.InvokeKind ik; 1165 if (!sym.isStatic()) { 1166 args.add(receiver); 1167 // @@@ expr.super(...) for inner class super constructor calls 1168 ik = switch (access.selected) { 1169 case JCIdent i when i.sym.name.equals(names._super) -> JavaOp.InvokeOp.InvokeKind.SUPER; 1170 case JCFieldAccess fa when fa.sym.name.equals(names._super) -> JavaOp.InvokeOp.InvokeKind.SUPER; 1171 default -> JavaOp.InvokeOp.InvokeKind.INSTANCE; 1172 }; 1173 } else { 1174 ik = JavaOp.InvokeOp.InvokeKind.STATIC; 1175 } 1176 1177 args.addAll(scanMethodArguments(tree.args, tree.meth.type, tree.varargsElement)); 1178 1179 MethodRef mr = symbolToMethodRef(sym, qualifierTarget.hasTag(NONE) ? 1180 access.selected.type : qualifierTarget); 1181 JavaType returnType = typeToTypeElement(meth.type.getReturnType()); 1182 JavaOp.InvokeOp iop = JavaOp.invoke(ik, tree.varargsElement != null, 1183 returnType, mr, args); 1184 Value res = append(iop); 1185 if (sym.type.getReturnType().getTag() != TypeTag.VOID) { 1186 result = res; 1187 } 1188 break; 1189 } 1190 default: 1191 unsupported(meth); 1192 } 1193 } 1194 1195 List<Value> scanMethodArguments(List<JCExpression> args, Type methodType, Type varargsElement) { 1196 ListBuffer<Value> argValues = new ListBuffer<>(); 1197 com.sun.tools.javac.util.List<Type> targetTypes = methodType.getParameterTypes(); 1198 if (varargsElement != null) { 1199 targetTypes = targetTypes.reverse().tail; 1200 for (int i = 0 ; i < args.size() - (methodType.getParameterTypes().size() - 1) ; i++) { 1201 targetTypes = targetTypes.prepend(varargsElement); 1202 } 1203 targetTypes = targetTypes.reverse(); 1204 } 1205 1206 for (JCTree.JCExpression arg : args) { 1207 argValues.add(toValue(arg, targetTypes.head)); 1208 targetTypes = targetTypes.tail; 1209 } 1210 return argValues.toList(); 1211 } 1212 1213 @Override 1214 public void visitReference(JCTree.JCMemberReference tree) { 1215 MemberReferenceToLambda memberReferenceToLambda = new MemberReferenceToLambda(tree, currentClassSym); 1216 JCVariableDecl recv = memberReferenceToLambda.receiverVar(); 1217 if (recv != null) { 1218 scan(recv); 1219 } 1220 scan(memberReferenceToLambda.lambda()); 1221 } 1222 1223 Type qualifierTarget(JCFieldAccess tree) { 1224 Type selectedType = types.skipTypeVars(tree.selected.type, true); 1225 return selectedType.isCompound() ? 1226 tree.sym.owner.type : 1227 Type.noType; 1228 } 1229 1230 @Override 1231 public void visitTypeCast(JCTree.JCTypeCast tree) { 1232 Value v = toValue(tree.expr); 1233 1234 Type expressionType = tree.expr.type; 1235 Type type = tree.type; 1236 if (expressionType.isPrimitive() && type.isPrimitive()) { 1237 if (expressionType.equals(type)) { 1238 // Redundant cast 1239 result = v; 1240 } else { 1241 result = append(JavaOp.conv(typeToTypeElement(type), v)); 1242 } 1243 } else if (expressionType.isPrimitive() || type.isPrimitive()) { 1244 result = convert(v, tree.type); 1245 } else if (!expressionType.hasTag(BOT) && 1246 types.isAssignable(expressionType, type)) { 1247 // Redundant cast 1248 result = v; 1249 } else { 1250 // Reference cast 1251 JavaType jt = typeToTypeElement(types.erasure(type)); 1252 result = append(JavaOp.cast(typeToTypeElement(type), jt, v)); 1253 } 1254 } 1255 1256 @Override 1257 public void visitTypeTest(JCTree.JCInstanceOf tree) { 1258 Value target = toValue(tree.expr); 1259 1260 if (tree.pattern.getTag() != Tag.IDENT) { 1261 result = scanPattern(tree.getPattern(), target); 1262 } else { 1263 result = append(JavaOp.instanceOf(typeToTypeElement(tree.pattern.type), target)); 1264 } 1265 } 1266 1267 Value scanPattern(JCTree.JCPattern pattern, Value target) { 1268 // Type of pattern 1269 JavaType patternType; 1270 if (pattern instanceof JCTree.JCBindingPattern p) { 1271 patternType = JavaOp.Pattern.bindingType(typeToTypeElement(p.type)); 1272 } else if (pattern instanceof JCTree.JCRecordPattern p) { 1273 patternType = JavaOp.Pattern.recordType(typeToTypeElement(p.record.type)); 1274 } else { 1275 throw unsupported(pattern); 1276 } 1277 1278 // Push pattern body 1279 pushBody(pattern, CoreType.functionType(patternType)); 1280 1281 // @@@ Assumes just pattern nodes, likely will change when method patterns are supported 1282 // that have expressions for any arguments (which perhaps in turn may have pattern expressions) 1283 List<JCVariableDecl> variables = new ArrayList<>(); 1284 class PatternScanner extends FilterScanner { 1285 1286 private Value result; 1287 1288 public PatternScanner() { 1289 super(Set.of(Tag.BINDINGPATTERN, Tag.RECORDPATTERN, Tag.ANYPATTERN)); 1290 } 1291 1292 @Override 1293 public void visitBindingPattern(JCTree.JCBindingPattern binding) { 1294 JCVariableDecl var = binding.var; 1295 variables.add(var); 1296 boolean unnamedPatternVariable = var.name.isEmpty(); 1297 String bindingName = unnamedPatternVariable ? null : var.name.toString(); 1298 result = append(JavaOp.typePattern(typeToTypeElement(var.type), bindingName)); 1299 } 1300 1301 @Override 1302 public void visitRecordPattern(JCTree.JCRecordPattern record) { 1303 // @@@ Is always Identifier to record? 1304 // scan(record.deconstructor); 1305 1306 List<Value> nestedValues = new ArrayList<>(); 1307 for (JCTree.JCPattern jcPattern : record.nested) { 1308 // @@@ when we support ANYPATTERN, we must add result of toValue only if it's non-null 1309 // because passing null to recordPattern methods will cause an error 1310 nestedValues.add(toValue(jcPattern)); 1311 } 1312 1313 result = append(JavaOp.recordPattern(symbolToRecordTypeRef(record.record), nestedValues)); 1314 } 1315 1316 @Override 1317 public void visitAnyPattern(JCTree.JCAnyPattern anyPattern) { 1318 result = append(JavaOp.matchAllPattern()); 1319 } 1320 1321 Value toValue(JCTree tree) { 1322 result = null; 1323 scan(tree); 1324 return result; 1325 } 1326 } 1327 // Scan pattern 1328 Value patternValue = new PatternScanner().toValue(pattern); 1329 append(CoreOp.core_yield(patternValue)); 1330 Body.Builder patternBody = stack.body; 1331 1332 // Pop body 1333 popBody(); 1334 1335 // Find nearest ancestor body stack element associated with a statement tree 1336 // @@@ Strengthen check of tree? 1337 BodyStack _variablesStack = stack; 1338 while (!(_variablesStack.tree instanceof JCTree.JCStatement)) { 1339 _variablesStack = _variablesStack.parent; 1340 } 1341 BodyStack variablesStack = _variablesStack; 1342 1343 // Create pattern var ops for pattern variables using the 1344 // builder associated with the nearest statement tree 1345 for (JCVariableDecl jcVar : variables) { 1346 // @@@ use uninitialized variable 1347 Value defaultValue = variablesStack.block.op(defaultValue(jcVar.type)); 1348 Value init = convert(defaultValue, jcVar.type); 1349 Op.Result op = variablesStack.block.op(CoreOp.var(jcVar.name.toString(), typeToTypeElement(jcVar.type), init)); 1350 variablesStack.localToOp.put(jcVar.sym, op); 1351 } 1352 1353 // Create pattern descriptor 1354 List<JavaType> patternDescParams = variables.stream().map(var -> typeToTypeElement(var.type)).toList(); 1355 FunctionType matchFuncType = CoreType.functionType(JavaType.VOID, patternDescParams); 1356 1357 // Create the match body, assigning pattern values to pattern variables 1358 Body.Builder matchBody = Body.Builder.of(patternBody.ancestorBody(), matchFuncType); 1359 Block.Builder matchBuilder = matchBody.entryBlock(); 1360 for (int i = 0; i < variables.size(); i++) { 1361 Value v = matchBuilder.parameters().get(i); 1362 Value var = variablesStack.localToOp.get(variables.get(i).sym); 1363 matchBuilder.op(CoreOp.varStore(var, v)); 1364 } 1365 matchBuilder.op(CoreOp.core_yield()); 1366 1367 // Create the match operation 1368 return append(JavaOp.match(target, patternBody, matchBody)); 1369 } 1370 1371 @Override 1372 public void visitNewClass(JCTree.JCNewClass tree) { 1373 if (tree.def != null) { 1374 scan(tree.def); 1375 } 1376 1377 // @@@ Support local classes in pre-construction contexts 1378 if (tree.type.tsym.isDirectlyOrIndirectlyLocal() && (tree.type.tsym.flags() & NOOUTERTHIS) != 0) { 1379 throw unsupported(tree); 1380 } 1381 1382 List<TypeElement> argtypes = new ArrayList<>(); 1383 Type type = tree.type; 1384 Type outer = type.getEnclosingType(); 1385 List<Value> args = new ArrayList<>(); 1386 if (!outer.hasTag(TypeTag.NONE)) { 1387 // Obtain outer value for inner class, and add as first argument 1388 JCTree.JCExpression encl = tree.encl; 1389 Value outerInstance; 1390 if (encl == null) { 1391 outerInstance = thisValue(); 1392 } else { 1393 outerInstance = toValue(tree.encl); 1394 } 1395 args.add(outerInstance); 1396 argtypes.add(outerInstance.type()); 1397 } 1398 if (tree.type.tsym.isDirectlyOrIndirectlyLocal()) { 1399 for (Symbol c : localCaptures.get(tree.type.tsym)) { 1400 args.add(loadVar(c)); 1401 argtypes.add(symbolToErasedDesc(c)); 1402 } 1403 } 1404 1405 // Create erased method type reference for constructor, where 1406 // the return type declares the class to instantiate 1407 // We need to manually construct the constructor reference, 1408 // as the signature of the constructor symbol is not augmented 1409 // with enclosing this and captured params. 1410 MethodRef methodRef = symbolToMethodRef(tree.constructor); 1411 argtypes.addAll(methodRef.type().parameterTypes()); 1412 FunctionType constructorType = CoreType.functionType( 1413 symbolToErasedDesc(tree.constructor.owner), 1414 argtypes); 1415 ConstructorRef constructorRef = ConstructorRef.constructor(constructorType); 1416 1417 args.addAll(scanMethodArguments(tree.args, tree.constructorType, tree.varargsElement)); 1418 1419 result = append(JavaOp.new_(tree.varargsElement != null, typeToTypeElement(type), constructorRef, args)); 1420 } 1421 1422 @Override 1423 public void visitNewArray(JCTree.JCNewArray tree) { 1424 if (tree.elems != null) { 1425 int length = tree.elems.size(); 1426 Op.Result a = append(JavaOp.newArray( 1427 typeToTypeElement(tree.type), 1428 append(CoreOp.constant(JavaType.INT, length)))); 1429 int i = 0; 1430 for (JCExpression elem : tree.elems) { 1431 Value element = toValue(elem, types.elemtype(tree.type)); 1432 append(JavaOp.arrayStoreOp( 1433 a, 1434 append(CoreOp.constant(JavaType.INT, i)), 1435 element)); 1436 i++; 1437 } 1438 1439 result = a; 1440 } else { 1441 List<Value> indexes = new ArrayList<>(); 1442 for (JCTree.JCExpression dim : tree.dims) { 1443 indexes.add(toValue(dim)); 1444 } 1445 1446 JavaType arrayType = typeToTypeElement(tree.type); 1447 ConstructorRef constructorRef = ConstructorRef.constructor(arrayType, 1448 indexes.stream().map(Value::type).toList()); 1449 result = append(JavaOp.new_(constructorRef, indexes)); 1450 } 1451 } 1452 1453 @Override 1454 public void visitLambda(JCTree.JCLambda tree) { 1455 FunctionalExpressionKind kind = functionalKind(tree); 1456 final FunctionType lambdaType = switch (kind) { 1457 case QUOTED_STRUCTURAL -> typeToFunctionType(tree.target); 1458 default -> typeToFunctionType(types.findDescriptorType(tree.target)); 1459 }; 1460 1461 // Push quoted body 1462 // We can either be explicitly quoted or a structural quoted expression 1463 // within some larger reflected code 1464 if (isQuoted || kind == FunctionalExpressionKind.QUOTED_STRUCTURAL) { 1465 pushBody(tree.body, CoreType.FUNCTION_TYPE_VOID); 1466 } 1467 1468 // Push lambda body 1469 pushBody(tree.body, lambdaType); 1470 1471 // Map lambda parameters to varOp values 1472 for (int i = 0; i < tree.params.size(); i++) { 1473 JCVariableDecl p = tree.params.get(i); 1474 Op.Result paramOp = append(CoreOp.var( 1475 p.name.toString(), 1476 stack.block.parameters().get(i))); 1477 stack.localToOp.put(p.sym, paramOp); 1478 } 1479 1480 // Scan the lambda body 1481 if (tree.getBodyKind() == LambdaExpressionTree.BodyKind.EXPRESSION) { 1482 Value exprVal = toValue(((JCExpression) tree.body), tree.getDescriptorType(types).getReturnType()); 1483 if (!tree.body.type.hasTag(TypeTag.VOID)) { 1484 append(CoreOp.return_(exprVal)); 1485 } else { 1486 appendTerminating(CoreOp::return_); 1487 } 1488 } else { 1489 Type prevBodyTarget = bodyTarget; 1490 try { 1491 bodyTarget = tree.getDescriptorType(types).getReturnType(); 1492 toValue(((JCTree.JCStatement) tree.body)); 1493 appendReturnOrUnreachable(tree.body); 1494 } finally { 1495 bodyTarget = prevBodyTarget; 1496 } 1497 } 1498 1499 Op lambdaOp = switch (kind) { 1500 case QUOTED_STRUCTURAL -> { 1501 yield CoreOp.closure(stack.body); 1502 } 1503 case QUOTABLE, NOT_QUOTED -> { 1504 // Get the functional interface type 1505 JavaType fiType = typeToTypeElement(tree.target); 1506 // build functional lambda 1507 yield JavaOp.lambda(fiType, stack.body); 1508 } 1509 }; 1510 1511 // Pop lambda body 1512 popBody(); 1513 1514 Value lambdaResult; 1515 if (isQuoted) { 1516 lambdaResult = append(lambdaOp, generateLocation(tree, true)); 1517 } else { 1518 lambdaResult = append(lambdaOp); 1519 } 1520 1521 if (isQuoted || kind == FunctionalExpressionKind.QUOTED_STRUCTURAL) { 1522 append(CoreOp.core_yield(lambdaResult)); 1523 CoreOp.QuotedOp quotedOp = CoreOp.quoted(stack.body); 1524 1525 // Pop quoted body 1526 popBody(); 1527 1528 lambdaResult = append(quotedOp); 1529 } 1530 1531 result = lambdaResult; 1532 } 1533 1534 @Override 1535 public void visitIf(JCTree.JCIf tree) { 1536 List<Body.Builder> bodies = new ArrayList<>(); 1537 1538 while (tree != null) { 1539 JCTree.JCExpression cond = TreeInfo.skipParens(tree.cond); 1540 1541 // Push if condition 1542 pushBody(cond, 1543 CoreType.functionType(JavaType.BOOLEAN)); 1544 Value last = toValue(cond); 1545 last = convert(last, typeElementToType(JavaType.BOOLEAN)); 1546 // Yield the boolean result of the condition 1547 append(CoreOp.core_yield(last)); 1548 bodies.add(stack.body); 1549 1550 // Pop if condition 1551 popBody(); 1552 1553 // Push if body 1554 pushBody(tree.thenpart, CoreType.FUNCTION_TYPE_VOID); 1555 1556 scan(tree.thenpart); 1557 appendTerminating(CoreOp::core_yield); 1558 bodies.add(stack.body); 1559 1560 // Pop if body 1561 popBody(); 1562 1563 JCTree.JCStatement elsepart = tree.elsepart; 1564 if (elsepart == null) { 1565 tree = null; 1566 } else if (elsepart.getTag() == Tag.IF) { 1567 tree = (JCTree.JCIf) elsepart; 1568 } else { 1569 // Push else body 1570 pushBody(elsepart, CoreType.FUNCTION_TYPE_VOID); 1571 1572 scan(elsepart); 1573 appendTerminating(CoreOp::core_yield); 1574 bodies.add(stack.body); 1575 1576 // Pop else body 1577 popBody(); 1578 1579 tree = null; 1580 } 1581 } 1582 1583 append(JavaOp.if_(bodies)); 1584 result = null; 1585 } 1586 1587 @Override 1588 public void visitSwitchExpression(JCTree.JCSwitchExpression tree) { 1589 Value target = toValue(tree.selector); 1590 1591 Type switchType = adaptBottom(tree.type); 1592 FunctionType caseBodyType = CoreType.functionType(typeToTypeElement(switchType)); 1593 1594 List<Body.Builder> bodies = visitSwitchStatAndExpr(tree, tree.selector, target, tree.cases, caseBodyType, 1595 !tree.hasUnconditionalPattern); 1596 1597 result = append(JavaOp.switchExpression(caseBodyType.returnType(), target, bodies)); 1598 } 1599 1600 @Override 1601 public void visitSwitch(JCTree.JCSwitch tree) { 1602 Value target = toValue(tree.selector); 1603 1604 FunctionType actionType = CoreType.FUNCTION_TYPE_VOID; 1605 1606 List<Body.Builder> bodies = visitSwitchStatAndExpr(tree, tree.selector, target, tree.cases, actionType, 1607 tree.patternSwitch && !tree.hasUnconditionalPattern); 1608 1609 result = append(JavaOp.switchStatement(target, bodies)); 1610 } 1611 1612 private List<Body.Builder> visitSwitchStatAndExpr(JCTree tree, JCExpression selector, Value target, 1613 List<JCTree.JCCase> cases, FunctionType caseBodyType, 1614 boolean isDefaultCaseNeeded) { 1615 List<Body.Builder> bodies = new ArrayList<>(); 1616 Body.Builder defaultLabel = null; 1617 Body.Builder defaultBody = null; 1618 1619 for (JCTree.JCCase c : cases) { 1620 Body.Builder caseLabel = visitCaseLabel(tree, selector, target, c); 1621 Body.Builder caseBody = visitCaseBody(tree, c, caseBodyType); 1622 1623 if (c.labels.head instanceof JCTree.JCDefaultCaseLabel) { 1624 defaultLabel = caseLabel; 1625 defaultBody = caseBody; 1626 } else { 1627 bodies.add(caseLabel); 1628 bodies.add(caseBody); 1629 } 1630 } 1631 1632 if (defaultLabel != null) { 1633 bodies.add(defaultLabel); 1634 bodies.add(defaultBody); 1635 } else if (isDefaultCaseNeeded) { 1636 // label 1637 pushBody(tree, CoreType.functionType(JavaType.BOOLEAN)); 1638 append(CoreOp.core_yield(append(CoreOp.constant(JavaType.BOOLEAN, true)))); 1639 bodies.add(stack.body); 1640 popBody(); 1641 1642 // body 1643 pushBody(tree, caseBodyType); 1644 append(JavaOp.throw_( 1645 append(JavaOp.new_(ConstructorRef.constructor(MatchException.class))) 1646 )); 1647 bodies.add(stack.body); 1648 popBody(); 1649 } 1650 1651 return bodies; 1652 } 1653 1654 private Body.Builder visitCaseLabel(JCTree tree, JCExpression selector, Value target, JCTree.JCCase c) { 1655 Body.Builder body; 1656 FunctionType caseLabelType = CoreType.functionType(JavaType.BOOLEAN, target.type()); 1657 1658 JCTree.JCCaseLabel headCl = c.labels.head; 1659 if (headCl instanceof JCTree.JCPatternCaseLabel pcl) { 1660 if (c.labels.size() > 1) { 1661 throw unsupported(c); 1662 } 1663 1664 pushBody(pcl, caseLabelType); 1665 1666 Value localTarget = stack.block.parameters().get(0); 1667 final Value localResult; 1668 if (c.guard != null) { 1669 List<Body.Builder> clBodies = new ArrayList<>(); 1670 1671 pushBody(pcl.pat, CoreType.functionType(JavaType.BOOLEAN)); 1672 Value patVal = scanPattern(pcl.pat, localTarget); 1673 append(CoreOp.core_yield(patVal)); 1674 clBodies.add(stack.body); 1675 popBody(); 1676 1677 pushBody(c.guard, CoreType.functionType(JavaType.BOOLEAN)); 1678 append(CoreOp.core_yield(toValue(c.guard))); 1679 clBodies.add(stack.body); 1680 popBody(); 1681 1682 localResult = append(JavaOp.conditionalAnd(clBodies)); 1683 } else { 1684 localResult = scanPattern(pcl.pat, localTarget); 1685 } 1686 // Yield the boolean result of the condition 1687 append(CoreOp.core_yield(localResult)); 1688 body = stack.body; 1689 1690 // Pop label 1691 popBody(); 1692 } else if (headCl instanceof JCTree.JCConstantCaseLabel ccl) { 1693 pushBody(headCl, caseLabelType); 1694 1695 Value localTarget = stack.block.parameters().get(0); 1696 final Value localResult; 1697 if (c.labels.size() == 1) { 1698 Value expr = toValue(ccl.expr); 1699 // per java spec, constant type is compatible with the type of the selector expression 1700 // so, we convert constant to the type of the selector expression 1701 expr = convert(expr, selector.type); 1702 if (selector.type.isPrimitive()) { 1703 localResult = append(JavaOp.eq(localTarget, expr)); 1704 } else { 1705 localResult = append(JavaOp.invoke( 1706 MethodRef.method(Objects.class, "equals", boolean.class, Object.class, Object.class), 1707 localTarget, expr)); 1708 } 1709 } else { 1710 List<Body.Builder> clBodies = new ArrayList<>(); 1711 for (JCTree.JCCaseLabel cl : c.labels) { 1712 ccl = (JCTree.JCConstantCaseLabel) cl; 1713 pushBody(ccl, CoreType.functionType(JavaType.BOOLEAN)); 1714 1715 Value expr = toValue(ccl.expr); 1716 expr = convert(expr, selector.type); 1717 final Value labelResult; 1718 if (selector.type.isPrimitive()) { 1719 labelResult = append(JavaOp.eq(localTarget, expr)); 1720 } else { 1721 labelResult = append(JavaOp.invoke( 1722 MethodRef.method(Objects.class, "equals", boolean.class, Object.class, Object.class), 1723 localTarget, expr)); 1724 } 1725 1726 append(CoreOp.core_yield(labelResult)); 1727 clBodies.add(stack.body); 1728 1729 // Pop label 1730 popBody(); 1731 } 1732 1733 localResult = append(JavaOp.conditionalOr(clBodies)); 1734 } 1735 1736 append(CoreOp.core_yield(localResult)); 1737 body = stack.body; 1738 1739 // Pop labels 1740 popBody(); 1741 } else if (headCl instanceof JCTree.JCDefaultCaseLabel) { 1742 // @@@ Do we need to model the default label body? 1743 pushBody(headCl, CoreType.functionType(JavaType.BOOLEAN)); 1744 1745 append(CoreOp.core_yield(append(CoreOp.constant(JavaType.BOOLEAN, true)))); 1746 body = stack.body; 1747 1748 // Pop label 1749 popBody(); 1750 } else { 1751 throw unsupported(tree); 1752 } 1753 1754 return body; 1755 } 1756 1757 private Body.Builder visitCaseBody(JCTree tree, JCTree.JCCase c, FunctionType caseBodyType) { 1758 Body.Builder body = null; 1759 Type yieldType = tree.type != null ? adaptBottom(tree.type) : null; 1760 1761 JCTree.JCCaseLabel headCl = c.labels.head; 1762 switch (c.caseKind) { 1763 case RULE -> { 1764 pushBody(c.body, caseBodyType); 1765 1766 if (c.body instanceof JCTree.JCExpression e) { 1767 Value bodyVal = toValue(e, yieldType); 1768 append(CoreOp.core_yield(bodyVal)); 1769 } else if (c.body instanceof JCTree.JCStatement s){ // this includes Block 1770 // Otherwise there is a yield statement 1771 Type prevBodyTarget = bodyTarget; 1772 try { 1773 bodyTarget = yieldType; 1774 toValue(s); 1775 } finally { 1776 bodyTarget = prevBodyTarget; 1777 } 1778 appendTerminating(c.completesNormally ? CoreOp::core_yield : CoreOp::unreachable); 1779 } 1780 body = stack.body; 1781 1782 // Pop block 1783 popBody(); 1784 } 1785 case STATEMENT -> { 1786 // @@@ Avoid nesting for a single block? Goes against "say what you see" 1787 // boolean oneBlock = c.stats.size() == 1 && c.stats.head instanceof JCBlock; 1788 pushBody(c, caseBodyType); 1789 1790 scan(c.stats); 1791 1792 appendTerminating(c.completesNormally ? 1793 headCl instanceof JCTree.JCDefaultCaseLabel ? CoreOp::core_yield : JavaOp::switchFallthroughOp 1794 : CoreOp::unreachable); 1795 1796 body = stack.body; 1797 1798 // Pop block 1799 popBody(); 1800 } 1801 } 1802 return body; 1803 } 1804 1805 @Override 1806 public void visitYield(JCTree.JCYield tree) { 1807 Value retVal = toValue(tree.value, bodyTarget); 1808 if (retVal == null) { 1809 result = append(JavaOp.java_yield()); 1810 } else { 1811 result = append(JavaOp.java_yield(retVal)); 1812 } 1813 } 1814 1815 @Override 1816 public void visitWhileLoop(JCTree.JCWhileLoop tree) { 1817 // @@@ Patterns 1818 JCTree.JCExpression cond = TreeInfo.skipParens(tree.cond); 1819 1820 // Push while condition 1821 pushBody(cond, CoreType.functionType(JavaType.BOOLEAN)); 1822 Value last = toValue(cond); 1823 // Yield the boolean result of the condition 1824 last = convert(last, typeElementToType(JavaType.BOOLEAN)); 1825 append(CoreOp.core_yield(last)); 1826 Body.Builder condition = stack.body; 1827 1828 // Pop while condition 1829 popBody(); 1830 1831 // Push while body 1832 pushBody(tree.body, CoreType.FUNCTION_TYPE_VOID); 1833 scan(tree.body); 1834 appendTerminating(JavaOp::continue_); 1835 Body.Builder body = stack.body; 1836 1837 // Pop while body 1838 popBody(); 1839 1840 append(JavaOp.while_(condition, body)); 1841 result = null; 1842 } 1843 1844 @Override 1845 public void visitDoLoop(JCTree.JCDoWhileLoop tree) { 1846 // @@@ Patterns 1847 JCTree.JCExpression cond = TreeInfo.skipParens(tree.cond); 1848 1849 // Push while body 1850 pushBody(tree.body, CoreType.FUNCTION_TYPE_VOID); 1851 scan(tree.body); 1852 appendTerminating(JavaOp::continue_); 1853 Body.Builder body = stack.body; 1854 1855 // Pop while body 1856 popBody(); 1857 1858 // Push while condition 1859 pushBody(cond, CoreType.functionType(JavaType.BOOLEAN)); 1860 Value last = toValue(cond); 1861 last = convert(last, typeElementToType(JavaType.BOOLEAN)); 1862 // Yield the boolean result of the condition 1863 append(CoreOp.core_yield(last)); 1864 Body.Builder condition = stack.body; 1865 1866 // Pop while condition 1867 popBody(); 1868 1869 append(JavaOp.doWhile(body, condition)); 1870 result = null; 1871 } 1872 1873 @Override 1874 public void visitForeachLoop(JCTree.JCEnhancedForLoop tree) { 1875 // Push expression 1876 pushBody(tree.expr, CoreType.functionType(typeToTypeElement(tree.expr.type))); 1877 Value last = toValue(tree.expr); 1878 // Yield the Iterable result of the expression 1879 append(CoreOp.core_yield(last)); 1880 Body.Builder expression = stack.body; 1881 1882 // Pop expression 1883 popBody(); 1884 1885 JCVariableDecl var = tree.getVariable(); 1886 JavaType eType = typeToTypeElement(var.type); 1887 VarType varEType = CoreType.varType(typeToTypeElement(var.type)); 1888 1889 // Push init 1890 // @@@ When lhs assignment is a pattern we embed the pattern match into the init body and 1891 // return the bound variables 1892 pushBody(var, CoreType.functionType(varEType, eType)); 1893 Op.Result varEResult = append(CoreOp.var(var.name.toString(), stack.block.parameters().get(0))); 1894 append(CoreOp.core_yield(varEResult)); 1895 Body.Builder init = stack.body; 1896 // Pop init 1897 popBody(); 1898 1899 // Push body 1900 pushBody(tree.body, CoreType.functionType(JavaType.VOID, varEType)); 1901 stack.localToOp.put(var.sym, stack.block.parameters().get(0)); 1902 1903 scan(tree.body); 1904 appendTerminating(JavaOp::continue_); 1905 Body.Builder body = stack.body; 1906 // Pop body 1907 popBody(); 1908 1909 append(JavaOp.enhancedFor(expression, init, body)); 1910 result = null; 1911 } 1912 1913 @Override 1914 public void visitForLoop(JCTree.JCForLoop tree) { 1915 class VarDefScanner extends FilterScanner { 1916 final List<JCVariableDecl> decls; 1917 1918 public VarDefScanner() { 1919 super(Set.of(Tag.VARDEF)); 1920 this.decls = new ArrayList<>(); 1921 } 1922 1923 @Override 1924 public void visitVarDef(JCVariableDecl tree) { 1925 decls.add(tree); 1926 } 1927 1928 void mapVarsToBlockArguments() { 1929 for (int i = 0; i < decls.size(); i++) { 1930 stack.localToOp.put(decls.get(i).sym, stack.block.parameters().get(i)); 1931 } 1932 } 1933 1934 List<VarType> varTypes() { 1935 return decls.stream() 1936 .map(t -> CoreType.varType(typeToTypeElement(t.type))) 1937 .toList(); 1938 } 1939 1940 List<Value> varValues() { 1941 return decls.stream() 1942 .map(t -> stack.localToOp.get(t.sym)) 1943 .toList(); 1944 } 1945 } 1946 1947 // Scan local variable declarations 1948 VarDefScanner vds = new VarDefScanner(); 1949 vds.scan(tree.init); 1950 List<VarType> varTypes = vds.varTypes(); 1951 1952 // Push init 1953 if (varTypes.size() > 1) { 1954 pushBody(null, CoreType.functionType(CoreType.tupleType(varTypes))); 1955 scan(tree.init); 1956 1957 // Capture all local variable declarations in tuple 1958 append(CoreOp.core_yield(append(CoreOp.tuple(vds.varValues())))); 1959 } else if (varTypes.size() == 1) { 1960 pushBody(null, CoreType.functionType(varTypes.get(0))); 1961 scan(tree.init); 1962 1963 append(CoreOp.core_yield(vds.varValues().get(0))); 1964 } else { 1965 pushBody(null, CoreType.FUNCTION_TYPE_VOID); 1966 scan(tree.init); 1967 1968 append(CoreOp.core_yield()); 1969 } 1970 Body.Builder init = stack.body; 1971 1972 // Pop init 1973 popBody(); 1974 1975 // Push cond 1976 pushBody(tree.cond, CoreType.functionType(JavaType.BOOLEAN, varTypes)); 1977 if (tree.cond != null) { 1978 vds.mapVarsToBlockArguments(); 1979 1980 Value last = toValue(tree.cond); 1981 // Yield the boolean result of the condition 1982 append(CoreOp.core_yield(last)); 1983 } else { 1984 append(CoreOp.core_yield(append(CoreOp.constant(JavaType.BOOLEAN, true)))); 1985 } 1986 Body.Builder cond = stack.body; 1987 1988 // Pop cond 1989 popBody(); 1990 1991 // Push update 1992 // @@@ tree.step is a List<JCStatement> 1993 pushBody(null, CoreType.functionType(JavaType.VOID, varTypes)); 1994 if (!tree.step.isEmpty()) { 1995 vds.mapVarsToBlockArguments(); 1996 1997 scan(tree.step); 1998 } 1999 append(CoreOp.core_yield()); 2000 Body.Builder update = stack.body; 2001 2002 // Pop update 2003 popBody(); 2004 2005 // Push body 2006 pushBody(tree.body, CoreType.functionType(JavaType.VOID, varTypes)); 2007 if (tree.body != null) { 2008 vds.mapVarsToBlockArguments(); 2009 2010 scan(tree.body); 2011 } 2012 appendTerminating(JavaOp::continue_); 2013 Body.Builder body = stack.body; 2014 2015 // Pop update 2016 popBody(); 2017 2018 append(JavaOp.for_(init, cond, update, body)); 2019 result = null; 2020 } 2021 2022 @Override 2023 public void visitConditional(JCTree.JCConditional tree) { 2024 List<Body.Builder> bodies = new ArrayList<>(); 2025 2026 JCTree.JCExpression cond = TreeInfo.skipParens(tree.cond); 2027 2028 // Push condition 2029 pushBody(cond, 2030 CoreType.functionType(JavaType.BOOLEAN)); 2031 Value condVal = toValue(cond); 2032 // Yield the boolean result of the condition 2033 append(CoreOp.core_yield(condVal)); 2034 bodies.add(stack.body); 2035 2036 // Pop condition 2037 popBody(); 2038 2039 JCTree.JCExpression truepart = TreeInfo.skipParens(tree.truepart); 2040 2041 Type condType = adaptBottom(tree.type); 2042 2043 // Push true body 2044 pushBody(truepart, 2045 CoreType.functionType(typeToTypeElement(condType))); 2046 2047 Value trueVal = toValue(truepart, condType); 2048 // Yield the result 2049 append(CoreOp.core_yield(trueVal)); 2050 bodies.add(stack.body); 2051 2052 // Pop true body 2053 popBody(); 2054 2055 JCTree.JCExpression falsepart = TreeInfo.skipParens(tree.falsepart); 2056 2057 // Push false body 2058 pushBody(falsepart, 2059 CoreType.functionType(typeToTypeElement(condType))); 2060 2061 Value falseVal = toValue(falsepart, condType); 2062 // Yield the result 2063 append(CoreOp.core_yield(falseVal)); 2064 bodies.add(stack.body); 2065 2066 // Pop false body 2067 popBody(); 2068 2069 result = append(JavaOp.conditionalExpression(typeToTypeElement(condType), bodies)); 2070 } 2071 2072 private Type condType(JCExpression tree, Type type) { 2073 if (type.hasTag(BOT)) { 2074 return adaptBottom(tree.type); 2075 } else { 2076 return type; 2077 } 2078 } 2079 2080 private Type adaptBottom(Type type) { 2081 return type.hasTag(BOT) ? 2082 (pt.hasTag(NONE) ? syms.objectType : pt) : 2083 type; 2084 } 2085 2086 @Override 2087 public void visitAssert(JCAssert tree) { 2088 // assert <cond:body1> [detail:body2] 2089 2090 List<Body.Builder> bodies = new ArrayList<>(); 2091 JCTree.JCExpression cond = TreeInfo.skipParens(tree.cond); 2092 2093 // Push condition 2094 pushBody(cond, 2095 CoreType.functionType(JavaType.BOOLEAN)); 2096 Value condVal = toValue(cond); 2097 2098 // Yield the boolean result of the condition 2099 append(CoreOp.core_yield(condVal)); 2100 bodies.add(stack.body); 2101 2102 // Pop condition 2103 popBody(); 2104 2105 if (tree.detail != null) { 2106 JCTree.JCExpression detail = TreeInfo.skipParens(tree.detail); 2107 2108 pushBody(detail, 2109 CoreType.functionType(typeToTypeElement(tree.detail.type))); 2110 Value detailVal = toValue(detail); 2111 2112 append(CoreOp.core_yield(detailVal)); 2113 bodies.add(stack.body); 2114 2115 //Pop detail 2116 popBody(); 2117 } 2118 2119 result = append(JavaOp.assert_(bodies)); 2120 2121 } 2122 2123 @Override 2124 public void visitBlock(JCTree.JCBlock tree) { 2125 if (stack.tree == tree) { 2126 // Block is associated with the visit of a parent structure 2127 scan(tree.stats); 2128 } else { 2129 // Otherwise, independent block structure 2130 // Push block 2131 pushBody(tree, CoreType.FUNCTION_TYPE_VOID); 2132 scan(tree.stats); 2133 appendTerminating(CoreOp::core_yield); 2134 Body.Builder body = stack.body; 2135 2136 // Pop block 2137 popBody(); 2138 2139 append(JavaOp.block(body)); 2140 } 2141 result = null; 2142 } 2143 2144 @Override 2145 public void visitSynchronized(JCTree.JCSynchronized tree) { 2146 // Push expr 2147 pushBody(tree.lock, CoreType.functionType(typeToTypeElement(tree.lock.type))); 2148 Value last = toValue(tree.lock); 2149 append(CoreOp.core_yield(last)); 2150 Body.Builder expr = stack.body; 2151 2152 // Pop expr 2153 popBody(); 2154 2155 // Push body block 2156 pushBody(tree.body, CoreType.FUNCTION_TYPE_VOID); 2157 // Scan body block statements 2158 scan(tree.body.stats); 2159 appendTerminating(CoreOp::core_yield); 2160 Body.Builder blockBody = stack.body; 2161 2162 // Pop body block 2163 popBody(); 2164 2165 append(JavaOp.synchronized_(expr, blockBody)); 2166 } 2167 2168 @Override 2169 public void visitLabelled(JCTree.JCLabeledStatement tree) { 2170 // Push block 2171 pushBody(tree, CoreType.FUNCTION_TYPE_VOID); 2172 // Create constant for label 2173 String labelName = tree.label.toString(); 2174 Op.Result label = append(CoreOp.constant(JavaType.J_L_STRING, labelName)); 2175 // Set label on body stack 2176 stack.setLabel(labelName, label); 2177 scan(tree.body); 2178 appendTerminating(CoreOp::core_yield); 2179 Body.Builder body = stack.body; 2180 2181 // Pop block 2182 popBody(); 2183 2184 result = append(JavaOp.labeled(body)); 2185 } 2186 2187 @Override 2188 public void visitTry(JCTree.JCTry tree) { 2189 List<JCVariableDecl> rVariableDecls = new ArrayList<>(); 2190 List<TypeElement> rTypes = new ArrayList<>(); 2191 Body.Builder resources; 2192 if (!tree.resources.isEmpty()) { 2193 // Resources body returns a tuple that contains the resource variables/values 2194 // in order of declaration 2195 for (JCTree resource : tree.resources) { 2196 if (resource instanceof JCVariableDecl vdecl) { 2197 rVariableDecls.add(vdecl); 2198 rTypes.add(CoreType.varType(typeToTypeElement(vdecl.type))); 2199 } else { 2200 rTypes.add(typeToTypeElement(resource.type)); 2201 } 2202 } 2203 2204 // Push resources body 2205 pushBody(null, CoreType.functionType(CoreType.tupleType(rTypes))); 2206 2207 List<Value> rValues = new ArrayList<>(); 2208 for (JCTree resource : tree.resources) { 2209 if (resource instanceof JCTree.JCExpression e) { 2210 rValues.add(toValue(e)); 2211 } else if (resource instanceof JCTree.JCStatement s) { 2212 rValues.add(toValue(s)); 2213 } 2214 } 2215 2216 append(CoreOp.core_yield(append(CoreOp.tuple(rValues)))); 2217 resources = stack.body; 2218 2219 // Pop resources body 2220 popBody(); 2221 } else { 2222 resources = null; 2223 } 2224 2225 // Push body 2226 // Try body accepts the resource variables (in order of declaration). 2227 List<VarType> rVarTypes = rTypes.stream().<VarType>mapMulti((t, c) -> { 2228 if (t instanceof VarType vt) { 2229 c.accept(vt); 2230 } 2231 }).toList(); 2232 pushBody(tree.body, CoreType.functionType(JavaType.VOID, rVarTypes)); 2233 for (int i = 0; i < rVariableDecls.size(); i++) { 2234 stack.localToOp.put(rVariableDecls.get(i).sym, stack.block.parameters().get(i)); 2235 } 2236 scan(tree.body); 2237 appendTerminating(CoreOp::core_yield); 2238 Body.Builder body = stack.body; 2239 2240 // Pop block 2241 popBody(); 2242 2243 List<Body.Builder> catchers = new ArrayList<>(); 2244 for (JCTree.JCCatch catcher : tree.catchers) { 2245 // Push body 2246 pushBody(catcher.body, CoreType.functionType(JavaType.VOID, typeToTypeElement(catcher.param.type))); 2247 Op.Result exVariable = append(CoreOp.var( 2248 catcher.param.name.toString(), 2249 stack.block.parameters().get(0))); 2250 stack.localToOp.put(catcher.param.sym, exVariable); 2251 scan(catcher.body); 2252 appendTerminating(CoreOp::core_yield); 2253 catchers.add(stack.body); 2254 2255 // Pop block 2256 popBody(); 2257 } 2258 2259 Body.Builder finalizer; 2260 if (tree.finalizer != null) { 2261 // Push body 2262 pushBody(tree.finalizer, CoreType.FUNCTION_TYPE_VOID); 2263 scan(tree.finalizer); 2264 appendTerminating(CoreOp::core_yield); 2265 finalizer = stack.body; 2266 2267 // Pop block 2268 popBody(); 2269 } 2270 else { 2271 finalizer = null; 2272 } 2273 2274 result = append(JavaOp.try_(resources, body, catchers, finalizer)); 2275 } 2276 2277 @Override 2278 public void visitUnary(JCTree.JCUnary tree) { 2279 Tag tag = tree.getTag(); 2280 switch (tag) { 2281 case POSTINC, POSTDEC, PREINC, PREDEC -> { 2282 // Capture applying rhs and operation 2283 Function<Value, Value> scanRhs = (lhs) -> { 2284 Type unboxedType = types.unboxedTypeOrType(tree.type); 2285 Value one = convert(append(numericOneValue(unboxedType)), unboxedType); 2286 Value unboxedLhs = unboxIfNeeded(lhs); 2287 2288 Value unboxedLhsPlusOne = switch (tree.getTag()) { 2289 // Arithmetic operations 2290 case POSTINC, PREINC -> append(JavaOp.add(unboxedLhs, one)); 2291 case POSTDEC, PREDEC -> append(JavaOp.sub(unboxedLhs, one)); 2292 2293 default -> throw unsupported(tree); 2294 }; 2295 Value lhsPlusOne = convert(unboxedLhsPlusOne, tree.type); 2296 2297 // Assign expression result 2298 result = switch (tree.getTag()) { 2299 case POSTINC, POSTDEC -> lhs; 2300 case PREINC, PREDEC -> lhsPlusOne; 2301 2302 default -> throw unsupported(tree); 2303 }; 2304 return lhsPlusOne; 2305 }; 2306 2307 applyCompoundAssign(tree.arg, scanRhs); 2308 } 2309 case NEG -> { 2310 Value rhs = toValue(tree.arg, tree.type); 2311 result = append(JavaOp.neg(rhs)); 2312 } 2313 case NOT -> { 2314 Value rhs = toValue(tree.arg, tree.type); 2315 result = append(JavaOp.not(rhs)); 2316 } 2317 case COMPL -> { 2318 Value rhs = toValue(tree.arg, tree.type); 2319 result = append(JavaOp.compl(rhs)); 2320 } 2321 case POS -> { 2322 // Result is value of the operand 2323 result = toValue(tree.arg, tree.type); 2324 } 2325 default -> throw unsupported(tree); 2326 } 2327 } 2328 2329 @Override 2330 public void visitBinary(JCBinary tree) { 2331 Tag tag = tree.getTag(); 2332 if (tag == Tag.AND || tag == Tag.OR) { 2333 // Logical operations 2334 // @@@ Flatten nested sequences 2335 2336 // Push lhs 2337 pushBody(tree.lhs, CoreType.functionType(JavaType.BOOLEAN)); 2338 Value lhs = toValue(tree.lhs); 2339 // Yield the boolean result of the condition 2340 append(CoreOp.core_yield(lhs)); 2341 Body.Builder bodyLhs = stack.body; 2342 2343 // Pop lhs 2344 popBody(); 2345 2346 // Push rhs 2347 pushBody(tree.rhs, CoreType.functionType(JavaType.BOOLEAN)); 2348 Value rhs = toValue(tree.rhs); 2349 // Yield the boolean result of the condition 2350 append(CoreOp.core_yield(rhs)); 2351 Body.Builder bodyRhs = stack.body; 2352 2353 // Pop lhs 2354 popBody(); 2355 2356 List<Body.Builder> bodies = List.of(bodyLhs, bodyRhs); 2357 result = append(tag == Tag.AND 2358 ? JavaOp.conditionalAnd(bodies) 2359 : JavaOp.conditionalOr(bodies)); 2360 } else if (tag == Tag.PLUS && tree.operator.opcode == ByteCodes.string_add) { 2361 //Ignore the operator and query both subexpressions for their type with concats 2362 Type lhsType = tree.lhs.type; 2363 Type rhsType = tree.rhs.type; 2364 2365 Value lhs = toValue(tree.lhs, lhsType); 2366 Value rhs = toValue(tree.rhs, rhsType); 2367 2368 result = append(JavaOp.concat(lhs, rhs)); 2369 } 2370 else { 2371 Type opType = tree.operator.type.getParameterTypes().getFirst(); 2372 // @@@ potentially handle shift input conversion like other binary ops 2373 boolean isShift = tag == Tag.SL || tag == Tag.SR || tag == Tag.USR; 2374 Value lhs = toValue(tree.lhs, opType); 2375 Value rhs = toValue(tree.rhs, isShift ? tree.operator.type.getParameterTypes().getLast() : opType); 2376 2377 result = switch (tag) { 2378 // Arithmetic operations 2379 case PLUS -> append(JavaOp.add(lhs, rhs)); 2380 case MINUS -> append(JavaOp.sub(lhs, rhs)); 2381 case MUL -> append(JavaOp.mul(lhs, rhs)); 2382 case DIV -> append(JavaOp.div(lhs, rhs)); 2383 case MOD -> append(JavaOp.mod(lhs, rhs)); 2384 2385 // Test operations 2386 case EQ -> append(JavaOp.eq(lhs, rhs)); 2387 case NE -> append(JavaOp.neq(lhs, rhs)); 2388 // 2389 case LT -> append(JavaOp.lt(lhs, rhs)); 2390 case LE -> append(JavaOp.le(lhs, rhs)); 2391 case GT -> append(JavaOp.gt(lhs, rhs)); 2392 case GE -> append(JavaOp.ge(lhs, rhs)); 2393 2394 // Bitwise operations (including their boolean variants) 2395 case BITOR -> append(JavaOp.or(lhs, rhs)); 2396 case BITAND -> append(JavaOp.and(lhs, rhs)); 2397 case BITXOR -> append(JavaOp.xor(lhs, rhs)); 2398 2399 // Shift operations 2400 case SL -> append(JavaOp.lshl(lhs, rhs)); 2401 case SR -> append(JavaOp.ashr(lhs, rhs)); 2402 case USR -> append(JavaOp.lshr(lhs, rhs)); 2403 2404 default -> throw unsupported(tree); 2405 }; 2406 } 2407 } 2408 2409 @Override 2410 public void visitLiteral(JCLiteral tree) { 2411 Object value = switch (tree.type.getTag()) { 2412 case BOOLEAN -> tree.value instanceof Integer i && i == 1; 2413 case CHAR -> (char) (int) tree.value; 2414 default -> tree.value; 2415 }; 2416 Type constantType = adaptBottom(tree.type); 2417 result = append(CoreOp.constant(typeToTypeElement(constantType), value)); 2418 } 2419 2420 @Override 2421 public void visitReturn(JCReturn tree) { 2422 Value retVal = toValue(tree.expr, bodyTarget); 2423 if (retVal == null) { 2424 result = append(CoreOp.return_()); 2425 } else { 2426 result = append(CoreOp.return_(retVal)); 2427 } 2428 } 2429 2430 @Override 2431 public void visitThrow(JCTree.JCThrow tree) { 2432 Value throwVal = toValue(tree.expr); 2433 result = append(JavaOp.throw_(throwVal)); 2434 } 2435 2436 @Override 2437 public void visitBreak(JCTree.JCBreak tree) { 2438 Value label = tree.label != null 2439 ? getLabel(tree.label.toString()) 2440 : null; 2441 result = append(JavaOp.break_(label)); 2442 } 2443 2444 @Override 2445 public void visitContinue(JCTree.JCContinue tree) { 2446 Value label = tree.label != null 2447 ? getLabel(tree.label.toString()) 2448 : null; 2449 result = append(JavaOp.continue_(label)); 2450 } 2451 2452 @Override 2453 public void visitClassDef(JCClassDecl tree) { 2454 if (tree.sym.isDirectlyOrIndirectlyLocal()) { 2455 // we need to keep track of captured locals using same strategy as Lower 2456 class FreeVarScanner extends Lower.FreeVarCollector { 2457 FreeVarScanner() { 2458 lower.super(tree); 2459 } 2460 2461 @Override 2462 protected void addFreeVars(ClassSymbol c) { 2463 localCaptures.getOrDefault(c, List.of()) 2464 .forEach(s -> addFreeVar((VarSymbol)s)); 2465 } 2466 } 2467 FreeVarScanner fvs = new FreeVarScanner(); 2468 localCaptures.put(tree.sym, List.copyOf(fvs.analyzeCaptures())); 2469 } 2470 } 2471 2472 UnsupportedASTException unsupported(JCTree tree) { 2473 return new UnsupportedASTException(tree); 2474 } 2475 2476 CoreOp.FuncOp scanMethod() { 2477 scan(body); 2478 appendReturnOrUnreachable(body); 2479 CoreOp.FuncOp func = CoreOp.func(name.toString(), stack.body); 2480 func.setLocation(generateLocation(currentNode, true)); 2481 return func; 2482 } 2483 2484 CoreOp.FuncOp scanLambda() { 2485 scan(body); 2486 // Return the quoted result 2487 append(CoreOp.return_(result)); 2488 return CoreOp.func(name.toString(), stack.body); 2489 } 2490 2491 Op defaultValue(Type t) { 2492 return switch (t.getTag()) { 2493 case BYTE, SHORT, INT -> CoreOp.constant(JavaType.INT, 0); 2494 case CHAR -> CoreOp.constant(typeToTypeElement(t), (char)0); 2495 case BOOLEAN -> CoreOp.constant(typeToTypeElement(t), false); 2496 case FLOAT -> CoreOp.constant(typeToTypeElement(t), 0f); 2497 case LONG -> CoreOp.constant(typeToTypeElement(t), 0L); 2498 case DOUBLE -> CoreOp.constant(typeToTypeElement(t), 0d); 2499 default -> CoreOp.constant(typeToTypeElement(t), null); 2500 }; 2501 } 2502 2503 Op numericOneValue(Type t) { 2504 return switch (t.getTag()) { 2505 case BYTE, SHORT, INT -> CoreOp.constant(JavaType.INT, 1); 2506 case CHAR -> CoreOp.constant(typeToTypeElement(t), (char)1); 2507 case FLOAT -> CoreOp.constant(typeToTypeElement(t), 1f); 2508 case LONG -> CoreOp.constant(typeToTypeElement(t), 1L); 2509 case DOUBLE -> CoreOp.constant(typeToTypeElement(t), 1d); 2510 default -> throw new UnsupportedOperationException(t.toString()); 2511 }; 2512 } 2513 } 2514 2515 /** 2516 * An exception thrown when an unsupported AST node is found when building a method IR. 2517 */ 2518 static class UnsupportedASTException extends RuntimeException { 2519 2520 private static final long serialVersionUID = 0; 2521 transient final JCTree tree; 2522 2523 public UnsupportedASTException(JCTree tree) { 2524 this.tree = tree; 2525 } 2526 } 2527 2528 enum FunctionalExpressionKind { 2529 QUOTED_STRUCTURAL(true), // this is transitional 2530 QUOTABLE(true), 2531 NOT_QUOTED(false); 2532 2533 final boolean isQuoted; 2534 2535 FunctionalExpressionKind(boolean isQuoted) { 2536 this.isQuoted = isQuoted; 2537 } 2538 } 2539 2540 FunctionalExpressionKind functionalKind(JCFunctionalExpression functionalExpression) { 2541 if (functionalExpression.target.hasTag(TypeTag.METHOD)) { 2542 return FunctionalExpressionKind.QUOTED_STRUCTURAL; 2543 } else if (types.asSuper(functionalExpression.target, crSyms.quotableType.tsym) != null) { 2544 return FunctionalExpressionKind.QUOTABLE; 2545 } else { 2546 return FunctionalExpressionKind.NOT_QUOTED; 2547 } 2548 } 2549 2550 /* 2551 * Converts a method reference which cannot be used directly into a lambda. 2552 * This code has been derived from LambdaToMethod::MemberReferenceToLambda. The main 2553 * difference is that, while that code concerns with translation strategy, boxing 2554 * conversion and type erasure, this version does not and, as such, can remain 2555 * at a higher level. Note that this code needs to create a synthetic variable 2556 * declaration in case of a bounded method reference whose receiver expression 2557 * is other than 'this'/'super' (this is done to prevent the receiver expression 2558 * from being computed twice). 2559 */ 2560 private class MemberReferenceToLambda { 2561 2562 private final JCMemberReference tree; 2563 private final Symbol owner; 2564 private final ListBuffer<JCExpression> args = new ListBuffer<>(); 2565 private final ListBuffer<JCVariableDecl> params = new ListBuffer<>(); 2566 private JCVariableDecl receiverVar = null; 2567 2568 MemberReferenceToLambda(JCMemberReference tree, Symbol currentClass) { 2569 this.tree = tree; 2570 this.owner = new MethodSymbol(0, names.lambda, tree.target, currentClass); 2571 if (tree.kind == ReferenceKind.BOUND && !isThisOrSuper(tree.getQualifierExpression())) { 2572 // true bound method reference, hoist receiver expression out 2573 Type recvType = types.asSuper(tree.getQualifierExpression().type, tree.sym.owner); 2574 VarSymbol vsym = makeSyntheticVar("rec$", recvType); 2575 receiverVar = make.VarDef(vsym, tree.getQualifierExpression()); 2576 } 2577 } 2578 2579 JCVariableDecl receiverVar() { 2580 return receiverVar; 2581 } 2582 2583 JCLambda lambda() { 2584 int prevPos = make.pos; 2585 try { 2586 make.at(tree); 2587 2588 //body generation - this can be either a method call or a 2589 //new instance creation expression, depending on the member reference kind 2590 VarSymbol rcvr = addParametersReturnReceiver(); 2591 JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE) 2592 ? expressionInvoke(rcvr) 2593 : expressionNew(); 2594 2595 JCLambda slam = make.Lambda(params.toList(), expr); 2596 slam.target = tree.target; 2597 slam.type = tree.type; 2598 slam.pos = tree.pos; 2599 return slam; 2600 } finally { 2601 make.at(prevPos); 2602 } 2603 } 2604 2605 /** 2606 * Generate the parameter list for the converted member reference. 2607 * 2608 * @return The receiver variable symbol, if any 2609 */ 2610 VarSymbol addParametersReturnReceiver() { 2611 com.sun.tools.javac.util.List<Type> descPTypes = tree.getDescriptorType(types).getParameterTypes(); 2612 VarSymbol receiverParam = null; 2613 switch (tree.kind) { 2614 case BOUND: 2615 if (receiverVar != null) { 2616 receiverParam = receiverVar.sym; 2617 } 2618 break; 2619 case UNBOUND: 2620 // The receiver is the first parameter, extract it and 2621 // adjust the SAM and unerased type lists accordingly 2622 receiverParam = addParameter("rec$", descPTypes.head, false); 2623 descPTypes = descPTypes.tail; 2624 break; 2625 } 2626 for (int i = 0; descPTypes.nonEmpty(); ++i) { 2627 // By default use the implementation method parameter type 2628 Type parmType = descPTypes.head; 2629 addParameter("x$" + i, parmType, true); 2630 2631 // Advance to the next parameter 2632 descPTypes = descPTypes.tail; 2633 } 2634 2635 return receiverParam; 2636 } 2637 2638 /** 2639 * determine the receiver of the method call - the receiver can 2640 * be a type qualifier, the synthetic receiver parameter or 'super'. 2641 */ 2642 private JCExpression expressionInvoke(VarSymbol receiverParam) { 2643 JCExpression qualifier = receiverParam != null ? 2644 make.at(tree.pos).Ident(receiverParam) : 2645 tree.getQualifierExpression(); 2646 2647 //create the qualifier expression 2648 JCFieldAccess select = make.Select(qualifier, tree.sym.name); 2649 select.sym = tree.sym; 2650 select.type = tree.referentType; 2651 2652 //create the method call expression 2653 JCMethodInvocation apply = make.Apply(com.sun.tools.javac.util.List.nil(), select, args.toList()). 2654 setType(tree.referentType.getReturnType()); 2655 2656 apply.varargsElement = tree.varargsElement; 2657 return apply; 2658 } 2659 2660 /** 2661 * Lambda body to use for a 'new'. 2662 */ 2663 private JCExpression expressionNew() { 2664 Type expectedType = tree.referentType.getReturnType().hasTag(TypeTag.VOID) ? 2665 tree.expr.type : tree.referentType.getReturnType(); 2666 if (tree.kind == ReferenceKind.ARRAY_CTOR) { 2667 //create the array creation expression 2668 JCNewArray newArr = make.NewArray( 2669 make.Type(types.elemtype(expectedType)), 2670 com.sun.tools.javac.util.List.of(make.Ident(params.first())), 2671 null); 2672 newArr.type = tree.getQualifierExpression().type; 2673 return newArr; 2674 } else { 2675 //create the instance creation expression 2676 //note that method reference syntax does not allow an explicit 2677 //enclosing class (so the enclosing class is null) 2678 // but this may need to be patched up later with the proxy for the outer this 2679 JCExpression newType = make.Type(types.erasure(expectedType)); 2680 if (expectedType.tsym.type.getTypeArguments().nonEmpty()) { 2681 newType = make.TypeApply(newType, com.sun.tools.javac.util.List.nil()); 2682 } 2683 JCNewClass newClass = make.NewClass(null, 2684 com.sun.tools.javac.util.List.nil(), 2685 newType, 2686 args.toList(), 2687 null); 2688 newClass.constructor = tree.sym; 2689 newClass.constructorType = tree.referentType; 2690 newClass.type = expectedType; 2691 newClass.varargsElement = tree.varargsElement; 2692 return newClass; 2693 } 2694 } 2695 2696 private VarSymbol makeSyntheticVar(String name, Type type) { 2697 VarSymbol vsym = new VarSymbol(PARAMETER | SYNTHETIC, names.fromString(name), type, owner); 2698 vsym.pos = tree.pos; 2699 return vsym; 2700 } 2701 2702 private VarSymbol addParameter(String name, Type type, boolean genArg) { 2703 VarSymbol vsym = makeSyntheticVar(name, type); 2704 params.append(make.VarDef(vsym, null)); 2705 if (genArg) { 2706 args.append(make.Ident(vsym)); 2707 } 2708 return vsym; 2709 } 2710 2711 boolean isThisOrSuper(JCExpression expression) { 2712 return TreeInfo.isThisQualifier(expression) || TreeInfo.isSuperQualifier(tree); 2713 } 2714 } 2715 2716 public static class Provider implements CodeReflectionTransformer { 2717 @Override 2718 public JCTree translateTopLevelClass(Context context, JCTree tree, TreeMaker make) { 2719 return ReflectMethods.instance(context).translateTopLevelClass(tree, make); 2720 } 2721 } 2722 2723 /** 2724 * Translate a code model (a function op) into the corresponding AST. 2725 * The input function op is assumed to be generated by {@code OpBuilder}. 2726 */ 2727 class CodeModelTranslator { 2728 private static final MethodRef M_BLOCK_BUILDER_OP = MethodRef.method(Block.Builder.class, "op", 2729 Op.Result.class, Op.class); 2730 private static final MethodRef M_BLOCK_BUILDER_PARAM = MethodRef.method(Block.Builder.class, "parameter", 2731 Block.Parameter.class, TypeElement.class); 2732 2733 private final Map<Value, JCTree> valueToTree = new HashMap<>(); 2734 private int localVarCount = 0; // used to name variables we introduce in the AST 2735 2736 private JCExpression toExpr(JCTree t) { 2737 return switch (t) { 2738 case JCExpression e -> e; 2739 case JCTree.JCVariableDecl vd -> make.Ident(vd); 2740 case null, default -> throw new IllegalArgumentException(); 2741 }; 2742 } 2743 2744 private JCTree translateInvokeOp(JavaOp.InvokeOp invokeOp) { 2745 Value receiver = (invokeOp.invokeKind() == JavaOp.InvokeOp.InvokeKind.INSTANCE) ? 2746 invokeOp.operands().get(0) : null; 2747 com.sun.tools.javac.util.List<Value> arguments = invokeOp.operands().stream() 2748 .skip(receiver == null ? 0 : 1) 2749 .collect(com.sun.tools.javac.util.List.collector()); 2750 var methodSym = methodDescriptorToSymbol(invokeOp.invokeDescriptor()); 2751 var meth = (receiver == null) ? 2752 make.Ident(methodSym) : 2753 make.Select(toExpr(translateOp(receiver)), methodSym); 2754 var args = new ListBuffer<JCTree.JCExpression>(); 2755 for (Value operand : arguments) { 2756 args.add(toExpr(translateOp(operand))); 2757 } 2758 var methodInvocation = make.App(meth, args.toList()); 2759 if (invokeOp.isVarArgs()) { 2760 setVarargs(methodInvocation, invokeOp.invokeDescriptor().type()); 2761 } 2762 return methodInvocation; 2763 } 2764 2765 private void setVarargs(JCExpression tree, FunctionType type) { 2766 var lastParam = type.parameterTypes().getLast(); 2767 if (lastParam instanceof jdk.incubator.code.dialect.java.ArrayType varargType) { 2768 TreeInfo.setVarargsElement(tree, typeElementToType(varargType.componentType())); 2769 } else { 2770 Assert.error("Expected trailing array type: " + type); 2771 } 2772 } 2773 2774 public JCTree.JCStatement translateFuncOp(CoreOp.FuncOp funcOp, MethodSymbol ms) { 2775 Assert.check(funcOp.parameters().isEmpty()); 2776 Assert.check(funcOp.body().blocks().size() == 1); 2777 2778 java.util.List<Value> rootValues = funcOp.traverse(new ArrayList<>(), (l, ce) -> { 2779 boolean isRoot = switch (ce) { 2780 case JavaOp.InvokeOp invokeOp when invokeOp.invokeDescriptor().equals(M_BLOCK_BUILDER_OP) 2781 || invokeOp.invokeDescriptor().equals(M_BLOCK_BUILDER_PARAM) -> true; 2782 case CoreOp.ReturnOp _, JavaOp.ArrayAccessOp.ArrayStoreOp _ -> true; 2783 case Op op when op.result() != null && op.result().uses().size() > 1 -> true; 2784 default -> false; 2785 }; 2786 if (isRoot) { 2787 l.add(((Op) ce).result()); 2788 } 2789 return l; 2790 }); 2791 2792 var stats = new ListBuffer<JCTree.JCStatement>(); 2793 for (Value root : rootValues) { 2794 JCTree tree = translateOp(root); 2795 if (tree instanceof JCExpression e) { 2796 if (!root.uses().isEmpty()) { 2797 var vs = new Symbol.VarSymbol(LocalVarFlags | SYNTHETIC, names.fromString("_$" + localVarCount++), tree.type, ms); 2798 tree = make.VarDef(vs, e); 2799 valueToTree.put(root, tree); 2800 } else { 2801 tree = make.Exec(e); 2802 } 2803 } 2804 stats.add((JCTree.JCStatement) tree); 2805 } 2806 var mb = make.Block(0, stats.toList()); 2807 2808 return mb; 2809 } 2810 2811 private JCTree translateOp(Value v) { 2812 if (valueToTree.containsKey(v)) { 2813 return valueToTree.get(v); 2814 } 2815 Op op = ((Op.Result) v).op(); 2816 JCTree tree = switch (op) { 2817 case CoreOp.ConstantOp constantOp when constantOp.value() == null -> 2818 make.Literal(TypeTag.BOT, null).setType(syms.botType); 2819 case CoreOp.ConstantOp constantOp -> make.Literal(constantOp.value()); 2820 case JavaOp.InvokeOp invokeOp -> translateInvokeOp(invokeOp); 2821 case JavaOp.NewOp newOp when newOp.resultType() instanceof jdk.incubator.code.dialect.java.ArrayType at -> { 2822 var elemType = make.Ident(typeElementToType(at.componentType()).tsym); 2823 var dims = new ListBuffer<JCTree.JCExpression>(); 2824 for (int d = 0; d < at.dimensions(); d++) { 2825 dims.add(toExpr(translateOp(newOp.operands().get(d)))); 2826 } 2827 var na = make.NewArray(elemType, dims.toList(), null); 2828 na.type = typeElementToType(at); 2829 yield na; 2830 } 2831 case JavaOp.NewOp newOp -> { 2832 var ownerType = typeElementToType(newOp.constructorDescriptor().refType()); 2833 var clazz = make.Ident(ownerType.tsym); 2834 var args = new ListBuffer<JCTree.JCExpression>(); 2835 for (Value operand : newOp.operands()) { 2836 args.add(toExpr(translateOp(operand))); 2837 } 2838 var nc = make.NewClass(null, null, clazz, args.toList(), null); 2839 if (newOp.isVarargs()) { 2840 setVarargs(nc, newOp.constructorDescriptor().type()); 2841 } 2842 nc.type = ownerType; 2843 nc.constructor = constructorDescriptorToSymbol(newOp.constructorDescriptor()); 2844 nc.constructorType = nc.constructor.type; 2845 yield nc; 2846 } 2847 case CoreOp.ReturnOp returnOp -> make.Return(toExpr(translateOp(returnOp.returnValue()))); 2848 case JavaOp.FieldAccessOp.FieldLoadOp fieldLoadOp -> { 2849 var sym = fieldDescriptorToSymbol(fieldLoadOp.fieldDescriptor()); 2850 Assert.check(sym.isStatic()); 2851 yield make.Select(make.Ident(sym.owner), sym); 2852 } 2853 case JavaOp.ArrayAccessOp.ArrayStoreOp arrayStoreOp -> { 2854 var array = arrayStoreOp.operands().get(0); 2855 var index = arrayStoreOp.operands().get(1); 2856 var val = arrayStoreOp.operands().get(2); 2857 var as = make.Assign( 2858 make.Indexed( 2859 toExpr(translateOp(array)), toExpr(translateOp(index))), toExpr(translateOp(val)) 2860 ); 2861 as.type = typeElementToType(((jdk.incubator.code.dialect.java.ArrayType) array.type()).componentType()); 2862 yield as; 2863 } 2864 default -> 2865 throw new IllegalStateException("Op -> JCTree not supported for :" + op.getClass().getName()); 2866 }; 2867 valueToTree.put(v, tree); 2868 return tree; 2869 } 2870 } 2871 2872 // type and ref conversion utils 2873 2874 JavaType symbolToErasedDesc(Symbol s) { 2875 return typeToTypeElement(s.erasure(types)); 2876 } 2877 2878 JavaType typeToTypeElement(Type t) { 2879 Assert.check(!t.hasTag(METHOD)); 2880 t = types.upward(t, false, types.captures(t)); 2881 return switch (t.getTag()) { 2882 case VOID -> JavaType.VOID; 2883 case CHAR -> JavaType.CHAR; 2884 case BOOLEAN -> JavaType.BOOLEAN; 2885 case BYTE -> JavaType.BYTE; 2886 case SHORT -> JavaType.SHORT; 2887 case INT -> JavaType.INT; 2888 case FLOAT -> JavaType.FLOAT; 2889 case LONG -> JavaType.LONG; 2890 case DOUBLE -> JavaType.DOUBLE; 2891 case ARRAY -> { 2892 Type et = ((ArrayType)t).elemtype; 2893 yield JavaType.array(typeToTypeElement(et)); 2894 } 2895 case WILDCARD -> { 2896 Type.WildcardType wt = (Type.WildcardType)t; 2897 yield wt.isUnbound() ? 2898 JavaType.wildcard() : 2899 JavaType.wildcard(wt.isExtendsBound() ? BoundKind.EXTENDS : BoundKind.SUPER, typeToTypeElement(wt.type)); 2900 } 2901 case TYPEVAR -> t.tsym.owner.kind == Kind.MTH ? 2902 JavaType.typeVarRef(t.tsym.name.toString(), symbolToMethodRef(t.tsym.owner), 2903 typeToTypeElement(t.getUpperBound())) : 2904 JavaType.typeVarRef(t.tsym.name.toString(), 2905 (jdk.incubator.code.dialect.java.ClassType)symbolToErasedDesc(t.tsym.owner), 2906 typeToTypeElement(t.getUpperBound())); 2907 case CLASS -> { 2908 Assert.check(!t.isIntersection() && !t.isUnion()); 2909 JavaType typ; 2910 if (t.getEnclosingType() != Type.noType) { 2911 Name innerName = t.tsym.flatName().subName(t.getEnclosingType().tsym.flatName().length() + 1); 2912 typ = JavaType.qualified(typeToTypeElement(t.getEnclosingType()), innerName.toString()); 2913 } else { 2914 typ = JavaType.type(ClassDesc.of(t.tsym.flatName().toString())); 2915 } 2916 2917 List<JavaType> typeArguments; 2918 if (t.getTypeArguments().nonEmpty()) { 2919 typeArguments = new ArrayList<>(); 2920 for (Type ta : t.getTypeArguments()) { 2921 typeArguments.add(typeToTypeElement(ta)); 2922 } 2923 } else { 2924 typeArguments = List.of(); 2925 } 2926 2927 // Use flat name to ensure demarcation of nested classes 2928 yield JavaType.parameterized(typ, typeArguments); 2929 } 2930 default -> throw new UnsupportedOperationException("Unsupported type: kind=" + t.getKind() + " type=" + t); 2931 }; 2932 } 2933 2934 Type typeElementToType(TypeElement jt) { 2935 return switch (jt) { 2936 case PrimitiveType pt when pt == JavaType.BOOLEAN -> syms.booleanType; 2937 case PrimitiveType pt when pt == JavaType.CHAR -> syms.charType; 2938 case PrimitiveType pt when pt == JavaType.BYTE -> syms.byteType; 2939 case PrimitiveType pt when pt == JavaType.SHORT -> syms.shortType; 2940 case PrimitiveType pt when pt == JavaType.INT -> syms.intType; 2941 case PrimitiveType pt when pt == JavaType.LONG -> syms.longType; 2942 case PrimitiveType pt when pt == JavaType.FLOAT -> syms.floatType; 2943 case PrimitiveType pt when pt == JavaType.DOUBLE -> syms.doubleType; 2944 case ClassType ct when ct.hasTypeArguments() -> { 2945 Type enclosing = ct.enclosingType().map(this::typeElementToType).orElse(Type.noType); 2946 com.sun.tools.javac.util.List<Type> typeArgs = com.sun.tools.javac.util.List.from(ct.typeArguments()).map(this::typeElementToType); 2947 yield new Type.ClassType(enclosing, typeArgs, typeElementToType(ct.rawType()).tsym); 2948 } 2949 case ClassType ct -> types.erasure(syms.enterClass(attrEnv().toplevel.modle, ct.toClassName())); 2950 case jdk.incubator.code.dialect.java.ArrayType at -> new Type.ArrayType(typeElementToType(at.componentType()), syms.arrayClass); 2951 default -> Type.noType; 2952 }; 2953 } 2954 2955 Type symbolSiteType(Symbol s) { 2956 boolean isMember = s.owner == syms.predefClass || 2957 s.isMemberOf(currentClassSym, types); 2958 return isMember ? currentClassSym.type : s.owner.type; 2959 } 2960 2961 FieldRef symbolToFieldRef(Symbol s, Type site) { 2962 // @@@ Made Gen::binaryQualifier public, duplicate logic? 2963 // Ensure correct qualifying class is used in the reference, see JLS 13.1 2964 // https://docs.oracle.com/javase/specs/jls/se20/html/jls-13.html#jls-13.1 2965 return symbolFieldRef(gen.binaryQualifier(s, types.erasure(site))); 2966 } 2967 2968 FieldRef symbolFieldRef(Symbol s) { 2969 Type erasedType = s.erasure(types); 2970 return FieldRef.field( 2971 typeToTypeElement(s.owner.erasure(types)), 2972 s.name.toString(), 2973 typeToTypeElement(erasedType)); 2974 } 2975 2976 MethodRef symbolToMethodRef(Symbol s, Type site) { 2977 // @@@ Made Gen::binaryQualifier public, duplicate logic? 2978 // Ensure correct qualifying class is used in the reference, see JLS 13.1 2979 // https://docs.oracle.com/javase/specs/jls/se20/html/jls-13.html#jls-13.1 2980 return symbolToMethodRef(gen.binaryQualifier(s, types.erasure(site))); 2981 } 2982 2983 MethodRef symbolToMethodRef(Symbol s) { 2984 Type erasedType = s.erasure(types); 2985 return MethodRef.method( 2986 typeToTypeElement(s.owner.erasure(types)), 2987 s.name.toString(), 2988 typeToTypeElement(erasedType.getReturnType()), 2989 erasedType.getParameterTypes().stream().map(this::typeToTypeElement).toArray(TypeElement[]::new)); 2990 } 2991 2992 FunctionType typeToFunctionType(Type t) { 2993 return CoreType.functionType( 2994 typeToTypeElement(t.getReturnType()), 2995 t.getParameterTypes().stream().map(this::typeToTypeElement).toArray(TypeElement[]::new)); 2996 } 2997 2998 RecordTypeRef symbolToRecordTypeRef(Symbol.ClassSymbol s) { 2999 TypeElement recordType = typeToTypeElement(s.type); 3000 List<RecordTypeRef.ComponentRef> components = s.getRecordComponents().stream() 3001 .map(rc -> new RecordTypeRef.ComponentRef(typeToTypeElement(rc.type), rc.name.toString())) 3002 .toList(); 3003 return RecordTypeRef.recordType(recordType, components); 3004 } 3005 3006 Env<AttrContext> attrEnv() { 3007 return typeEnvs.get(currentClassSym); 3008 } 3009 3010 VarSymbol fieldDescriptorToSymbol(FieldRef fieldRef) { 3011 Name name = names.fromString(fieldRef.name()); 3012 Type site = typeElementToType(fieldRef.refType()); 3013 return resolve.resolveInternalField(attrEnv().enclClass, attrEnv(), site, name); 3014 } 3015 3016 MethodSymbol methodDescriptorToSymbol(MethodRef methodRef) { 3017 Name name = names.fromString(methodRef.name()); 3018 Type site = typeElementToType(methodRef.refType()); 3019 com.sun.tools.javac.util.List<Type> argtypes = methodRef.type().parameterTypes().stream() 3020 .map(this::typeElementToType).collect(com.sun.tools.javac.util.List.collector()); 3021 return resolve.resolveInternalMethod(attrEnv().enclClass, attrEnv(), site, name, argtypes, com.sun.tools.javac.util.List.nil()); 3022 } 3023 3024 MethodSymbol constructorDescriptorToSymbol(ConstructorRef constructorRef) { 3025 Type site = typeElementToType(constructorRef.refType()); 3026 com.sun.tools.javac.util.List<Type> argtypes = constructorRef.type().parameterTypes().stream() 3027 .map(this::typeElementToType).collect(com.sun.tools.javac.util.List.collector()); 3028 return resolve.resolveInternalConstructor(attrEnv().enclClass, attrEnv(), site, argtypes, com.sun.tools.javac.util.List.nil()); 3029 } 3030 }