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