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