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