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