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