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