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