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