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