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 }