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