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