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