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