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