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