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