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