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