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