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.dialect.java;
27
28 import java.lang.constant.ClassDesc;
29 import jdk.incubator.code.*;
30 import jdk.incubator.code.dialect.java.JavaOp.JavaSwitchOp.SwitchNullHandling;
31 import jdk.incubator.code.extern.DialectFactory;
32 import jdk.incubator.code.dialect.core.*;
33 import jdk.incubator.code.extern.ExternalizedOp;
34 import jdk.incubator.code.extern.OpFactory;
35 import jdk.incubator.code.internal.ArithmeticAndConvOpImpls;
36 import jdk.incubator.code.internal.BranchTarget;
37 import jdk.incubator.code.internal.OpDeclaration;
38
39 import java.lang.invoke.MethodHandles;
40 import java.lang.invoke.VarHandle;
41 import java.lang.reflect.Field;
42 import java.lang.reflect.Modifier;
43 import java.util.*;
44 import java.util.concurrent.atomic.AtomicBoolean;
45 import java.util.function.BiFunction;
46 import java.util.function.Consumer;
47 import java.util.function.Function;
48 import java.util.function.Predicate;
49
50 import static jdk.incubator.code.Op.Lowerable.*;
51 import static jdk.incubator.code.dialect.core.CoreOp.*;
52 import static jdk.incubator.code.dialect.java.JavaType.*;
53 import static jdk.incubator.code.dialect.java.JavaType.VOID;
54 import static jdk.incubator.code.internal.ArithmeticAndConvOpImpls.*;
55 import static jdk.incubator.code.internal.StructuralPreconditions.*;
56
57 /**
58 * The top-level operation class for Java operations.
59 * <p>
60 * A code model, produced by the Java compiler from Java program source, may consist of core operations and Java
61 * operations. Such a model represents the same Java program and preserves the program meaning as defined by the
62 * Java Language Specification.
63 * <p>
64 * Java operations model specific Java language constructs or Java program behavior. Some Java operations model
65 * structured control flow and nested code. These operations are transformable, commonly referred to as lowering, into
66 * a sequence of other core or Java operations. Those that implement {@link Op.Lowerable} can transform themselves and
67 * will transform associated operations that are not explicitly lowerable.
68 * <p>
69 * A code model, produced by the Java compiler from source, and consisting of core operations and Java operations
70 * can be transformed to one consisting only of non-lowerable operations, where all lowerable operations are lowered.
71 * This transformation preserves programming meaning. The resulting lowered code model also represents the same Java
72 * program.
73 */
74 public sealed abstract class JavaOp extends AbstractOp {
75
76 JavaOp(AbstractOp that, CodeContext cc) {
77 super(that, cc);
78 }
79
80 JavaOp(List<? extends Value> operands) {
81 super(operands);
82 }
83
84 @Override
85 public String externalizeOpName() {
86 OpDeclaration opDecl = this.getClass().getDeclaredAnnotation(OpDeclaration.class);
87 assert opDecl != null : this.getClass().getName();
88 return opDecl.value();
89 }
90
91 /**
92 * An operation that models a Java expression
93 *
94 * @jls 15 Expressions
95 */
96 public sealed interface JavaExpression permits
97 ArithmeticOperation,
98 ArrayAccessOp.ArrayLoadOp,
99 ArrayAccessOp.ArrayStoreOp,
100 ArrayLengthOp,
101 CastOp,
102 ConvOp,
103 ConcatOp,
104 ConstantOp,
105 FieldAccessOp.FieldLoadOp,
106 FieldAccessOp.FieldStoreOp,
107 InstanceOfOp,
108 InvokeOp,
109 LambdaOp,
110 NewOp,
111 VarAccessOp.VarLoadOp,
112 VarAccessOp.VarStoreOp,
113 ConditionalExpressionOp,
114 JavaConditionalOp,
115 SwitchExpressionOp {
116
117 /**
118 * Evaluates an operation result whose operation models a constant expression.
119 * <p>
120 * This method deviates from the language specification of a constant expression in the following cases.
121 * <ul>
122 * <li>A name that refers to a final class variable of primitive type or type String, is evaluated as if a constant variable.
123 * Such referral is modeled as field load operation to a static final field. At runtime, it is not possible to
124 * determine if that class variable, the static final field, is initialized with a constant expression.
125 * <li>A name that refers to constant variable that is an instance variable is evaluated as if it is a
126 * non-constant variable, and therefore any expression referring to such a variable is not considered a constant
127 * expression.
128 * Such referral is modeled as field load operation to a non-static final field. At runtime, it is not possible
129 * to access the value of the field, since the instance of the class that has the field that is the instance
130 * variable is unknown. And, same as the first case, at runtime it is not possible to determine if the variable
131 * is initialized with a constant expression, whose value is independent of the class instance.
132 * <li>An effectively final local variable is evaluated as if a constant variable.
133 * Such a variable is modelled as a variable operation, which does not model if the variable is a final
134 * variable.
135 *</ul>
136 *
137 * @param l the {@link MethodHandles.Lookup} to provide name resolution and access control context
138 * @param v the value to evaluate
139 * @return an {@code Optional} containing the evaluated result, otherwise an empty {@code Optional} if the value
140 * is not an instance of {@link Op.Result} or the operation does not model a constant expression
141 * @throws IllegalArgumentException if a failure to resolve
142 * @jls 15.29 Constant Expressions
143 *}
144 */
145 static Optional<Object> evaluate(MethodHandles.Lookup l, Value v) {
146 return new ConstantExpressionEvaluator(l).evaluate(v);
147 }
148
149 /**
150 * Evaluates an operation that models a constant expression.
151 * <p>
152 * This method deviates from the language specification of a constant expression in the following cases.
153 * <ul>
154 * <li>A name that refers to a final class variable of primitive type or type String, is evaluated as if a constant variable.
155 * Such referral is modeled as field load operation to a static final field. At runtime, it is not possible to
156 * determine if that class variable, the static final field, is initialized with a constant expression.
157 * <li>A name that refers to constant variable that is an instance variable is evaluated as if it is a
158 * non-constant variable, and therefore any expression referring to such a variable is not considered a constant
159 * expression.
160 * Such referral is modeled as field load operation to a non-static final field. At runtime, it is not possible
161 * to access the value of the field, since the instance of the class that has the field that is the instance
162 * variable is unknown. And, same as the first case, at runtime it is not possible to determine if the variable
163 * is initialized with a constant expression, whose value is independent of the class instance.
164 * <li>An effectively final local variable is evaluated as if a constant variable.
165 * Such a variable is modelled as a variable operation, which does not model if the variable is a final
166 * variable.
167 *</ul>
168 *
169 * @param l the {@link MethodHandles.Lookup} to provide name resolution and access control context
170 * @param op the operation to evaluate
171 * @param <T> the type of the operation
172 * @return an {@code Optional} containing the evaluated result, otherwise an empty {@code Optional} if the
173 * operation does not model a constant expression
174 * @throws IllegalArgumentException if a failure to resolve
175 * @jls 15.29 Constant Expressions
176 */
177 static <T extends Op & JavaExpression> Optional<Object> evaluate(MethodHandles.Lookup l, T op) {
178 return new ConstantExpressionEvaluator(l).evaluate(op);
179 }
180
181 }
182
183 static final class ConstantExpressionEvaluator {
184 private final MethodHandles.Lookup l;
185 private final Map<Value, Object> m = new HashMap<>();
186
187 ConstantExpressionEvaluator(MethodHandles.Lookup l) {
188 this.l = l;
189 }
190
191 <T extends Op & JavaExpression> Optional<Object> evaluate(T op) {
192 try {
193 Object v = this.eval(op);
194 return Optional.ofNullable(v);
195 } catch (NonConstantExpression e) {
196 return Optional.empty();
197 }
198 }
199
200 Optional<Object> evaluate(Value v) {
201 try {
202 Object o = this.eval(v);
203 return Optional.ofNullable(o);
204 } catch (NonConstantExpression e) {
205 return Optional.empty();
206 }
207 }
208
209 private Object eval(Op op) {
210 if (m.containsKey(op.result())) {
211 return m.get(op.result());
212 }
213 Object r = switch (op) {
214 case ConstantOp cop when isConstant(cop) -> {
215 Object v = cop.value();
216 yield v instanceof String s ? s.intern() : v;
217 }
218 case VarAccessOp.VarLoadOp varLoadOp when varLoadOp.operands().getFirst() instanceof Result &&
219 isConstant(varLoadOp.varOp()) -> eval(varLoadOp.varOp().initOperand());
220 case ConvOp _ -> {
221 // we expect cast to primitive type
222 var v = eval(op.operands().getFirst());
223 yield ArithmeticAndConvOpImpls.evaluate(op, List.of(v));
224 }
225 case CastOp castOp -> {
226 // we expect cast to String
227 Value operand = castOp.operands().getFirst();
228 if (!castOp.resultType().equals(J_L_STRING) || !operand.type().equals(J_L_STRING)) {
229 throw new NonConstantExpression();
230 }
231 Object v = eval(operand);
232 if (!(v instanceof String s)) {
233 throw new NonConstantExpression();
234 }
235 yield s;
236 }
237 case ConcatOp concatOp -> {
238 Object first = eval(concatOp.operands().getFirst());
239 Object second = eval(concatOp.operands().getLast());
240 yield (first.toString() + second).intern();
241 }
242 case FieldAccessOp.FieldLoadOp fieldLoadOp -> {
243 Field field;
244 VarHandle vh;
245 try {
246 field = fieldLoadOp.fieldReference().resolveToField(l);
247 vh = fieldLoadOp.fieldReference().resolveToHandle(l);
248 } catch (ReflectiveOperationException | IllegalArgumentException _) {
249 // we cann't reflectivelly get the field
250 throw new NonConstantExpression();
251 }
252 // Requirement: the field must be a constant variable.
253 // Current checks:
254 // 1) The field is declared final.
255 // 2) The field type is a primitive or String.
256 // Missing check:
257 // 3) Verify the field is initialized and the initializer is a constant expression.
258 if ((field.getModifiers() & Modifier.FINAL) == 0 ||
259 !isConstantType(fieldLoadOp.fieldReference().type())) {
260 throw new NonConstantExpression();
261 }
262 if ((field.getModifiers() & Modifier.STATIC) != 0) {
263 Object v;
264 try {
265 v = vh.get();
266 } catch (Throwable t) {
267 throw new NonConstantExpression();
268 }
269 if (!isConstantValue(v)) {
270 throw new NonConstantExpression();
271 }
272 yield v instanceof String s ? s.intern() : v;
273 } else {
274 // we can't get the value of an instance field from the model
275 // we need the value of the receiver
276 throw new NonConstantExpression();
277 }
278 }
279 case ArithmeticOperation _ -> {
280 List<Object> values = op.operands().stream().map(this::eval).toList();
281 yield ArithmeticAndConvOpImpls.evaluate(op, values);
282 }
283 case ConditionalExpressionOp _ -> {
284 boolean p = evalBoolean(op.bodies().get(0));
285 Object t = eval(op.bodies().get(1));
286 Object f = eval(op.bodies().get(2));
287 yield p ? t : f;
288 }
289 case ConditionalAndOp _ -> {
290 boolean left = evalBoolean(op.bodies().get(0));
291 boolean right = evalBoolean(op.bodies().get(1));
292 yield left && right;
293 }
294 case ConditionalOrOp _ -> {
295 boolean left = evalBoolean(op.bodies().get(0));
296 boolean right = evalBoolean(op.bodies().get(1));
297 yield left || right;
298 }
299 default -> throw new NonConstantExpression();
300 };
301 m.put(op.result(), r);
302 return r;
303 }
304
305 private Object eval(Value v) {
306 if (v.declaringElement() instanceof JavaExpression e) {
307 return eval((Op & JavaExpression) e);
308 }
309 throw new NonConstantExpression();
310 }
311
312 private Object eval(Body body) throws NonConstantExpression {
313 if (body.blocks().size() != 1 ||
314 !(body.entryBlock().terminatingOp() instanceof CoreOp.YieldOp yop) ||
315 yop.yieldValue() == null ||
316 !isConstantType(yop.yieldValue().type())) {
317 throw new NonConstantExpression();
318 }
319 return eval(yop.yieldValue());
320 }
321
322 private boolean evalBoolean(Body body) throws NonConstantExpression {
323 Object eval = eval(body);
324 if (!(eval instanceof Boolean b)) {
325 throw new NonConstantExpression();
326 }
327 return b;
328 }
329
330 private static boolean isConstant(CoreOp.ConstantOp op) {
331 return isConstantType(op.resultType()) && isConstantValue(op.value());
332 }
333
334 private static boolean isConstant(VarOp op) {
335 // Requirement: the local variable must be a constant variable.
336 // Current checks:
337 // 1) The variable is initialized, and the initializer is a constant expression.
338 // 2) The variable type is a primitive or String.
339 // Missing check:
340 // 3) Ensure the variable is declared final
341 return isConstantType(op.varValueType()) &&
342 !op.isUninitialized() &&
343 // @@@ Add to VarOp
344 op.result().uses().stream().noneMatch(u -> u.op() instanceof CoreOp.VarAccessOp.VarStoreOp);
345 }
346
347 private static boolean isConstantValue(Object o) {
348 return switch (o) {
349 case String _ -> true;
350 case Boolean _, Byte _, Short _, Character _, Integer _, Long _, Float _, Double _ -> true;
351 case null, default -> false;
352 };
353 }
354
355 private static boolean isConstantType(CodeType e) {
356 return (e instanceof PrimitiveType && !VOID.equals(e)) || J_L_STRING.equals(e);
357 }
358 }
359
360 /**
361 * An operation that models a Java statement.
362 *
363 * @jls 14.5 Statements
364 */
365 public sealed interface JavaStatement permits
366 ArrayAccessOp.ArrayStoreOp,
367 AssertOp,
368 FieldAccessOp.FieldStoreOp,
369 InvokeOp,
370 NewOp,
371 ReturnOp,
372 ThrowOp,
373 VarAccessOp.VarStoreOp,
374 VarOp,
375 BlockOp,
376 DoWhileOp,
377 EnhancedForOp,
378 ForOp,
379 IfOp,
380 StatementTargetOp,
381 LabeledOp,
382 SynchronizedOp,
383 TryOp,
384 WhileOp,
385 YieldOp,
386 SwitchStatementOp {
387 }
388
389 /**
390 * An operation characteristic indicating the operation's behavior may be emulated using Java reflection.
391 * A reference is derived from or declared by the operation that can be resolved at runtime to
392 * an instance of a reflective handle or member. That handle or member can be operated on to
393 * emulate the operation's behavior, specifically as bytecode behavior.
394 */
395 public sealed interface ReflectiveOp {
396 }
397
398 /**
399 * An operation that performs access.
400 */
401 public sealed interface AccessOp permits
402 CoreOp.VarAccessOp,
403 FieldAccessOp,
404 ArrayAccessOp {
405 }
406
407
408
409 /**
410 * The lambda operation, that can model Java language lambda expressions.
411 * <p>
412 * Lambda operations are associated with a {@linkplain #functionalInterface() functional interface type}.
413 * They feature one body, the {@linkplain #body() function body}.
414 * The result type of a lambda operation is its functional interface type.
415 * <p>
416 * The function body takes as many arguments as the function type associated with the functional interface type.
417 * The function body yields a value if that function type has a non-{@linkplain JavaType#VOID void} return type.
418 * <p>
419 * Lambda operations can also model Java language method reference expressions. A method reference is modeled as a
420 * lambda operation whose function body forwards its parameters to a corresponding {@link InvokeOp}, and that
421 * yields the result (if any) of that operation.
422 * <p>
423 * Some lambda operations are <em>reflectable</em> (see {@link Reflect}), meaning their code model is persisted at
424 * runtime.
425 *
426 * @jls 15.27 Lambda Expressions
427 * @jls 15.13 Method Reference Expressions
428 * @jls 9.8 Functional Interfaces
429 * @jls 9.9 Function Types
430 */
431 @OpDeclaration(LambdaOp.NAME)
432 public static final class LambdaOp extends JavaOp
433 implements Invokable, Lowerable, JavaExpression {
434
435 /**
436 * A builder for constructing a lambda operation.
437 */
438 public static class Builder {
439 final Body.Builder connectedAncestorBody;
440 final FunctionType signature;
441 final CodeType functionalInterface;
442 final boolean isReflectable;
443
444 Builder(Body.Builder connectedAncestorBody, FunctionType signature, CodeType functionalInterface) {
445 this.connectedAncestorBody = connectedAncestorBody;
446 this.signature = signature;
447 this.functionalInterface = functionalInterface;
448 this.isReflectable = false;
449 }
450
451 Builder(Body.Builder connectedAncestorBody, FunctionType signature, CodeType functionalInterface,
452 boolean isReflectable) {
453 this.connectedAncestorBody = connectedAncestorBody;
454 this.signature = signature;
455 this.functionalInterface = functionalInterface;
456 this.isReflectable = isReflectable;
457 }
458
459 /**
460 * Completes the lambda operation by adding the function body.
461 *
462 * @param c a consumer that populates the function body
463 * @return the completed lambda operation
464 */
465 public LambdaOp body(Consumer<Block.Builder> c) {
466 Body.Builder body = Body.Builder.of(connectedAncestorBody, signature);
467 c.accept(body.entryBlock());
468 return new LambdaOp(functionalInterface, body, isReflectable);
469 }
470
471 /**
472 * Returns a builder that constructs a reflectable lambda operation.
473 *
474 * @return this builder
475 * @see Reflect
476 */
477 public Builder reflectable() {
478 return new Builder(connectedAncestorBody, signature, functionalInterface, true);
479 }
480 }
481
482 static final String NAME = "lambda";
483 static final String ATTRIBUTE_LAMBDA_IS_REFLECTABLE = NAME + ".isReflectable";
484
485 final CodeType functionalInterface;
486 final Body body;
487 final boolean isReflectable;
488
489 LambdaOp(ExternalizedOp def) {
490 this(def.resultType(), requireSingleBody(def), optionalBooleanAttribute(def, ATTRIBUTE_LAMBDA_IS_REFLECTABLE));
491 }
492
493 LambdaOp(LambdaOp that, CodeContext cc, CodeTransformer ct) {
494 super(that, cc);
495
496 this.functionalInterface = that.functionalInterface;
497 this.body = that.body.transform(cc, ct).build(this);
498 this.isReflectable = that.isReflectable;
499 }
500
501 @Override
502 public LambdaOp transform(CodeContext cc, CodeTransformer ct) {
503 return new LambdaOp(this, cc, ct);
504 }
505
506 LambdaOp(CodeType functionalInterface, Body.Builder bodyC, boolean isReflectable) {
507 super(List.of());
508
509 this.functionalInterface = functionalInterface;
510 this.body = bodyC.build(this);
511 this.isReflectable = isReflectable;
512 }
513
514 @Override
515 public List<Body> bodies() {
516 return List.of(body);
517 }
518
519 /**
520 * {@return the functional interface type modeled by this lambda operation}
521 */
522 public CodeType functionalInterface() {
523 return functionalInterface;
524 }
525
526 @Override
527 public Body body() {
528 return body;
529 }
530
531 @Override
532 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> _ignore) {
533 // Isolate body with respect to ancestor transformations
534 b.withContextAndTransformer(b.context(), CodeTransformer.LOWERING_TRANSFORMER).add(this);
535 return b;
536 }
537
538 @Override
539 public CodeType resultType() {
540 return functionalInterface();
541 }
542
543 /**
544 * {@return whether this lambda operation is reflectable}
545 * @see Reflect
546 */
547 public boolean isReflectable() {
548 return isReflectable;
549 }
550
551 @Override
552 public Map<String, Object> externalize() {
553 return Map.of(ATTRIBUTE_LAMBDA_IS_REFLECTABLE, isReflectable);
554 }
555
556 /**
557 * Determines if this lambda operation could have originated from a
558 * method reference declared in Java source code.
559 * <p>
560 * Such a lambda operation is one with the following constraints:
561 * <ol>
562 * <li>Zero or one captured value (assuming correspondence to the {@code this} variable).
563 * <li>A body with only one (entry) block that contains only variable declaration
564 * operations, variable load operations, invoke operations to box or unbox
565 * primitive values, a single invoke operation to the method that is
566 * referenced, and a return operation.
567 * <li>if the return operation returns a non-void result then that result is,
568 * or uniquely depends on, the result of the referencing invoke operation.
569 * <li>If the lambda operation captures one value then the first operand corresponds
570 * to captured the value, and subsequent operands of the referencing invocation
571 * operation are, or uniquely depend on, the lambda operation's parameters, in order.
572 * Otherwise, the first and subsequent operands of the referencing invocation
573 * operation are, or uniquely depend on, the lambda operation's parameters, in order.
574 * </ol>
575 * A value, V2, uniquely depends on another value, V1, if the graph of what V2 depends on
576 * contains only nodes with single edges terminating in V1, and the graph of what depends on V1
577 * is bidirectionally equal to the graph of what V2 depends on.
578 *
579 * @return the invocation operation to the method referenced by the lambda
580 * operation, otherwise empty.
581 */
582 public Optional<InvokeOp> methodReference() {
583 // Single block
584 if (body().blocks().size() > 1) {
585 return Optional.empty();
586 }
587
588 // Zero or one (this) capture
589 List<Value> cvs = capturedValues();
590 if (cvs.size() > 1) {
591 return Optional.empty();
592 }
593
594 Map<Value, Value> valueMapping = new HashMap<>();
595 InvokeOp methodRefInvokeOp = extractMethodInvoke(valueMapping, body().entryBlock().ops());
596 if (methodRefInvokeOp == null) {
597 return Optional.empty();
598 }
599
600 // Lambda's parameters map in encounter order with the invocation's operands
601 List<Value> lambdaParameters = new ArrayList<>();
602 if (cvs.size() == 1) {
603 lambdaParameters.add(cvs.getFirst());
604 }
605 lambdaParameters.addAll(parameters());
606 List<Value> methodRefOperands = methodRefInvokeOp.operands().stream().map(valueMapping::get).toList();
607 if (!lambdaParameters.equals(methodRefOperands)) {
608 return Optional.empty();
609 }
610
611 return Optional.of(methodRefInvokeOp);
612 }
613
614 /**
615 * Determines if this lambda operation contains a direct invocation of a method.
616 * <p>
617 * Such a lambda operation is one with the following constraints:
618 * <ol>
619 * <li>A body with only one (entry) block that contains only variable declaration
620 * operations, variable load operations, invoke operations to box or unbox
621 * primitive values, a single invoke operation to the method that is
622 * referenced, and a return operation.
623 * <li>if the return operation returns a non-void result then that result is,
624 * or uniquely depends on, the result of the referencing invoke operation.
625 * </ol>
626 * A value, V2, uniquely depends on another value, V1, if the graph of what V2 depends on
627 * contains only nodes with single edges terminating in V1, and the graph of what depends on V1
628 * is bidirectionally equal to the graph of what V2 depends on.
629 *
630 * @return the invocation operation to the method referenced by the lambda
631 * operation, otherwise empty.
632 */
633 public Optional<InvokeOp> directInvocation() {
634 // Single block
635 if (body().blocks().size() > 1) {
636 return Optional.empty();
637 }
638
639 Map<Value, Value> valueMapping = new HashMap<>();
640 InvokeOp methodRefInvokeOp = extractMethodInvoke(valueMapping, body().entryBlock().ops());
641 if (methodRefInvokeOp == null) {
642 return Optional.empty();
643 }
644
645 return Optional.of(methodRefInvokeOp);
646 }
647
648 /**
649 * Converts this lambda operation to an equivalent function operation.
650 *
651 * @param lambdaName the name to use for the resulting function (may be empty, or {@code null})
652 * @return a function operation that models this lambda
653 */
654 public CoreOp.FuncOp toFuncOp(String lambdaName) {
655 if (lambdaName == null) lambdaName = "";
656 List<CodeType> parameters = new ArrayList<>(this.invokableSignature().parameterTypes());
657 for (Value v : this.capturedValues()) {
658 CodeType capturedType = v.type() instanceof VarType varType ? varType.valueType() : v.type();
659 parameters.add(capturedType);
660 }
661 return CoreOp.func(lambdaName, CoreType.functionType(this.invokableSignature().returnType(), parameters)).body(builder -> {
662 int idx = this.invokableSignature().parameterTypes().size();
663 for (Value v : capturedValues()) {
664 Block.Parameter p = builder.parameters().get(idx++);
665 Value functionValue = v.type() instanceof VarType ? builder.add(CoreOp.var(p)) : p;
666 builder.context().mapValue(v, functionValue);
667 }
668 List<Block.Parameter> outputValues = builder.parameters().subList(0, this.invokableSignature().parameterTypes().size());
669 builder.transformBody(this.body(), outputValues, CodeTransformer.COPYING_TRANSFORMER);
670 });
671 }
672
673 static InvokeOp extractMethodInvoke(Map<Value, Value> valueMapping, List<Op> ops) {
674 InvokeOp methodRefInvokeOp = null;
675 for (Op op : ops) {
676 switch (op) {
677 case VarOp varOp -> {
678 if (isValueUsedWithOp(varOp.result(), o -> o instanceof VarAccessOp.VarStoreOp)) {
679 return null;
680 }
681 }
682 case VarAccessOp.VarLoadOp varLoadOp -> {
683 Value v = varLoadOp.varOp().operands().getFirst();
684 valueMapping.put(varLoadOp.result(), valueMapping.getOrDefault(v, v));
685 }
686 case InvokeOp iop when isBoxOrUnboxInvocation(iop) -> {
687 Value v = iop.operands().getFirst();
688 valueMapping.put(iop.result(), valueMapping.getOrDefault(v, v));
689 }
690 case InvokeOp iop -> {
691 if (methodRefInvokeOp != null) {
692 return null;
693 }
694
695 for (Value o : iop.operands()) {
696 valueMapping.put(o, valueMapping.getOrDefault(o, o));
697 }
698 methodRefInvokeOp = iop;
699 }
700 case ReturnOp rop -> {
701 if (methodRefInvokeOp == null) {
702 return null;
703 }
704 Value r = rop.returnValue();
705 if (r == null) break;
706 if (!(valueMapping.getOrDefault(r, r) instanceof Result invokeResult)) {
707 return null;
708 }
709 if (invokeResult.op() != methodRefInvokeOp) {
710 return null;
711 }
712 assert methodRefInvokeOp.result().uses().size() == 1;
713 }
714 default -> {
715 return null;
716 }
717 }
718 }
719
720 return methodRefInvokeOp;
721 }
722
723 private static boolean isValueUsedWithOp(Value value, Predicate<Op> opPredicate) {
724 for (Result user : value.uses()) {
725 if (opPredicate.test(user.op())) {
726 return true;
727 }
728 }
729 return false;
730 }
731
732 // @@@ Move to functionality on JavaType(s)
733 static final Set<String> UNBOX_NAMES = Set.of(
734 "byteValue",
735 "shortValue",
736 "charValue",
737 "intValue",
738 "longValue",
739 "floatValue",
740 "doubleValue",
741 "booleanValue");
742
743 private static boolean isBoxOrUnboxInvocation(InvokeOp iop) {
744 MethodRef mr = iop.invokeReference();
745 return mr.refType() instanceof ClassType ct && ct.unbox().isPresent() &&
746 (UNBOX_NAMES.contains(mr.name()) || mr.name().equals("valueOf"));
747 }
748 }
749
750 /**
751 * The throw operation, that can model the Java language throw statement.
752 * <p>
753 * A throw operation is a body-terminating operation that features one operand, the value being thrown.
754 * <p>
755 * The result type of a throw operation is {@link JavaType#VOID}.
756 *
757 * @jls 14.18 The throw Statement
758 */
759 @OpDeclaration(ThrowOp.NAME)
760 public static final class ThrowOp extends JavaOp
761 implements BodyTerminating, JavaStatement {
762 static final String NAME = "throw";
763
764 ThrowOp(ExternalizedOp def) {
765 this(requireSingleOperand(def));
766 }
767
768 ThrowOp(ThrowOp that, CodeContext cc) {
769 super(that, cc);
770 }
771
772 @Override
773 public ThrowOp transform(CodeContext cc, CodeTransformer ct) {
774 return new ThrowOp(this, cc);
775 }
776
777 ThrowOp(Value e) {
778 super(List.of(e));
779 }
780
781 /**
782 * {@return the value being thrown}
783 */
784 public Value argumentOperand() {
785 return operands().get(0);
786 }
787
788 @Override
789 public CodeType resultType() {
790 return VOID;
791 }
792 }
793
794 /**
795 * The assertion operation, that can model Java language assert statements.
796 * <p>
797 * Assert operations feature one or two bodies. The first body, called the <em>predicate body</em>, models the
798 * assertion condition. If present, the second body, called the <em>details body</em>, models the detail
799 * expression.
800 * <p>
801 * The predicate body should accept no arguments and yield a {@link JavaType#BOOLEAN} value.
802 * If present, the details body should accept no arguments and yield a value.
803 * <p>
804 * The result type of an assert operation is {@link JavaType#VOID}.
805 *
806 * @jls 14.10 The assert Statement
807 */
808 @OpDeclaration(AssertOp.NAME)
809 public static final class AssertOp extends JavaOp
810 implements Nested, JavaStatement {
811 static final String NAME = "assert";
812
813 private final List<Body> bodies;
814
815 AssertOp(ExternalizedOp def) {
816 this(def.bodyDefinitions());
817 }
818
819 AssertOp(List<Body.Builder> bodies) {
820 if (bodies.size() != 1 && bodies.size() != 2) {
821 throw structuralException(NAME, "requires 1 or 2 bodies, found %d".formatted(bodies.size()));
822 }
823 requireBodySignature(NAME + " predicate", bodies.get(0), CoreType.functionType(BOOLEAN));
824 if (bodies.size() > 1) {
825 requireNonVoidReturnType(NAME + " details", bodies.get(1), 0);
826 }
827 super(List.of());
828 this.bodies = bodies.stream().map(b -> b.build(this)).toList();
829 }
830
831 AssertOp(AssertOp that, CodeContext cc, CodeTransformer ct) {
832 super(that, cc);
833 this.bodies = that.bodies.stream().map(b -> b.transform(cc, ct).build(this)).toList();
834 }
835
836 @Override
837 public Op transform(CodeContext cc, CodeTransformer ct) {
838 return new AssertOp(this, cc, ct);
839 }
840
841 @Override
842 public CodeType resultType() {
843 return VOID;
844 }
845
846 @Override
847 public List<Body> bodies() {
848 return bodies;
849 }
850
851 /**
852 * {@return the predicate body}
853 */
854 public Body predicateBody() {
855 return bodies.get(0);
856 }
857
858 /**
859 * {@return the details body, or {@code null} if not present}
860 */
861 public Body detailsBody() {
862 return bodies.size() == 2 ? bodies.get(1) : null;
863 }
864 }
865
866 /**
867 * A monitor operation.
868 */
869 public sealed abstract static class MonitorOp extends JavaOp {
870 MonitorOp(MonitorOp that, CodeContext cc) {
871 super(that, cc);
872 }
873
874 MonitorOp(Value monitor) {
875 super(List.of(monitor));
876 }
877
878 /**
879 * {@return the monitor value}
880 */
881 public Value monitorOperand() {
882 return operands().getFirst();
883 }
884
885 @Override
886 public CodeType resultType() {
887 return VOID;
888 }
889
890 /**
891 * The monitor enter operation.
892 */
893 @OpDeclaration(MonitorEnterOp.NAME)
894 public static final class MonitorEnterOp extends MonitorOp {
895 static final String NAME = "monitor.enter";
896
897 MonitorEnterOp(ExternalizedOp def) {
898 this(requireSingleOperand(def));
899 }
900
901 MonitorEnterOp(MonitorEnterOp that, CodeContext cc) {
902 super(that, cc);
903 }
904
905 @Override
906 public MonitorEnterOp transform(CodeContext cc, CodeTransformer ct) {
907 return new MonitorEnterOp(this, cc);
908 }
909
910 MonitorEnterOp(Value monitor) {
911 super(monitor);
912 }
913 }
914
915 /**
916 * The monitor exit operation.
917 */
918 @OpDeclaration(MonitorExitOp.NAME)
919 public static final class MonitorExitOp extends MonitorOp {
920 static final String NAME = "monitor.exit";
921
922 MonitorExitOp(ExternalizedOp def) {
923 this(requireSingleOperand(def));
924 }
925
926 MonitorExitOp(MonitorExitOp that, CodeContext cc) {
927 super(that, cc);
928 }
929
930 @Override
931 public MonitorExitOp transform(CodeContext cc, CodeTransformer ct) {
932 return new MonitorExitOp(this, cc);
933 }
934
935 MonitorExitOp(Value monitor) {
936 super(monitor);
937 }
938 }
939 }
940
941 /**
942 * The invoke operation, that can model Java language method invocation expressions.
943 * <p>
944 * The method invoked by an invoke operation is specified using a
945 * {@linkplain MethodRef method reference}.
946 * The operands of an invoke operation are specified as follows:
947 * <ul>
948 * <li>For {@linkplain InvokeKind#STATIC static} invocations, operands are the invocation arguments.</li>
949 * <li>For {@linkplain InvokeKind#INSTANCE instance} and {@linkplain InvokeKind#SUPER super} invocations, the first
950 * operand is the receiver and the remaining operands are the invocation arguments.</li>
951 * </ul>
952 *
953 * @jls 15.12 Method Invocation Expressions
954 */
955 @OpDeclaration(InvokeOp.NAME)
956 public static final class InvokeOp extends JavaOp
957 implements ReflectiveOp, JavaExpression, JavaStatement {
958
959 /**
960 * The kind of invocation.
961 */
962 public enum InvokeKind {
963 /**
964 * An invocation on a class (static) method.
965 */
966 STATIC,
967 /**
968 * An invocation on an instance method.
969 */
970 INSTANCE,
971 /**
972 * A super invocation on an instance method.
973 */
974 SUPER
975 }
976
977 static final String NAME = "invoke";
978 /** The externalized attribute key for a method invocation reference. */
979 static final String ATTRIBUTE_INVOKE_REF = NAME + ".ref";
980 /** The externalized attribute key indicating the invocation kind. */
981 static final String ATTRIBUTE_INVOKE_KIND = NAME + ".kind";
982 /** The externalized attribute key for marking a varargs invocation. */
983 static final String ATTRIBUTE_INVOKE_VARARGS = NAME + ".varargs";
984
985 final InvokeKind invokeKind;
986 final boolean isVarArgs;
987 final MethodRef invokeReference;
988 final CodeType resultType;
989
990 InvokeOp(ExternalizedOp def) {
991 // Required attribute
992 MethodRef invokeRef = requireAttribute(def, ATTRIBUTE_INVOKE_REF, true, MethodRef.class);
993
994 // If not present defaults to false
995 boolean isVarArgs = optionalBooleanAttribute(def, ATTRIBUTE_INVOKE_VARARGS);
996
997 // If not present and is not varargs defaults to class or instance invocation
998 // based on number of operands and parameters
999 InvokeKind ik = optionalAttribute(def, ATTRIBUTE_INVOKE_KIND, false, Object.class).map(v ->
1000 switch (v) {
1001 case String s -> InvokeKind.valueOf(s);
1002 case InvokeKind k -> k;
1003 default -> throw unsupportedAttributeValueException(def, ATTRIBUTE_INVOKE_KIND, v);
1004 }).orElseGet(() -> {
1005 if (isVarArgs) {
1006 // If varargs then we cannot infer invoke kind
1007 throw unsupportedAttributeValueException(def, ATTRIBUTE_INVOKE_KIND, null);
1008 }
1009 int paramCount = invokeRef.signature().parameterTypes().size();
1010 int argCount = def.operands().size();
1011 return (argCount == paramCount + 1)
1012 ? InvokeKind.INSTANCE
1013 : InvokeKind.STATIC;
1014 });
1015
1016
1017 this(ik, isVarArgs, def.resultType(), invokeRef, def.operands());
1018 }
1019
1020 InvokeOp(InvokeOp that, CodeContext cc) {
1021 super(that, cc);
1022
1023 this.invokeKind = that.invokeKind;
1024 this.isVarArgs = that.isVarArgs;
1025 this.invokeReference = that.invokeReference;
1026 this.resultType = that.resultType;
1027 }
1028
1029 @Override
1030 public InvokeOp transform(CodeContext cc, CodeTransformer ct) {
1031 return new InvokeOp(this, cc);
1032 }
1033
1034 InvokeOp(InvokeKind invokeKind, boolean isVarArgs, CodeType resultType, MethodRef invokeReference, List<Value> args) {
1035 super(args);
1036
1037 validateArgCount(invokeKind, isVarArgs, invokeReference, args);
1038
1039 this.invokeKind = invokeKind;
1040 this.isVarArgs = isVarArgs;
1041 this.invokeReference = invokeReference;
1042 this.resultType = resultType;
1043 }
1044
1045 static void validateArgCount(InvokeKind invokeKind, boolean isVarArgs, MethodRef invokeRef, List<Value> operands) {
1046 int paramCount = invokeRef.signature().parameterTypes().size();
1047 int argCount = operands.size() - (invokeKind == InvokeKind.STATIC ? 0 : 1);
1048 if ((!isVarArgs && argCount != paramCount)
1049 || argCount < paramCount - 1) {
1050 throw structuralException(NAME, "kind=%s, varargs=%s, requires %s%d operands, found %d".formatted(
1051 invokeKind,
1052 isVarArgs,
1053 isVarArgs ? "at least " : "",
1054 isVarArgs ? paramCount - 1 : paramCount,
1055 argCount));
1056 }
1057 }
1058
1059 @Override
1060 public Map<String, Object> externalize() {
1061 HashMap<String, Object> m = new HashMap<>();
1062 m.put("", invokeReference);
1063 if (isVarArgs) {
1064 // If varargs then we need to declare the invoke.kind attribute
1065 // Given a method `A::m(A... more)` and an invocation with one
1066 // operand, we don't know if that operand corresponds to the
1067 // receiver or a method argument
1068 m.put(ATTRIBUTE_INVOKE_KIND, invokeKind);
1069 m.put(ATTRIBUTE_INVOKE_VARARGS, isVarArgs);
1070 } else if (invokeKind == InvokeKind.SUPER) {
1071 m.put(ATTRIBUTE_INVOKE_KIND, invokeKind);
1072 }
1073 return Collections.unmodifiableMap(m);
1074 }
1075
1076 /**
1077 * {@return the invocation kind}
1078 */
1079 public InvokeKind invokeKind() {
1080 return invokeKind;
1081 }
1082
1083 /**
1084 * {@return {@code true} if this invocation uses a variable number of arguments}
1085 */
1086 public boolean isVarArgs() {
1087 return isVarArgs;
1088 }
1089
1090 /**
1091 * {@return the method invocation reference}
1092 */
1093 public MethodRef invokeReference() {
1094 return invokeReference;
1095 }
1096
1097 /**
1098 * {@return {@code true} if this invocation refers to an instance method)}
1099 */
1100 public boolean hasReceiver() {
1101 return invokeKind != InvokeKind.STATIC;
1102 }
1103
1104 /**
1105 * {@return the receiver, otherwise {@code null} if no receiver}
1106 */
1107 public Value receiverOperand() {
1108 return hasReceiver() ? operands().getFirst() : null;
1109 }
1110
1111 /**
1112 * {@return the operands used as varargs, if this is a varargs invocation,
1113 * or {@code null}}
1114 */
1115 public List<Value> varArgOperands() {
1116 if (!isVarArgs) {
1117 return null;
1118 }
1119
1120 int operandCount = operands().size();
1121 int argCount = operandCount - (invokeKind == InvokeKind.STATIC ? 0 : 1);
1122 int paramCount = invokeReference.signature().parameterTypes().size();
1123 int varArgCount = argCount - (paramCount - 1);
1124 return operands().subList(operandCount - varArgCount, operandCount);
1125 }
1126
1127 /**
1128 * {@return the method invocation arguments, including the receiver as the first argument if present}
1129 */
1130 public List<Value> argOperands() {
1131 if (!isVarArgs) {
1132 return operands();
1133 }
1134 int paramCount = invokeReference().signature().parameterTypes().size();
1135 int argOperandsCount = paramCount - (invokeKind() == InvokeKind.STATIC ? 1 : 0);
1136 return operands().subList(0, argOperandsCount);
1137 }
1138
1139 @Override
1140 public CodeType resultType() {
1141 return resultType;
1142 }
1143 }
1144
1145 /**
1146 * The conversion operation, that can model Java language cast expressions
1147 * for numerical conversion, or such implicit conversion.
1148 * <p>
1149 * Conversion operations feature one operand, the value to convert.
1150 *
1151 * @jls 15.16 Cast Expressions
1152 * @jls 5.1.2 Widening Primitive Conversion
1153 * @jls 5.1.3 Narrowing Primitive Conversion
1154 */
1155 @OpDeclaration(ConvOp.NAME)
1156 public static final class ConvOp extends JavaOp
1157 implements Pure, JavaExpression {
1158 static final String NAME = "conv";
1159
1160 final CodeType resultType;
1161
1162 ConvOp(ExternalizedOp def) {
1163 this(def.resultType(), requireSingleOperand(def));
1164 }
1165
1166 ConvOp(ConvOp that, CodeContext cc) {
1167 super(that, cc);
1168
1169 this.resultType = that.resultType;
1170 }
1171
1172 @Override
1173 public Op transform(CodeContext cc, CodeTransformer ct) {
1174 return new ConvOp(this, cc);
1175 }
1176
1177 ConvOp(CodeType resultType, Value arg) {
1178 super(List.of(arg));
1179
1180 this.resultType = resultType;
1181 }
1182
1183 /**
1184 * {@return the value to convert}
1185 */
1186 public Value valueOperand() {
1187 return operands().getFirst();
1188 }
1189
1190 @Override
1191 public CodeType resultType() {
1192 return resultType;
1193 }
1194 }
1195
1196 /**
1197 * The new operation, that can model Java language instance creation expressions and array creation expressions.
1198 * <p>
1199 * The constructor invoked by a new operation is specified using a
1200 * {@linkplain MethodRef constructor reference}.
1201 * New operations feature operands corresponding to the constructor arguments.
1202 *
1203 * @jls 15.9 Class Instance Creation Expressions
1204 * @jls 15.10.1 Array Creation Expressions
1205 */
1206 @OpDeclaration(NewOp.NAME)
1207 public static final class NewOp extends JavaOp
1208 implements ReflectiveOp, JavaExpression, JavaStatement {
1209
1210 static final String NAME = "new";
1211 /**
1212 * The externalized attribute key for a constructor reference in a new operation.
1213 */
1214 static final String ATTRIBUTE_NEW_REF = NAME + ".ref";
1215 /**
1216 * The externalized attribute key indicating a varargs constructor in a new operation.
1217 */
1218 static final String ATTRIBUTE_NEW_VARARGS = NAME + ".varargs";
1219
1220 final boolean isVarArgs;
1221 final MethodRef constructorReference;
1222 final CodeType resultType;
1223
1224 NewOp(ExternalizedOp def) {
1225 this(optionalBooleanAttribute(def, ATTRIBUTE_NEW_VARARGS),
1226 def.resultType(),
1227 requireAttribute(def, ATTRIBUTE_NEW_REF, true, MethodRef.class),
1228 def.operands());
1229 }
1230
1231 NewOp(NewOp that, CodeContext cc) {
1232 super(that, cc);
1233
1234 this.isVarArgs = that.isVarArgs;
1235 this.constructorReference = that.constructorReference;
1236 this.resultType = that.resultType;
1237 }
1238
1239 @Override
1240 public NewOp transform(CodeContext cc, CodeTransformer ct) {
1241 return new NewOp(this, cc);
1242 }
1243
1244 NewOp(boolean isVarargs, CodeType resultType, MethodRef ctorRef, List<Value> args) {
1245 validateArgCount(isVarargs, ctorRef, args);
1246 if (!ctorRef.isConstructor()) {
1247 throw structuralException(NAME, "requires a constructor reference, found %s".formatted(ctorRef));
1248 }
1249 super(args);
1250 this.isVarArgs = isVarargs;
1251 this.constructorReference = ctorRef;
1252 this.resultType = resultType;
1253 }
1254
1255 static void validateArgCount(boolean isVarArgs, MethodRef ctorRef, List<Value> operands) {
1256 int paramCount = ctorRef.signature().parameterTypes().size();
1257 int argCount = operands.size();
1258 if ((!isVarArgs && argCount != paramCount)
1259 || argCount < paramCount - 1) {
1260 throw structuralException(NAME, "varargs=%s, requires %s%d operands, found %d".formatted(
1261 isVarArgs,
1262 isVarArgs ? "at least " : "",
1263 isVarArgs ? paramCount - 1 : paramCount,
1264 argCount));
1265 }
1266 }
1267
1268 @Override
1269 public Map<String, Object> externalize() {
1270 HashMap<String, Object> m = new HashMap<>();
1271 m.put("", constructorReference);
1272 if (isVarArgs) {
1273 m.put(ATTRIBUTE_NEW_VARARGS, isVarArgs);
1274 }
1275 return Collections.unmodifiableMap(m);
1276 }
1277
1278 /**
1279 * {@return {@code true}, if this instance creation operation is a varargs constructor call}
1280 */
1281 public boolean isVarargs() {
1282 return isVarArgs;
1283 }
1284
1285 /**
1286 * {@return the constructor reference for this instance creation operation}
1287 */
1288 public MethodRef constructorReference() {
1289 return constructorReference;
1290 }
1291
1292 @Override
1293 public CodeType resultType() {
1294 return resultType;
1295 }
1296 }
1297
1298 /**
1299 * A field access operation, that can model Java language field access expressions.
1300 * <p>
1301 * The field accessed by a field access operation is specified using a {@linkplain FieldRef field
1302 * reference}.
1303 * <p>
1304 * Instance field accesses feature a receiver operand. Static field accesses have no receiver operand.
1305 *
1306 * @see CoreOp.VarAccessOp
1307 * @jls 15.11 Field Access Expressions
1308 */
1309 public sealed abstract static class FieldAccessOp extends JavaOp
1310 implements AccessOp, ReflectiveOp {
1311 /**
1312 * The externalized attribute modeling the field reference.
1313 */
1314 static final String ATTRIBUTE_FIELD_REF = "field.ref";
1315
1316 final FieldRef fieldReference;
1317
1318 FieldAccessOp(FieldAccessOp that, CodeContext cc) {
1319 super(that, cc);
1320 this.fieldReference = that.fieldReference;
1321 }
1322
1323 FieldAccessOp(List<Value> operands,
1324 FieldRef fieldReference) {
1325 super(operands);
1326
1327 this.fieldReference = fieldReference;
1328 }
1329
1330 @Override
1331 public Map<String, Object> externalize() {
1332 return Map.of("", fieldReference);
1333 }
1334
1335 /**
1336 * {@return the reference to the accessed field}
1337 */
1338 public final FieldRef fieldReference() {
1339 return fieldReference;
1340 }
1341
1342 /**
1343 * {@return the value of the receiver, or {@code null} if no receiver}
1344 */
1345 public Value receiverOperand() {
1346 return operands().isEmpty() ? null : operands().getFirst();
1347 }
1348
1349 /**
1350 * The field load operation, that can model Java language field access expressions used to read a field value.
1351 *
1352 * @see CoreOp.VarAccessOp.VarLoadOp
1353 * @jls 15.11 Field Access Expressions
1354 */
1355 @OpDeclaration(FieldLoadOp.NAME)
1356 public static final class FieldLoadOp extends FieldAccessOp
1357 implements Pure, JavaExpression {
1358 static final String NAME = "field.load";
1359
1360 final CodeType resultType;
1361
1362 FieldLoadOp(ExternalizedOp def) {
1363 super(requireOperands(def, 0, 1), requireAttribute(def, ATTRIBUTE_FIELD_REF, true, FieldRef.class));
1364 this.resultType = def.resultType();
1365 }
1366
1367 FieldLoadOp(FieldLoadOp that, CodeContext cc) {
1368 super(that, cc);
1369
1370 resultType = that.resultType();
1371 }
1372
1373 @Override
1374 public FieldLoadOp transform(CodeContext cc, CodeTransformer ct) {
1375 return new FieldLoadOp(this, cc);
1376 }
1377
1378 // instance
1379 FieldLoadOp(CodeType resultType, FieldRef fieldRef, Value receiver) {
1380 super(List.of(receiver), fieldRef);
1381
1382 this.resultType = resultType;
1383 }
1384
1385 // static
1386 FieldLoadOp(CodeType resultType, FieldRef fieldRef) {
1387 super(List.of(), fieldRef);
1388
1389 this.resultType = resultType;
1390 }
1391
1392 @Override
1393 public CodeType resultType() {
1394 return resultType;
1395 }
1396 }
1397
1398 /**
1399 * The field store operation, that can model Java language field access expressions used to write a field value.
1400 * <p>
1401 * The result type is always {@link JavaType#VOID}.
1402 *
1403 * @see CoreOp.VarAccessOp.VarStoreOp
1404 * @jls 15.11 Field Access Expressions
1405 */
1406 @OpDeclaration(FieldStoreOp.NAME)
1407 public static final class FieldStoreOp extends FieldAccessOp
1408 implements JavaExpression, JavaStatement {
1409 static final String NAME = "field.store";
1410
1411 FieldStoreOp(ExternalizedOp def) {
1412 super(requireOperands(def, 1, 2), requireAttribute(def, ATTRIBUTE_FIELD_REF, true, FieldRef.class));
1413 }
1414
1415 FieldStoreOp(FieldStoreOp that, CodeContext cc) {
1416 super(that, cc);
1417 }
1418
1419 @Override
1420 public FieldStoreOp transform(CodeContext cc, CodeTransformer ct) {
1421 return new FieldStoreOp(this, cc);
1422 }
1423
1424 // instance
1425 FieldStoreOp(FieldRef fieldRef, Value receiver, Value v) {
1426 super(List.of(receiver, v), fieldRef);
1427 }
1428
1429 // static
1430 FieldStoreOp(FieldRef fieldRef, Value v) {
1431 super(List.of(v), fieldRef);
1432 }
1433
1434 /**
1435 * {@return the value to store}
1436 */
1437 public Value valueOperand() {
1438 return operands().get(operands().size() - 1);
1439 }
1440
1441 @Override
1442 public CodeType resultType() {
1443 return VOID;
1444 }
1445 }
1446 }
1447
1448 /**
1449 * The array length operation, that can model Java language field access expressions to the length field of an
1450 * array.
1451 * <p>
1452 * Array length operations feature one operand, the array value.
1453 * The result type of an array length operation is {@link JavaType#INT}.
1454 *
1455 * @jls 15.11 Field Access Expressions
1456 */
1457 @OpDeclaration(ArrayLengthOp.NAME)
1458 public static final class ArrayLengthOp extends JavaOp
1459 implements ReflectiveOp, JavaExpression {
1460 static final String NAME = "array.length";
1461
1462 ArrayLengthOp(ExternalizedOp def) {
1463 this(requireSingleOperand(def));
1464 }
1465
1466 ArrayLengthOp(ArrayLengthOp that, CodeContext cc) {
1467 super(that, cc);
1468 }
1469
1470 @Override
1471 public ArrayLengthOp transform(CodeContext cc, CodeTransformer ct) {
1472 return new ArrayLengthOp(this, cc);
1473 }
1474
1475 ArrayLengthOp(Value array) {
1476 super(List.of(array));
1477 }
1478
1479 /**
1480 * {@return the larray}
1481 */
1482 public Value arrayOperand() {
1483 return operands().getFirst();
1484 }
1485
1486 @Override
1487 public CodeType resultType() {
1488 return INT;
1489 }
1490 }
1491
1492 /**
1493 * The array access operation, that can model Java language array access expressions.
1494 * <p>
1495 * Array load operations feature two operands, the array value and the index value.
1496 * Array store operations feature an additional operand, the stored value.
1497 *
1498 * @jls 15.10.3 Array Access Expressions
1499 */
1500 public sealed abstract static class ArrayAccessOp extends JavaOp
1501 implements AccessOp, ReflectiveOp {
1502
1503 ArrayAccessOp(ArrayAccessOp that, CodeContext cc) {
1504 super(that, cc);
1505 }
1506
1507 ArrayAccessOp(List<Value> operands) {
1508 super(operands);
1509 }
1510
1511 /**
1512 * {@return the array}
1513 */
1514 public Value arrayOperand() {
1515 return operands().get(0);
1516 }
1517
1518 /**
1519 * {@return the array index}
1520 */
1521 public Value indexOperand() {
1522 return operands().get(1);
1523 }
1524
1525 /**
1526 * The array load operation, that can model Java language array expressions combined with load access to the
1527 * components of an array.
1528 *
1529 * @jls 15.10.3 Array Access Expressions
1530 */
1531 @OpDeclaration(ArrayLoadOp.NAME)
1532 public static final class ArrayLoadOp extends ArrayAccessOp
1533 implements Pure, JavaExpression {
1534 static final String NAME = "array.load";
1535 final CodeType componentType;
1536
1537 ArrayLoadOp(ExternalizedOp def) {
1538 super(requireOperands(def, 2));
1539 this.componentType = def.resultType();
1540 }
1541
1542 ArrayLoadOp(ArrayLoadOp that, CodeContext cc) {
1543 super(that, cc);
1544 this.componentType = that.componentType;
1545 }
1546
1547 @Override
1548 public ArrayLoadOp transform(CodeContext cc, CodeTransformer ct) {
1549 return new ArrayLoadOp(this, cc);
1550 }
1551
1552 ArrayLoadOp(Value array, Value index) {
1553 // @@@ revisit this when the component type is not explicitly given (see VarOp.resultType as an example)
1554 this(array, index, ((ArrayType)array.type()).componentType());
1555 }
1556
1557 ArrayLoadOp(Value array, Value index, CodeType componentType) {
1558 super(List.of(array, index));
1559 this.componentType = componentType;
1560 }
1561
1562 @Override
1563 public CodeType resultType() {
1564 return componentType;
1565 }
1566 }
1567
1568 /**
1569 * The array store operation, that can model Java language array expressions combined with store access to the
1570 * components of an array.
1571 * <p>
1572 * The result type of an array store operation is {@link JavaType#VOID}.
1573 *
1574 * @jls 15.10.3 Array Access Expressions
1575 */
1576 @OpDeclaration(ArrayStoreOp.NAME)
1577 public static final class ArrayStoreOp extends ArrayAccessOp
1578 implements JavaExpression, JavaStatement {
1579 static final String NAME = "array.store";
1580
1581 ArrayStoreOp(ExternalizedOp def) {
1582 List<Value> operands = requireOperands(def, 3);
1583 this(operands.get(0), operands.get(1), operands.get(2));
1584 }
1585
1586 ArrayStoreOp(ArrayStoreOp that, CodeContext cc) {
1587 super(that, cc);
1588 }
1589
1590 @Override
1591 public ArrayStoreOp transform(CodeContext cc, CodeTransformer ct) {
1592 return new ArrayStoreOp(this, cc);
1593 }
1594
1595 ArrayStoreOp(Value array, Value index, Value v) {
1596 super(List.of(array, index, v));
1597 }
1598
1599 /**
1600 * {@return the value to store}
1601 */
1602 public Value valueOperand() {
1603 return operands().get(2);
1604 }
1605
1606 @Override
1607 public CodeType resultType() {
1608 return VOID;
1609 }
1610 }
1611 }
1612
1613 /**
1614 * The instanceof operation, that can model Java language instanceof expressions that use the
1615 * {@code instanceof} keyword as the <em>type comparison operator</em>.
1616 * <p>
1617 * Instanceof operations feature one operand, the value being tested, and are associated with a
1618 * {@linkplain JavaType type} modeling the target type of the type comparison operator.
1619 *
1620 * @jls 15.20.2 The instanceof Operator
1621 */
1622 @OpDeclaration(InstanceOfOp.NAME)
1623 public static final class InstanceOfOp extends JavaOp
1624 implements Pure, ReflectiveOp, JavaExpression {
1625 static final String NAME = "instanceof";
1626 /** The externalized attribute key for the code type modeling the instanceof target type. */
1627 static final String ATTRIBUTE_INSTANCEOF_TYPE = NAME + ".type";
1628
1629 final CodeType targetType;
1630
1631 InstanceOfOp(ExternalizedOp def) {
1632 this(requireAttribute(def, ATTRIBUTE_INSTANCEOF_TYPE, true, JavaType.class), requireSingleOperand(def));
1633 }
1634
1635 InstanceOfOp(InstanceOfOp that, CodeContext cc) {
1636 super(that, cc);
1637
1638 this.targetType = that.targetType;
1639 }
1640
1641 @Override
1642 public InstanceOfOp transform(CodeContext cc, CodeTransformer ct) {
1643 return new InstanceOfOp(this, cc);
1644 }
1645
1646 InstanceOfOp(CodeType t, Value v) {
1647 super(List.of(v));
1648
1649 this.targetType = t;
1650 }
1651
1652 @Override
1653 public Map<String, Object> externalize() {
1654 return Map.of("", targetType);
1655 }
1656
1657 /**
1658 * {@return the value to test}
1659 */
1660 public Value valueOperand() {
1661 return operands().getFirst();
1662 }
1663
1664 /**
1665 * {@return the code type modeling the target type of this instanceof operation}
1666 */
1667 public CodeType targetType() {
1668 return targetType;
1669 }
1670
1671 @Override
1672 public CodeType resultType() {
1673 return BOOLEAN;
1674 }
1675 }
1676
1677 /**
1678 * The cast operation, that can model Java language cast expressions for reference types.
1679 * <p>
1680 * Cast operations feature one operand, the value being cast, and are associated with a
1681 * {@linkplain JavaType type} modeling the target type of the cast.
1682 *
1683 * @jls 15.16 Cast Expressions
1684 */
1685 @OpDeclaration(CastOp.NAME)
1686 public static final class CastOp extends JavaOp
1687 implements Pure, ReflectiveOp, JavaExpression {
1688 static final String NAME = "cast";
1689 /** The externalized attribute key for the code type modeling the target type of the cast. */
1690 static final String ATTRIBUTE_CAST_TYPE = NAME + ".type";
1691
1692 final CodeType resultType;
1693 final CodeType targetType;
1694
1695 CastOp(ExternalizedOp def) {
1696 this(def.resultType(), requireAttribute(def, ATTRIBUTE_CAST_TYPE, true, JavaType.class), requireSingleOperand(def));
1697 }
1698
1699 CastOp(CastOp that, CodeContext cc) {
1700 super(that, cc);
1701
1702 this.resultType = that.resultType;
1703 this.targetType = that.targetType;
1704 }
1705
1706 @Override
1707 public CastOp transform(CodeContext cc, CodeTransformer ct) {
1708 return new CastOp(this, cc);
1709 }
1710
1711 CastOp(CodeType resultType, CodeType t, Value v) {
1712 super(List.of(v));
1713
1714 this.resultType = resultType;
1715 this.targetType = t;
1716 }
1717
1718 @Override
1719 public Map<String, Object> externalize() {
1720 return Map.of("", targetType);
1721 }
1722
1723 /**
1724 * {@return the value to cast}
1725 */
1726 public Value valueOperand() {
1727 return operands().get(0);
1728 }
1729
1730 /**
1731 * {@return the code type modeling the target type of this cast operation}
1732 */
1733 public CodeType targetType() {
1734 return targetType;
1735 }
1736
1737 @Override
1738 public CodeType resultType() {
1739 return resultType;
1740 }
1741 }
1742
1743 /**
1744 * The exception region start operation, that can model entry into an exception region.
1745 * <p>
1746 * An exception region start operation is a block-terminating operation whose first successor is the starting
1747 * block of the exception region, and whose remaining successors are the catch blocks for that region.
1748 */
1749 @OpDeclaration(ExceptionRegionEnter.NAME)
1750 public static final class ExceptionRegionEnter extends JavaOp
1751 implements BlockTerminating {
1752 static final String NAME = "exception.region.enter";
1753
1754 // First successor is the non-exceptional successor whose target indicates
1755 // the first block in the exception region.
1756 // One or more subsequent successors target the exception catching blocks
1757 // each of which have one block argument whose type is an exception type.
1758 final List<Block.Reference> references;
1759
1760 ExceptionRegionEnter(ExternalizedOp def) {
1761 this(def.successors());
1762 }
1763
1764 ExceptionRegionEnter(ExceptionRegionEnter that, CodeContext cc) {
1765 super(that, cc);
1766
1767 this.references = that.references.stream().map(cc::getReferenceOrCreate).toList();
1768 }
1769
1770 @Override
1771 public ExceptionRegionEnter transform(CodeContext cc, CodeTransformer ct) {
1772 return new ExceptionRegionEnter(this, cc);
1773 }
1774
1775 ExceptionRegionEnter(List<Block.Reference> references) {
1776 if (references.size() < 2) {
1777 throw structuralException(NAME, "requires at least 2 successors, found %d".formatted(references.size()));
1778 }
1779 super(List.of());
1780 this.references = List.copyOf(references);
1781 }
1782
1783 @Override
1784 public List<Block.Reference> successors() {
1785 return references;
1786 }
1787
1788 /**
1789 * {@return the starting block reference of this exception region}
1790 */
1791 public Block.Reference startReference() {
1792 return references.get(0);
1793 }
1794
1795 /**
1796 * {@return the catch block references of this exception region}
1797 */
1798 public List<Block.Reference> catchReferences() {
1799 return references.subList(1, references.size());
1800 }
1801
1802 @Override
1803 public CodeType resultType() {
1804 return VOID;
1805 }
1806 }
1807
1808 /**
1809 * The exception region end operation, that can model exit from an exception region.
1810 * <p>
1811 * An exception region end operation is a block-terminating operation with one operand and one successor.
1812 * The operand is the result of the dominant {@link ExceptionRegionEnter}. The successor is the block that
1813 * follows the exception region.
1814 */
1815 @OpDeclaration(ExceptionRegionExit.NAME)
1816 public static final class ExceptionRegionExit extends JavaOp
1817 implements BlockTerminating {
1818 static final String NAME = "exception.region.exit";
1819
1820 // Non-exceptional successor
1821 final Block.Reference end;
1822
1823 ExceptionRegionExit(ExternalizedOp def) {
1824 this(requireSingleOperand(def), requireSingleSuccessor(def));
1825 }
1826
1827 ExceptionRegionExit(ExceptionRegionExit that, CodeContext cc) {
1828 super(that, cc);
1829
1830 this.end = cc.getReferenceOrCreate(that.end);
1831 }
1832
1833 @Override
1834 public ExceptionRegionExit transform(CodeContext cc, CodeTransformer ct) {
1835 return new ExceptionRegionExit(this, cc);
1836 }
1837
1838 ExceptionRegionExit(Value enter, Block.Reference end) {
1839 if (!(enter instanceof Op.Result or && or.op() instanceof ExceptionRegionEnter)) {
1840 throw structuralException(NAME, "operand is not an exception region entry: " + enter);
1841 }
1842 super(List.of(enter));
1843 this.end = end;
1844 }
1845
1846 @Override
1847 public List<Block.Reference> successors() {
1848 return List.of(end);
1849 }
1850
1851 /**
1852 * {@return the block reference reached after exiting this exception region}
1853 */
1854 public Block.Reference endReference() {
1855 return end;
1856 }
1857
1858 /**
1859 * {@return the dominant exception region enter operation}
1860 */
1861 public ExceptionRegionEnter enterOp() {
1862 return (ExceptionRegionEnter)operands().getFirst().asResult().op();
1863 }
1864
1865 @Override
1866 public CodeType resultType() {
1867 return VOID;
1868 }
1869 }
1870
1871 /**
1872 * The string concatenation operation, that can model the Java language string concatenation operator
1873 * {@code +}.
1874 * <p>
1875 * Concatenation operations feature two operands.
1876 * The result type of a string concatenation operation is {@linkplain JavaType#J_L_STRING java.lang.String}.
1877 *
1878 * @jls 15.18.1 String Concatenation Operator +
1879 */
1880 @OpDeclaration(ConcatOp.NAME)
1881 public static final class ConcatOp extends JavaOp
1882 implements Pure, JavaExpression {
1883 static final String NAME = "concat";
1884
1885 ConcatOp(ConcatOp that, CodeContext cc) {
1886 super(that, cc);
1887 }
1888
1889 ConcatOp(ExternalizedOp def) {
1890 List<Value> operands = requireOperands(def, 2);
1891 this(operands.get(0), operands.get(1));
1892 }
1893
1894 ConcatOp(Value lhs, Value rhs) {
1895 super(List.of(lhs, rhs));
1896 }
1897
1898 @Override
1899 public Op transform(CodeContext cc, CodeTransformer ct) {
1900 return new ConcatOp(this, cc);
1901 }
1902
1903 /**
1904 * {@return the left hand operand}
1905 */
1906 public Value lhsOperand() {
1907 return operands().get(0);
1908 }
1909
1910 /**
1911 * {@return the right hand operand}
1912 */
1913 public Value rhsOperand() {
1914 return operands().get(1);
1915 }
1916
1917 @Override
1918 public CodeType resultType() {
1919 return J_L_STRING;
1920 }
1921 }
1922
1923 /**
1924 * The arithmetic operation.
1925 */
1926 public sealed static abstract class ArithmeticOperation extends JavaOp
1927 implements Pure, JavaExpression {
1928 ArithmeticOperation(ArithmeticOperation that, CodeContext cc) {
1929 super(that, cc);
1930 }
1931
1932 ArithmeticOperation(List<Value> operands) {
1933 super(operands);
1934 }
1935 }
1936
1937 /**
1938 * A binary arithmetic operation.
1939 * <p>
1940 * Binary arithmetic operations feature two operands. Usually, both operands have the same type,
1941 * although that is not always the case. The result type of a binary arithmetic operation is
1942 * the type of the first operand.
1943 */
1944 public sealed static abstract class BinaryOp extends ArithmeticOperation {
1945 BinaryOp(BinaryOp that, CodeContext cc) {
1946 super(that, cc);
1947 }
1948
1949 BinaryOp(ExternalizedOp def) {
1950 super(requireOperands(def, 2));
1951 }
1952
1953 BinaryOp(Value lhs, Value rhs) {
1954 super(List.of(lhs, rhs));
1955 }
1956
1957 /**
1958 * {@return the left hand operand}
1959 */
1960 public Value lhsOperand() {
1961 return operands().get(0);
1962 }
1963
1964 /**
1965 * {@return the right hand operand}
1966 */
1967 public Value rhsOperand() {
1968 return operands().get(1);
1969 }
1970
1971 @Override
1972 public CodeType resultType() {
1973 return operands().get(0).type();
1974 }
1975 }
1976
1977 /**
1978 * The unary arithmetic operation.
1979 * <p>
1980 * Unary arithmetic operations feature one operand.
1981 * The result type of a unary arithmetic operation is the type of its operand.
1982 */
1983 public sealed static abstract class UnaryOp extends ArithmeticOperation {
1984 UnaryOp(UnaryOp that, CodeContext cc) {
1985 super(that, cc);
1986 }
1987
1988 UnaryOp(ExternalizedOp def) {
1989 super(requireOperands(def, 1));
1990 }
1991
1992 UnaryOp(Value v) {
1993 super(List.of(v));
1994 }
1995
1996 /**
1997 * {@return the operand}
1998 */
1999 public Value operand() {
2000 return operands().get(0);
2001 }
2002
2003 @Override
2004 public CodeType resultType() {
2005 return operands().get(0).type();
2006 }
2007 }
2008
2009 /**
2010 * The compare operation.
2011 * <p>
2012 * Compare operations feature two operands, and yield a {@link JavaType#BOOLEAN} value.
2013 */
2014 public sealed static abstract class CompareOp extends ArithmeticOperation {
2015 CompareOp(CompareOp that, CodeContext cc) {
2016 super(that, cc);
2017 }
2018
2019 CompareOp(ExternalizedOp def) {
2020 super(requireOperands(def, 2));
2021 }
2022
2023 CompareOp(Value lhs, Value rhs) {
2024 super(List.of(lhs, rhs));
2025 }
2026
2027 /**
2028 * {@return the left hand operand}
2029 */
2030 public Value lhsOperand() {
2031 return operands().get(0);
2032 }
2033
2034 /**
2035 * {@return the right hand operand}
2036 */
2037 public Value rhsOperand() {
2038 return operands().get(1);
2039 }
2040
2041 @Override
2042 public CodeType resultType() {
2043 return BOOLEAN;
2044 }
2045 }
2046
2047 /**
2048 * The add operation, that can model the Java language binary {@code +} operator for numeric types
2049 *
2050 * @jls 15.18.2 Additive Operators (+ and -) for Numeric Types
2051 */
2052 @OpDeclaration(AddOp.NAME)
2053 public static final class AddOp extends BinaryOp {
2054 static final String NAME = "add";
2055
2056 AddOp(ExternalizedOp def) {
2057 super(def);
2058 }
2059
2060 AddOp(AddOp that, CodeContext cc) {
2061 super(that, cc);
2062 }
2063
2064 @Override
2065 public AddOp transform(CodeContext cc, CodeTransformer ct) {
2066 return new AddOp(this, cc);
2067 }
2068
2069 AddOp(Value lhs, Value rhs) {
2070 super(lhs, rhs);
2071 }
2072 }
2073
2074 /**
2075 * The sub operation, that can model the Java language binary {@code -} operator for numeric types
2076 *
2077 * @jls 15.18.2 Additive Operators (+ and -) for Numeric Types
2078 */
2079 @OpDeclaration(SubOp.NAME)
2080 public static final class SubOp extends BinaryOp {
2081 static final String NAME = "sub";
2082
2083 SubOp(ExternalizedOp def) {
2084 super(def);
2085 }
2086
2087 SubOp(SubOp that, CodeContext cc) {
2088 super(that, cc);
2089 }
2090
2091 @Override
2092 public SubOp transform(CodeContext cc, CodeTransformer ct) {
2093 return new SubOp(this, cc);
2094 }
2095
2096 SubOp(Value lhs, Value rhs) {
2097 super(lhs, rhs);
2098 }
2099 }
2100
2101 /**
2102 * The mul operation, that can model the Java language binary {@code *} operator for numeric types
2103 *
2104 * @jls 15.17.1 Multiplication Operator *
2105 */
2106 @OpDeclaration(MulOp.NAME)
2107 public static final class MulOp extends BinaryOp {
2108 static final String NAME = "mul";
2109
2110 MulOp(ExternalizedOp def) {
2111 super(def);
2112 }
2113
2114 MulOp(MulOp that, CodeContext cc) {
2115 super(that, cc);
2116 }
2117
2118 @Override
2119 public MulOp transform(CodeContext cc, CodeTransformer ct) {
2120 return new MulOp(this, cc);
2121 }
2122
2123 MulOp(Value lhs, Value rhs) {
2124 super(lhs, rhs);
2125 }
2126 }
2127
2128 /**
2129 * The div operation, that can model the Java language binary {@code /} operator for numeric types
2130 *
2131 * @jls 15.17.2 Division Operator /
2132 */
2133 @OpDeclaration(DivOp.NAME)
2134 public static final class DivOp extends BinaryOp {
2135 static final String NAME = "div";
2136
2137 DivOp(ExternalizedOp def) {
2138 super(def);
2139 }
2140
2141 DivOp(DivOp that, CodeContext cc) {
2142 super(that, cc);
2143 }
2144
2145 @Override
2146 public DivOp transform(CodeContext cc, CodeTransformer ct) {
2147 return new DivOp(this, cc);
2148 }
2149
2150 DivOp(Value lhs, Value rhs) {
2151 super(lhs, rhs);
2152 }
2153 }
2154
2155 /**
2156 * The mod operation, that can model the Java language binary {@code %} operator for numeric types
2157 *
2158 * @jls 15.17.3 Remainder Operator %
2159 */
2160 @OpDeclaration(ModOp.NAME)
2161 public static final class ModOp extends BinaryOp {
2162 static final String NAME = "mod";
2163
2164 ModOp(ExternalizedOp def) {
2165 super(def);
2166 }
2167
2168 ModOp(ModOp that, CodeContext cc) {
2169 super(that, cc);
2170 }
2171
2172 @Override
2173 public ModOp transform(CodeContext cc, CodeTransformer ct) {
2174 return new ModOp(this, cc);
2175 }
2176
2177 ModOp(Value lhs, Value rhs) {
2178 super(lhs, rhs);
2179 }
2180 }
2181
2182 /**
2183 * The bitwise/logical or operation, that can model the Java language binary {@code |} operator for integral types
2184 * and booleans
2185 *
2186 * @jls 15.22 Bitwise and Logical Operators
2187 */
2188 @OpDeclaration(OrOp.NAME)
2189 public static final class OrOp extends BinaryOp {
2190 static final String NAME = "or";
2191
2192 OrOp(ExternalizedOp def) {
2193 super(def);
2194 }
2195
2196 OrOp(OrOp that, CodeContext cc) {
2197 super(that, cc);
2198 }
2199
2200 @Override
2201 public OrOp transform(CodeContext cc, CodeTransformer ct) {
2202 return new OrOp(this, cc);
2203 }
2204
2205 OrOp(Value lhs, Value rhs) {
2206 super(lhs, rhs);
2207 }
2208 }
2209
2210 /**
2211 * The bitwise/logical and operation, that can model the Java language binary {@code &} operator for integral types
2212 * and booleans
2213 *
2214 * @jls 15.22 Bitwise and Logical Operators
2215 */
2216 @OpDeclaration(AndOp.NAME)
2217 public static final class AndOp extends BinaryOp {
2218 static final String NAME = "and";
2219
2220 AndOp(ExternalizedOp def) {
2221 super(def);
2222 }
2223
2224 AndOp(AndOp that, CodeContext cc) {
2225 super(that, cc);
2226 }
2227
2228 @Override
2229 public AndOp transform(CodeContext cc, CodeTransformer ct) {
2230 return new AndOp(this, cc);
2231 }
2232
2233 AndOp(Value lhs, Value rhs) {
2234 super(lhs, rhs);
2235 }
2236 }
2237
2238 /**
2239 * The xor operation, that can model the Java language binary {@code ^} operator for integral types
2240 * and booleans
2241 *
2242 * @jls 15.22 Bitwise and Logical Operators
2243 */
2244 @OpDeclaration(XorOp.NAME)
2245 public static final class XorOp extends BinaryOp {
2246 static final String NAME = "xor";
2247
2248 XorOp(ExternalizedOp def) {
2249 super(def);
2250 }
2251
2252 XorOp(XorOp that, CodeContext cc) {
2253 super(that, cc);
2254 }
2255
2256 @Override
2257 public XorOp transform(CodeContext cc, CodeTransformer ct) {
2258 return new XorOp(this, cc);
2259 }
2260
2261 XorOp(Value lhs, Value rhs) {
2262 super(lhs, rhs);
2263 }
2264 }
2265
2266 /**
2267 * The (logical) shift left operation, that can model the Java language binary {@code <<} operator for integral types
2268 *
2269 * @jls 15.19 Shift Operators
2270 */
2271 @OpDeclaration(LshlOp.NAME)
2272 public static final class LshlOp extends BinaryOp {
2273 static final String NAME = "lshl";
2274
2275 LshlOp(ExternalizedOp def) {
2276 super(def);
2277 }
2278
2279 LshlOp(LshlOp that, CodeContext cc) {
2280 super(that, cc);
2281 }
2282
2283 @Override
2284 public LshlOp transform(CodeContext cc, CodeTransformer ct) {
2285 return new LshlOp(this, cc);
2286 }
2287
2288 LshlOp(Value lhs, Value rhs) {
2289 super(lhs, rhs);
2290 }
2291 }
2292
2293 /**
2294 * The (arithmetic) shift right operation, that can model the Java language binary {@code >>} operator for integral types
2295 *
2296 * @jls 15.19 Shift Operators
2297 */
2298 @OpDeclaration(AshrOp.NAME)
2299 public static final class AshrOp extends JavaOp.BinaryOp {
2300 static final String NAME = "ashr";
2301
2302 AshrOp(ExternalizedOp def) {
2303 super(def);
2304 }
2305
2306 AshrOp(AshrOp that, CodeContext cc) {
2307 super(that, cc);
2308 }
2309
2310 @Override
2311 public AshrOp transform(CodeContext cc, CodeTransformer ct) {
2312 return new AshrOp(this, cc);
2313 }
2314
2315 AshrOp(Value lhs, Value rhs) {
2316 super(lhs, rhs);
2317 }
2318 }
2319
2320 /**
2321 * The unsigned (logical) shift right operation, that can model the Java language binary {@code >>>} operator for integral types
2322 *
2323 * @jls 15.19 Shift Operators
2324 */
2325 @OpDeclaration(LshrOp.NAME)
2326 public static final class LshrOp extends JavaOp.BinaryOp {
2327 static final String NAME = "lshr";
2328
2329 LshrOp(ExternalizedOp def) {
2330 super(def);
2331 }
2332
2333 LshrOp(LshrOp that, CodeContext cc) {
2334 super(that, cc);
2335 }
2336
2337 @Override
2338 public LshrOp transform(CodeContext cc, CodeTransformer ct) {
2339 return new LshrOp(this, cc);
2340 }
2341
2342 LshrOp(Value lhs, Value rhs) {
2343 super(lhs, rhs);
2344 }
2345 }
2346
2347 /**
2348 * The neg operation, that can model the Java language unary {@code -} operator for numeric types
2349 *
2350 * @jls 15.15.4 Unary Minus Operator {@code -}
2351 */
2352 @OpDeclaration(NegOp.NAME)
2353 public static final class NegOp extends UnaryOp {
2354 static final String NAME = "neg";
2355
2356 NegOp(ExternalizedOp def) {
2357 super(def);
2358 }
2359
2360 NegOp(NegOp that, CodeContext cc) {
2361 super(that, cc);
2362 }
2363
2364 @Override
2365 public NegOp transform(CodeContext cc, CodeTransformer ct) {
2366 return new NegOp(this, cc);
2367 }
2368
2369 NegOp(Value v) {
2370 super(v);
2371 }
2372 }
2373
2374 /**
2375 * The bitwise complement operation, that can model the Java language unary {@code ~} operator for integral types
2376 *
2377 * @jls 15.15.5 Bitwise Complement Operator {@code ~}
2378 */
2379 @OpDeclaration(ComplOp.NAME)
2380 public static final class ComplOp extends UnaryOp {
2381 static final String NAME = "compl";
2382
2383 ComplOp(ExternalizedOp def) {
2384 super(def);
2385 }
2386
2387 ComplOp(ComplOp that, CodeContext cc) {
2388 super(that, cc);
2389 }
2390
2391 @Override
2392 public ComplOp transform(CodeContext cc, CodeTransformer ct) {
2393 return new ComplOp(this, cc);
2394 }
2395
2396 ComplOp(Value v) {
2397 super(v);
2398 }
2399 }
2400
2401 /**
2402 * The not operation, that can model the Java language unary {@code !} operator for boolean types
2403 *
2404 * @jls 15.15.6 Logical Complement Operator {@code !}
2405 */
2406 @OpDeclaration(NotOp.NAME)
2407 public static final class NotOp extends UnaryOp {
2408 static final String NAME = "not";
2409
2410 NotOp(ExternalizedOp def) {
2411 super(def);
2412 }
2413
2414 NotOp(NotOp that, CodeContext cc) {
2415 super(that, cc);
2416 }
2417
2418 @Override
2419 public NotOp transform(CodeContext cc, CodeTransformer ct) {
2420 return new NotOp(this, cc);
2421 }
2422
2423 NotOp(Value v) {
2424 super(v);
2425 }
2426 }
2427
2428 /**
2429 * The equals operation, that can model the Java language equality {@code ==} operator for numeric, boolean
2430 * and reference types
2431 *
2432 * @jls 15.21 Equality Operators
2433 */
2434 @OpDeclaration(EqOp.NAME)
2435 public static final class EqOp extends CompareOp {
2436 static final String NAME = "eq";
2437
2438 EqOp(ExternalizedOp def) {
2439 super(def);
2440 }
2441
2442 EqOp(EqOp that, CodeContext cc) {
2443 super(that, cc);
2444 }
2445
2446 @Override
2447 public EqOp transform(CodeContext cc, CodeTransformer ct) {
2448 return new EqOp(this, cc);
2449 }
2450
2451 EqOp(Value lhs, Value rhs) {
2452 super(lhs, rhs);
2453 }
2454 }
2455
2456 /**
2457 * The not equals operation, that can model the Java language equality {@code !=} operator for numeric, boolean
2458 * and reference types
2459 *
2460 * @jls 15.21 Equality Operators
2461 */
2462 @OpDeclaration(NeqOp.NAME)
2463 public static final class NeqOp extends CompareOp {
2464 static final String NAME = "neq";
2465
2466 NeqOp(ExternalizedOp def) {
2467 super(def);
2468 }
2469
2470 NeqOp(NeqOp that, CodeContext cc) {
2471 super(that, cc);
2472 }
2473
2474 @Override
2475 public NeqOp transform(CodeContext cc, CodeTransformer ct) {
2476 return new NeqOp(this, cc);
2477 }
2478
2479 NeqOp(Value lhs, Value rhs) {
2480 super(lhs, rhs);
2481 }
2482 }
2483
2484 /**
2485 * The greater than operation, that can model the Java language relational {@code >} operator for numeric types
2486 *
2487 * @jls 15.20.1 Numerical Comparison Operators {@code <}, {@code <=}, {@code >}, and {@code >=}
2488 */
2489 @OpDeclaration(GtOp.NAME)
2490 public static final class GtOp extends CompareOp {
2491 static final String NAME = "gt";
2492
2493 GtOp(ExternalizedOp def) {
2494 super(def);
2495 }
2496
2497 GtOp(GtOp that, CodeContext cc) {
2498 super(that, cc);
2499 }
2500
2501 @Override
2502 public GtOp transform(CodeContext cc, CodeTransformer ct) {
2503 return new GtOp(this, cc);
2504 }
2505
2506 GtOp(Value lhs, Value rhs) {
2507 super(lhs, rhs);
2508 }
2509 }
2510
2511 /**
2512 * The greater than or equal to operation, that can model the Java language relational {@code >=} operator for
2513 * numeric types
2514 *
2515 * @jls 15.20.1 Numerical Comparison Operators {@code <}, {@code <=}, {@code >}, and {@code >=}
2516 */
2517 @OpDeclaration(GeOp.NAME)
2518 public static final class GeOp extends CompareOp {
2519 static final String NAME = "ge";
2520
2521 GeOp(ExternalizedOp def) {
2522 super(def);
2523 }
2524
2525 GeOp(GeOp that, CodeContext cc) {
2526 super(that, cc);
2527 }
2528
2529 @Override
2530 public GeOp transform(CodeContext cc, CodeTransformer ct) {
2531 return new GeOp(this, cc);
2532 }
2533
2534 GeOp(Value lhs, Value rhs) {
2535 super(lhs, rhs);
2536 }
2537 }
2538
2539 /**
2540 * The less than operation, that can model the Java language relational {@code <} operator for
2541 * numeric types
2542 *
2543 * @jls 15.20.1 Numerical Comparison Operators {@code <}, {@code <=}, {@code >}, and {@code >=}
2544 */
2545 @OpDeclaration(LtOp.NAME)
2546 public static final class LtOp extends CompareOp {
2547 static final String NAME = "lt";
2548
2549 LtOp(ExternalizedOp def) {
2550 super(def);
2551 }
2552
2553 LtOp(LtOp that, CodeContext cc) {
2554 super(that, cc);
2555 }
2556
2557 @Override
2558 public LtOp transform(CodeContext cc, CodeTransformer ct) {
2559 return new LtOp(this, cc);
2560 }
2561
2562 LtOp(Value lhs, Value rhs) {
2563 super(lhs, rhs);
2564 }
2565 }
2566
2567 /**
2568 * The less than or equal to operation, that can model the Java language relational {@code <=} operator for
2569 * numeric types
2570 *
2571 * @jls 15.20.1 Numerical Comparison Operators {@code <}, {@code <=}, {@code >}, and {@code >=}
2572 */
2573 @OpDeclaration(LeOp.NAME)
2574 public static final class LeOp extends CompareOp {
2575 static final String NAME = "le";
2576
2577 LeOp(ExternalizedOp def) {
2578 super(def);
2579 }
2580
2581 LeOp(LeOp that, CodeContext cc) {
2582 super(that, cc);
2583 }
2584
2585 @Override
2586 public LeOp transform(CodeContext cc, CodeTransformer ct) {
2587 return new LeOp(this, cc);
2588 }
2589
2590 LeOp(Value lhs, Value rhs) {
2591 super(lhs, rhs);
2592 }
2593 }
2594
2595 /**
2596 * A statement target operation, that can model Java language statements associated with label identifiers.
2597 * <p>
2598 * A statement target operation is a body-terminating operation that features zero or one operand, the label
2599 * identifier. If present, the label identifier is modeled as a {@link ConstantOp} value.
2600 * <p>
2601 * The result type of a statement target operation is {@link JavaType#VOID}.
2602 *
2603 * @jls 14.15 The break Statement
2604 * @jls 14.16 The continue Statement
2605 */
2606 public sealed static abstract class StatementTargetOp extends JavaOp
2607 implements Op.Lowerable, Op.BodyTerminating, JavaStatement {
2608 StatementTargetOp(StatementTargetOp that, CodeContext cc) {
2609 super(that, cc);
2610 }
2611
2612 StatementTargetOp(ExternalizedOp def) {
2613 super(requireOperands(def, 0, 1));
2614 }
2615
2616 StatementTargetOp(Value label) {
2617 super(checkLabel(label));
2618 }
2619
2620 static List<Value> checkLabel(Value label) {
2621 return label == null ? List.of() : List.of(label);
2622 }
2623
2624 Op innerMostEnclosingTarget() {
2625 /*
2626 A break statement with no label attempts to transfer control to the
2627 innermost enclosing switch, while, do, or for statement; this enclosing statement,
2628 which is called the break target, then immediately completes normally.
2629
2630 A break statement with label Identifier attempts to transfer control to the
2631 enclosing labeled statement (14.7) that has the same Identifier as its label;
2632 this enclosing statement, which is called the break target, then immediately completes normally.
2633 In this case, the break target need not be a switch, while, do, or for statement.
2634 */
2635
2636 // No label
2637 // Get innermost enclosing loop operation
2638 Op op = this;
2639 Body b;
2640 do {
2641 b = op.ancestorBody();
2642 op = b.ancestorOp();
2643 if (op == null) {
2644 throw new IllegalStateException("No enclosing loop");
2645 }
2646 } while (!(op instanceof Op.Loop || op instanceof SwitchStatementOp));
2647
2648 return switch (op) {
2649 case Op.Loop lop -> lop.loopBody() == b ? op : null;
2650 case SwitchStatementOp swStat -> swStat.bodies().contains(b) ? op : null;
2651 default -> throw new IllegalStateException();
2652 };
2653 }
2654
2655 boolean isUnlabeled() {
2656 return operands().isEmpty();
2657 }
2658
2659 Op target() {
2660 // If unlabeled then find the nearest enclosing op
2661 // Otherwise obtain the label target
2662 if (isUnlabeled()) {
2663 return innerMostEnclosingTarget();
2664 }
2665
2666 Value value = operands().get(0);
2667 if (value instanceof Result r && r.op().ancestorOp() instanceof LabeledOp lop) {
2668 return lop.target();
2669 } else {
2670 throw new IllegalStateException("Bad label value: " + value + " " + ((Result) value).op());
2671 }
2672 }
2673
2674 Block.Builder lower(Block.Builder b, Function<BranchTarget, Block.Builder> f) {
2675 Op opt = target();
2676 BranchTarget t = BranchTarget.getBranchTarget(b.context(), opt);
2677 if (t != null) {
2678 b.add(branch(f.apply(t).reference()));
2679 } else {
2680 throw new IllegalStateException("No branch target for operation: " + opt);
2681 }
2682 return b;
2683 }
2684
2685 /**
2686 * {@return the label identifier, otherwise {@code null} if no label}
2687 */
2688 public Value labelOperand() {
2689 return operands().isEmpty() ? null : operands().getFirst();
2690 }
2691
2692 @Override
2693 public CodeType resultType() {
2694 return VOID;
2695 }
2696 }
2697
2698 /**
2699 * The break operation, that can model Java language break statements.
2700 * <p>
2701 * A break operation is a body-terminating statement target operation.
2702 *
2703 * @jls 14.15 The break Statement
2704 */
2705 @OpDeclaration(BreakOp.NAME)
2706 public static final class BreakOp extends StatementTargetOp {
2707 static final String NAME = "java.break";
2708
2709 BreakOp(ExternalizedOp def) {
2710 super(def);
2711 }
2712
2713 BreakOp(BreakOp that, CodeContext cc) {
2714 super(that, cc);
2715 }
2716
2717 @Override
2718 public BreakOp transform(CodeContext cc, CodeTransformer ct) {
2719 return new BreakOp(this, cc);
2720 }
2721
2722 BreakOp(Value label) {
2723 super(label);
2724 }
2725
2726 @Override
2727 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
2728 return lower(b, BranchTarget::breakBlock);
2729 }
2730 }
2731
2732 /**
2733 * The continue operation, that can model Java language continue statements.
2734 * <p>
2735 * A continue operation is a body-terminating statement target operation.
2736 *
2737 * @jls 14.16 The continue Statement
2738 */
2739 @OpDeclaration(ContinueOp.NAME)
2740 public static final class ContinueOp extends StatementTargetOp {
2741 static final String NAME = "java.continue";
2742
2743 ContinueOp(ExternalizedOp def) {
2744 super(def);
2745 }
2746
2747 ContinueOp(ContinueOp that, CodeContext cc) {
2748 super(that, cc);
2749 }
2750
2751 @Override
2752 public ContinueOp transform(CodeContext cc, CodeTransformer ct) {
2753 return new ContinueOp(this, cc);
2754 }
2755
2756 ContinueOp(Value label) {
2757 super(label);
2758 }
2759
2760 @Override
2761 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
2762 return lower(b, BranchTarget::continueBlock);
2763 }
2764 }
2765
2766 /**
2767 * The yield operation, that can model Java language yield statements.
2768 * <p>
2769 * A yield operation is a body-terminating operation that features one operand, the yielded value.
2770 * <p>
2771 * The result type of a yield operation is {@link JavaType#VOID}.
2772 *
2773 * @jls 14.21 The yield Statement
2774 */
2775 @OpDeclaration(YieldOp.NAME)
2776 public static final class YieldOp extends JavaOp
2777 implements Op.BodyTerminating, JavaStatement, Op.Lowerable {
2778 static final String NAME = "java.yield";
2779
2780 YieldOp(ExternalizedOp def) {
2781 this(requireSingleOperand(def));
2782 }
2783
2784 YieldOp(YieldOp that, CodeContext cc) {
2785 super(that, cc);
2786 }
2787
2788 @Override
2789 public YieldOp transform(CodeContext cc, CodeTransformer ct) {
2790 return new YieldOp(this, cc);
2791 }
2792
2793 YieldOp(Value operand) {
2794 super(List.of(Objects.requireNonNull(operand)));
2795 }
2796
2797 /**
2798 * {@return the yielded value}
2799 */
2800 public Value yieldOperand() {
2801 return operands().get(0);
2802 }
2803
2804 @Override
2805 public CodeType resultType() {
2806 return VOID;
2807 }
2808
2809 @Override
2810 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
2811 // for now, we will use breakBlock field to indicate java.yield target block
2812 return lower(b, BranchTarget::breakBlock);
2813 }
2814
2815 Block.Builder lower(Block.Builder b, Function<BranchTarget, Block.Builder> f) {
2816 Op opt = target();
2817 BranchTarget t = BranchTarget.getBranchTarget(b.context(), opt);
2818 if (t != null) {
2819 b.add(branch(f.apply(t).reference(b.context().getValue(yieldOperand()))));
2820 } else {
2821 throw new IllegalStateException("No branch target for operation: " + opt);
2822 }
2823 return b;
2824 }
2825
2826 Op target() {
2827 return innerMostEnclosingTarget();
2828 }
2829
2830 Op innerMostEnclosingTarget() {
2831 Op op = this;
2832 Body b;
2833 do {
2834 b = op.ancestorBody();
2835 op = b.ancestorOp();
2836 if (op == null) {
2837 throw new IllegalStateException("No enclosing switch");
2838 }
2839 } while (!(op instanceof SwitchExpressionOp));
2840 return op;
2841 }
2842 }
2843
2844 /**
2845 * The block operation, that can model Java language blocks.
2846 * <p>
2847 * Block operations feature one statements body, modeling the list of statements enclosed by the Java block.
2848 * The statements body should accept no arguments and yield {@linkplain JavaType#VOID no value}.
2849 * <p>
2850 * The result type of a block operation is {@link JavaType#VOID}.
2851 *
2852 * @jls 14.2 Blocks
2853 */
2854 @OpDeclaration(BlockOp.NAME)
2855 public static final class BlockOp extends JavaOp
2856 implements Op.Nested, Op.Lowerable, JavaStatement {
2857 static final String NAME = "java.block";
2858
2859 final Body body;
2860
2861 BlockOp(ExternalizedOp def) {
2862 this(requireSingleBody(def));
2863 }
2864
2865 BlockOp(BlockOp that, CodeContext cc, CodeTransformer ct) {
2866 super(that, cc);
2867
2868 // Copy body
2869 this.body = that.body.transform(cc, ct).build(this);
2870 }
2871
2872 @Override
2873 public BlockOp transform(CodeContext cc, CodeTransformer ct) {
2874 return new BlockOp(this, cc, ct);
2875 }
2876
2877 BlockOp(Body.Builder bodyC) {
2878 super(List.of());
2879 this.body = requireVoidBodySignature(NAME, bodyC).build(this);
2880 }
2881
2882 @Override
2883 public List<Body> bodies() {
2884 return List.of(body);
2885 }
2886
2887 /**
2888 * {@return the block operation body}
2889 */
2890 public Body body() {
2891 return body;
2892 }
2893
2894 @Override
2895 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
2896 Block.Builder exit = b.block();
2897 BranchTarget.setBranchTarget(b.context(), this, exit, null);
2898
2899 b.transformBody(body, List.of(), loweringTransformer(inherited, (block, op) -> {
2900 if (op instanceof CoreOp.YieldOp) {
2901 block.add(branch(exit.reference()));
2902 return block;
2903 } else {
2904 return null;
2905 }
2906 }));
2907
2908 return exit;
2909 }
2910
2911 @Override
2912 public CodeType resultType() {
2913 return VOID;
2914 }
2915 }
2916
2917 /**
2918 * The synchronized operation, that can model Java synchronized statements.
2919 * <p>
2920 * Synchronized operations feature two bodies. The <em>expression body</em> accepts no arguments
2921 * and yields a value, the object associated with the monitor that will be acquired by the synchronized
2922 * operation. The <em>block body</em> models the statements to execute while holding the monitor,
2923 * and yields {@linkplain JavaType#VOID no value}.
2924 * <p>
2925 * The result type of a synchronized operation is {@link JavaType#VOID}.
2926 *
2927 * @jls 14.19 The synchronized Statement
2928 */
2929 @OpDeclaration(SynchronizedOp.NAME)
2930 public static final class SynchronizedOp extends JavaOp
2931 implements Op.Nested, Op.Lowerable, JavaStatement {
2932 static final String NAME = "java.synchronized";
2933
2934 final Body exprBody;
2935 final Body blockBody;
2936
2937 SynchronizedOp(ExternalizedOp def) {
2938 List<Body.Builder> bodies = requireBodies(def, 2);
2939 this(bodies.get(0), bodies.get(1));
2940 }
2941
2942 SynchronizedOp(SynchronizedOp that, CodeContext cc, CodeTransformer ct) {
2943 super(that, cc);
2944
2945 // Copy bodies
2946 this.exprBody = that.exprBody.transform(cc, ct).build(this);
2947 this.blockBody = that.blockBody.transform(cc, ct).build(this);
2948 }
2949
2950 @Override
2951 public SynchronizedOp transform(CodeContext cc, CodeTransformer ct) {
2952 return new SynchronizedOp(this, cc, ct);
2953 }
2954
2955 // @@@: builder?
2956 SynchronizedOp(Body.Builder exprC, Body.Builder bodyC) {
2957 super(List.of());
2958 this.exprBody = requireNonVoidReturnType(NAME + " expression", exprC, 0).build(this);
2959 this.blockBody = requireVoidBodySignature(NAME + " block", bodyC).build(this);
2960 }
2961
2962 @Override
2963 public List<Body> bodies() {
2964 return List.of(exprBody, blockBody);
2965 }
2966
2967 /**
2968 * {@return the expression body whose result is the monitor object for synchronization}
2969 */
2970 public Body exprBody() {
2971 return exprBody;
2972 }
2973
2974 /**
2975 * {@return the body that is executed within the synchronized block}
2976 */
2977 public Body blockBody() {
2978 return blockBody;
2979 }
2980
2981 @Override
2982 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
2983 // Lower the expression body, yielding a monitor target
2984 b = lowerExpr(b, inherited);
2985 Value monitorTarget = b.parameters().get(0);
2986
2987 // Monitor enter
2988 b.add(monitorEnter(monitorTarget));
2989
2990 Block.Builder exit = b.block();
2991 BranchTarget.setBranchTarget(b.context(), this, exit, null);
2992
2993 // Exception region for the body
2994 Block.Builder syncRegionEnter = b.block();
2995 Block.Builder catcherFinally = b.block();
2996 Op.Result enter = b.add(exceptionRegionEnter(
2997 syncRegionEnter.reference(), catcherFinally.reference()));
2998
2999 BiFunction<Block.Builder, Op, Block.Builder> syncExitTransformer = composeFirst(inherited, (block, op) -> {
3000 if (op instanceof CoreOp.ReturnOp ||
3001 (op instanceof StatementTargetOp lop && ifExitFromSynchronized(lop))) {
3002 // Monitor exit
3003 block.add(monitorExit(monitorTarget));
3004 // Exit the exception region
3005 Block.Builder exitRegion = block.block();
3006 block.add(exceptionRegionExit(enter, exitRegion.reference()));
3007 return exitRegion;
3008 } else {
3009 return block;
3010 }
3011 });
3012
3013 syncRegionEnter.transformBody(blockBody, List.of(), loweringTransformer(syncExitTransformer, (block, op) -> {
3014 if (op instanceof CoreOp.YieldOp) {
3015 // Monitor exit
3016 block.add(monitorExit(monitorTarget));
3017 // Exit the exception region
3018 block.add(exceptionRegionExit(enter, exit.reference()));
3019 return block;
3020 } else {
3021 return null;
3022 }
3023 }));
3024
3025 // The catcher, with an exception region back branching to itself
3026 Block.Builder catcherFinallyRegionEnter = b.block();
3027 Op.Result catcherEnter = catcherFinally.add(exceptionRegionEnter(
3028 catcherFinallyRegionEnter.reference(), catcherFinally.reference()));
3029
3030 // Monitor exit
3031 catcherFinallyRegionEnter.add(monitorExit(monitorTarget));
3032 Block.Builder catcherFinallyRegionExit = b.block();
3033 // Exit the exception region
3034 catcherFinallyRegionEnter.add(exceptionRegionExit(
3035 catcherEnter, catcherFinallyRegionExit.reference()));
3036 // Rethrow outside of region
3037 Block.Parameter t = catcherFinally.parameter(type(Throwable.class));
3038 catcherFinallyRegionExit.add(throw_(t));
3039
3040 return exit;
3041 }
3042
3043 Block.Builder lowerExpr(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
3044 Block.Builder exprExit = b.block(exprBody.bodySignature().returnType());
3045 b.transformBody(exprBody, List.of(), loweringTransformer(inherited, (block, op) -> {
3046 if (op instanceof CoreOp.YieldOp yop) {
3047 Value monitorTarget = block.context().getValue(yop.yieldValue());
3048 block.add(branch(exprExit.reference(monitorTarget)));
3049 return block;
3050 } else {
3051 return null;
3052 }
3053 }));
3054 return exprExit;
3055 }
3056
3057 boolean ifExitFromSynchronized(StatementTargetOp lop) {
3058 Op target = lop.target();
3059 return target == this || target.isAncestorOf(this);
3060 }
3061
3062 @Override
3063 public CodeType resultType() {
3064 return VOID;
3065 }
3066 }
3067
3068 /**
3069 * The labeled operation, that can model Java language labeled statements.
3070 * <p>
3071 * Labeled operations feature one body, the labeled body. The labeled body accepts no arguments and
3072 * yield {@linkplain JavaType#VOID no value}.
3073 * <p>
3074 * The entry block of the labeled body always begins with a {@linkplain ConstantOp} constant modeling
3075 * the label associated with the labeled statement, followed by the statement being labeled.
3076 * <p>
3077 * The result type of a labeled operation is {@link JavaType#VOID}.
3078 *
3079 * @jls 14.7 Labeled Statements
3080 */
3081 @OpDeclaration(LabeledOp.NAME)
3082 public static final class LabeledOp extends JavaOp
3083 implements Op.Nested, Op.Lowerable, JavaStatement {
3084 static final String NAME = "java.labeled";
3085
3086 final Body body;
3087
3088 LabeledOp(ExternalizedOp def) {
3089 requireNoOperands(def);
3090 this(requireSingleBody(def));
3091 }
3092
3093 LabeledOp(LabeledOp that, CodeContext cc, CodeTransformer ct) {
3094 super(that, cc);
3095
3096 // Copy body
3097 this.body = that.body.transform(cc, ct).build(this);
3098 }
3099
3100 @Override
3101 public LabeledOp transform(CodeContext cc, CodeTransformer ct) {
3102 return new LabeledOp(this, cc, ct);
3103 }
3104
3105 LabeledOp(Body.Builder bodyC) {
3106 super(List.of());
3107 this.body = requireVoidBodySignature(NAME, bodyC).build(this);
3108 }
3109
3110 @Override
3111 public List<Body> bodies() {
3112 return List.of(body);
3113 }
3114
3115 /**
3116 * {@return the labeled body}
3117 */
3118 public Body body() {
3119 return body;
3120 }
3121
3122 /**
3123 * {@return the label associated with this labeled operation}
3124 */
3125 public Op label() {
3126 return body.entryBlock().firstOp();
3127 }
3128
3129 /**
3130 * {@return the label identifier, the operation result of the label}
3131 */
3132 public Op.Result labelIdentifier() {
3133 return label().result();
3134 }
3135
3136 /**
3137 * {@return the first operation associated with this labeled operation}
3138 */
3139 public Op target() {
3140 return body.entryBlock().nextOp(label());
3141 }
3142
3143 @Override
3144 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
3145 Block.Builder exit = b.block();
3146 BranchTarget.setBranchTarget(b.context(), this, exit, null);
3147
3148 AtomicBoolean first = new AtomicBoolean();
3149 b.transformBody(body, List.of(), loweringTransformer(inherited, (block, op) -> {
3150 // Drop first operation that corresponds to the label
3151 if (!first.get()) {
3152 first.set(true);
3153 return block;
3154 }
3155
3156 if (op instanceof CoreOp.YieldOp) {
3157 block.add(branch(exit.reference()));
3158 return block;
3159 } else {
3160 return null;
3161 }
3162 }));
3163
3164 return exit;
3165 }
3166
3167 @Override
3168 public CodeType resultType() {
3169 return VOID;
3170 }
3171 }
3172
3173 /**
3174 * The if operation, that can model Java language if statements.
3175 * <p>
3176 * If operations feature multiple bodies. Some bodies, called <em>predicate bodies</em>, model conditions that
3177 * determine which execution path the evaluation of the if operation should take. Other bodies, called
3178 * <em>action bodies</em>, model the statements to be executed when the preceding predicate is satisfied.
3179 * <p>
3180 * Each predicate body has a corresponding action body, and there may be a trailing action body with no
3181 * predicate, modeling the code after the Java {@code else} keyword.
3182 * <p>
3183 * Predicate bodies should accept no arguments and yield a {@link JavaType#BOOLEAN} value.
3184 * Action bodies similarly accept no arguments, and yield {@linkplain JavaType#VOID no value}.
3185 * <p>
3186 * The result type of an if operation is {@link JavaType#VOID}.
3187 *
3188 * @jls 14.9 The if Statement
3189 */
3190 @OpDeclaration(IfOp.NAME)
3191 public static final class IfOp extends JavaOp
3192 implements Op.Nested, Op.Lowerable, JavaStatement {
3193
3194 static final FunctionType PREDICATE_SIGNATURE = CoreType.functionType(BOOLEAN);
3195
3196 static final FunctionType ACTION_SIGNATURE = CoreType.FUNCTION_TYPE_VOID;
3197
3198 /**
3199 * Builder for the initial predicate body of an if operation.
3200 */
3201 public static class IfBuilder {
3202 final Body.Builder connectedAncestorBody;
3203 final List<Body.Builder> bodies;
3204
3205 IfBuilder(Body.Builder connectedAncestorBody) {
3206 this.connectedAncestorBody = connectedAncestorBody;
3207 this.bodies = new ArrayList<>();
3208 }
3209
3210 /**
3211 * Begins an if operation by adding the initial predicate body.
3212 *
3213 * @param c a consumer that populates the predicate body
3214 * @return a builder to add an action body to the if operation
3215 */
3216 public ThenBuilder if_(Consumer<Block.Builder> c) {
3217 Body.Builder body = Body.Builder.of(connectedAncestorBody, PREDICATE_SIGNATURE);
3218 c.accept(body.entryBlock());
3219 bodies.add(body);
3220
3221 return new ThenBuilder(connectedAncestorBody, bodies);
3222 }
3223 }
3224
3225 /**
3226 * Builder for the action body of an if operation.
3227 */
3228 public static class ThenBuilder {
3229 final Body.Builder connectedAncestorBody;
3230 final List<Body.Builder> bodies;
3231
3232 ThenBuilder(Body.Builder connectedAncestorBody, List<Body.Builder> bodies) {
3233 this.connectedAncestorBody = connectedAncestorBody;
3234 this.bodies = bodies;
3235 }
3236
3237 /**
3238 * Adds an action body to the if operation.
3239 *
3240 * @param c a consumer that populates the action body
3241 * @return a builder for further predicate and action bodies
3242 */
3243 public ElseIfBuilder then(Consumer<Block.Builder> c) {
3244 Body.Builder body = Body.Builder.of(connectedAncestorBody, ACTION_SIGNATURE);
3245 c.accept(body.entryBlock());
3246 bodies.add(body);
3247
3248 return new ElseIfBuilder(connectedAncestorBody, bodies);
3249 }
3250
3251 /**
3252 * Adds an empty action body to the if operation.
3253 * @return a builder for further predicate and action bodies
3254 */
3255 public ElseIfBuilder then() {
3256 Body.Builder body = Body.Builder.of(connectedAncestorBody, ACTION_SIGNATURE);
3257 body.entryBlock().add(core_yield());
3258 bodies.add(body);
3259
3260 return new ElseIfBuilder(connectedAncestorBody, bodies);
3261 }
3262 }
3263
3264 /**
3265 * Builder for additional predicate and action bodies of an if operation.
3266 */
3267 public static class ElseIfBuilder {
3268 final Body.Builder connectedAncestorBody;
3269 final List<Body.Builder> bodies;
3270
3271 ElseIfBuilder(Body.Builder connectedAncestorBody, List<Body.Builder> bodies) {
3272 this.connectedAncestorBody = connectedAncestorBody;
3273 this.bodies = bodies;
3274 }
3275
3276 /**
3277 * Adds a predicate body to the if operation.
3278 *
3279 * @param c a consumer that populates the predicate body
3280 * @return a builder to add an action body to the if operation
3281 */
3282 public ThenBuilder elseif(Consumer<Block.Builder> c) {
3283 Body.Builder body = Body.Builder.of(connectedAncestorBody, PREDICATE_SIGNATURE);
3284 c.accept(body.entryBlock());
3285 bodies.add(body);
3286
3287 return new ThenBuilder(connectedAncestorBody, bodies);
3288 }
3289
3290 /**
3291 * Completes the if operation by adding the final action body.
3292 *
3293 * @param c a consumer that populates the action body
3294 * @return the completed if operation
3295 */
3296 public IfOp else_(Consumer<Block.Builder> c) {
3297 Body.Builder body = Body.Builder.of(connectedAncestorBody, ACTION_SIGNATURE);
3298 c.accept(body.entryBlock());
3299 bodies.add(body);
3300
3301 return new IfOp(bodies);
3302 }
3303
3304 /**
3305 * Complete the if operation with an empty action body.
3306 * @return the completed if operation
3307 */
3308 public IfOp else_() {
3309 Body.Builder body = Body.Builder.of(connectedAncestorBody, ACTION_SIGNATURE);
3310 body.entryBlock().add(core_yield());
3311 bodies.add(body);
3312
3313 return new IfOp(bodies);
3314 }
3315 }
3316
3317 static final String NAME = "java.if";
3318
3319 final List<Body> bodies;
3320
3321 IfOp(ExternalizedOp def) {
3322 requireNoOperands(def);
3323 this(def.bodyDefinitions());
3324 }
3325
3326 IfOp(IfOp that, CodeContext cc, CodeTransformer ct) {
3327 super(that, cc);
3328
3329 // Copy body
3330 this.bodies = that.bodies.stream()
3331 .map(b -> b.transform(cc, ct).build(this)).toList();
3332 }
3333
3334 @Override
3335 public IfOp transform(CodeContext cc, CodeTransformer ct) {
3336 return new IfOp(this, cc, ct);
3337 }
3338
3339 IfOp(List<Body.Builder> bodyCs) {
3340 if (bodyCs.size() < 2) {
3341 throw structuralException(NAME, "requires 2 or more bodies, found %d".formatted(bodyCs.size()));
3342 }
3343 for (int i = 0; i < bodyCs.size(); i++) {
3344 requireBodySignature("%s body[%d]".formatted(NAME, i), bodyCs.get(i), i % 2 == 0 && i < bodyCs.size() - 1 ? PREDICATE_SIGNATURE : ACTION_SIGNATURE);
3345 }
3346 super(List.of());
3347
3348 // Normalize by adding an empty else action
3349 // @@@ Is this needed?
3350 if (bodyCs.size() % 2 == 0) {
3351 bodyCs = new ArrayList<>(bodyCs);
3352 Body.Builder end = Body.Builder.of(bodyCs.get(0).connectedAncestorBody(),
3353 CoreType.FUNCTION_TYPE_VOID);
3354 end.entryBlock().add(core_yield());
3355 bodyCs.add(end);
3356 }
3357 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList();
3358 }
3359
3360 @Override
3361 public List<Body> bodies() {
3362 return bodies;
3363 }
3364
3365 @Override
3366 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
3367 Block.Builder exit = b.block();
3368 BranchTarget.setBranchTarget(b.context(), this, exit, null);
3369
3370 // Create predicate and action blocks
3371 List<Block.Builder> builders = new ArrayList<>();
3372 for (int i = 0; i < bodies.size(); i += 2) {
3373 if (i == bodies.size() - 1) {
3374 builders.add(b.block());
3375 } else {
3376 builders.add(i == 0 ? b : b.block());
3377 builders.add(b.block());
3378 }
3379 }
3380
3381 for (int i = 0; i < bodies.size(); i += 2) {
3382 Body actionBody;
3383 Block.Builder action;
3384 if (i == bodies.size() - 1) {
3385 actionBody = bodies.get(i);
3386 action = builders.get(i);
3387 } else {
3388 Body predBody = bodies.get(i);
3389 actionBody = bodies.get(i + 1);
3390
3391 Block.Builder pred = builders.get(i);
3392 action = builders.get(i + 1);
3393 Block.Builder next = builders.get(i + 2);
3394
3395 pred.transformBody(predBody, List.of(), loweringTransformer(inherited, (block, op) -> {
3396 if (op instanceof CoreOp.YieldOp yo) {
3397 block.add(conditionalBranch(block.context().getValue(yo.yieldValue()),
3398 action.reference(), next.reference()));
3399 return block;
3400 } else {
3401 return null;
3402 }
3403 }));
3404 }
3405
3406 action.transformBody(actionBody, List.of(), loweringTransformer(inherited, (block, op) -> {
3407 if (op instanceof CoreOp.YieldOp) {
3408 block.add(branch(exit.reference()));
3409 return block;
3410 } else {
3411 return null;
3412 }
3413 }));
3414 }
3415
3416 return exit;
3417 }
3418
3419 @Override
3420 public CodeType resultType() {
3421 return VOID;
3422 }
3423 }
3424
3425 /**
3426 * An operation modeling a Java switch statement or expression.
3427 * <p>
3428 * Switch operations are parameterized by a selector value.
3429 * They feature a sequence of case bodies, each modeled as a pair of bodies: a <em>predicate body</em> and an
3430 * <em>action body</em>.
3431 * <p>
3432 * Each predicate body accepts one argument, the selector value, and yields a {@link JavaType#BOOLEAN} value.
3433 * Each action body yields a value of the same type {@code T}. For switch statement operations, {@code T} is
3434 * {@code void}. For switch expression operations, {@code T} is the switch expression type.
3435 *
3436 * @jls 14.11 The switch Statement
3437 * @jls 15.28 {@code switch} Expressions
3438 */
3439 public abstract static sealed class JavaSwitchOp extends JavaOp implements Op.Nested, Op.Lowerable
3440 permits SwitchStatementOp, SwitchExpressionOp {
3441
3442 final List<Body> bodies;
3443 final boolean handleNulls;
3444
3445 enum SwitchNullHandling {
3446 ALLOW_NULL,
3447 REJECT_NULL,
3448 INFER;
3449
3450 static SwitchNullHandling of(ExternalizedOp def) {
3451 return of(optionalBooleanAttribute(def, ATTRIBUTE_SWITCH_HANDLE_NULLS));
3452
3453 }
3454
3455 static SwitchNullHandling of(boolean handleNulls) {
3456 return handleNulls ?
3457 ALLOW_NULL : REJECT_NULL;
3458 }
3459 }
3460
3461 /**
3462 * The externalized attribute key for a switch that handles nulls.
3463 */
3464 static final String ATTRIBUTE_SWITCH_HANDLE_NULLS = "switch.handle.nulls";
3465
3466 JavaSwitchOp(JavaSwitchOp that, CodeContext cc, CodeTransformer ct) {
3467 super(that, cc);
3468
3469 // Copy body
3470 this.bodies = that.bodies.stream()
3471 .map(b -> b.transform(cc, ct).build(this)).toList();
3472 this.handleNulls = that.handleNulls;
3473 }
3474
3475 JavaSwitchOp(Value target, SwitchNullHandling nullHandling, List<Body.Builder> bodyCs) {
3476 super(List.of(target));
3477
3478 // Each case is modeled as a contiguous pair of bodies
3479 // The first body models the case labels, and the second models the case statements
3480 // The labels body has a parameter whose type is target operand's type and returns a boolean value
3481 // The action body has no parameters and returns void
3482 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList();
3483 this.handleNulls = switch (nullHandling) {
3484 case ALLOW_NULL -> true;
3485 case REJECT_NULL -> false;
3486 case INFER -> inferNullCase();
3487 };
3488 }
3489
3490 @Override
3491 public List<Body> bodies() {
3492 return bodies;
3493 }
3494
3495 @Override
3496 public Map<String, Object> externalize() {
3497 return handleNulls ?
3498 Map.of(ATTRIBUTE_SWITCH_HANDLE_NULLS, true) :
3499 Map.of();
3500 }
3501
3502 @Override
3503 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
3504 Value selectorExpression = b.context().getValue(operands().get(0));
3505
3506 // @@@ we can add this during model generation
3507 // if no case null, add one that throws NPE
3508 if (!(selectorExpression.type() instanceof PrimitiveType) && !handleNulls) {
3509 Block.Builder throwBlock = b.block();
3510 throwBlock.add(throw_(
3511 throwBlock.add(new_(MethodRef.constructor(NullPointerException.class)))
3512 ));
3513
3514 Block.Builder continueBlock = b.block();
3515
3516 Result p = b.add(invoke(MethodRef.method(Objects.class, "equals", boolean.class, Object.class, Object.class),
3517 selectorExpression, b.add(constant(J_L_OBJECT, null))));
3518 b.add(conditionalBranch(p, throwBlock.reference(), continueBlock.reference()));
3519
3520 b = continueBlock;
3521 }
3522
3523 int defLabelIndex = -1;
3524 for (int i = 0; i < bodies().size(); i+=2) {
3525 Block eb = bodies().get(i).entryBlock();
3526 // @@@ confusing YieldOp with Core.YieldOp in checks
3527 if (eb.terminatingOp() instanceof CoreOp.YieldOp yop && yop.yieldValue() instanceof Op.Result r
3528 && r.op() instanceof ConstantOp cop && cop.resultType().equals(BOOLEAN)) {
3529 defLabelIndex = i;
3530 break;
3531 }
3532 }
3533 if (defLabelIndex == -1 && this instanceof SwitchExpressionOp) {
3534 // if it's a switch expression, it must have a default
3535 // if not explicit, it's an unconditional pattern which is the last label
3536 defLabelIndex = bodies().size() - 2;
3537 }
3538
3539 List<Block.Builder> blocks = new ArrayList<>();
3540 for (int i = 0; i < bodies().size(); i++) {
3541 Block.Builder bb;
3542 if (i == defLabelIndex) {
3543 // we don't need a block for default label
3544 bb = null;
3545 } else {
3546 bb = b.block();
3547 }
3548 blocks.add(bb);
3549 }
3550 // append ops of the first non default label to b
3551 for (int i = 0; i < blocks.size(); i+=2) {
3552 if (blocks.get(i) == null) {
3553 continue;
3554 }
3555 blocks.set(i, b);
3556 break;
3557 }
3558
3559 Block.Builder exit;
3560 if (bodies().isEmpty()) {
3561 exit = b;
3562 } else {
3563 exit = resultType() == VOID ? b.block() : b.block(resultType());
3564 if (!exit.parameters().isEmpty()) {
3565 exit.context().mapValue(result(), exit.parameters().get(0));
3566 }
3567 }
3568
3569 BranchTarget.setBranchTarget(b.context(), this, exit, null);
3570 // map statement body to nextExprBlock
3571 // this mapping will be used for lowering SwitchFallThroughOp
3572 for (int i = 1; i < bodies().size() - 2; i+=2) {
3573 BranchTarget.setBranchTarget(b.context(), bodies().get(i), null, blocks.get(i + 2));
3574 }
3575
3576 for (int i = 0; i < bodies().size(); i+=2) {
3577 if (i == defLabelIndex) {
3578 continue;
3579 }
3580 Block.Builder statement = blocks.get(i + 1);
3581 boolean isLastLabel = i == blocks.size() - 2;
3582 Block.Builder nextLabel = isLastLabel ? null : blocks.get(i + 2);
3583 int finalDefLabelIndex = defLabelIndex;
3584 blocks.get(i).transformBody(bodies().get(i), List.of(selectorExpression), loweringTransformer(inherited,
3585 (block, op) -> switch (op) {
3586 case CoreOp.YieldOp yop -> {
3587 Block.Reference falseTarget;
3588 if (nextLabel != null) {
3589 falseTarget = nextLabel.reference();
3590 } else if (finalDefLabelIndex != -1) {
3591 falseTarget = blocks.get(finalDefLabelIndex + 1).reference();
3592 } else {
3593 falseTarget = exit.reference();
3594 }
3595 block.add(conditionalBranch(block.context().getValue(yop.yieldValue()),
3596 statement.reference(), falseTarget));
3597 yield block;
3598 }
3599 default -> null;
3600 }));
3601
3602 blocks.get(i + 1).transformBody(bodies().get(i + 1), List.of(), loweringTransformer(inherited,
3603 (block, op) -> switch (op) {
3604 case CoreOp.YieldOp yop -> {
3605 List<Value> args = yop.yieldValue() == null ? List.of() : List.of(block.context().getValue(yop.yieldValue()));
3606 block.add(branch(exit.reference(args)));
3607 yield block;
3608 }
3609 default -> null;
3610 }));
3611 }
3612
3613 if (defLabelIndex != -1) {
3614 blocks.get(defLabelIndex + 1).transformBody(bodies().get(defLabelIndex + 1), List.of(), loweringTransformer(inherited,
3615 (block, op) -> switch (op) {
3616 case CoreOp.YieldOp yop -> {
3617 List<Value> args = yop.yieldValue() == null ? List.of() : List.of(block.context().getValue(yop.yieldValue()));
3618 block.add(branch(exit.reference(args)));
3619 yield block;
3620 }
3621 default -> null;
3622 }));
3623 }
3624
3625 return exit;
3626 }
3627
3628 /**
3629 * {@return {@code true} if this switch operation handles nulls}
3630 */
3631 public boolean handleNulls() {
3632 return handleNulls;
3633 }
3634
3635 private boolean inferNullCase() {
3636 /*
3637 case null is modeled like this:
3638 (%4 : T)boolean -> {
3639 %5 : java.lang.Object = constant @null;
3640 %6 : boolean = invoke %4 %5 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
3641 yield %6;
3642 }
3643 * */
3644 for (int i = 0; i < bodies().size() - 2; i+=2) {
3645 Body labelBody = bodies().get(i);
3646 if (labelBody.blocks().size() != 1) {
3647 continue; // we skip, for now
3648 }
3649 Op terminatingOp = bodies().get(i).entryBlock().terminatingOp();
3650 //@@@ when op pattern matching is ready, we can use it
3651 if (terminatingOp instanceof CoreOp.YieldOp yieldOp &&
3652 yieldOp.yieldValue() instanceof Op.Result opr &&
3653 opr.op() instanceof InvokeOp invokeOp &&
3654 invokeOp.invokeReference().equals(MethodRef.method(Objects.class, "equals", boolean.class, Object.class, Object.class)) &&
3655 invokeOp.operands().stream().anyMatch(o -> o instanceof Op.Result r && r.op() instanceof ConstantOp cop && cop.value() == null)) {
3656 return true;
3657 }
3658 }
3659 return false;
3660 }
3661 }
3662
3663 /**
3664 * The switch expression operation, that can model Java language switch expressions.
3665 * <p>
3666 * For switch expression operations, action bodies yield a value of type {@code T}, where {@code T} is also the
3667 * type of the switch expression operation.
3668 *
3669 * @jls 15.28 {@code switch} Expressions
3670 */
3671 @OpDeclaration(SwitchExpressionOp.NAME)
3672 public static final class SwitchExpressionOp extends JavaSwitchOp
3673 implements JavaExpression {
3674 static final String NAME = "java.switch.expression";
3675
3676 final CodeType resultType;
3677
3678 SwitchExpressionOp(ExternalizedOp def) {
3679 this(def.resultType(), requireSingleOperand(def), SwitchNullHandling.of(def), def.bodyDefinitions());
3680 }
3681
3682 SwitchExpressionOp(SwitchExpressionOp that, CodeContext cc, CodeTransformer ct) {
3683 super(that, cc, ct);
3684
3685 this.resultType = that.resultType;
3686 }
3687
3688 @Override
3689 public SwitchExpressionOp transform(CodeContext cc, CodeTransformer ct) {
3690 return new SwitchExpressionOp(this, cc, ct);
3691 }
3692
3693 SwitchExpressionOp(CodeType resultType, Value target, SwitchNullHandling nullHandling, List<Body.Builder> bodyCs) {
3694 super(target, nullHandling, requireBodyPairs(NAME, bodyCs));
3695 this.resultType = resultType == null ? bodies.get(1).yieldType() : resultType;
3696 }
3697
3698 @Override
3699 public CodeType resultType() {
3700 return resultType;
3701 }
3702 }
3703
3704 /**
3705 * The switch statement operation, that can model Java language switch statement.
3706 * <p>
3707 * For switch statement operations, action bodies yield {@linkplain JavaType#VOID no value}.
3708 * <p>
3709 * The result type of a switch statement operation is {@link JavaType#VOID}.
3710 *
3711 * @jls 14.11 The switch Statement
3712 */
3713 @OpDeclaration(SwitchStatementOp.NAME)
3714 public static final class SwitchStatementOp extends JavaSwitchOp
3715 implements JavaStatement {
3716 static final String NAME = "java.switch.statement";
3717
3718 SwitchStatementOp(ExternalizedOp def) {
3719 this(requireSingleOperand(def), SwitchNullHandling.of(def), def.bodyDefinitions());
3720 }
3721
3722 SwitchStatementOp(SwitchStatementOp that, CodeContext cc, CodeTransformer ct) {
3723 super(that, cc, ct);
3724 }
3725
3726 @Override
3727 public SwitchStatementOp transform(CodeContext cc, CodeTransformer ct) {
3728 return new SwitchStatementOp(this, cc, ct);
3729 }
3730
3731 SwitchStatementOp(Value target, SwitchNullHandling nullHandling, List<Body.Builder> bodyCs) {
3732 super(target, nullHandling, requireBodyPairs(NAME, bodyCs));
3733 }
3734
3735 @Override
3736 public CodeType resultType() {
3737 return VOID;
3738 }
3739 }
3740
3741 /**
3742 * The switch fall-through operation, that can model fall-through to the next statement in the switch block after
3743 * the last statement of the current switch label.
3744 * <p>
3745 * A switch fall-through operation is a body-terminating operation.
3746 */
3747 @OpDeclaration(SwitchFallthroughOp.NAME)
3748 public static final class SwitchFallthroughOp extends JavaOp
3749 implements Op.BodyTerminating, Op.Lowerable {
3750 static final String NAME = "java.switch.fallthrough";
3751
3752 SwitchFallthroughOp(ExternalizedOp def) {
3753 this();
3754 }
3755
3756 SwitchFallthroughOp(SwitchFallthroughOp that, CodeContext cc) {
3757 super(that, cc);
3758 }
3759
3760 @Override
3761 public SwitchFallthroughOp transform(CodeContext cc, CodeTransformer ct) {
3762 return new SwitchFallthroughOp(this, cc);
3763 }
3764
3765 SwitchFallthroughOp() {
3766 super(List.of());
3767 }
3768
3769 @Override
3770 public CodeType resultType() {
3771 return VOID;
3772 }
3773
3774 @Override
3775 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
3776 return lower(b, BranchTarget::continueBlock);
3777 }
3778
3779 Block.Builder lower(Block.Builder b, Function<BranchTarget, Block.Builder> f) {
3780 BranchTarget t = BranchTarget.getBranchTarget(b.context(), ancestorBody());
3781 if (t != null) {
3782 b.add(branch(f.apply(t).reference()));
3783 } else {
3784 throw new IllegalStateException("No branch target for operation: " + this);
3785 }
3786 return b;
3787 }
3788 }
3789
3790 /**
3791 * The for operation, that can model a Java language basic for statement.
3792 * <p>
3793 * For operations feature four bodies that model a basic {@code for} statement:
3794 * an <em>initialization body</em>, a <em>predicate body</em>, an <em>update body</em>, and a <em>loop body</em>.
3795 * <p>
3796 * The initialization body accepts no arguments and yields the loop state, of type {@code S}. For instance,
3797 * a loop with a single loop variable of type {@code T} might use a loop state of type {@code T}.
3798 * A loop with two loop variables of type {@code X} and {@code Y} might use a loop state whose type is
3799 * a {@linkplain TupleType tuple type}, such as {@code (X, Y)}. A loop with no loop variables might use
3800 * a loop state of type {@link JavaType#VOID}, and have its initialization body yield no value.
3801 * <p>
3802 * The predicate body accepts an argument of type {@code S} and yields a {@link JavaType#BOOLEAN} value.
3803 * The update and loop bodies accept an argument of type {@code S} and yield {@linkplain JavaType#VOID no value}.
3804 * <p>
3805 * The result type of a for operation is {@link JavaType#VOID}.
3806 *
3807 * @jls 14.14.1 The basic for Statement
3808 */
3809 @OpDeclaration(ForOp.NAME)
3810 public static final class ForOp extends JavaOp
3811 implements Op.Loop, Op.Lowerable, JavaStatement {
3812
3813 /**
3814 * Builder for the initialization body of a for operation.
3815 */
3816 public static final class InitBuilder {
3817 final Body.Builder connectedAncestorBody;
3818 final List<? extends CodeType> initTypes;
3819
3820 InitBuilder(Body.Builder connectedAncestorBody,
3821 List<? extends CodeType> initTypes) {
3822 this.connectedAncestorBody = connectedAncestorBody;
3823 this.initTypes = initTypes.stream().map(CoreType::varType).toList();
3824 }
3825
3826 /**
3827 * Builds the initialization body of a for-loop.
3828 *
3829 * @param c a consumer that populates the initialization body
3830 * @return a builder for specifying the loop predicate body
3831 */
3832 public ForOp.CondBuilder init(Consumer<Block.Builder> c) {
3833 Body.Builder init = Body.Builder.of(connectedAncestorBody,
3834 CoreType.functionType(CoreType.tupleType(initTypes)));
3835 c.accept(init.entryBlock());
3836
3837 return new CondBuilder(connectedAncestorBody, initTypes, init);
3838 }
3839 }
3840
3841 /**
3842 * Builder for the predicate body of a for operation.
3843 */
3844 public static final class CondBuilder {
3845 final Body.Builder connectedAncestorBody;
3846 final List<? extends CodeType> initTypes;
3847 final Body.Builder init;
3848
3849 CondBuilder(Body.Builder connectedAncestorBody,
3850 List<? extends CodeType> initTypes,
3851 Body.Builder init) {
3852 this.connectedAncestorBody = connectedAncestorBody;
3853 this.initTypes = initTypes;
3854 this.init = init;
3855 }
3856
3857 /**
3858 * Builds the predicate body of a for-loop.
3859 *
3860 * @param c a consumer that populates the predicate body
3861 * @return a builder for specifying the update body
3862 */
3863 public ForOp.UpdateBuilder cond(Consumer<Block.Builder> c) {
3864 Body.Builder cond = Body.Builder.of(connectedAncestorBody,
3865 CoreType.functionType(BOOLEAN, initTypes));
3866 c.accept(cond.entryBlock());
3867
3868 return new UpdateBuilder(connectedAncestorBody, initTypes, init, cond);
3869 }
3870 }
3871
3872 /**
3873 * Builder for the update body of a for operation.
3874 */
3875 public static final class UpdateBuilder {
3876 final Body.Builder connectedAncestorBody;
3877 final List<? extends CodeType> initTypes;
3878 final Body.Builder init;
3879 final Body.Builder cond;
3880
3881 UpdateBuilder(Body.Builder connectedAncestorBody,
3882 List<? extends CodeType> initTypes,
3883 Body.Builder init, Body.Builder cond) {
3884 this.connectedAncestorBody = connectedAncestorBody;
3885 this.initTypes = initTypes;
3886 this.init = init;
3887 this.cond = cond;
3888 }
3889
3890 /**
3891 * Builds the update body of a for-loop.
3892 *
3893 * @param c a consumer that populates the update body
3894 * @return a builder for specifying the loop body
3895 */
3896 public ForOp.BodyBuilder update(Consumer<Block.Builder> c) {
3897 Body.Builder update = Body.Builder.of(connectedAncestorBody,
3898 CoreType.functionType(VOID, initTypes));
3899 c.accept(update.entryBlock());
3900
3901 return new BodyBuilder(connectedAncestorBody, initTypes, init, cond, update);
3902 }
3903 }
3904
3905 /**
3906 * Builder for the body (main logic) portion of a for-loop.
3907 */
3908 public static final class BodyBuilder {
3909 final Body.Builder connectedAncestorBody;
3910 final List<? extends CodeType> initTypes;
3911 final Body.Builder init;
3912 final Body.Builder cond;
3913 final Body.Builder update;
3914
3915 BodyBuilder(Body.Builder connectedAncestorBody,
3916 List<? extends CodeType> initTypes,
3917 Body.Builder init, Body.Builder cond, Body.Builder update) {
3918 this.connectedAncestorBody = connectedAncestorBody;
3919 this.initTypes = initTypes;
3920 this.init = init;
3921 this.cond = cond;
3922 this.update = update;
3923 }
3924
3925 /**
3926 * Completes for operation by adding the loop body.
3927 *
3928 * @param c a consumer that populates the loop body
3929 * @return the completed for-loop operation
3930 */
3931 public ForOp body(Consumer<Block.Builder> c) {
3932 Body.Builder body = Body.Builder.of(connectedAncestorBody,
3933 CoreType.functionType(VOID, initTypes));
3934 c.accept(body.entryBlock());
3935
3936 return new ForOp(init, cond, update, body);
3937 }
3938 }
3939
3940 static final String NAME = "java.for";
3941
3942 final Body initBody;
3943 final Body condBody;
3944 final Body updateBody;
3945 final Body loopBody;
3946
3947 ForOp(ExternalizedOp def) {
3948 List<Body.Builder> bodies = requireBodies(def, 4);
3949 this(bodies.get(0), bodies.get(1), bodies.get(2), bodies.get(3));
3950 }
3951
3952 ForOp(ForOp that, CodeContext cc, CodeTransformer ct) {
3953 super(that, cc);
3954
3955 this.initBody = that.initBody.transform(cc, ct).build(this);
3956 this.condBody = that.condBody.transform(cc, ct).build(this);
3957 this.updateBody = that.updateBody.transform(cc, ct).build(this);
3958 this.loopBody = that.loopBody.transform(cc, ct).build(this);
3959 }
3960
3961 @Override
3962 public ForOp transform(CodeContext cc, CodeTransformer ct) {
3963 return new ForOp(this, cc, ct);
3964 }
3965
3966 ForOp(Body.Builder initC,
3967 Body.Builder condC,
3968 Body.Builder updateC,
3969 Body.Builder bodyC) {
3970 super(List.of());
3971
3972 List<CodeType> varTypes = switch (initC.bodySignature().returnType()) {
3973 case TupleType tt -> tt.componentTypes();
3974 case PrimitiveType pt when pt.equals(VOID) -> List.of();
3975 case CodeType t -> List.of(t);
3976 };
3977 FunctionType condType = CoreType.functionType(BOOLEAN, varTypes);
3978 FunctionType bodyType = CoreType.functionType(VOID, varTypes);
3979
3980 this.initBody = requireNoParameters(NAME + " init", initC).build(this);
3981 this.condBody = requireBodySignature(NAME + " predicate", condC, condType).build(this);
3982 this.updateBody = requireBodySignature(NAME + " update", updateC, bodyType).build(this);
3983 this.loopBody = requireBodySignature(NAME + " loop", bodyC, bodyType).build(this);
3984 }
3985
3986 @Override
3987 public List<Body> bodies() {
3988 return List.of(initBody, condBody, updateBody, loopBody);
3989 }
3990
3991 /**
3992 * {@return the initialization body}
3993 */
3994 public Body initBody() {
3995 return initBody;
3996 }
3997
3998 /**
3999 * {@return the loop condition (predicate) body}
4000 */
4001 public Body condBody() {
4002 return condBody;
4003 }
4004
4005 /**
4006 * {@return the update body}
4007 */
4008 public Body updateBody() {
4009 return updateBody;
4010 }
4011
4012 @Override
4013 public Body loopBody() {
4014 return loopBody;
4015 }
4016
4017 @Override
4018 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
4019 Block.Builder header = b.block();
4020 Block.Builder body = b.block();
4021 Block.Builder update = b.block();
4022 Block.Builder exit = b.block();
4023
4024 List<Value> initValues = new ArrayList<>();
4025 // @@@ Init body has one yield operation yielding
4026 // void, a single variable, or a tuple of one or more variables
4027 b.transformBody(initBody, List.of(), loweringTransformer(inherited, (block, op) -> switch (op) {
4028 case TupleOp _ -> {
4029 // Drop Tuple if a yielded
4030 boolean isResult = op.result().uses().size() == 1 &&
4031 op.result().uses().stream().allMatch(r -> r.op() instanceof CoreOp.YieldOp);
4032 if (!isResult) {
4033 block.add(op);
4034 }
4035 yield block;
4036 }
4037 case CoreOp.YieldOp yop -> {
4038 if (yop.yieldValue() == null) {
4039 block.add(branch(header.reference()));
4040 yield block;
4041 } else if (yop.yieldValue() instanceof Result or) {
4042 if (or.op() instanceof TupleOp top) {
4043 initValues.addAll(block.context().getValues(top.operands()));
4044 } else {
4045 initValues.addAll(block.context().getValues(yop.operands()));
4046 }
4047 block.add(branch(header.reference()));
4048 yield block;
4049 }
4050
4051 throw new IllegalStateException("Bad yield operation");
4052 }
4053 default -> null;
4054 }));
4055
4056 header.transformBody(condBody, initValues, loweringTransformer(inherited, (block, op) -> {
4057 if (op instanceof CoreOp.YieldOp yo) {
4058 block.add(conditionalBranch(block.context().getValue(yo.yieldValue()),
4059 body.reference(), exit.reference()));
4060 return block;
4061 } else {
4062 return null;
4063 }
4064 }));
4065
4066 BranchTarget.setBranchTarget(b.context(), this, exit, update);
4067
4068 body.transformBody(this.loopBody, initValues, loweringTransformer(inherited, (_, _) -> null));
4069
4070 update.transformBody(this.updateBody, initValues, loweringTransformer(inherited, (block, op) -> {
4071 if (op instanceof CoreOp.YieldOp) {
4072 block.add(branch(header.reference()));
4073 return block;
4074 } else {
4075 return null;
4076 }
4077 }));
4078
4079 return exit;
4080 }
4081
4082 @Override
4083 public CodeType resultType() {
4084 return VOID;
4085 }
4086 }
4087
4088 /**
4089 * The enhanced for operation, that can model a Java language enhanced for statement.
4090 * <p>
4091 * Enhanced-for operations feature three bodies. The <em>expression body</em> models the expression to be
4092 * iterated. The <em>definition body</em> models the definition of the loop variable. The <em>loop body</em>
4093 * models the statements to execute.
4094 * <p>
4095 * The expression body accepts no arguments and yields a value of type {@code I}, corresponding to the type of the
4096 * expression to be iterated. The definition body accepts one argument of type {@code E}, corresponding to an element
4097 * type derived from {@code I}, and yields a value of type {@code V}, the type of the loop variable. Finally, the loop
4098 * body accepts that value and yields {@linkplain JavaType#VOID no value}.
4099 * <p>
4100 * The result type of an enhanced-for operation is {@link JavaType#VOID}.
4101 *
4102 * @jls 14.14.2 The enhanced for statement
4103 */
4104 @OpDeclaration(EnhancedForOp.NAME)
4105 public static final class EnhancedForOp extends JavaOp
4106 implements Op.Loop, Op.Lowerable, JavaStatement {
4107
4108 /**
4109 * Builder for the expression body of an enhanced-for operation.
4110 */
4111 public static final class ExpressionBuilder {
4112 final Body.Builder connectedAncestorBody;
4113 final CodeType iterableType;
4114 final CodeType elementType;
4115
4116 ExpressionBuilder(Body.Builder connectedAncestorBody,
4117 CodeType iterableType, CodeType elementType) {
4118 this.connectedAncestorBody = connectedAncestorBody;
4119 this.iterableType = iterableType;
4120 this.elementType = elementType;
4121 }
4122
4123 /**
4124 * Builds the expression body of an enhanced-for operation.
4125 *
4126 * @param c a consumer that populates the expression body
4127 * @return a builder for specifying the definition body
4128 */
4129 public DefinitionBuilder expression(Consumer<Block.Builder> c) {
4130 Body.Builder expression = Body.Builder.of(connectedAncestorBody,
4131 CoreType.functionType(iterableType));
4132 c.accept(expression.entryBlock());
4133
4134 return new DefinitionBuilder(connectedAncestorBody, elementType, expression);
4135 }
4136 }
4137
4138 /**
4139 * Builder for the definition body of an enhanced-for operation.
4140 */
4141 public static final class DefinitionBuilder {
4142 final Body.Builder connectedAncestorBody;
4143 final CodeType elementType;
4144 final Body.Builder expression;
4145
4146 DefinitionBuilder(Body.Builder connectedAncestorBody,
4147 CodeType elementType, Body.Builder expression) {
4148 this.connectedAncestorBody = connectedAncestorBody;
4149 this.elementType = elementType;
4150 this.expression = expression;
4151 }
4152
4153 /**
4154 * Builds the definition body of an enhanced-for operation, using a type derived from the type
4155 * of the loop expression.
4156 *
4157 * @param c a consumer that populates the definition body
4158 * @return a builder for specifying the loop body
4159 */
4160 public BodyBuilder definition(Consumer<Block.Builder> c) {
4161 return definition(elementType, c);
4162 }
4163
4164 /**
4165 * Builds the definition body of an enhanced-for operation with the provided type.
4166 *
4167 * @param bodyElementType the type to provide to the loop body
4168 * @param c a consumer that populates the definition body
4169 * @return a builder for specifying the loop body
4170 */
4171 public BodyBuilder definition(CodeType bodyElementType, Consumer<Block.Builder> c) {
4172 Body.Builder definition = Body.Builder.of(connectedAncestorBody,
4173 CoreType.functionType(bodyElementType, elementType));
4174 c.accept(definition.entryBlock());
4175
4176 return new BodyBuilder(connectedAncestorBody, elementType, expression, definition);
4177 }
4178 }
4179
4180 /**
4181 * Builder for the loop body of an enhanced-for operation.
4182 */
4183 public static final class BodyBuilder {
4184 final Body.Builder connectedAncestorBody;
4185 final CodeType elementType;
4186 final Body.Builder expression;
4187 final Body.Builder definition;
4188
4189 BodyBuilder(Body.Builder connectedAncestorBody,
4190 CodeType elementType, Body.Builder expression, Body.Builder definition) {
4191 this.connectedAncestorBody = connectedAncestorBody;
4192 this.elementType = elementType;
4193 this.expression = expression;
4194 this.definition = definition;
4195 }
4196
4197 /**
4198 * Completes the enhanced-for operation by adding the loop body.
4199 *
4200 * @param c a consumer that populates the loop body
4201 * @return the completed enhanced-for operation
4202 */
4203 public EnhancedForOp body(Consumer<Block.Builder> c) {
4204 Body.Builder body = Body.Builder.of(connectedAncestorBody,
4205 CoreType.functionType(VOID, elementType));
4206 c.accept(body.entryBlock());
4207
4208 return new EnhancedForOp(expression, definition, body);
4209 }
4210 }
4211
4212 static final String NAME = "java.enhancedFor";
4213
4214 final Body exprBody;
4215 final Body initBody;
4216 final Body loopBody;
4217
4218 EnhancedForOp(ExternalizedOp def) {
4219 List<Body.Builder> bodies = requireBodies(def, 3);
4220 this(bodies.get(0), bodies.get(1), bodies.get(2));
4221 }
4222
4223 EnhancedForOp(EnhancedForOp that, CodeContext cc, CodeTransformer ct) {
4224 super(that, cc);
4225
4226 this.exprBody = that.exprBody.transform(cc, ct).build(this);
4227 this.initBody = that.initBody.transform(cc, ct).build(this);
4228 this.loopBody = that.loopBody.transform(cc, ct).build(this);
4229 }
4230
4231 @Override
4232 public EnhancedForOp transform(CodeContext cc, CodeTransformer ct) {
4233 return new EnhancedForOp(this, cc, ct);
4234 }
4235
4236 EnhancedForOp(Body.Builder expressionC, Body.Builder initC, Body.Builder bodyC) {
4237 super(List.of());
4238
4239 this.exprBody = requireNonVoidReturnType(NAME + " expression", expressionC, 0).build(this);
4240 this.initBody = requireNonVoidReturnType(NAME + " initialization", initC, 1).build(this);
4241 this.loopBody = requireVoidReturnType(NAME + " loop", bodyC, 1).build(this);
4242 }
4243
4244 @Override
4245 public List<Body> bodies() {
4246 return List.of(exprBody, initBody, loopBody);
4247 }
4248
4249 /**
4250 * {@return the expression body}
4251 */
4252 public Body exprBody() {
4253 return exprBody;
4254 }
4255
4256 /**
4257 * {@return the initialization body}
4258 */
4259 public Body initBody() {
4260 return initBody;
4261 }
4262
4263 @Override
4264 public Body loopBody() {
4265 return loopBody;
4266 }
4267
4268 static final MethodRef ITERABLE_ITERATOR = MethodRef.method(Iterable.class, "iterator", Iterator.class);
4269 static final MethodRef ITERATOR_HAS_NEXT = MethodRef.method(Iterator.class, "hasNext", boolean.class);
4270 static final MethodRef ITERATOR_NEXT = MethodRef.method(Iterator.class, "next", Object.class);
4271
4272 @Override
4273 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
4274 JavaType elementType = (JavaType) initBody.entryBlock().parameters().get(0).type();
4275 boolean isArray = exprBody.bodySignature().returnType() instanceof ArrayType;
4276
4277 Block.Builder preHeader = b.block(exprBody.bodySignature().returnType());
4278 Block.Builder header = b.block(isArray ? List.of(INT) : List.of());
4279 Block.Builder init = b.block();
4280 Block.Builder body = b.block();
4281 Block.Builder exit = b.block();
4282
4283 b.transformBody(exprBody, List.of(), loweringTransformer(inherited, (block, op) -> {
4284 if (op instanceof CoreOp.YieldOp yop) {
4285 Value loopSource = block.context().getValue(yop.yieldValue());
4286 block.add(branch(preHeader.reference(loopSource)));
4287 return block;
4288 } else {
4289 return null;
4290 }
4291 }));
4292
4293 if (isArray) {
4294 Value array = preHeader.parameters().get(0);
4295 Value arrayLength = preHeader.add(arrayLength(array));
4296 Value i = preHeader.add(constant(INT, 0));
4297 preHeader.add(branch(header.reference(i)));
4298
4299 i = header.parameters().get(0);
4300 Value p = header.add(lt(i, arrayLength));
4301 header.add(conditionalBranch(p, init.reference(), exit.reference()));
4302
4303 Value e = init.add(arrayLoadOp(array, i));
4304 List<Value> initValues = new ArrayList<>();
4305 init.transformBody(this.initBody, List.of(e), loweringTransformer(inherited, (block, op) -> {
4306 if (op instanceof CoreOp.YieldOp yop) {
4307 initValues.addAll(block.context().getValues(yop.operands()));
4308 block.add(branch(body.reference()));
4309 return block;
4310 } else {
4311 return null;
4312 }
4313 }));
4314
4315 Block.Builder update = b.block();
4316 BranchTarget.setBranchTarget(b.context(), this, exit, update);
4317
4318 body.transformBody(this.loopBody, initValues, loweringTransformer(inherited, (_, _) -> null));
4319
4320 i = update.add(add(i, update.add(constant(INT, 1))));
4321 update.add(branch(header.reference(i)));
4322 } else {
4323 JavaType iterable = parameterized(type(Iterator.class), elementType);
4324 Value iterator = preHeader.add(invoke(iterable, ITERABLE_ITERATOR, preHeader.parameters().get(0)));
4325 preHeader.add(branch(header.reference()));
4326
4327 Value p = header.add(invoke(ITERATOR_HAS_NEXT, iterator));
4328 header.add(conditionalBranch(p, init.reference(), exit.reference()));
4329
4330 Value e = init.add(invoke(elementType, ITERATOR_NEXT, iterator));
4331 List<Value> initValues = new ArrayList<>();
4332 init.transformBody(this.initBody, List.of(e), loweringTransformer(inherited, (block, op) -> {
4333 if (op instanceof CoreOp.YieldOp yop) {
4334 initValues.addAll(block.context().getValues(yop.operands()));
4335 block.add(branch(body.reference()));
4336 return block;
4337 } else {
4338 return null;
4339 }
4340 }));
4341
4342 BranchTarget.setBranchTarget(b.context(), this, exit, header);
4343
4344 body.transformBody(this.loopBody, initValues, loweringTransformer(inherited, (_, _) -> null));
4345 }
4346
4347 return exit;
4348 }
4349
4350 @Override
4351 public CodeType resultType() {
4352 return VOID;
4353 }
4354 }
4355
4356 /**
4357 * The while operation, that can model a Java language while statement.
4358 * <p>
4359 * While operations feature two bodies. The <em>predicate body</em> models the loop condition.
4360 * The <em>loop body</em> models the statements to execute.
4361 * <p>
4362 * The predicate body should accept no arguments and yield a {@link JavaType#BOOLEAN} value.
4363 * The loop body should accept no arguments, and yield {@linkplain JavaType#VOID no value}.
4364 * <p>
4365 * The result type of a while operation is {@link JavaType#VOID}.
4366 *
4367 * @jls 14.12 The while Statement
4368 */
4369 @OpDeclaration(WhileOp.NAME)
4370 public static final class WhileOp extends JavaOp
4371 implements Op.Loop, Op.Lowerable, JavaStatement {
4372
4373 /**
4374 * Builder for the predicate body of a while operation.
4375 */
4376 public static class PredicateBuilder {
4377 final Body.Builder connectedAncestorBody;
4378
4379 PredicateBuilder(Body.Builder connectedAncestorBody) {
4380 this.connectedAncestorBody = connectedAncestorBody;
4381 }
4382
4383 /**
4384 * Builds the predicate body of a while operation.
4385 *
4386 * @param c a consumer that populates the predicate body
4387 * @return a builder for specifying the loop body
4388 */
4389 public WhileOp.BodyBuilder predicate(Consumer<Block.Builder> c) {
4390 Body.Builder body = Body.Builder.of(connectedAncestorBody, CoreType.functionType(BOOLEAN));
4391 c.accept(body.entryBlock());
4392
4393 return new WhileOp.BodyBuilder(connectedAncestorBody, body);
4394 }
4395 }
4396
4397 /**
4398 * Builder for the loop body of a while operation.
4399 */
4400 public static class BodyBuilder {
4401 final Body.Builder connectedAncestorBody;
4402 private final Body.Builder predicate;
4403
4404 BodyBuilder(Body.Builder connectedAncestorBody, Body.Builder predicate) {
4405 this.connectedAncestorBody = connectedAncestorBody;
4406 this.predicate = predicate;
4407 }
4408
4409 /**
4410 * Completes the while operation by adding the loop body.
4411 *
4412 * @param c a consumer that populates the loop body
4413 * @return the completed while operation
4414 */
4415 public WhileOp body(Consumer<Block.Builder> c) {
4416 Body.Builder body = Body.Builder.of(connectedAncestorBody, CoreType.FUNCTION_TYPE_VOID);
4417 c.accept(body.entryBlock());
4418
4419 return new WhileOp(predicate, body);
4420 }
4421 }
4422
4423 private static final String NAME = "java.while";
4424
4425 private final List<Body> bodies;
4426
4427 WhileOp(ExternalizedOp def) {
4428 List<Body.Builder> bodies = requireBodies(def, 2);
4429 this(bodies.get(0), bodies.get(1));
4430 }
4431
4432 WhileOp(Body.Builder predicate, Body.Builder body) {
4433 super(List.of());
4434 this.bodies = List.of(requireBodySignature(NAME + " predicate", predicate, CoreType.functionType(BOOLEAN)).build(this),
4435 requireVoidBodySignature(NAME + " body", body).build(this));
4436 }
4437
4438 WhileOp(WhileOp that, CodeContext cc, CodeTransformer ct) {
4439 super(that, cc);
4440
4441 this.bodies = that.bodies.stream()
4442 .map(b -> b.transform(cc, ct).build(this)).toList();
4443 }
4444
4445 @Override
4446 public WhileOp transform(CodeContext cc, CodeTransformer ct) {
4447 return new WhileOp(this, cc, ct);
4448 }
4449
4450 @Override
4451 public List<Body> bodies() {
4452 return bodies;
4453 }
4454
4455 /**
4456 * {@return the loop condition body}
4457 */
4458 public Body predicateBody() {
4459 return bodies.get(0);
4460 }
4461
4462 @Override
4463 public Body loopBody() {
4464 return bodies.get(1);
4465 }
4466
4467 @Override
4468 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
4469 Block.Builder header = b.block();
4470 Block.Builder body = b.block();
4471 Block.Builder exit = b.block();
4472
4473 b.add(branch(header.reference()));
4474
4475 header.transformBody(predicateBody(), List.of(), loweringTransformer(inherited, (block, op) -> {
4476 if (op instanceof CoreOp.YieldOp yo) {
4477 block.add(conditionalBranch(block.context().getValue(yo.yieldValue()),
4478 body.reference(), exit.reference()));
4479 return block;
4480 } else {
4481 return null;
4482 }
4483 }));
4484
4485 BranchTarget.setBranchTarget(b.context(), this, exit, header);
4486
4487 body.transformBody(loopBody(), List.of(), loweringTransformer(inherited, (_, _) -> null));
4488
4489 return exit;
4490 }
4491
4492 @Override
4493 public CodeType resultType() {
4494 return VOID;
4495 }
4496 }
4497
4498 /**
4499 * The do-while operation, that can model a Java language do statement.
4500 * <p>
4501 * Do-while operations feature two bodies. The <em>loop body</em> models the statements to execute.
4502 * The <em>predicate body</em> models the loop condition.
4503 * <p>
4504 * The loop body should accept no arguments, and yield {@linkplain JavaType#VOID no value}. The predicate body
4505 * should accept no arguments, and yield a {@link JavaType#BOOLEAN} value.
4506 * <p>
4507 * The result type of a do-while operation is {@link JavaType#VOID}.
4508 *
4509 * @jls 14.13 The do Statement
4510 */
4511 // @@@ Unify JavaDoWhileOp and JavaWhileOp with common abstract superclass
4512 @OpDeclaration(DoWhileOp.NAME)
4513 public static final class DoWhileOp extends JavaOp
4514 implements Op.Loop, Op.Lowerable, JavaStatement {
4515
4516 /**
4517 * Builder for the predicate body of a do-while operation.
4518 */
4519 public static class PredicateBuilder {
4520 final Body.Builder connectedAncestorBody;
4521 private final Body.Builder body;
4522
4523 PredicateBuilder(Body.Builder connectedAncestorBody, Body.Builder body) {
4524 this.connectedAncestorBody = connectedAncestorBody;
4525 this.body = body;
4526 }
4527
4528 /**
4529 * Completes the do-while operation by adding the predicate body.
4530 *
4531 * @param c a consumer that populates the predicate body
4532 * @return the completed do-while operation
4533 */
4534 public DoWhileOp predicate(Consumer<Block.Builder> c) {
4535 Body.Builder predicate = Body.Builder.of(connectedAncestorBody, CoreType.functionType(BOOLEAN));
4536 c.accept(predicate.entryBlock());
4537 return new DoWhileOp(body, predicate);
4538 }
4539 }
4540
4541 /**
4542 * Builder for the loop body of a do-while operation.
4543 */
4544 public static class BodyBuilder {
4545 final Body.Builder connectedAncestorBody;
4546
4547 BodyBuilder(Body.Builder connectedAncestorBody) {
4548 this.connectedAncestorBody = connectedAncestorBody;
4549 }
4550
4551 /**
4552 * Builds the loop body of a do-while operation.
4553 *
4554 * @param c a consumer that populates the loop body
4555 * @return a builder for specifying the predicate body
4556 */
4557 public DoWhileOp.PredicateBuilder body(Consumer<Block.Builder> c) {
4558 Body.Builder body = Body.Builder.of(connectedAncestorBody, CoreType.FUNCTION_TYPE_VOID);
4559 c.accept(body.entryBlock());
4560
4561 return new DoWhileOp.PredicateBuilder(connectedAncestorBody, body);
4562 }
4563 }
4564
4565 private static final String NAME = "java.do.while";
4566
4567 private final List<Body> bodies;
4568
4569 DoWhileOp(ExternalizedOp def) {
4570 List<Body.Builder> bodies = requireBodies(def, 2);
4571 this(bodies.get(0), bodies.get(1));
4572 }
4573
4574 DoWhileOp(Body.Builder body, Body.Builder predicate) {
4575 super(List.of());
4576
4577 Objects.requireNonNull(body);
4578
4579 this.bodies = List.of(requireVoidBodySignature(NAME + " body", body).build(this),
4580 requireBodySignature(NAME + " predicate", predicate, CoreType.functionType(BOOLEAN)).build(this));
4581 }
4582
4583 DoWhileOp(DoWhileOp that, CodeContext cc, CodeTransformer ct) {
4584 super(that, cc);
4585
4586 this.bodies = that.bodies.stream()
4587 .map(b -> b.transform(cc, ct).build(this)).toList();
4588 }
4589
4590 @Override
4591 public DoWhileOp transform(CodeContext cc, CodeTransformer ct) {
4592 return new DoWhileOp(this, cc, ct);
4593 }
4594
4595 @Override
4596 public List<Body> bodies() {
4597 return bodies;
4598 }
4599
4600 /**
4601 * {@return the predicate body for the do-while operation}
4602 */
4603 public Body predicateBody() {
4604 return bodies.get(1);
4605 }
4606
4607 @Override
4608 public Body loopBody() {
4609 return bodies.get(0);
4610 }
4611
4612 @Override
4613 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
4614 Block.Builder body = b.block();
4615 Block.Builder header = b.block();
4616 Block.Builder exit = b.block();
4617
4618 b.add(branch(body.reference()));
4619
4620 BranchTarget.setBranchTarget(b.context(), this, exit, header);
4621
4622 body.transformBody(loopBody(), List.of(), loweringTransformer(inherited, (_, _) -> null));
4623
4624 header.transformBody(predicateBody(), List.of(), loweringTransformer(inherited, (block, op) -> {
4625 if (op instanceof CoreOp.YieldOp yo) {
4626 block.add(conditionalBranch(block.context().getValue(yo.yieldValue()),
4627 body.reference(), exit.reference()));
4628 return block;
4629 } else {
4630 return null;
4631 }
4632 }));
4633
4634 return exit;
4635 }
4636
4637 @Override
4638 public CodeType resultType() {
4639 return VOID;
4640 }
4641 }
4642
4643 /**
4644 * The conditional operation, that can model Java language conditional-and and conditional-or expressions.
4645 * <p>
4646 * Conditional operations feature two or more predicate bodies, each yielding a {@link JavaType#BOOLEAN} value.
4647 *
4648 * @jls 15.23 Conditional-And Operator {@code &&}
4649 * @jls 15.24 Conditional-Or Operator {@code ||}
4650 */
4651 public sealed static abstract class JavaConditionalOp extends JavaOp
4652 implements Op.Nested, Op.Lowerable, JavaExpression {
4653
4654 static final FunctionType BODY_TYPE = CoreType.functionType(BOOLEAN);
4655
4656 final List<Body> bodies;
4657
4658 JavaConditionalOp(JavaConditionalOp that, CodeContext cc, CodeTransformer ct) {
4659 super(that, cc);
4660
4661 // Copy body
4662 this.bodies = that.bodies.stream().map(b -> b.transform(cc, ct).build(this)).toList();
4663 }
4664
4665 JavaConditionalOp(List<Body.Builder> bodyCs) {
4666 super(List.of());
4667 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList();
4668 }
4669
4670 @Override
4671 public List<Body> bodies() {
4672 return bodies;
4673 }
4674
4675 static Block.Builder lower(Block.Builder startBlock, BiFunction<Block.Builder, Op, Block.Builder> before, JavaConditionalOp cop) {
4676 List<Body> bodies = cop.bodies();
4677
4678 Block.Builder exit = startBlock.block();
4679 CodeType oprType = cop.result().type();
4680 Block.Parameter arg = exit.parameter(oprType);
4681 startBlock.context().mapValue(cop.result(), arg);
4682
4683 // Transform bodies in reverse order
4684 // This makes available the blocks to be referenced as successors in prior blocks
4685
4686 Block.Builder pred = null;
4687 for (int i = bodies.size() - 1; i >= 0; i--) {
4688 CodeTransformer bodyTransformer;
4689 BiFunction<Block.Builder, Op, Block.Builder> lowering;
4690 if (i == bodies.size() - 1) {
4691 bodyTransformer = loweringTransformer(before, (block, op) -> {
4692 if (op instanceof CoreOp.YieldOp yop) {
4693 Value p = block.context().getValue(yop.yieldValue());
4694 block.add(branch(exit.reference(p)));
4695 return block;
4696 } else {
4697 return null;
4698 }
4699 });
4700 } else {
4701 Block.Builder nextPred = pred;
4702 bodyTransformer = loweringTransformer(before, (block, op) -> {
4703 if (op instanceof CoreOp.YieldOp yop) {
4704 Value p = block.context().getValue(yop.yieldValue());
4705 if (cop instanceof ConditionalAndOp) {
4706 block.add(conditionalBranch(p, nextPred.reference(), exit.reference(p)));
4707 } else {
4708 block.add(conditionalBranch(p, exit.reference(p), nextPred.reference()));
4709 }
4710 return block;
4711 } else {
4712 return null;
4713 }
4714 });
4715 }
4716
4717 Body fromPred = bodies.get(i);
4718 if (i == 0) {
4719 startBlock.transformBody(fromPred, List.of(), bodyTransformer);
4720 } else {
4721 pred = startBlock.block(fromPred.bodySignature().parameterTypes());
4722 pred.transformBody(fromPred, pred.parameters(), bodyTransformer);
4723 }
4724 }
4725
4726 return exit;
4727 }
4728
4729 @Override
4730 public CodeType resultType() {
4731 return BOOLEAN;
4732 }
4733 }
4734
4735 /**
4736 * The conditional-and operation, that can model Java language conditional-and expressions.
4737 *
4738 * @jls 15.23 Conditional-And Operator {@code &&}
4739 */
4740 @OpDeclaration(ConditionalAndOp.NAME)
4741 public static final class ConditionalAndOp extends JavaConditionalOp {
4742
4743 /**
4744 * Builder for conditional-and operations.
4745 */
4746 public static class Builder {
4747 final Body.Builder connectedAncestorBody;
4748 final List<Body.Builder> bodies;
4749
4750 Builder(Body.Builder connectedAncestorBody, Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) {
4751 this.connectedAncestorBody = connectedAncestorBody;
4752 this.bodies = new ArrayList<>();
4753 and(lhs);
4754 and(rhs);
4755 }
4756
4757 /**
4758 * Adds a predicate body to this conditional-and operation.
4759 *
4760 * @param c a consumer that populates the predicate body
4761 * @return this builder
4762 */
4763 public Builder and(Consumer<Block.Builder> c) {
4764 Body.Builder body = Body.Builder.of(connectedAncestorBody, CoreType.functionType(BOOLEAN));
4765 c.accept(body.entryBlock());
4766 bodies.add(body);
4767
4768 return this;
4769 }
4770
4771 /**
4772 * {@return the completed conditional-and operation}
4773 */
4774 public ConditionalAndOp build() {
4775 return new ConditionalAndOp(bodies);
4776 }
4777 }
4778
4779 static final String NAME = "java.cand";
4780
4781 ConditionalAndOp(ExternalizedOp def) {
4782 this(def.bodyDefinitions());
4783 }
4784
4785 ConditionalAndOp(ConditionalAndOp that, CodeContext cc, CodeTransformer ct) {
4786 super(that, cc, ct);
4787 }
4788
4789 @Override
4790 public ConditionalAndOp transform(CodeContext cc, CodeTransformer ct) {
4791 return new ConditionalAndOp(this, cc, ct);
4792 }
4793
4794 ConditionalAndOp(List<Body.Builder> bodyCs) {
4795 bodyCs.forEach(b -> requireBodySignature(NAME, b, BODY_TYPE));
4796 super(requireMinBodies(NAME, bodyCs, 2));
4797 }
4798
4799 @Override
4800 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
4801 return lower(b, inherited, this);
4802 }
4803 }
4804
4805 /**
4806 * The conditional-or operation, that can model Java language conditional-or expressions.
4807 *
4808 * @jls 15.24 Conditional-Or Operator {@code ||}
4809 */
4810 @OpDeclaration(ConditionalOrOp.NAME)
4811 public static final class ConditionalOrOp extends JavaConditionalOp {
4812
4813 /**
4814 * Builder for conditional-or operations.
4815 */
4816 public static class Builder {
4817 final Body.Builder connectedAncestorBody;
4818 final List<Body.Builder> bodies;
4819
4820 Builder(Body.Builder connectedAncestorBody, Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) {
4821 this.connectedAncestorBody = connectedAncestorBody;
4822 this.bodies = new ArrayList<>();
4823 or(lhs);
4824 or(rhs);
4825 }
4826
4827 /**
4828 * Adds a predicate body to this conditional-or operation.
4829 *
4830 * @param c a consumer that populates the predicate body
4831 * @return this builder
4832 */
4833 public Builder or(Consumer<Block.Builder> c) {
4834 Body.Builder body = Body.Builder.of(connectedAncestorBody, CoreType.functionType(BOOLEAN));
4835 c.accept(body.entryBlock());
4836 bodies.add(body);
4837
4838 return this;
4839 }
4840
4841 /**
4842 * {@return the completed conditional-or operation}
4843 */
4844 public ConditionalOrOp build() {
4845 return new ConditionalOrOp(bodies);
4846 }
4847 }
4848
4849 static final String NAME = "java.cor";
4850
4851 ConditionalOrOp(ExternalizedOp def) {
4852 this(def.bodyDefinitions());
4853 }
4854
4855 ConditionalOrOp(ConditionalOrOp that, CodeContext cc, CodeTransformer ct) {
4856 super(that, cc, ct);
4857 }
4858
4859 @Override
4860 public ConditionalOrOp transform(CodeContext cc, CodeTransformer ct) {
4861 return new ConditionalOrOp(this, cc, ct);
4862 }
4863
4864 ConditionalOrOp(List<Body.Builder> bodyCs) {
4865 bodyCs.forEach(b -> requireBodySignature(NAME, b, BODY_TYPE));
4866 super(requireMinBodies(NAME, bodyCs, 2));
4867 }
4868
4869 @Override
4870 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
4871 return lower(b, inherited, this);
4872 }
4873 }
4874
4875 /**
4876 * The conditional operation, that can model Java language conditional operator {@code ?} expressions.
4877 * <p>
4878 * Conditional expression operations feature three bodies: the predicate body, the true body, and the false body.
4879 * <p>
4880 * The predicate body accepts no arguments and yields a {@link JavaType#BOOLEAN} value.
4881 * The true and false bodies accepts no arguments and yield a value.
4882 *
4883 * @jls 15.25 Conditional Operator {@code ? :}
4884 */
4885 @OpDeclaration(ConditionalExpressionOp.NAME)
4886 public static final class ConditionalExpressionOp extends JavaOp
4887 implements Op.Nested, Op.Lowerable, JavaExpression {
4888
4889 static final String NAME = "java.cexpression";
4890
4891 final CodeType resultType;
4892 // {cond, truepart, falsepart}
4893 final List<Body> bodies;
4894
4895 ConditionalExpressionOp(ExternalizedOp def) {
4896 List<Body.Builder> bodies = requireBodies(def, 3);
4897 this(def.resultType(), bodies.get(0), bodies.get(1), bodies.get(2));
4898 }
4899
4900 ConditionalExpressionOp(ConditionalExpressionOp that, CodeContext cc, CodeTransformer ct) {
4901 super(that, cc);
4902
4903 // Copy body
4904 this.bodies = that.bodies.stream()
4905 .map(b -> b.transform(cc, ct).build(this)).toList();
4906 this.resultType = that.resultType;
4907 }
4908
4909 @Override
4910 public ConditionalExpressionOp transform(CodeContext cc, CodeTransformer ct) {
4911 return new ConditionalExpressionOp(this, cc, ct);
4912 }
4913
4914 ConditionalExpressionOp(CodeType expressionType, Body.Builder predicateBody, Body.Builder trueBody, Body.Builder falseBody) {
4915 super(List.of());
4916
4917 this.bodies = List.of(requireBodySignature(NAME + " predicate", predicateBody, CoreType.functionType(BOOLEAN)).build(this),
4918 requireNoParameters(NAME + " true body", trueBody).build(this),
4919 requireNoParameters(NAME + " false body", falseBody).build(this));
4920 // @@@ when expressionType is null, we assume truepart and falsepart have the same yieldType
4921 this.resultType = expressionType == null ? bodies.get(1).yieldType() : expressionType;
4922 }
4923
4924 @Override
4925 public List<Body> bodies() {
4926 return bodies;
4927 }
4928
4929 /**
4930 * {@return the predicate body}
4931 */
4932 public Body predicateBody() {
4933 return bodies.get(0);
4934 }
4935
4936 /**
4937 * {@return the true body}
4938 */
4939 public Body trueBody() {
4940 return bodies.get(1);
4941 }
4942
4943 /**
4944 * {@return the false body}
4945 */
4946 public Body falseBody() {
4947 return bodies.get(2);
4948 }
4949
4950 @Override
4951 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
4952 Block.Builder exit = b.block(resultType());
4953 exit.context().mapValue(result(), exit.parameters().get(0));
4954
4955 BranchTarget.setBranchTarget(b.context(), this, exit, null);
4956
4957 List<Block.Builder> builders = List.of(b.block(), b.block());
4958 b.transformBody(bodies.get(0), List.of(), loweringTransformer(inherited, (block, op) -> {
4959 if (op instanceof CoreOp.YieldOp yo) {
4960 block.add(conditionalBranch(block.context().getValue(yo.yieldValue()),
4961 builders.get(0).reference(), builders.get(1).reference()));
4962 return block;
4963 } else {
4964 return null;
4965 }
4966 }));
4967
4968 for (int i = 0; i < 2; i++) {
4969 builders.get(i).transformBody(bodies.get(i + 1), List.of(), loweringTransformer(inherited, (block, op) -> {
4970 if (op instanceof CoreOp.YieldOp yop) {
4971 block.add(branch(exit.reference(block.context().getValue(yop.yieldValue()))));
4972 return block;
4973 } else {
4974 return null;
4975 }
4976 }));
4977 }
4978
4979 return exit;
4980 }
4981
4982 @Override
4983 public CodeType resultType() {
4984 return resultType;
4985 }
4986 }
4987
4988 /**
4989 * The try operation, that can model Java language try statements.
4990 * <p>
4991 * Try operations feature a <em>try body</em>, zero or more <em>catch bodies</em>, and an optional
4992 * <em>finally body</em>. Try operations may also feature zero or more <em>resources bodies</em>, modeling a
4993 * try-with-resources statement.
4994 * <p>
4995 * Each resource body yields a value. The first resource body accepts no arguments. A second resource body accepts
4996 * an argument whose type is the same as the yield type of the first resource body. A subsequent resource accepts,
4997 * in order, arguments whose types are the same as all the prior resource body yield types.
4998 * <p>
4999 * The try body yields {@linkplain JavaType#VOID no value}. If one or more resources bodies are present then
5000 * the try body accepts, in order, arguments whose types are the same as the resource bodies yield types.
5001 * <p>
5002 * Each catch body should accept an exception value and yield {@linkplain JavaType#VOID no value}. The
5003 * finally body, if present, should accept no arguments and yield {@linkplain JavaType#VOID no value}.
5004 * <p>
5005 * The result type of a try operation is {@link JavaType#VOID}.
5006 *
5007 * @jls 14.20 The try statement
5008 * @jls 14.20.3 try-with-resources
5009 */
5010 @OpDeclaration(TryOp.NAME)
5011 public static final class TryOp extends JavaOp
5012 implements Op.Nested, Op.Lowerable, JavaStatement {
5013
5014 /**
5015 * Builder for the resource bodies and the try body of a try operation.
5016 */
5017 public static final class BodyBuilder {
5018 final Body.Builder connectedAncestorBody;
5019 final List<Body.Builder> resources;
5020
5021 BodyBuilder(Body.Builder connectedAncestorBody) {
5022 this.connectedAncestorBody = connectedAncestorBody;
5023 this.resources = new ArrayList<>();
5024 }
5025
5026 /**
5027 * Adds a resource body to a try-with-resources operation.
5028 *
5029 * @param yieldType the resource type for a resource expression, or the Var type for a resource declaration
5030 * @param c a consumer that populates the resource body
5031 * @return this builder
5032 */
5033 public BodyBuilder resource(CodeType yieldType, Consumer<Block.Builder> c) {
5034 List<CodeType> paramTypes = resources.stream().map(r -> r.bodySignature().returnType()).toList();
5035 Body.Builder resource = Body.Builder.of(connectedAncestorBody,
5036 CoreType.functionType(yieldType, paramTypes));
5037 c.accept(resource.entryBlock());
5038 resources.add(resource);
5039 return this;
5040 }
5041
5042 /**
5043 * Builds the try body of the try operation.
5044 *
5045 * @param c a consumer that populates the try body
5046 * @return a builder for specifying catch bodies and an optional finalizer
5047 */
5048 public CatchBuilder body(Consumer<Block.Builder> c) {
5049 Body.Builder body = Body.Builder.of(connectedAncestorBody,
5050 CoreType.functionType(VOID, resources.stream().map(bb -> bb.bodySignature().returnType()).toList()));
5051 c.accept(body.entryBlock());
5052
5053 return new CatchBuilder(connectedAncestorBody, resources, body);
5054 }
5055 }
5056
5057 /**
5058 * Builder for specifying catch bodies and an optional finalizer body of a try operation.
5059 */
5060 public static final class CatchBuilder {
5061 final Body.Builder connectedAncestorBody;
5062 final List<Body.Builder> resources;
5063 final Body.Builder body;
5064 final List<Body.Builder> catchers;
5065
5066 CatchBuilder(Body.Builder connectedAncestorBody, List<Body.Builder> resources, Body.Builder body) {
5067 this.connectedAncestorBody = connectedAncestorBody;
5068 this.resources = resources;
5069 this.body = body;
5070 this.catchers = new ArrayList<>();
5071 }
5072
5073 // @@@ multi-catch
5074 /**
5075 * Adds a catch body for handling exceptions of a specific type.
5076 *
5077 * @param exceptionType the type of exception handled
5078 * @param c a consumer that populates the catch body
5079 * @return this builder
5080 */
5081 public CatchBuilder catch_(CodeType exceptionType, Consumer<Block.Builder> c) {
5082 Body.Builder _catch = Body.Builder.of(connectedAncestorBody,
5083 CoreType.functionType(VOID, exceptionType));
5084 c.accept(_catch.entryBlock());
5085 catchers.add(_catch);
5086
5087 return this;
5088 }
5089
5090 /**
5091 * Completes the try operation by adding the finalizer body.
5092 *
5093 * @param c a consumer that populates the finalizer body
5094 * @return the completed try operation
5095 */
5096 public TryOp finally_(Consumer<Block.Builder> c) {
5097 Body.Builder _finally = Body.Builder.of(connectedAncestorBody, CoreType.FUNCTION_TYPE_VOID);
5098 c.accept(_finally.entryBlock());
5099
5100 return new TryOp(resources, body, catchers, _finally);
5101 }
5102
5103 /**
5104 * Completes the try operation without a finalizer body.
5105 *
5106 * @return the completed try operation
5107 */
5108 public TryOp noFinalizer() {
5109 return new TryOp(resources, body, catchers, null);
5110 }
5111 }
5112
5113 static final String NAME = "java.try";
5114 static final MethodRef AUTO_CLOSEABLE_CLOSE_METHOD = MethodRef.method(AutoCloseable.class, "close", void.class);
5115 static final MethodRef THROWABLE_ADD_SUPPRESSED_METHOD = MethodRef.method(Throwable.class, "addSuppressed", void.class, Throwable.class);
5116
5117 final List<Body> resourcesBodies;
5118 final Body body;
5119 final List<Body> catchBodies;
5120 final Body finallyBody;
5121
5122 TryOp(ExternalizedOp def) {
5123 List<Body.Builder> bodies = def.bodyDefinitions();
5124 if (bodies.size() < 1) {
5125 throw structuralException(def.name(), "requires at least 1 body");
5126 }
5127 int bodyIndex = 0;
5128 while (bodyIndex < bodies.size() && !bodies.get(bodyIndex).bodySignature().returnType().equals(VOID)) {
5129 bodyIndex++;
5130 }
5131 if (bodyIndex == bodies.size()) {
5132 throw structuralException(def.name(), "no void try body found");
5133 }
5134 List<Body.Builder> resources = bodies.subList(0, bodyIndex);
5135 Body.Builder body = bodies.get(bodyIndex);
5136 Body.Builder last = bodies.getLast();
5137 Body.Builder finalizer;
5138 if (last != body && last.bodySignature().parameterTypes().isEmpty()) {
5139 finalizer = last;
5140 } else {
5141 finalizer = null;
5142 }
5143 List<Body.Builder> catchers = bodies.subList(
5144 bodyIndex + 1,
5145 bodies.size() - (finalizer == null ? 0 : 1));
5146
5147 this(resources, body, catchers, finalizer);
5148 }
5149
5150 TryOp(TryOp that, CodeContext cc, CodeTransformer ct) {
5151 super(that, cc);
5152
5153 this.resourcesBodies = that.resourcesBodies.stream()
5154 .map(b -> b.transform(cc, ct).build(this))
5155 .toList();
5156 this.body = that.body.transform(cc, ct).build(this);
5157 this.catchBodies = that.catchBodies.stream()
5158 .map(b -> b.transform(cc, ct).build(this))
5159 .toList();
5160 if (that.finallyBody != null) {
5161 this.finallyBody = that.finallyBody.transform(cc, ct).build(this);
5162 } else {
5163 this.finallyBody = null;
5164 }
5165 }
5166
5167 @Override
5168 public TryOp transform(CodeContext cc, CodeTransformer ct) {
5169 return new TryOp(this, cc, ct);
5170 }
5171
5172 TryOp(List<Body.Builder> resourcesC,
5173 Body.Builder bodyC,
5174 List<Body.Builder> catchersC,
5175 Body.Builder finalizerC) {
5176 super(List.of());
5177
5178 List<CodeType> resourceTypes = new ArrayList<>();
5179 for (Body.Builder _resource : resourcesC) {
5180 requireNonVoidReturnType(NAME + " resource", _resource, resourceTypes.size());
5181 if (!_resource.bodySignature().parameterTypes().equals(resourceTypes)) {
5182 throw structuralException(NAME, "resource #%d requires %s parameter types, found %s".formatted(resourceTypes.size(), resourceTypes, _resource.bodySignature().parameterTypes()));
5183 }
5184 resourceTypes.add(_resource.bodySignature().returnType());
5185 }
5186 this.resourcesBodies = resourcesC.stream().map(r -> r.build(this)).toList();
5187 this.body = requireBodySignature(NAME + " try", bodyC, CoreType.functionType(VOID, resourceTypes)).build(this);
5188 this.catchBodies = catchersC.stream().map(c -> requireVoidReturnType(NAME + " catch", c, 1).build(this)).toList();
5189 if (finalizerC != null) {
5190 this.finallyBody = requireVoidBodySignature(NAME + " finalizer", finalizerC).build(this);
5191 } else {
5192 this.finallyBody = null;
5193 }
5194 }
5195
5196 @Override
5197 public List<Body> bodies() {
5198 ArrayList<Body> bodies = new ArrayList<>();
5199 bodies.addAll(resourcesBodies);
5200 bodies.add(body);
5201 bodies.addAll(catchBodies);
5202 if (finallyBody != null) {
5203 bodies.add(finallyBody);
5204 }
5205 return bodies;
5206 }
5207
5208 /**
5209 * {@return the resources bodies}
5210 */
5211 public List<Body> resourceBodies() {
5212 return resourcesBodies;
5213 }
5214
5215 /**
5216 * {@return the body of the try operation}
5217 */
5218 public Body body() {
5219 return body;
5220 }
5221
5222 /**
5223 * {@return the catch bodies}
5224 */
5225 public List<Body> catchBodies() {
5226 return catchBodies;
5227 }
5228
5229 /**
5230 * {@return the finally body, or {@code null} if this try operation has no finally body}
5231 */
5232 public Body finallyBody() {
5233 return finallyBody;
5234 }
5235
5236 @Override
5237 public Block.Builder lower(Block.Builder b, final BiFunction<Block.Builder, Op, Block.Builder> inherited) {
5238 Block.Builder exit = b.block();
5239 BranchTarget.setBranchTarget(b.context(), this, exit, null);
5240
5241 // Lowering is staged by repeated dispatching of the intermediate models through
5242 // the lower method: extended try-with-resources -> basic try-with-resources ->
5243 // try-catch-finally -> lower-level try form.
5244 // There is no recursion here, each time it is structurally different TryOp.
5245 if (!resourcesBodies.isEmpty()) {
5246 b.transformBody(resourcesBodies.size() == 1
5247 && catchBodies.isEmpty()
5248 && finallyBody == null
5249 ? lowerBasicTryWithResources()
5250 : normalizeTryWithResources(),
5251 b.context().getValues(capturedValues()),
5252 loweringTransformer(inherited, (block, op) -> {
5253 if (op instanceof CoreOp.YieldOp) {
5254 block.add(branch(exit.reference()));
5255 return block;
5256 } else {
5257 return null;
5258 }
5259 }));
5260 return exit;
5261 }
5262
5263 // Simple case with no catch and finally bodies
5264 if (catchBodies.isEmpty() && finallyBody == null) {
5265 b.transformBody(body, List.of(), loweringTransformer(inherited, (block, op) -> {
5266 if (op instanceof CoreOp.YieldOp) {
5267 block.add(branch(exit.reference()));
5268 return block;
5269 } else {
5270 return null;
5271 }
5272 }));
5273 return exit;
5274 }
5275
5276 Block.Builder tryRegionEnter = b.block();
5277 Block.Builder tryRegionExit = b.block();
5278
5279 // Construct the catcher block builders
5280 List<Block.Builder> catchers = catchBodies().stream()
5281 .map(catcher -> b.block())
5282 .toList();
5283 Block.Builder catcherFinally;
5284 if (finallyBody == null) {
5285 catcherFinally = null;
5286 } else {
5287 catcherFinally = b.block();
5288 catchers = new ArrayList<>(catchers);
5289 catchers.add(catcherFinally);
5290 }
5291
5292 // Enter the try exception region
5293 List<Block.Reference> exitHandlers = catchers.stream()
5294 .map(Block.Builder::reference)
5295 .toList();
5296 Op.Result enter = b.add(exceptionRegionEnter(tryRegionEnter.reference(), exitHandlers.reversed()));
5297
5298 BiFunction<Block.Builder, Op, Block.Builder> tryExitTransformer;
5299 if (finallyBody != null) {
5300 tryExitTransformer = composeFirst(inherited, (block, op) -> {
5301 if (op instanceof CoreOp.ReturnOp ||
5302 (op instanceof StatementTargetOp lop && ifExitFromTry(lop))) {
5303 return inlineFinalizer(block, enter, inherited);
5304 } else {
5305 return block;
5306 }
5307 });
5308 } else {
5309 tryExitTransformer = composeFirst(inherited, (block, op) -> {
5310 if (op instanceof CoreOp.ReturnOp ||
5311 (op instanceof StatementTargetOp lop && ifExitFromTry(lop))) {
5312 Block.Builder tryRegionReturnExit = block.block();
5313 block.add(exceptionRegionExit(enter, tryRegionReturnExit.reference()));
5314 return tryRegionReturnExit;
5315 } else {
5316 return block;
5317 }
5318 });
5319 }
5320 // Inline the try body
5321 AtomicBoolean hasTryRegionExit = new AtomicBoolean();
5322 tryRegionEnter.transformBody(body, List.of(), loweringTransformer(tryExitTransformer, (block, op) -> {
5323 if (op instanceof CoreOp.YieldOp) {
5324 hasTryRegionExit.set(true);
5325 block.add(branch(tryRegionExit.reference()));
5326 return block;
5327 } else {
5328 return null;
5329 }
5330 }));
5331
5332 Block.Builder finallyEnter = null;
5333 if (finallyBody != null) {
5334 finallyEnter = b.block();
5335 if (hasTryRegionExit.get()) {
5336 // Exit the try exception region
5337 tryRegionExit.add(exceptionRegionExit(enter, finallyEnter.reference()));
5338 }
5339 } else if (hasTryRegionExit.get()) {
5340 // Exit the try exception region
5341 tryRegionExit.add(exceptionRegionExit(enter, exit.reference()));
5342 }
5343
5344 // Inline the catch bodies
5345 for (int i = 0; i < this.catchBodies.size(); i++) {
5346 Block.Builder catcher = catchers.get(i);
5347 Body catcherBody = this.catchBodies.get(i);
5348 // Create the throwable argument
5349 Block.Parameter t = catcher.parameter(catcherBody.bodySignature().parameterTypes().get(0));
5350
5351 if (finallyBody != null) {
5352 Block.Builder catchRegionEnter = b.block();
5353 Block.Builder catchRegionExit = b.block();
5354
5355 // Enter the catch exception region
5356 Result catchExceptionRegion = catcher.add(
5357 exceptionRegionEnter(catchRegionEnter.reference(), catcherFinally.reference()));
5358
5359 BiFunction<Block.Builder, Op, Block.Builder> catchExitTransformer = composeFirst(inherited, (block, op) -> {
5360 if (op instanceof CoreOp.ReturnOp) {
5361 return inlineFinalizer(block, catchExceptionRegion, inherited);
5362 } else if (op instanceof StatementTargetOp lop && ifExitFromTry(lop)) {
5363 return inlineFinalizer(block, catchExceptionRegion, inherited);
5364 } else {
5365 return block;
5366 }
5367 });
5368 // Inline the catch body
5369 AtomicBoolean hasCatchRegionExit = new AtomicBoolean();
5370 catchRegionEnter.transformBody(catcherBody, List.of(t), loweringTransformer(catchExitTransformer, (block, op) -> {
5371 if (op instanceof CoreOp.YieldOp) {
5372 hasCatchRegionExit.set(true);
5373 block.add(branch(catchRegionExit.reference()));
5374 return block;
5375 } else {
5376 return null;
5377 }
5378 }));
5379
5380 // Exit the catch exception region
5381 if (hasCatchRegionExit.get()) {
5382 hasTryRegionExit.set(true);
5383 catchRegionExit.add(exceptionRegionExit(catchExceptionRegion, finallyEnter.reference()));
5384 }
5385 } else {
5386 // Inline the catch body
5387 catcher.transformBody(catcherBody, List.of(t), loweringTransformer(inherited, (block, op) -> {
5388 if (op instanceof CoreOp.YieldOp) {
5389 block.add(branch(exit.reference()));
5390 return block;
5391 } else {
5392 return null;
5393 }
5394 }));
5395 }
5396 }
5397
5398 if (finallyBody != null && hasTryRegionExit.get()) {
5399 // Inline the finally body
5400 finallyEnter.transformBody(finallyBody, List.of(), loweringTransformer(inherited, (block, op) -> {
5401 if (op instanceof CoreOp.YieldOp) {
5402 block.add(branch(exit.reference()));
5403 return block;
5404 } else {
5405 return null;
5406 }
5407 }));
5408 }
5409
5410 // Inline the finally body as a catcher of Throwable and adjusting to throw
5411 if (finallyBody != null) {
5412 // Create the throwable argument
5413 Block.Parameter t = catcherFinally.parameter(type(Throwable.class));
5414
5415 catcherFinally.transformBody(finallyBody, List.of(), loweringTransformer(inherited, (block, op) -> {
5416 if (op instanceof CoreOp.YieldOp) {
5417 block.add(throw_(t));
5418 return block;
5419 } else {
5420 return null;
5421 }
5422 }));
5423 }
5424 return exit;
5425 }
5426
5427 /// Normalize try-with-resources in two stages.
5428 ///
5429 /// First normalize an extended form to nested basic forms, one resource per
5430 /// level, left to right.
5431 ///
5432 /// Then lower each basic form to `try / catch / finally` logic.
5433 ///
5434 /// Stage boundaries use standalone synthetic bodies, so the next step always
5435 /// starts from a complete model.
5436 ///
5437 /// ```
5438 /// try (r1; r2; ...; rn) { body } catch (...) { catches } finally { finalizer }
5439 ///
5440 /// =>
5441 ///
5442 /// try (r1) {
5443 /// try (r2) {
5444 /// ...
5445 /// try (rn) { body }
5446 /// ...
5447 /// }
5448 /// } catch (...) {
5449 /// catches
5450 /// } finally {
5451 /// finalizer
5452 /// }
5453 ///
5454 /// =>
5455 ///
5456 /// try {
5457 /// r1 = acquire1()
5458 /// primary1 = null
5459 /// try {
5460 /// r2 = acquire2()
5461 /// primary2 = null
5462 /// try {
5463 /// ...
5464 /// rn = acquireN()
5465 /// primaryN = null
5466 /// try {
5467 /// body
5468 /// } catch (eN) {
5469 /// primaryN = eN
5470 /// throw eN
5471 /// } finally {
5472 /// if (primaryN != null) {
5473 /// try { resourceN.close(); }
5474 /// catch (closeExcN) { primaryN.addSuppressed(closeExcN); }
5475 /// } else {
5476 /// resourceN.close();
5477 /// }
5478 /// }
5479 /// ...
5480 /// } catch (e2) {
5481 /// primary2 = e2
5482 /// throw e2
5483 /// } finally {
5484 /// if (primary2 != null) {
5485 /// try { resource2.close(); }
5486 /// catch (closeExc2) { primary2.addSuppressed(closeExc2); }
5487 /// } else {
5488 /// resource2.close();
5489 /// }
5490 /// }
5491 /// } catch (e1) {
5492 /// primary1 = e1
5493 /// throw e1
5494 /// } finally {
5495 /// if (primary1 != null) {
5496 /// try { resource1.close(); }
5497 /// catch (closeExc1) { primary1.addSuppressed(closeExc1); }
5498 /// } else {
5499 /// resource1.close();
5500 /// }
5501 /// }
5502 /// } catch (...) {
5503 /// catches
5504 /// } finally {
5505 /// finalizer
5506 /// }
5507 /// ```
5508 ///
5509 /// @jls 14.20.3 try-with-resources
5510 /// @jls 14.20.3.1 Basic try-with-resources
5511 /// @jls 14.20.3.2 Extended try-with-resources
5512 Body normalizeTryWithResources() {
5513 return syntheticBody(entryBlock -> {
5514 Function<Block.Builder, TryOp> normalizedTry = block -> {
5515 block.context().mapValues(capturedValues(), entryBlock.parameters());
5516 return normalizeExtendedTryWithResources(block.parentBody(), block.context(), new ArrayList<>());
5517 };
5518 if (catchBodies.isEmpty() && finallyBody == null) {
5519 entryBlock.add(normalizedTry.apply(entryBlock));
5520 } else {
5521 CatchBuilder catchBuilder = try_(entryBlock.parentBody(), tryB -> {
5522 tryB.add(normalizedTry.apply(tryB));
5523 tryB.add(core_yield());
5524 });
5525 for (Body catcher : catchBodies) {
5526 catchBuilder.catch_(catcher.bodySignature().parameterTypes().getFirst(), catchB ->
5527 catchB.transformBody(catcher, catchB.parameters(), entryBlock.context(), CodeTransformer.COPYING_TRANSFORMER));
5528 }
5529 entryBlock.add(finallyBody == null
5530 ? catchBuilder.noFinalizer()
5531 : catchBuilder.finally_(finB ->
5532 finB.transformBody(finallyBody, List.of(), entryBlock.context(), CodeTransformer.COPYING_TRANSFORMER)));
5533 }
5534 entryBlock.add(core_yield());
5535 });
5536 }
5537
5538 /// Lower basic try-with-resources to `try / catch / finally`.
5539 ///
5540 /// Keeps the primary exception from the try body and adds as suppressed an exception from resource close.
5541 ///
5542 /// Use standalone synthetic body, so the lowered model is complete and can be further transformed.
5543 ///
5544 /// ```
5545 /// resource = acquire()
5546 /// primary = null
5547 /// try {
5548 /// body(resources)
5549 /// } catch (e) {
5550 /// primary = e
5551 /// throw t
5552 /// } finally {
5553 /// if (resource != null) {
5554 /// if (primary != null) {
5555 /// try { resource.close(); }
5556 /// catch (closeExc) { primary.addSuppressed(closeExc); }
5557 /// } else {
5558 /// resource.close();
5559 /// }
5560 /// }
5561 /// }
5562 /// ```
5563 ///
5564 /// @jls 14.20.3.1 Basic try-with-resources
5565 Body lowerBasicTryWithResources() {
5566 assert resourcesBodies.size() == 1;
5567 CodeType resourceType = resourcesBodies.getFirst().bodySignature().returnType();
5568 return syntheticBody(entryBlock -> {
5569 Block.Builder afterAcquire = entryBlock.block(resourceType);
5570 entryBlock.transformBody(resourcesBodies.getFirst(), List.of(), (block, op) -> {
5571 if (op instanceof CoreOp.YieldOp yop) {
5572 block.add(branch(afterAcquire.reference(block.context().getValue(yop.yieldValue()))));
5573 } else {
5574 block.add(op);
5575 }
5576 return block;
5577 });
5578 Value resource = afterAcquire.parameters().getFirst();
5579 Value primaryExceptionVar = afterAcquire.add(var(afterAcquire.add(constant(type(Throwable.class), null))));
5580 // @@@ following builder code may be refactored into a reflected template method transformation
5581 afterAcquire.add(try_(entryBlock.parentBody(), tryEntry -> {
5582 tryEntry.transformBody(body, List.of(resource), afterAcquire.context(), CodeTransformer.COPYING_TRANSFORMER);
5583 }).catch_(type(Throwable.class), catchB -> {
5584 Block.Parameter thrown = catchB.parameters().getFirst();
5585 catchB.add(varStore(primaryExceptionVar, thrown));
5586 catchB.add(throw_(thrown));
5587 }).finally_(finB -> {
5588 Value nullObj = finB.add(constant(J_L_OBJECT, null));
5589 finB.add(if_(finB.parentBody()).if_(predB -> {
5590 predB.add(core_yield(predB.add(neq(resource, nullObj))));
5591 }).then(closeB -> {
5592 Value primaryException = closeB.add(varLoad(primaryExceptionVar));
5593 closeB.add(if_(closeB.parentBody()).if_(predB -> {
5594 predB.add(core_yield(predB.add(neq(primaryException, nullObj))));
5595 }).then(suppB -> {
5596 suppB.add(try_(suppB.parentBody(), tryB -> {
5597 tryB.add(invoke(AUTO_CLOSEABLE_CLOSE_METHOD, resource));
5598 tryB.add(core_yield());
5599 }).catch_(type(Throwable.class), catchB -> {
5600 Block.Parameter closeException = catchB.parameters().getFirst();
5601 catchB.add(invoke(THROWABLE_ADD_SUPPRESSED_METHOD, primaryException, closeException));
5602 catchB.add(core_yield());
5603 }).noFinalizer());
5604 suppB.add(core_yield());
5605 }).else_(normB -> {
5606 normB.add(invoke(AUTO_CLOSEABLE_CLOSE_METHOD, resource));
5607 normB.add(core_yield());
5608 }));
5609 closeB.add(core_yield());
5610 }).else_());
5611 finB.add(core_yield());
5612 }));
5613 afterAcquire.add(core_yield());
5614 });
5615 }
5616
5617 /// Recursive step for extended try-with-resources.
5618 ///
5619 /// Resource `index` becomes the current outer basic try-with-resources.
5620 ///
5621 /// @jls 14.20.3.2 Extended try-with-resources
5622 TryOp normalizeExtendedTryWithResources(Body.Builder ancestorBody, CodeContext cc, List<Value> resourceValues) {
5623 Body resource = resourcesBodies.get(resourceValues.size());
5624 Body.Builder resourceBody = Body.Builder.of(ancestorBody, CoreType.functionType(resource.yieldType()), cc);
5625 resourceBody.entryBlock().transformBody(resource, resourceValues, cc, CodeTransformer.COPYING_TRANSFORMER);
5626 Body.Builder basicBody = Body.Builder.of(ancestorBody, CoreType.functionType(VOID, List.of(resource.yieldType())), cc);
5627 Block.Builder bodyB = basicBody.entryBlock();
5628 resourceValues.add(bodyB.parameters().getFirst());
5629 if (resourceValues.size() < resourcesBodies.size()) {
5630 bodyB.add(normalizeExtendedTryWithResources(basicBody, cc, resourceValues));
5631 bodyB.add(core_yield());
5632 } else {
5633 bodyB.transformBody(body, resourceValues, cc, CodeTransformer.COPYING_TRANSFORMER);
5634 }
5635 return try_(List.of(resourceBody), basicBody, List.of(), null);
5636 }
5637
5638 Body syntheticBody(Consumer<Block.Builder> c) {
5639 List<Value> captures = capturedValues();
5640 Body.Builder syntheticBody = Body.Builder.of(null, CoreType.functionType(VOID, captures.stream().map(Value::type).toList()));
5641 Block.Builder entryBlock = syntheticBody.entryBlock();
5642 entryBlock.context().mapValues(captures, entryBlock.parameters());
5643 c.accept(entryBlock);
5644 return syntheticBody.build(unreachable());
5645 }
5646
5647 boolean ifExitFromTry(StatementTargetOp lop) {
5648 Op target = lop.target();
5649 return target == this || target.isAncestorOf(this);
5650 }
5651
5652 Block.Builder inlineFinalizer(Block.Builder block1, Value enter, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
5653 Block.Builder finallyEnter = block1.block();
5654 Block.Builder finallyExit = block1.block();
5655
5656 block1.add(exceptionRegionExit(enter, finallyEnter.reference()));
5657
5658 // Inline the finally body
5659 finallyEnter.transformBody(finallyBody, List.of(), loweringTransformer(inherited, (block2, op2) -> {
5660 if (op2 instanceof CoreOp.YieldOp) {
5661 block2.add(branch(finallyExit.reference()));
5662 return block2;
5663 } else {
5664 return null;
5665 }
5666 }));
5667
5668 return finallyExit;
5669 }
5670
5671 @Override
5672 public CodeType resultType() {
5673 return VOID;
5674 }
5675 }
5676
5677 //
5678 // Patterns
5679
5680 // Reified pattern nodes
5681
5682 /**
5683 * Synthetic pattern types
5684 * // @@@ Replace with types extending from CodeType
5685 */
5686 public sealed interface Pattern {
5687
5688 /**
5689 * Synthetic type pattern type.
5690 *
5691 * @param <T> the type of values that are bound
5692 */
5693 final class Type<T> implements Pattern {
5694 Type() {
5695 }
5696 }
5697
5698 /**
5699 * Synthetic record pattern type.
5700 *
5701 * @param <T> the type of records that are bound
5702 */
5703 final class Record<T> implements Pattern {
5704 Record() {
5705 }
5706 }
5707
5708 /**
5709 * A synthetic match-all pattern type representing an unconditional pattern.
5710 */
5711 final class MatchAll implements Pattern {
5712 MatchAll() {
5713 }
5714 }
5715
5716 // @@@ Pattern types
5717
5718 /** The synthetic type of a type test pattern. */
5719 JavaType PATTERN_BINDING_TYPE = JavaType.type(Type.class);
5720
5721 /** The synthetic type of a record pattern. */
5722 JavaType PATTERN_RECORD_TYPE = JavaType.type(Record.class);
5723
5724 /** The synthetic type of an unconditional pattern. */
5725 JavaType PATTERN_MATCH_ALL_TYPE = JavaType.type(MatchAll.class);
5726
5727 /**
5728 * {@return a synthetic type for a type test pattern with the provided type}
5729 * @param t the type of the type test pattern
5730 */
5731 static JavaType bindingType(CodeType t) {
5732 return parameterized(PATTERN_BINDING_TYPE, (JavaType) t);
5733 }
5734
5735 /**
5736 * {@return a synthetic type for a record pattern with the provided record type}
5737 * @param t the record type
5738 */
5739 static JavaType recordType(CodeType t) {
5740 return parameterized(PATTERN_RECORD_TYPE, (JavaType) t);
5741 }
5742
5743 /**
5744 * {@return a synthetic type for an unconditional pattern}
5745 */
5746 static JavaType matchAllType() {
5747 return PATTERN_MATCH_ALL_TYPE;
5748 }
5749
5750 /**
5751 * {@return the type bound by a synthetic type test/record pattern}
5752 * @param t the synthetic pattern type
5753 */
5754 static CodeType targetType(CodeType t) {
5755 return ((ClassType) t).typeArguments().get(0);
5756 }
5757 }
5758
5759 /**
5760 * Pattern operations.
5761 *
5762 * @jls 14.30 Patterns
5763 */
5764 public static final class PatternOps {
5765 PatternOps() {
5766 }
5767
5768 /**
5769 * The pattern operation.
5770 * <p>
5771 * The result type of a pattern operation is a synthetic {@linkplain Pattern pattern type}.
5772 * Pattern operations are used in pattern bodies of {@link MatchOp} and as nested pattern operands of
5773 * {@link RecordPatternOp}.
5774 */
5775 public sealed static abstract class PatternOp extends JavaOp implements Op.Pure {
5776 PatternOp(PatternOp that, CodeContext cc) {
5777 super(that, cc);
5778 }
5779
5780 PatternOp(List<Value> operands) {
5781 super(operands);
5782 }
5783 }
5784
5785 /**
5786 * The type pattern operation, that can model Java language type test patterns.
5787 * <p>
5788 * Type pattern operations are associated with a target type (a {@link JavaType})
5789 * and an optional binding name.
5790 *
5791 * @jls 14.30.1 Kinds of Patterns
5792 * @jls 15.20.2 The instanceof Operator
5793 */
5794 @OpDeclaration(TypePatternOp.NAME)
5795 public static final class TypePatternOp extends PatternOp {
5796 static final String NAME = "pattern.type";
5797
5798 /**
5799 * The externalized attribute key for a pattern binding name in a type pattern operation.
5800 */
5801 static final String ATTRIBUTE_BINDING_NAME = NAME + ".binding.name";
5802
5803 final CodeType resultType;
5804 final String bindingName;
5805
5806 TypePatternOp(ExternalizedOp def) {
5807 super(List.of());
5808 this.bindingName = optionalAttribute(def, ATTRIBUTE_BINDING_NAME, true, String.class).orElse(null);
5809 // @@@ Cannot use canonical constructor because it wraps the given type
5810 this.resultType = def.resultType();
5811 }
5812
5813 TypePatternOp(TypePatternOp that, CodeContext cc) {
5814 super(that, cc);
5815
5816 this.bindingName = that.bindingName;
5817 this.resultType = that.resultType;
5818 }
5819
5820 @Override
5821 public TypePatternOp transform(CodeContext cc, CodeTransformer ct) {
5822 return new TypePatternOp(this, cc);
5823 }
5824
5825 TypePatternOp(CodeType targetType, String bindingName) {
5826 super(List.of());
5827
5828 this.bindingName = bindingName;
5829 this.resultType = Pattern.bindingType(targetType);
5830 }
5831
5832 @Override
5833 public Map<String, Object> externalize() {
5834 return bindingName == null ? Map.of() : Map.of("", bindingName);
5835 }
5836
5837 /**
5838 * {@return the variable name bound by this type test pattern, or {@code null} if none}
5839 */
5840 public String bindingName() {
5841 return bindingName;
5842 }
5843
5844 /**
5845 * {@return the type matched by this type test pattern}
5846 */
5847 public CodeType targetType() {
5848 return Pattern.targetType(resultType());
5849 }
5850
5851 @Override
5852 public CodeType resultType() {
5853 return resultType;
5854 }
5855 }
5856
5857 /**
5858 * The record pattern operation, that can model Java language record patterns.
5859 * <p>
5860 * Record pattern operations are associated with a {@linkplain RecordTypeRef record reference}.
5861 * The operands are nested pattern values.
5862 *
5863 * @jls 14.30.1 Kinds of Patterns
5864 */
5865 @OpDeclaration(RecordPatternOp.NAME)
5866 public static final class RecordPatternOp extends PatternOp {
5867 static final String NAME = "pattern.record";
5868
5869 /**
5870 * The externalized attribute key for a record reference in a record pattern operation.
5871 */
5872 static final String ATTRIBUTE_RECORD_REF = NAME + ".ref";
5873
5874 final RecordTypeRef recordReference;
5875
5876 RecordPatternOp(ExternalizedOp def) {
5877 this(requireAttribute(def, ATTRIBUTE_RECORD_REF, true, RecordTypeRef.class), def.operands());
5878 }
5879
5880 RecordPatternOp(RecordPatternOp that, CodeContext cc) {
5881 super(that, cc);
5882
5883 this.recordReference = that.recordReference;
5884 }
5885
5886 @Override
5887 public RecordPatternOp transform(CodeContext cc, CodeTransformer ct) {
5888 return new RecordPatternOp(this, cc);
5889 }
5890
5891 RecordPatternOp(RecordTypeRef recordReference, List<Value> nestedPatterns) {
5892 // The type of each value is a subtype of Pattern
5893 // The number of values corresponds to the number of components of the record
5894 if (recordReference.components().size() != nestedPatterns.size()) {
5895 throw structuralException(NAME, "requires %d nested pattern operands, found %d".formatted(recordReference.components().size(), nestedPatterns.size()));
5896 }
5897 super(List.copyOf(nestedPatterns));
5898
5899 this.recordReference = recordReference;
5900 }
5901
5902 @Override
5903 public Map<String, Object> externalize() {
5904 return Map.of("", recordReference());
5905 }
5906
5907 /**
5908 * {@return the record reference associated with this record pattern}
5909 */
5910 public RecordTypeRef recordReference() {
5911 return recordReference;
5912 }
5913
5914 /**
5915 * {@return the type matched by this record pattern}
5916 */
5917 public CodeType targetType() {
5918 return Pattern.targetType(resultType());
5919 }
5920
5921 @Override
5922 public CodeType resultType() {
5923 return Pattern.recordType(recordReference.recordType());
5924 }
5925 }
5926
5927 /**
5928 * A pattern operation representing a match-all (unconditional) pattern.
5929 *
5930 * @jls 14.30.1 Kinds of Patterns
5931 */
5932 @OpDeclaration(MatchAllPatternOp.NAME)
5933 public static final class MatchAllPatternOp extends PatternOp {
5934
5935 // @@@ we may need to add info about the type of the record component
5936 // this info can be used when lowering
5937
5938 static final String NAME = "pattern.match.all";
5939
5940 MatchAllPatternOp(ExternalizedOp def) {
5941 this();
5942 }
5943
5944 MatchAllPatternOp(MatchAllPatternOp that, CodeContext cc) {
5945 super(that, cc);
5946 }
5947
5948 MatchAllPatternOp() {
5949 super(List.of());
5950 }
5951
5952 @Override
5953 public Op transform(CodeContext cc, CodeTransformer ct) {
5954 return new MatchAllPatternOp(this, cc);
5955 }
5956
5957 @Override
5958 public CodeType resultType() {
5959 return Pattern.matchAllType();
5960 }
5961 }
5962
5963 /**
5964 * The match operation, that can model Java language pattern matching.
5965 * <p>
5966 * Match operations can be used to model instanceof expressions with a pattern match operator, or
5967 * case labels with case patterns in switch statements and switch expressions.
5968 * <p>
5969 * Match operations feature one operand, the target value being matched, and two bodies: the pattern body and
5970 * the match body.
5971 * <p>
5972 * The pattern body should accept no arguments and yield a pattern value.
5973 * The match body accepts the values bound by the pattern body and yields {@linkplain JavaType#VOID no value}.
5974 * The result type of a match operation is {@link JavaType#BOOLEAN}.
5975 *
5976 * @jls 14.30.2 Pattern Matching
5977 * @jls 14.11 The switch Statement
5978 * @jls 15.28 switch Expressions
5979 * @jls 15.20.2 The instanceof Operator
5980 */
5981 @OpDeclaration(MatchOp.NAME)
5982 public static final class MatchOp extends JavaOp implements Op.Isolated, Op.Lowerable {
5983 static final String NAME = "pattern.match";
5984
5985 final Body patternBody;
5986 final Body matchBody;
5987
5988 MatchOp(ExternalizedOp def) {
5989 List<Body.Builder> bodies = requireBodies(def, 2);
5990 this(requireSingleOperand(def), bodies.get(0), bodies.get(1));
5991 }
5992
5993 MatchOp(MatchOp that, CodeContext cc, CodeTransformer ct) {
5994 super(that, cc);
5995
5996 this.patternBody = that.patternBody.transform(cc, ct).build(this);
5997 this.matchBody = that.matchBody.transform(cc, ct).build(this);
5998 }
5999
6000 @Override
6001 public MatchOp transform(CodeContext cc, CodeTransformer ct) {
6002 return new MatchOp(this, cc, ct);
6003 }
6004
6005 MatchOp(Value target, Body.Builder patternC, Body.Builder matchC) {
6006 super(List.of(target));
6007
6008 this.patternBody = requireNoParameters(NAME + " pattern", patternC).build(this);
6009 this.matchBody = matchC.build(this);
6010 }
6011
6012 @Override
6013 public List<Body> bodies() {
6014 return List.of(patternBody, matchBody);
6015 }
6016
6017 /**
6018 * Returns the pattern body for this match operation.
6019 *
6020 * @return the pattern body
6021 */
6022 public Body patternBody() {
6023 return patternBody;
6024 }
6025
6026 /**
6027 * Returns the match body for this match operation.
6028 *
6029 * @return the match body
6030 */
6031 public Body matchBody() {
6032 return matchBody;
6033 }
6034
6035 /**
6036 * Returns the target value being matched in this match operation.
6037 *
6038 * @return the match target value
6039 */
6040 public Value targetOperand() {
6041 return operands().get(0);
6042 }
6043
6044 @Override
6045 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
6046 // No match block
6047 Block.Builder endNoMatchBlock = b.block();
6048 // Match block
6049 Block.Builder endMatchBlock = b.block();
6050 // End block
6051 Block.Builder endBlock = b.block();
6052 Block.Parameter matchResult = endBlock.parameter(resultType());
6053 // Map match operation result
6054 b.context().mapValue(result(), matchResult);
6055
6056 List<Value> patternValues = new ArrayList<>();
6057 Op patternYieldOp = patternBody.entryBlock().terminatingOp();
6058 Op.Result rootPatternValue = (Op.Result) patternYieldOp.operands().get(0);
6059 Block.Builder currentBlock = lower(endNoMatchBlock, b,
6060 patternValues,
6061 rootPatternValue.op(),
6062 b.context().getValue(targetOperand()));
6063 currentBlock.add(branch(endMatchBlock.reference()));
6064
6065 // No match block
6066 // Pass false
6067 endNoMatchBlock.add(branch(endBlock.reference(
6068 endNoMatchBlock.add(constant(BOOLEAN, false)))));
6069
6070 // Match block
6071 // Lower match body and pass true
6072 endMatchBlock.transformBody(matchBody, patternValues, loweringTransformer(inherited, (block, op) -> {
6073 if (op instanceof CoreOp.YieldOp) {
6074 block.add(branch(endBlock.reference(
6075 block.add(constant(BOOLEAN, true)))));
6076 return block;
6077 } else {
6078 return null;
6079 }
6080 }));
6081
6082 return endBlock;
6083 }
6084
6085 static Block.Builder lower(Block.Builder endNoMatchBlock, Block.Builder currentBlock,
6086 List<Value> bindings,
6087 Op pattern, Value target) {
6088 return switch (pattern) {
6089 case RecordPatternOp rp -> lowerRecordPattern(endNoMatchBlock, currentBlock, bindings, rp, target);
6090 case TypePatternOp tp -> lowerTypePattern(endNoMatchBlock, currentBlock, bindings, tp, target);
6091 case MatchAllPatternOp map -> lowerMatchAllPattern(currentBlock);
6092 case null, default -> throw new UnsupportedOperationException("Unknown pattern op: " + pattern);
6093 };
6094 }
6095
6096 static Block.Builder lowerRecordPattern(Block.Builder endNoMatchBlock, Block.Builder currentBlock,
6097 List<Value> bindings,
6098 JavaOp.PatternOps.RecordPatternOp rpOp, Value target) {
6099 CodeType targetType = rpOp.targetType();
6100
6101 Block.Builder nextBlock = currentBlock.block();
6102
6103 // Check if instance of target type
6104 Op.Result isInstance = currentBlock.add(instanceOf(targetType, target));
6105 currentBlock.add(conditionalBranch(isInstance, nextBlock.reference(), endNoMatchBlock.reference()));
6106
6107 currentBlock = nextBlock;
6108
6109 target = currentBlock.add(cast(targetType, target));
6110
6111 // Access component values of record and match on each as nested target
6112 List<Value> dArgs = rpOp.operands();
6113 for (int i = 0; i < dArgs.size(); i++) {
6114 Op.Result nestedPattern = (Op.Result) dArgs.get(i);
6115 // @@@ Handle exceptions?
6116 Value nestedTarget = currentBlock.add(invoke(rpOp.recordReference().methodForComponent(i), target));
6117
6118 currentBlock = lower(endNoMatchBlock, currentBlock, bindings, nestedPattern.op(), nestedTarget);
6119 }
6120
6121 return currentBlock;
6122 }
6123
6124 static Block.Builder lowerTypePattern(Block.Builder endNoMatchBlock, Block.Builder currentBlock,
6125 List<Value> bindings,
6126 TypePatternOp tpOp, Value target) {
6127 CodeType targetType = tpOp.targetType();
6128
6129 // Check if instance of target type
6130 Op p; // op that perform type check
6131 Op c; // op that perform conversion
6132 CodeType s = target.type();
6133 CodeType t = targetType;
6134 if (t instanceof PrimitiveType pt) {
6135 if (s instanceof ClassType cs) {
6136 // unboxing conversions
6137 ClassType box;
6138 if (cs.unbox().isEmpty()) { // s not a boxed type
6139 // e.g. Number -> int, narrowing + unboxing
6140 box = pt.box().orElseThrow();
6141 p = instanceOf(box, target);
6142 } else {
6143 // e.g. Float -> float, unboxing
6144 // e.g. Integer -> long, unboxing + widening
6145 box = cs;
6146 p = null;
6147 }
6148 c = invoke(MethodRef.method(box, t + "Value", t), target);
6149 } else {
6150 // primitive to primitive conversion
6151 PrimitiveType ps = ((PrimitiveType) s);
6152 if (isNarrowingPrimitiveConv(ps, pt) || isWideningPrimitiveConvWithCheck(ps, pt)
6153 || isWideningAndNarrowingPrimitiveConv(ps, pt)) {
6154 // e.g. int -> byte, narrowing
6155 // e,g. int -> float, widening with check
6156 // e.g. byte -> char, widening and narrowing
6157 MethodRef mref = convMethodRef(s, t);
6158 p = invoke(mref, target);
6159 } else {
6160 p = null;
6161 }
6162 c = conv(targetType, target);
6163 }
6164 } else if (s instanceof PrimitiveType ps) {
6165 // boxing conversions
6166 // e.g. int -> Number, boxing + widening
6167 // e.g. byte -> Byte, boxing
6168 p = null;
6169 ClassType box = ps.box().orElseThrow();
6170 c = invoke(MethodRef.method(box, "valueOf", box, ps), target);
6171 } else if (!s.equals(t)) {
6172 // reference to reference, but not identity
6173 // e.g. Number -> Double, narrowing
6174 // e.g. Short -> Object, widening
6175 p = instanceOf(targetType, target);
6176 c = cast(targetType, target);
6177 } else {
6178 // identity reference
6179 // e.g. Character -> Character
6180 p = null;
6181 c = null;
6182 }
6183
6184 if (c != null) {
6185 if (p != null) {
6186 // p != null, we need to perform type check at runtime
6187 Block.Builder nextBlock = currentBlock.block();
6188 currentBlock.add(conditionalBranch(currentBlock.add(p), nextBlock.reference(), endNoMatchBlock.reference()));
6189 currentBlock = nextBlock;
6190 }
6191 target = currentBlock.add(c);
6192 }
6193
6194 bindings.add(target);
6195
6196 return currentBlock;
6197 }
6198
6199 private static boolean isWideningAndNarrowingPrimitiveConv(PrimitiveType s, PrimitiveType t) {
6200 return BYTE.equals(s) && CHAR.equals(t);
6201 }
6202
6203 private static boolean isWideningPrimitiveConvWithCheck(PrimitiveType s, PrimitiveType t) {
6204 return (INT.equals(s) && FLOAT.equals(t))
6205 || (LONG.equals(s) && FLOAT.equals(t))
6206 || (LONG.equals(s) && DOUBLE.equals(t));
6207 }
6208
6209 // s -> t is narrowing if order(t) <= order(s)
6210 private final static Map<PrimitiveType, Integer> narrowingOrder = Map.of(
6211 BYTE, 1,
6212 SHORT, 2,
6213 CHAR, 2,
6214 INT, 3,
6215 LONG, 4,
6216 FLOAT, 5,
6217 DOUBLE, 6
6218 );
6219 private static boolean isNarrowingPrimitiveConv(PrimitiveType s, PrimitiveType t) {
6220 return narrowingOrder.get(t) <= narrowingOrder.get(s) && !s.equals(t); // need to be strict, to not consider int -> int as narrowing
6221 }
6222
6223 private static MethodRef convMethodRef(CodeType s, CodeType t) {
6224 if (BYTE.equals(s) || SHORT.equals(s) || CHAR.equals(s)) {
6225 s = INT;
6226 }
6227 String sn = capitalize(s.toString());
6228 String tn = capitalize(t.toString());
6229 String mn = "is%sTo%sExact".formatted(sn, tn);
6230 JavaType exactConversionSupport = JavaType.type(ClassDesc.of("java.lang.runtime.ExactConversionsSupport"));
6231 return MethodRef.method(exactConversionSupport, mn, BOOLEAN, s);
6232 }
6233
6234 private static String capitalize(String s) {
6235 return s.substring(0, 1).toUpperCase() + s.substring(1);
6236 }
6237
6238 static Block.Builder lowerMatchAllPattern(Block.Builder currentBlock) {
6239 return currentBlock;
6240 }
6241
6242 @Override
6243 public CodeType resultType() {
6244 return BOOLEAN;
6245 }
6246 }
6247 }
6248
6249 /**
6250 * Returns a composed function that composes {@code g} into the first argument of {@code f}.
6251 * <p>
6252 * if {@code f} is {@code null} then this method returns {@code g}.
6253 *
6254 * @param f the outer function
6255 * @param g the inner function
6256 * @return the composed
6257 */
6258 static <T, U> BiFunction<T, U, T> composeFirst(
6259 BiFunction<T, U, T> f,
6260 BiFunction<T, U, T> g) {
6261 Objects.requireNonNull(g);
6262 return f == null
6263 ? g
6264 : (builder, op) -> f.apply(g.apply(builder, op), op);
6265 }
6266
6267 static Op createOp(ExternalizedOp def) {
6268 Op op = switch (def.name()) {
6269 case "add" -> new AddOp(def);
6270 case "and" -> new AndOp(def);
6271 case "array.length" -> new ArrayLengthOp(def);
6272 case "array.load" -> new ArrayAccessOp.ArrayLoadOp(def);
6273 case "array.store" -> new ArrayAccessOp.ArrayStoreOp(def);
6274 case "ashr" -> new AshrOp(def);
6275 case "assert" -> new AssertOp(def);
6276 case "cast" -> new CastOp(def);
6277 case "compl" -> new ComplOp(def);
6278 case "concat" -> new ConcatOp(def);
6279 case "conv" -> new ConvOp(def);
6280 case "div" -> new DivOp(def);
6281 case "eq" -> new EqOp(def);
6282 case "exception.region.enter" -> new ExceptionRegionEnter(def);
6283 case "exception.region.exit" -> new ExceptionRegionExit(def);
6284 case "field.load" -> new FieldAccessOp.FieldLoadOp(def);
6285 case "field.store" -> new FieldAccessOp.FieldStoreOp(def);
6286 case "ge" -> new GeOp(def);
6287 case "gt" -> new GtOp(def);
6288 case "instanceof" -> new InstanceOfOp(def);
6289 case "invoke" -> new InvokeOp(def);
6290 case "java.block" -> new BlockOp(def);
6291 case "java.break" -> new BreakOp(def);
6292 case "java.cand" -> new ConditionalAndOp(def);
6293 case "java.cexpression" -> new ConditionalExpressionOp(def);
6294 case "java.continue" -> new ContinueOp(def);
6295 case "java.cor" -> new ConditionalOrOp(def);
6296 case "java.do.while" -> new DoWhileOp(def);
6297 case "java.enhancedFor" -> new EnhancedForOp(def);
6298 case "java.for" -> new ForOp(def);
6299 case "java.if" -> new IfOp(def);
6300 case "java.labeled" -> new LabeledOp(def);
6301 case "java.switch.expression" -> new SwitchExpressionOp(def);
6302 case "java.switch.fallthrough" -> new SwitchFallthroughOp(def);
6303 case "java.switch.statement" -> new SwitchStatementOp(def);
6304 case "java.synchronized" -> new SynchronizedOp(def);
6305 case "java.try" -> new TryOp(def);
6306 case "java.while" -> new WhileOp(def);
6307 case "java.yield" -> new YieldOp(def);
6308 case "lambda" -> new LambdaOp(def);
6309 case "le" -> new LeOp(def);
6310 case "lshl" -> new LshlOp(def);
6311 case "lshr" -> new LshrOp(def);
6312 case "lt" -> new LtOp(def);
6313 case "mod" -> new ModOp(def);
6314 case "monitor.enter" -> new MonitorOp.MonitorEnterOp(def);
6315 case "monitor.exit" -> new MonitorOp.MonitorExitOp(def);
6316 case "mul" -> new MulOp(def);
6317 case "neg" -> new NegOp(def);
6318 case "neq" -> new NeqOp(def);
6319 case "new" -> new NewOp(def);
6320 case "not" -> new NotOp(def);
6321 case "or" -> new OrOp(def);
6322 case "pattern.match" -> new PatternOps.MatchOp(def);
6323 case "pattern.match.all" -> new PatternOps.MatchAllPatternOp(def);
6324 case "pattern.record" -> new PatternOps.RecordPatternOp(def);
6325 case "pattern.type" -> new PatternOps.TypePatternOp(def);
6326 case "sub" -> new SubOp(def);
6327 case "throw" -> new ThrowOp(def);
6328 case "xor" -> new XorOp(def);
6329 default -> null;
6330 };
6331 if (op != null) {
6332 op.setLocation(def.location());
6333 }
6334 return op;
6335 }
6336
6337 /**
6338 * An operation factory for core operations composed with Java operations.
6339 */
6340 public static final OpFactory JAVA_OP_FACTORY = CoreOp.CORE_OP_FACTORY.andThen(JavaOp::createOp);
6341
6342 /**
6343 * A Java dialect factory, for constructing core and Java operations and constructing
6344 * core types and Java types, where the core types can refer to Java
6345 * types.
6346 */
6347 public static final DialectFactory JAVA_DIALECT_FACTORY = new DialectFactory(
6348 JAVA_OP_FACTORY,
6349 JAVA_TYPE_FACTORY);
6350
6351 /**
6352 * Creates a lambda operation.
6353 *
6354 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
6355 * connected, or {@code null} if they are isolated
6356 * @param signature the lambda operation's signature, represented as a function type
6357 * @param functionalInterface the lambda operation's functional interface type
6358 * @return the lambda operation
6359 */
6360 public static LambdaOp.Builder lambda(Body.Builder connectedAncestorBody,
6361 FunctionType signature, CodeType functionalInterface) {
6362 return new LambdaOp.Builder(connectedAncestorBody, signature, functionalInterface);
6363 }
6364
6365 /**
6366 * Creates a lambda operation.
6367 *
6368 * @param functionalInterface the lambda operation's functional interface type
6369 * @param body the body of the lambda operation
6370 * @return the lambda operation
6371 */
6372 public static LambdaOp lambda(CodeType functionalInterface, Body.Builder body) {
6373 return new LambdaOp(functionalInterface, body, false);
6374 }
6375
6376 /**
6377 * Creates a lambda operation.
6378 *
6379 * @param functionalInterface the lambda operation's functional interface type
6380 * @param body the body of the lambda operation
6381 * @param isReflectable true if the lambda is reflectable
6382 * @return the lambda operation
6383 */
6384 public static LambdaOp lambda(CodeType functionalInterface, Body.Builder body, boolean isReflectable) {
6385 return new LambdaOp(functionalInterface, body, isReflectable);
6386 }
6387
6388 /**
6389 * Creates an exception region enter operation
6390 *
6391 * @param start the reference to the block that enters the exception region
6392 * @param catchers the references to blocks handling exceptions thrown by blocks within the exception region
6393 * @return the exception region enter operation
6394 */
6395 public static ExceptionRegionEnter exceptionRegionEnter(Block.Reference start, Block.Reference... catchers) {
6396 return exceptionRegionEnter(start, List.of(catchers));
6397 }
6398
6399 /**
6400 * Creates an exception region enter operation
6401 *
6402 * @param start the reference to the block that enters the exception region
6403 * @param catchers the references to blocks handling exceptions thrown by blocks within the exception region
6404 * @return the exception region enter operation
6405 */
6406 public static ExceptionRegionEnter exceptionRegionEnter(Block.Reference start, List<Block.Reference> catchers) {
6407 List<Block.Reference> s = new ArrayList<>();
6408 s.add(start);
6409 s.addAll(catchers);
6410 return new ExceptionRegionEnter(s);
6411 }
6412
6413 /**
6414 * Creates an exception region exit operation
6415 *
6416 * @param enter the result of the dominant {@link ExceptionRegionEnter}
6417 * @param end the reference to the block reached after exiting the exception region
6418 * @return the exception region exit operation
6419 */
6420 public static ExceptionRegionExit exceptionRegionExit(Value enter, Block.Reference end) {
6421 return new ExceptionRegionExit(enter, end);
6422 }
6423
6424 /**
6425 * Creates a throw operation.
6426 *
6427 * @param exceptionValue the thrown value
6428 * @return the throw operation
6429 */
6430 public static ThrowOp throw_(Value exceptionValue) {
6431 return new ThrowOp(exceptionValue);
6432 }
6433
6434 /**
6435 * Creates an assert operation.
6436 *
6437 * @param bodies the nested bodies
6438 * @return the assert operation
6439 */
6440 public static AssertOp assert_(List<Body.Builder> bodies) {
6441 return new AssertOp(bodies);
6442 }
6443
6444 /**
6445 * Creates a monitor enter operation.
6446 * @param monitor the monitor value
6447 * @return the monitor enter operation
6448 */
6449 public static MonitorOp.MonitorEnterOp monitorEnter(Value monitor) {
6450 return new MonitorOp.MonitorEnterOp(monitor);
6451 }
6452
6453 /**
6454 * Creates a monitor exit operation.
6455 * @param monitor the monitor value
6456 * @return the monitor exit operation
6457 */
6458 public static MonitorOp.MonitorExitOp monitorExit(Value monitor) {
6459 return new MonitorOp.MonitorExitOp(monitor);
6460 }
6461
6462 /**
6463 * Creates an invoke operation modeling an invocation to an
6464 * instance or static (class) method with no variable arguments.
6465 * <p>
6466 * The invoke kind of the invoke operation is determined by
6467 * comparing the argument count with the method reference's
6468 * parameter count. If they are equal then the invoke kind is
6469 * {@link InvokeOp.InvokeKind#STATIC static}. If the parameter count
6470 * plus one is equal to the argument count then the invoke kind
6471 * is {@link InvokeOp.InvokeKind#INSTANCE instance}.
6472 * <p>
6473 * The result type of the invoke operation is the method reference's return type.
6474 *
6475 * @param invokeRef the method reference
6476 * @param args the invoke arguments
6477 * @return the invoke operation
6478 */
6479 public static InvokeOp invoke(MethodRef invokeRef, Value... args) {
6480 return invoke(invokeRef, List.of(args));
6481 }
6482
6483 /**
6484 * Creates an invoke operation modeling an invocation to an
6485 * instance or static (class) method with no variable arguments.
6486 * <p>
6487 * The invoke kind of the invoke operation is determined by
6488 * comparing the argument count with the method reference's
6489 * parameter count. If they are equal then the invoke kind is
6490 * {@link InvokeOp.InvokeKind#STATIC static}. If the parameter count
6491 * plus one is equal to the argument count then the invoke kind
6492 * is {@link InvokeOp.InvokeKind#INSTANCE instance}.
6493 * <p>
6494 * The result type of the invoke operation is the method reference's return type.
6495 *
6496 * @param invokeRef the method reference
6497 * @param args the invoke arguments
6498 * @return the invoke operation
6499 */
6500 public static InvokeOp invoke(MethodRef invokeRef, List<Value> args) {
6501 return invoke(invokeRef.signature().returnType(), invokeRef, args);
6502 }
6503
6504 /**
6505 * Creates an invoke operation modeling an invocation to an
6506 * instance or static (class) method with no variable arguments.
6507 * <p>
6508 * The invoke kind of the invoke operation is determined by
6509 * comparing the argument count with the method reference's
6510 * parameter count. If they are equal then the invoke kind is
6511 * {@link InvokeOp.InvokeKind#STATIC static}. If the parameter count
6512 * plus one is equal to the argument count then the invoke kind
6513 * is {@link InvokeOp.InvokeKind#INSTANCE instance}.
6514 *
6515 * @param returnType the result type of the invoke operation
6516 * @param invokeRef the method reference
6517 * @param args the invoke arguments
6518 * @return the invoke operation
6519 */
6520 public static InvokeOp invoke(CodeType returnType, MethodRef invokeRef, Value... args) {
6521 return invoke(returnType, invokeRef, List.of(args));
6522 }
6523
6524 /**
6525 * Creates an invoke operation modeling an invocation to an
6526 * instance or static (class) method with no variable arguments.
6527 * <p>
6528 * The invoke kind of the invoke operation is determined by
6529 * comparing the argument count with the method reference's
6530 * parameter count. If they are equal then the invoke kind is
6531 * {@link InvokeOp.InvokeKind#STATIC static}. If the parameter count
6532 * plus one is equal to the argument count then the invoke kind
6533 * is {@link InvokeOp.InvokeKind#INSTANCE instance}.
6534 *
6535 * @param returnType the result type of the invoke operation
6536 * @param invokeRef the method reference
6537 * @param args the invoke arguments
6538 * @return the invoke super operation
6539 */
6540 public static InvokeOp invoke(CodeType returnType, MethodRef invokeRef, List<Value> args) {
6541 int paramCount = invokeRef.signature().parameterTypes().size();
6542 int argCount = args.size();
6543 InvokeOp.InvokeKind ik = (argCount == paramCount + 1)
6544 ? InvokeOp.InvokeKind.INSTANCE
6545 : InvokeOp.InvokeKind.STATIC;
6546 return new InvokeOp(ik, false, returnType, invokeRef, args);
6547 }
6548
6549 /**
6550 * Creates an invoke operation modeling an invocation to a method.
6551 *
6552 * @param invokeKind the invoke kind
6553 * @param isVarArgs true if an invocation to a variable argument method
6554 * @param returnType the result type of the invoke operation
6555 * @param invokeRef the method reference
6556 * @param args the invoke arguments
6557 * @return the invoke operation
6558 * @throws IllegalArgumentException if there is a mismatch between the argument count
6559 * and the method reference's parameter count.
6560 */
6561 public static InvokeOp invoke(InvokeOp.InvokeKind invokeKind, boolean isVarArgs,
6562 CodeType returnType, MethodRef invokeRef, Value... args) {
6563 return new InvokeOp(invokeKind, isVarArgs, returnType, invokeRef, List.of(args));
6564 }
6565
6566 /**
6567 * Creates an invoke operation modeling an invocation to a method.
6568 *
6569 * @param invokeKind the invoke kind
6570 * @param isVarArgs true if an invocation to a variable argument method
6571 * @param returnType the result type of the invoke operation
6572 * @param invokeRef the method reference
6573 * @param args the invoke arguments
6574 * @return the invoke operation
6575 * @throws IllegalArgumentException if there is a mismatch between the argument count
6576 * and the method reference's parameter count.
6577 */
6578 public static InvokeOp invoke(InvokeOp.InvokeKind invokeKind, boolean isVarArgs,
6579 CodeType returnType, MethodRef invokeRef, List<Value> args) {
6580 return new InvokeOp(invokeKind, isVarArgs, returnType, invokeRef, args);
6581 }
6582
6583 /**
6584 * Creates a conversion operation.
6585 *
6586 * @param to the conversion target type
6587 * @param from the value to be converted
6588 * @return the conversion operation
6589 */
6590 public static ConvOp conv(CodeType to, Value from) {
6591 return new ConvOp(to, from);
6592 }
6593
6594 /**
6595 * Creates an instance creation operation.
6596 *
6597 * @param constructorRef the constructor reference
6598 * @param args the constructor arguments
6599 * @return the instance creation operation
6600 */
6601 public static NewOp new_(MethodRef constructorRef, Value... args) {
6602 return new_(constructorRef, List.of(args));
6603 }
6604
6605 /**
6606 * Creates an instance creation operation.
6607 *
6608 * @param constructorRef the constructor reference
6609 * @param args the constructor arguments
6610 * @return the instance creation operation
6611 */
6612 public static NewOp new_(MethodRef constructorRef, List<Value> args) {
6613 return new NewOp(false, constructorRef.refType(), constructorRef, args);
6614 }
6615
6616 /**
6617 * Creates an instance creation operation.
6618 *
6619 * @param returnType the result type of the instance creation operation
6620 * @param constructorRef the constructor reference
6621 * @param args the constructor arguments
6622 * @return the instance creation operation
6623 */
6624 public static NewOp new_(CodeType returnType, MethodRef constructorRef,
6625 Value... args) {
6626 return new_(returnType, constructorRef, List.of(args));
6627 }
6628
6629 /**
6630 * Creates an instance creation operation.
6631 *
6632 * @param returnType the result type of the instance creation operation
6633 * @param constructorRef the constructor reference
6634 * @param args the constructor arguments
6635 * @return the instance creation operation
6636 */
6637 public static NewOp new_(CodeType returnType, MethodRef constructorRef,
6638 List<Value> args) {
6639 return new NewOp(false, returnType, constructorRef, args);
6640 }
6641
6642 /**
6643 * Creates an instance creation operation.
6644 *
6645 * @param isVarargs {@code true} if calling a varargs constructor
6646 * @param returnType the result type of the instance creation operation
6647 * @param constructorRef the constructor reference
6648 * @param args the constructor arguments
6649 * @return the instance creation operation
6650 */
6651 public static NewOp new_(boolean isVarargs, CodeType returnType, MethodRef constructorRef,
6652 List<Value> args) {
6653 return new NewOp(isVarargs, returnType, constructorRef, args);
6654 }
6655
6656 /**
6657 * Creates an array creation operation.
6658 *
6659 * @param arrayType the array type
6660 * @param length the array size
6661 * @return the array creation operation
6662 */
6663 public static NewOp newArray(CodeType arrayType, Value length) {
6664 MethodRef constructorRef = MethodRef.constructor(arrayType, INT);
6665 return new_(constructorRef, length);
6666 }
6667
6668 /**
6669 * Creates a field load operation to a non-static field.
6670 *
6671 * @param fieldRef the field reference
6672 * @param receiver the receiver value
6673 * @return the field load operation
6674 */
6675 public static FieldAccessOp.FieldLoadOp fieldLoad(FieldRef fieldRef, Value receiver) {
6676 return new FieldAccessOp.FieldLoadOp(fieldRef.type(), fieldRef, receiver);
6677 }
6678
6679 /**
6680 * Creates a field load operation to a non-static field.
6681 *
6682 * @param resultType the result type of the operation
6683 * @param fieldRef the field reference
6684 * @param receiver the receiver value
6685 * @return the field load operation
6686 */
6687 public static FieldAccessOp.FieldLoadOp fieldLoad(CodeType resultType, FieldRef fieldRef, Value receiver) {
6688 return new FieldAccessOp.FieldLoadOp(resultType, fieldRef, receiver);
6689 }
6690
6691 /**
6692 * Creates a field load operation to a static field.
6693 *
6694 * @param fieldRef the field reference
6695 * @return the field load operation
6696 */
6697 public static FieldAccessOp.FieldLoadOp fieldLoad(FieldRef fieldRef) {
6698 return new FieldAccessOp.FieldLoadOp(fieldRef.type(), fieldRef);
6699 }
6700
6701 /**
6702 * Creates a field load operation to a static field.
6703 *
6704 * @param resultType the result type of the operation
6705 * @param fieldRef the field reference
6706 * @return the field load operation
6707 */
6708 public static FieldAccessOp.FieldLoadOp fieldLoad(CodeType resultType, FieldRef fieldRef) {
6709 return new FieldAccessOp.FieldLoadOp(resultType, fieldRef);
6710 }
6711
6712 /**
6713 * Creates a field store operation to a non-static field.
6714 *
6715 * @param fieldRef the field reference
6716 * @param receiver the receiver value
6717 * @param v the value to store
6718 * @return the field store operation
6719 */
6720 public static FieldAccessOp.FieldStoreOp fieldStore(FieldRef fieldRef, Value receiver, Value v) {
6721 return new FieldAccessOp.FieldStoreOp(fieldRef, receiver, v);
6722 }
6723
6724 /**
6725 * Creates a field load operation to a static field.
6726 *
6727 * @param fieldRef the field reference
6728 * @param v the value to store
6729 * @return the field store operation
6730 */
6731 public static FieldAccessOp.FieldStoreOp fieldStore(FieldRef fieldRef, Value v) {
6732 return new FieldAccessOp.FieldStoreOp(fieldRef, v);
6733 }
6734
6735 /**
6736 * Creates an array length operation.
6737 *
6738 * @param array the array value
6739 * @return the array length operation
6740 */
6741 public static ArrayLengthOp arrayLength(Value array) {
6742 return new ArrayLengthOp(array);
6743 }
6744
6745 /**
6746 * Creates an array load operation.
6747 *
6748 * @param array the array value
6749 * @param index the index value
6750 * @return the array load operation
6751 */
6752 public static ArrayAccessOp.ArrayLoadOp arrayLoadOp(Value array, Value index) {
6753 return new ArrayAccessOp.ArrayLoadOp(array, index);
6754 }
6755
6756 /**
6757 * Creates an array load operation.
6758 *
6759 * @param array the array value
6760 * @param index the index value
6761 * @param componentType the type of the array component
6762 * @return the array load operation
6763 */
6764 public static ArrayAccessOp.ArrayLoadOp arrayLoadOp(Value array, Value index, CodeType componentType) {
6765 return new ArrayAccessOp.ArrayLoadOp(array, index, componentType);
6766 }
6767
6768 /**
6769 * Creates an array store operation.
6770 *
6771 * @param array the array value
6772 * @param index the index value
6773 * @param v the value to store
6774 * @return the array store operation
6775 */
6776 public static ArrayAccessOp.ArrayStoreOp arrayStoreOp(Value array, Value index, Value v) {
6777 return new ArrayAccessOp.ArrayStoreOp(array, index, v);
6778 }
6779
6780 /**
6781 * Creates an instanceof operation.
6782 *
6783 * @param t the type to test against
6784 * @param v the value to test
6785 * @return the instanceof operation
6786 */
6787 public static InstanceOfOp instanceOf(CodeType t, Value v) {
6788 return new InstanceOfOp(t, v);
6789 }
6790
6791 /**
6792 * Creates a cast operation.
6793 *
6794 * @param resultType the result type of the operation
6795 * @param v the value to cast
6796 * @return the cast operation
6797 */
6798 public static CastOp cast(CodeType resultType, Value v) {
6799 return new CastOp(resultType, resultType, v);
6800 }
6801
6802 /**
6803 * Creates a cast operation.
6804 *
6805 * @param resultType the result type of the operation
6806 * @param t the type to cast to
6807 * @param v the value to cast
6808 * @return the cast operation
6809 */
6810 public static CastOp cast(CodeType resultType, JavaType t, Value v) {
6811 return new CastOp(resultType, t, v);
6812 }
6813
6814 /**
6815 * Creates an add operation.
6816 *
6817 * @param lhs the first operand
6818 * @param rhs the second operand
6819 * @return the add operation
6820 */
6821 public static AddOp add(Value lhs, Value rhs) {
6822 return new AddOp(lhs, rhs);
6823 }
6824
6825 /**
6826 * Creates a sub operation.
6827 *
6828 * @param lhs the first operand
6829 * @param rhs the second operand
6830 * @return the sub operation
6831 */
6832 public static SubOp sub(Value lhs, Value rhs) {
6833 return new SubOp(lhs, rhs);
6834 }
6835
6836 /**
6837 * Creates a mul operation.
6838 *
6839 * @param lhs the first operand
6840 * @param rhs the second operand
6841 * @return the mul operation
6842 */
6843 public static MulOp mul(Value lhs, Value rhs) {
6844 return new MulOp(lhs, rhs);
6845 }
6846
6847 /**
6848 * Creates a div operation.
6849 *
6850 * @param lhs the first operand
6851 * @param rhs the second operand
6852 * @return the div operation
6853 */
6854 public static DivOp div(Value lhs, Value rhs) {
6855 return new DivOp(lhs, rhs);
6856 }
6857
6858 /**
6859 * Creates a mod operation.
6860 *
6861 * @param lhs the first operand
6862 * @param rhs the second operand
6863 * @return the mod operation
6864 */
6865 public static ModOp mod(Value lhs, Value rhs) {
6866 return new ModOp(lhs, rhs);
6867 }
6868
6869 /**
6870 * Creates a bitwise/logical or operation.
6871 *
6872 * @param lhs the first operand
6873 * @param rhs the second operand
6874 * @return the or operation
6875 */
6876 public static OrOp or(Value lhs, Value rhs) {
6877 return new OrOp(lhs, rhs);
6878 }
6879
6880 /**
6881 * Creates a bitwise/logical and operation.
6882 *
6883 * @param lhs the first operand
6884 * @param rhs the second operand
6885 * @return the and operation
6886 */
6887 public static AndOp and(Value lhs, Value rhs) {
6888 return new AndOp(lhs, rhs);
6889 }
6890
6891 /**
6892 * Creates a bitwise/logical xor operation.
6893 *
6894 * @param lhs the first operand
6895 * @param rhs the second operand
6896 * @return the xor operation
6897 */
6898 public static XorOp xor(Value lhs, Value rhs) {
6899 return new XorOp(lhs, rhs);
6900 }
6901
6902 /**
6903 * Creates a left shift operation.
6904 *
6905 * @param lhs the first operand
6906 * @param rhs the second operand
6907 * @return the left shift operation
6908 */
6909 public static LshlOp lshl(Value lhs, Value rhs) {
6910 return new LshlOp(lhs, rhs);
6911 }
6912
6913 /**
6914 * Creates a right shift operation.
6915 *
6916 * @param lhs the first operand
6917 * @param rhs the second operand
6918 * @return the right shift operation
6919 */
6920 public static AshrOp ashr(Value lhs, Value rhs) {
6921 return new AshrOp(lhs, rhs);
6922 }
6923
6924 /**
6925 * Creates an unsigned right shift operation.
6926 *
6927 * @param lhs the first operand
6928 * @param rhs the second operand
6929 * @return the unsigned right shift operation
6930 */
6931 public static LshrOp lshr(Value lhs, Value rhs) {
6932 return new LshrOp(lhs, rhs);
6933 }
6934
6935 /**
6936 * Creates a neg operation.
6937 *
6938 * @param v the operand
6939 * @return the neg operation
6940 */
6941 public static NegOp neg(Value v) {
6942 return new NegOp(v);
6943 }
6944
6945 /**
6946 * Creates a bitwise complement operation.
6947 *
6948 * @param v the operand
6949 * @return the bitwise complement operation
6950 */
6951 public static ComplOp compl(Value v) {
6952 return new ComplOp(v);
6953 }
6954
6955 /**
6956 * Creates a not operation.
6957 *
6958 * @param v the operand
6959 * @return the not operation
6960 */
6961 public static NotOp not(Value v) {
6962 return new NotOp(v);
6963 }
6964
6965 /**
6966 * Creates an equals comparison operation.
6967 *
6968 * @param lhs the first operand
6969 * @param rhs the second operand
6970 * @return the equals comparison operation
6971 */
6972 public static EqOp eq(Value lhs, Value rhs) {
6973 return new EqOp(lhs, rhs);
6974 }
6975
6976 /**
6977 * Creates a not equals comparison operation.
6978 *
6979 * @param lhs the first operand
6980 * @param rhs the second operand
6981 * @return the not equals comparison operation
6982 */
6983 public static NeqOp neq(Value lhs, Value rhs) {
6984 return new NeqOp(lhs, rhs);
6985 }
6986
6987 /**
6988 * Creates a greater than comparison operation.
6989 *
6990 * @param lhs the first operand
6991 * @param rhs the second operand
6992 * @return the greater than comparison operation
6993 */
6994 public static GtOp gt(Value lhs, Value rhs) {
6995 return new GtOp(lhs, rhs);
6996 }
6997
6998 /**
6999 * Creates a greater than or equals to comparison operation.
7000 *
7001 * @param lhs the first operand
7002 * @param rhs the second operand
7003 * @return the greater than or equals to comparison operation
7004 */
7005 public static GeOp ge(Value lhs, Value rhs) {
7006 return new GeOp(lhs, rhs);
7007 }
7008
7009 /**
7010 * Creates a less than comparison operation.
7011 *
7012 * @param lhs the first operand
7013 * @param rhs the second operand
7014 * @return the less than comparison operation
7015 */
7016 public static LtOp lt(Value lhs, Value rhs) {
7017 return new LtOp(lhs, rhs);
7018 }
7019
7020 /**
7021 * Creates a less than or equals to comparison operation.
7022 *
7023 * @param lhs the first operand
7024 * @param rhs the second operand
7025 * @return the less than or equals to comparison operation
7026 */
7027 public static LeOp le(Value lhs, Value rhs) {
7028 return new LeOp(lhs, rhs);
7029 }
7030
7031 /**
7032 * Creates a string concatenation operation.
7033 *
7034 * @param lhs the first operand
7035 * @param rhs the second operand
7036 * @return the string concatenation operation
7037 */
7038 public static ConcatOp concat(Value lhs, Value rhs) {
7039 return new ConcatOp(lhs, rhs);
7040 }
7041
7042 /**
7043 * Creates a continue operation.
7044 *
7045 * @return the continue operation
7046 */
7047 public static ContinueOp continue_() {
7048 return continue_(null);
7049 }
7050
7051 /**
7052 * Creates a continue operation.
7053 *
7054 * @param label the value associated with where to continue from
7055 * @return the continue operation
7056 */
7057 public static ContinueOp continue_(Value label) {
7058 return new ContinueOp(label);
7059 }
7060
7061 /**
7062 * Creates a break operation.
7063 *
7064 * @return the break operation
7065 */
7066 public static BreakOp break_() {
7067 return break_(null);
7068 }
7069
7070 /**
7071 * Creates a break operation.
7072 *
7073 * @param label the label identifier
7074 * @return the break operation
7075 */
7076 public static BreakOp break_(Value label) {
7077 return new BreakOp(label);
7078 }
7079
7080 /**
7081 * Creates a yield operation.
7082 *
7083 * @param operand the value to yield
7084 * @return the yield operation
7085 */
7086 public static YieldOp java_yield(Value operand) {
7087 return new YieldOp(operand);
7088 }
7089
7090 /**
7091 * Creates a block operation.
7092 *
7093 * @param body the statements body builder
7094 * @return the block operation
7095 */
7096 public static BlockOp block(Body.Builder body) {
7097 return new BlockOp(body);
7098 }
7099
7100 /**
7101 * Creates a synchronized operation.
7102 *
7103 * @param expr the expression body builder
7104 * @param blockBody the block body builder
7105 * @return the synchronized operation
7106 */
7107 public static SynchronizedOp synchronized_(Body.Builder expr, Body.Builder blockBody) {
7108 return new SynchronizedOp(expr, blockBody);
7109 }
7110
7111 /**
7112 * Creates a labeled operation.
7113 *
7114 * @param body the labeled body builder
7115 * @return the labeled operation
7116 */
7117 public static LabeledOp labeled(Body.Builder body) {
7118 return new LabeledOp(body);
7119 }
7120
7121 /**
7122 * Creates an if operation builder.
7123 *
7124 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7125 * connected, or {@code null} if they are isolated
7126 * @return the if operation builder
7127 */
7128 public static IfOp.IfBuilder if_(Body.Builder connectedAncestorBody) {
7129 return new IfOp.IfBuilder(connectedAncestorBody);
7130 }
7131
7132 // Pairs of
7133 // predicate ()boolean, body ()void
7134 // And one optional body ()void at the end
7135
7136 /**
7137 * Creates an if operation.
7138 *
7139 * @param bodies the body builders for the predicate and action bodies
7140 * @return the if operation
7141 */
7142 public static IfOp if_(List<Body.Builder> bodies) {
7143 return new IfOp(bodies);
7144 }
7145
7146 /**
7147 * Creates a switch expression operation.
7148 * <p>
7149 * Case bodies are provided as pairs of bodies, where the first body of each pair is the predicate body and the
7150 * second is the corresponding action body. The result type of the operation will be derived from the yield type of
7151 * the first action body.
7152 * <p>
7153 * The returned switch expression operation handles nulls if this factory can determine that at least one of the
7154 * predicate bodies accepts null selector values. For more explicit selection of null-handling policy, please
7155 * use {@link #switchExpression(CodeType, Value, boolean, List)}.</p>
7156 *
7157 * @param target the switch target value
7158 * @param bodies the body builders for the predicate and action bodies
7159 * @return the switch expression operation
7160 */
7161 public static SwitchExpressionOp switchExpression(Value target, List<Body.Builder> bodies) {
7162 return new SwitchExpressionOp(null, target, SwitchNullHandling.INFER, bodies);
7163 }
7164
7165 /**
7166 * Creates a switch expression operation.
7167 * <p>
7168 * Case bodies are provided as pairs of bodies, where the first body of each pair is the predicate body and the
7169 * second is the corresponding action body.
7170 * <p>
7171 * The returned switch expression operation handles nulls if this factory can determine that at least one of the
7172 * predicate bodies accepts null selector values. For more explicit selection of null-handling policy, please
7173 * use {@link #switchExpression(CodeType, Value, boolean, List)}.</p>
7174 *
7175 * @param resultType the result type of the expression
7176 * @param target the switch target value
7177 * @param bodies the body builders for the predicate and action bodies
7178 * @return the switch expression operation
7179 */
7180 public static SwitchExpressionOp switchExpression(CodeType resultType, Value target,
7181 List<Body.Builder> bodies) {
7182 Objects.requireNonNull(resultType);
7183 return new SwitchExpressionOp(resultType, target, SwitchNullHandling.INFER, bodies);
7184 }
7185
7186 /**
7187 * Creates a switch expression operation.
7188 * <p>
7189 * Case bodies are provided as pairs of bodies, where the first body of each pair is the predicate body and the
7190 * second is the corresponding action body.
7191 *
7192 * @param resultType the result type of the expression
7193 * @param target the switch target value
7194 * @param handleNulls whether the switch expression handles nulls
7195 * @param bodies the body builders for the predicate and action bodies
7196 * @return the switch expression operation
7197 */
7198 public static SwitchExpressionOp switchExpression(CodeType resultType, Value target,
7199 boolean handleNulls,
7200 List<Body.Builder> bodies) {
7201 Objects.requireNonNull(resultType);
7202 return new SwitchExpressionOp(resultType, target, SwitchNullHandling.of(handleNulls), bodies);
7203 }
7204
7205 /**
7206 * Creates a switch statement operation.
7207 * <p>
7208 * Case bodies are provided as pairs of bodies, where the first body of each pair is the predicate body and the
7209 * second is the corresponding action body.
7210 * <p>
7211 * The returned switch statement operation handles nulls if this factory can determine that at least one of the
7212 * predicate bodies accepts null selector values. For more explicit selection of null-handling policy, please
7213 * use {@link #switchStatement(Value, boolean, List)}.</p>
7214 *
7215 * @param target the switch target value
7216 * @param bodies the body builders for the predicate and action bodies
7217 * @return the switch statement operation
7218 */
7219 public static SwitchStatementOp switchStatement(Value target, List<Body.Builder> bodies) {
7220 return new SwitchStatementOp(target, SwitchNullHandling.INFER, bodies);
7221 }
7222
7223 /**
7224 * Creates a switch statement operation.
7225 * <p>
7226 * Case bodies are provided as pairs of bodies, where the first body of each pair is the predicate body and the
7227 * second is the corresponding action body.
7228 *
7229 * @param target the switch target value
7230 * @param handleNulls whether the switch statement handles nulls
7231 * @param bodies the body builders for the predicate and action bodies
7232 * @return the switch statement operation
7233 */
7234 public static SwitchStatementOp switchStatement(Value target, boolean handleNulls, List<Body.Builder> bodies) {
7235 return new SwitchStatementOp(target, SwitchNullHandling.of(handleNulls), bodies);
7236 }
7237
7238 /**
7239 * Creates a switch fallthrough operation.
7240 *
7241 * @return the switch fallthrough operation
7242 */
7243 public static SwitchFallthroughOp switchFallthroughOp() {
7244 return new SwitchFallthroughOp();
7245 }
7246
7247 /**
7248 * Creates a for operation builder.
7249 *
7250 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7251 * connected, or {@code null} if they are isolated
7252 * @param initTypes the types of initialized variables
7253 * @return the for operation builder
7254 */
7255 public static ForOp.InitBuilder for_(Body.Builder connectedAncestorBody, CodeType... initTypes) {
7256 return for_(connectedAncestorBody, List.of(initTypes));
7257 }
7258
7259 /**
7260 * Creates a for operation builder.
7261 *
7262 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7263 * connected, or {@code null} if they are isolated
7264 * @param initTypes the types of initialized variables
7265 * @return the for operation builder
7266 */
7267 public static ForOp.InitBuilder for_(Body.Builder connectedAncestorBody, List<? extends CodeType> initTypes) {
7268 return new ForOp.InitBuilder(connectedAncestorBody, initTypes);
7269 }
7270
7271
7272 /**
7273 * Creates a for operation.
7274 *
7275 * @param initBody the initialization body builder
7276 * @param condBody the predicate body builder
7277 * @param updateBody the update body builder
7278 * @param loopBody the loop body builder
7279 * @return the for operation
7280 */
7281 // initBody ()Tuple<Var<T1>, Var<T2>, ..., Var<TN>>, or initBody ()Var<T1>, or initBody ()void
7282 // condBody (Var<T1>, Var<T2>, ..., Var<TN>)boolean
7283 // updateBody (Var<T1>, Var<T2>, ..., Var<TN>)void
7284 // loopBody (Var<T1>, Var<T2>, ..., Var<TN>)void
7285 public static ForOp for_(Body.Builder initBody,
7286 Body.Builder condBody,
7287 Body.Builder updateBody,
7288 Body.Builder loopBody) {
7289 return new ForOp(initBody, condBody, updateBody, loopBody);
7290 }
7291
7292 /**
7293 * Creates an enhanced for operation builder.
7294 *
7295 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7296 * connected, or {@code null} if they are isolated
7297 * @param iterableType the iterable type
7298 * @param elementType the element type
7299 * @return the enhanced for operation builder
7300 */
7301 public static EnhancedForOp.ExpressionBuilder enhancedFor(Body.Builder connectedAncestorBody,
7302 CodeType iterableType, CodeType elementType) {
7303 return new EnhancedForOp.ExpressionBuilder(connectedAncestorBody, iterableType, elementType);
7304 }
7305
7306 /**
7307 * Creates an enhanced for operation.
7308 *
7309 * @param exprBody the expression body builder
7310 * @param initBody the initialization body builder
7311 * @param loopBody the loop body builder
7312 * @return the enhanced for operation
7313 */
7314 // expression ()I<E>
7315 // init (E )Var<T>
7316 // body (Var<T> )void
7317 public static EnhancedForOp enhancedFor(Body.Builder exprBody,
7318 Body.Builder initBody,
7319 Body.Builder loopBody) {
7320 return new EnhancedForOp(exprBody, initBody, loopBody);
7321 }
7322
7323 /**
7324 * Creates a while operation builder.
7325 *
7326 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7327 * connected, or {@code null} if they are isolated
7328 * @return the while operation builder
7329 */
7330 public static WhileOp.PredicateBuilder while_(Body.Builder connectedAncestorBody) {
7331 return new WhileOp.PredicateBuilder(connectedAncestorBody);
7332 }
7333
7334 /**
7335 * Creates a while operation.
7336 *
7337 * @param predicateBody the predicate body builder
7338 * @param loopBody the loop body builder
7339 * @return the while operation
7340 */
7341 // predicateBody, ()boolean, may be null for predicateBody returning true
7342 // loopBody, ()void
7343 public static WhileOp while_(Body.Builder predicateBody, Body.Builder loopBody) {
7344 return new WhileOp(predicateBody, loopBody);
7345 }
7346
7347 /**
7348 * Creates a do operation builder.
7349 *
7350 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7351 * connected, or {@code null} if they are isolated
7352 * @return the do operation builder
7353 */
7354 public static DoWhileOp.BodyBuilder doWhile(Body.Builder connectedAncestorBody) {
7355 return new DoWhileOp.BodyBuilder(connectedAncestorBody);
7356 }
7357
7358 /**
7359 * Creates a do operation.
7360 *
7361 * @param loopBody the loop body builder
7362 * @param predicateBody the predicate body builder
7363 * @return the do operation
7364 */
7365 public static DoWhileOp doWhile(Body.Builder loopBody, Body.Builder predicateBody) {
7366 return new DoWhileOp(loopBody, predicateBody);
7367 }
7368
7369 /**
7370 * Creates a conditional-and operation builder.
7371 *
7372 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7373 * connected, or {@code null} if they are isolated
7374 * @param lhs a consumer that populates the first predicate body
7375 * @param rhs a consumer that populates the second predicate body
7376 * @return the conditional-and operation builder
7377 */
7378 public static ConditionalAndOp.Builder conditionalAnd(Body.Builder connectedAncestorBody,
7379 Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) {
7380 return new ConditionalAndOp.Builder(connectedAncestorBody, lhs, rhs);
7381 }
7382
7383 /**
7384 * Creates a conditional-or operation builder.
7385 *
7386 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7387 * connected, or {@code null} if they are isolated
7388 * @param lhs a consumer that populates the first predicate body
7389 * @param rhs a consumer that populates the second predicate body
7390 * @return the conditional-or operation builder
7391 */
7392 public static ConditionalOrOp.Builder conditionalOr(Body.Builder connectedAncestorBody,
7393 Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) {
7394 return new ConditionalOrOp.Builder(connectedAncestorBody, lhs, rhs);
7395 }
7396
7397 /**
7398 * Creates a conditional-and operation
7399 *
7400 * @param bodies the body builders for the predicate bodies
7401 * @return the conditional-and operation
7402 */
7403 // predicates, ()boolean
7404 public static ConditionalAndOp conditionalAnd(List<Body.Builder> bodies) {
7405 return new ConditionalAndOp(bodies);
7406 }
7407
7408 /**
7409 * Creates a conditional-or operation
7410 *
7411 * @param bodies the body builders for the predicate bodies
7412 * @return the conditional-or operation
7413 */
7414 // predicates, ()boolean
7415 public static ConditionalOrOp conditionalOr(List<Body.Builder> bodies) {
7416 return new ConditionalOrOp(bodies);
7417 }
7418
7419 /**
7420 * Creates a conditional operation
7421 *
7422 * @param expressionType the result type of the expression
7423 * @param predicateBody the body builder for the predicate body
7424 * @param trueBody the body builder for the true body
7425 * @param falseBody the body builder for the false body
7426 * @return the conditional operation
7427 */
7428 public static ConditionalExpressionOp conditionalExpression(CodeType expressionType,
7429 Body.Builder predicateBody,
7430 Body.Builder trueBody,
7431 Body.Builder falseBody) {
7432 Objects.requireNonNull(expressionType);
7433 return new ConditionalExpressionOp(expressionType, predicateBody, trueBody, falseBody);
7434 }
7435
7436 /**
7437 * Creates a conditional operation
7438 * <p>
7439 * The result type of the operation will be derived from the yield type of the true body.
7440 *
7441 * @param predicateBody the body builder for the predicate body
7442 * @param trueBody the body builder for the true body
7443 * @param falseBody the body builder for the false body
7444 * @return the conditional operation
7445 */
7446 public static ConditionalExpressionOp conditionalExpression(Body.Builder predicateBody,
7447 Body.Builder trueBody,
7448 Body.Builder falseBody) {
7449 return new ConditionalExpressionOp(null, predicateBody, trueBody, falseBody);
7450 }
7451
7452 /**
7453 * Creates try operation builder.
7454 *
7455 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7456 * connected, or {@code null} if they are isolated
7457 * @param c a consumer that populates the try body
7458 * @return the try operation builder
7459 */
7460 public static TryOp.CatchBuilder try_(Body.Builder connectedAncestorBody, Consumer<Block.Builder> c) {
7461 Body.Builder _try = Body.Builder.of(connectedAncestorBody, CoreType.FUNCTION_TYPE_VOID);
7462 c.accept(_try.entryBlock());
7463 return new TryOp.CatchBuilder(connectedAncestorBody, List.of(), _try);
7464 }
7465
7466 /**
7467 * Creates try-with-resources operation builder.
7468 *
7469 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7470 * connected, or {@code null} if they are isolated
7471 * @return the try-with-resources operation builder
7472 */
7473 public static TryOp.BodyBuilder tryWithResources(Body.Builder connectedAncestorBody) {
7474 return new TryOp.BodyBuilder(connectedAncestorBody);
7475 }
7476
7477 // resources: ()T1, (T1)T2, ..., (T1, T2, ..., T{N-1})TN, or empty
7478 // Ti is Ri for a resource expression, or Var<Ri> for a resource declaration
7479 // try (T1, T2, ..., TN)void, or try ()void
7480 // catch (E )void, where E <: Throwable
7481 // finally ()void, or null
7482
7483 /**
7484 * Creates a try or try-with-resources operation.
7485 *
7486 * @param resourceBodies the resources body builders
7487 * @param body the try body builder
7488 * @param catchBodies the catch body builders
7489 * @param finallyBody the finalizer body builder, may be {@code null}
7490 * @return the try or try-with-resources operation
7491 */
7492 public static TryOp try_(List<Body.Builder> resourceBodies,
7493 Body.Builder body,
7494 List<Body.Builder> catchBodies,
7495 Body.Builder finallyBody) {
7496 return new TryOp(resourceBodies, body, catchBodies, finallyBody);
7497 }
7498
7499 //
7500 // Patterns
7501
7502 /**
7503 * Creates a pattern match operation.
7504 *
7505 * @param target the target value
7506 * @param patternBody the pattern body builder
7507 * @param matchBody the match body builder
7508 * @return the pattern match operation
7509 */
7510 public static PatternOps.MatchOp match(Value target,
7511 Body.Builder patternBody, Body.Builder matchBody) {
7512 return new PatternOps.MatchOp(target, patternBody, matchBody);
7513 }
7514
7515 /**
7516 * Creates a pattern binding operation.
7517 *
7518 * @param type the type of value to be bound
7519 * @param bindingName the binding name
7520 * @return the pattern binding operation
7521 */
7522 public static PatternOps.TypePatternOp typePattern(CodeType type, String bindingName) {
7523 return new PatternOps.TypePatternOp(type, bindingName);
7524 }
7525
7526 /**
7527 * Creates a record pattern operation.
7528 *
7529 * @param recordRef the record reference
7530 * @param nestedPatterns the nested pattern values
7531 * @return the record pattern operation
7532 */
7533 public static PatternOps.RecordPatternOp recordPattern(RecordTypeRef recordRef, Value... nestedPatterns) {
7534 return recordPattern(recordRef, List.of(nestedPatterns));
7535 }
7536
7537 /**
7538 * Creates a record pattern operation.
7539 *
7540 * @param recordRef the record reference
7541 * @param nestedPatterns the nested pattern values
7542 * @return the record pattern operation
7543 */
7544 public static PatternOps.RecordPatternOp recordPattern(RecordTypeRef recordRef, List<Value> nestedPatterns) {
7545 return new PatternOps.RecordPatternOp(recordRef, nestedPatterns);
7546 }
7547
7548 /**
7549 * Creates a match-all pattern operation.
7550 *
7551 * @return a match-all pattern
7552 */
7553 public static PatternOps.MatchAllPatternOp matchAllPattern() {
7554 return new PatternOps.MatchAllPatternOp();
7555 }
7556 }