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