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