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