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 * Completes the if operation with no final action body.
3217 * @return the completed if operation
3218 */
3219 public IfOp noElse() {
3220 return new IfOp(bodies);
3221 }
3222 }
3223
3224 static final String NAME = "java.if";
3225
3226 final List<Body> bodies;
3227
3228 IfOp(ExternalizedOp def) {
3229 requireNoOperands(def);
3230 this(def.bodyDefinitions());
3231 }
3232
3233 IfOp(IfOp that, CodeContext cc, CodeTransformer ct) {
3234 super(that, cc);
3235
3236 // Copy body
3237 this.bodies = that.bodies.stream()
3238 .map(b -> b.transform(cc, ct).build(this)).toList();
3239 }
3240
3241 @Override
3242 public IfOp transform(CodeContext cc, CodeTransformer ct) {
3243 return new IfOp(this, cc, ct);
3244 }
3245
3246 IfOp(List<Body.Builder> bodyCs) {
3247 if (bodyCs.size() < 2) {
3248 throw structuralException(NAME, "requires 2 or more bodies, found %d".formatted(bodyCs.size()));
3249 }
3250 for (int i = 0; i < bodyCs.size(); i++) {
3251 requireBodySignature("%s body[%d]".formatted(NAME, i), bodyCs.get(i), i % 2 == 0 && i < bodyCs.size() - 1 ? PREDICATE_SIGNATURE : ACTION_SIGNATURE);
3252 }
3253 super(List.of());
3254
3255 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList();
3256 }
3257
3258 @Override
3259 public List<Body> bodies() {
3260 return bodies;
3261 }
3262
3263 static boolean isEmptyBodyAction(Body body) {
3264 Block block = body.entryBlock();
3265 return body.blocks().size() == 1
3266 && block.ops().size() == 1
3267 && block.terminatingOp() instanceof CoreOp.YieldOp;
3268 }
3269
3270 @Override
3271 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
3272 Block.Builder exit = b.block();
3273 BranchTarget.setBranchTarget(b.context(), this, exit, null);
3274
3275 // Create predicate and action blocks
3276 List<Block.Builder> builders = new ArrayList<>();
3277 for (int i = 0; i < bodies.size(); i += 2) {
3278 if (i == bodies.size() - 1) {
3279 builders.add(b.block());
3280 } else {
3281 builders.add(i == 0 ? b : b.block());
3282 builders.add(b.block());
3283 }
3284 }
3285
3286 for (int i = 0; i < bodies.size(); i += 2) {
3287 Body actionBody;
3288 Block.Builder action;
3289 if (i == bodies.size() - 1) {
3290 actionBody = bodies.get(i);
3291 action = builders.get(i);
3292 } else {
3293 Body predBody = bodies.get(i);
3294 actionBody = bodies.get(i + 1);
3295
3296 Block.Builder pred = builders.get(i);
3297 action = builders.get(i + 1);
3298 Block.Builder nextAction = i + 2 < builders.size() ? builders.get(i + 2) : exit;
3299
3300 ControlFlowBooleanExpressionOp.lowerBooleanBody(pred, predBody, List.of(),
3301 new ControlFlowBooleanExpressionOp.ConditionalBranchContinuation(action.reference(), nextAction.reference()),
3302 inherited);
3303 }
3304
3305 action.transformBody(actionBody, List.of(), loweringTransformer(inherited, (block, op) -> {
3306 if (op instanceof CoreOp.YieldOp) {
3307 block.add(branch(exit.reference()));
3308 return block;
3309 } else {
3310 return null;
3311 }
3312 }));
3313 }
3314
3315 return exit;
3316 }
3317
3318 @Override
3319 public CodeType resultType() {
3320 return VOID;
3321 }
3322 }
3323
3324 /**
3325 * An operation modeling a Java switch statement or expression.
3326 * <p>
3327 * Switch operations are parameterized by a selector value.
3328 * They feature a sequence of case bodies, each modeled as a pair of bodies: a <em>predicate body</em> and an
3329 * <em>action body</em>.
3330 * <p>
3331 * Each predicate body accepts one argument, the selector value, and yields a {@link JavaType#BOOLEAN} value.
3332 * Each action body yields a value of the same type {@code T}. For switch statement operations, {@code T} is
3333 * {@code void}. For switch expression operations, {@code T} is the switch expression type.
3334 *
3335 * @jls 14.11 The switch Statement
3336 * @jls 15.28 {@code switch} Expressions
3337 */
3338 public abstract static sealed class SwitchOp extends AbstractOp
3339 implements JavaOp, Op.Nested, Op.Lowerable
3340 permits SwitchStatementOp, SwitchExpressionOp {
3341
3342 final List<Body> bodies;
3343 final boolean handleNulls;
3344
3345 enum SwitchNullHandling {
3346 ALLOW_NULL,
3347 REJECT_NULL,
3348 INFER;
3349
3350 static SwitchNullHandling of(ExternalizedOp def) {
3351 return of(optionalBooleanAttribute(def, ATTRIBUTE_SWITCH_HANDLE_NULLS));
3352
3353 }
3354
3355 static SwitchNullHandling of(boolean handleNulls) {
3356 return handleNulls ?
3357 ALLOW_NULL : REJECT_NULL;
3358 }
3359 }
3360
3361 /**
3362 * The externalized attribute key for a switch that handles nulls.
3363 */
3364 static final String ATTRIBUTE_SWITCH_HANDLE_NULLS = "switch.handle.nulls";
3365
3366 SwitchOp(SwitchOp that, CodeContext cc, CodeTransformer ct) {
3367 super(that, cc);
3368
3369 // Copy body
3370 this.bodies = that.bodies.stream()
3371 .map(b -> b.transform(cc, ct).build(this)).toList();
3372 this.handleNulls = that.handleNulls;
3373 }
3374
3375 /*
3376 Grammar for switch statements and expressions
3377 SwitchStatement:
3378 switch ( Expression ) SwitchBlock
3379
3380 SwitchExpression:
3381 switch ( Expression ) SwitchBlock
3382
3383 SwitchBlock:
3384 { SwitchRule {SwitchRule} }
3385 { {SwitchBlockStatementGroup} {SwitchLabel :} }
3386
3387 SwitchRule:
3388 SwitchLabel -> Expression ;
3389 SwitchLabel -> Block
3390 SwitchLabel -> ThrowStatement
3391
3392 SwitchBlockStatementGroup:
3393 SwitchLabel : {SwitchLabel :} BlockStatements
3394
3395 SwitchLabel:
3396 case CaseConstant {, CaseConstant}
3397 case null [, default]
3398 case CasePattern {, CasePattern} [Guard]
3399 default
3400
3401 CaseConstant:
3402 ConditionalExpression
3403
3404 CasePattern:
3405 Pattern
3406
3407 Guard:
3408 when Expression
3409
3410 A SwitchLabel is modeled as a body yielding a boolean value.
3411
3412 If the SwitchLabel is "default" or "case null, default" the predicate body is modeled as one that yields
3413 true, and the body has no parameter. Otherwise, the body has one parameter that models the result of the switch
3414 selector expression and its content models "case CaseConstant {, CaseConstant}" and
3415 "case CasePattern {, CasePattern} [Guard]".
3416
3417 An Expression, Block, ThrowStatement, or BlockStatements, associated with a SwitchLabel is modeled as a body
3418 yielding the result of the switch expression or void for a switch statement.
3419
3420 A SwitchBlock is modeled as a sequence of pairs of bodies, generally the first body in a pair, the predicate
3421 body, models the SwitchLabel, and the second body, the action body, models the Expression, Block,
3422 ThrowStatement, or BlockStatements.
3423
3424 For a switch statement containing a sequence of two or more SwitchLabel, each SwitchLabel up to but not
3425 including the last SwitchLabel is modeled as a pair of bodies, the predicate body modeling the SwitchLabel
3426 and a synthesized action body that models fall-through.
3427
3428 For a SwitchLabel containing a sequence of two or more CaseConstant or CasePattern, the predicate body
3429 yields the result of the logical-or of all the predicate bodies modeling each CasePattern.
3430 For a SwitchLabel containing a CasePattern with a Guard, the predicate body yields the result of the
3431 logical-and of the predicate body produced for the sequence of CasePattern and the boolean yielding body
3432 modeling the Guard expression.
3433
3434 For SwitchLabel that is "default" or "case null, default" the predicate body is modeled as one that yields
3435 true, and the body has no parameter. @@@ the corresponding pair of bodies should occur as the last pair
3436 in the sequence of pairs modeling the SwitchBlock.
3437
3438 If the SwitchBlock contains a SwitchLabel of "case null [, default]" then switch operation indicates that
3439 null values are accepted for results of the selector expression.
3440 */
3441
3442 SwitchOp(Value target, SwitchNullHandling nullHandling, List<Body.Builder> bodyCs) {
3443 super(List.of(target));
3444
3445 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList();
3446 this.handleNulls = switch (nullHandling) {
3447 case ALLOW_NULL -> true;
3448 case REJECT_NULL -> false;
3449 case INFER -> inferNullCase();
3450 };
3451 }
3452
3453 @Override
3454 public List<Body> bodies() {
3455 return bodies;
3456 }
3457
3458 @Override
3459 public Map<String, Object> externalize() {
3460 return handleNulls ?
3461 Map.of(ATTRIBUTE_SWITCH_HANDLE_NULLS, true) :
3462 Map.of();
3463 }
3464
3465 boolean hasYieldStatements() {
3466 return this.elements().anyMatch(
3467 e -> e instanceof JavaOp.YieldOp yop && yop.targetsOrAttemptsToExit(this));
3468 }
3469
3470 @Override
3471 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
3472 Value selectorExpression = b.context().getValue(operands().get(0));
3473
3474 // @@@ Add this during model generation?
3475 // If no "case null [, default]" then perform null check on result of selector expression
3476 if (!(selectorExpression.type() instanceof PrimitiveType) && !handleNulls) {
3477 Block.Builder continueBlock = b.block();
3478 Block.Builder throwBlock = b.block();
3479
3480 MethodRef equalsRef = MethodRef.method(Objects.class, "equals", boolean.class, Object.class, Object.class);
3481 Result p = b.add(invoke(equalsRef, selectorExpression, b.add(constant(J_L_OBJECT, null))));
3482 b.add(conditionalBranch(p, throwBlock.reference(), continueBlock.reference()));
3483
3484 throwBlock.add(throw_(
3485 throwBlock.add(new_(MethodRef.constructor(NullPointerException.class)))
3486 ));
3487
3488 b = continueBlock;
3489 }
3490
3491 // Default case is modeled as predicateBody with no parameters
3492 // that yields a constant boolean value of true
3493 int defaultCaseIndex = -1;
3494 for (int i = 0; i < bodies().size(); i += 2) {
3495 Body predicateBody = bodies().get(i);
3496 Block predicateEntryBlock = predicateBody.entryBlock();
3497 if (predicateEntryBlock.parameters().isEmpty() &&
3498 predicateEntryBlock.terminatingOp() instanceof CoreOp.YieldOp yop &&
3499 yop.yieldValue().declaringElement() instanceof ConstantOp cop &&
3500 cop.resultType().equals(BOOLEAN) &&
3501 cop.value() instanceof Boolean trueValue && trueValue) {
3502 defaultCaseIndex = i;
3503 break;
3504 }
3505 }
3506
3507 // Create predicate and action blocks
3508 List<Block.Builder> blocks = new ArrayList<>(bodies().size());
3509 // Reuse incoming block for the first predicate
3510 int reuseIdx = 0;
3511 for (int i = 0; i < bodies().size(); i ++) {
3512 if (i == defaultCaseIndex) {
3513 // No block needed for default predicate
3514 blocks.add(null);
3515 if (i == 0) {
3516 // The first predicate is default. Reuese incoming block
3517 // for the default action block itself if the switch is
3518 // only-default, or for the next predicate otherwise
3519 reuseIdx = bodies().size() > 2 ? i + 2 : i + 1;
3520 }
3521 } else if (i == reuseIdx) {
3522 blocks.add(b);
3523 } else {
3524 blocks.add(b.block());
3525 }
3526 }
3527
3528 boolean hasYieldStatements = hasYieldStatements();
3529 boolean isBooleanExpression = resultType().equals(BOOLEAN);
3530 // Poll for implicit boolean continuation parameter
3531 ControlFlowBooleanExpressionOp.BooleanResultContinuation continuation = isBooleanExpression
3532 ? ControlFlowBooleanExpressionOp.BOOLEAN_CONTINUATION_ARG.poll(b.context())
3533 : null;
3534 Block.Builder exit;
3535 if (isBooleanExpression) {
3536 if (continuation == null) {
3537 exit = b.block();
3538 b.context().mapValue(result(), exit.parameter(resultType()));
3539 continuation = new ControlFlowBooleanExpressionOp.BranchWithArgumentContinuation(exit);
3540 } else if (hasYieldStatements) {
3541 exit = b.block();
3542 Value value = exit.parameter(resultType());
3543 continuation.continueWith(exit, value);
3544 } else {
3545 exit = b;
3546 }
3547
3548 if (hasYieldStatements) {
3549 BranchTarget.setBranchTarget(b.context(), this, exit, null);
3550 }
3551 } else {
3552 exit = b.block();
3553 if (resultType() != VOID) {
3554 Value r = exit.parameter(resultType());
3555 exit.context().mapValue(result(), r);
3556 }
3557 BranchTarget.setBranchTarget(b.context(), this, exit, null);
3558 }
3559
3560 // Set action body's continue target to next action block for lowering of SwitchFallThroughOp
3561 for (int i = 1; i < bodies().size() - 2; i += 2) {
3562 Body actionBody = bodies().get(i);
3563 Block.Builder nextActionBlock = blocks.get(i + 2);
3564 BranchTarget.setBranchTarget(b.context(), actionBody, null, nextActionBlock);
3565 }
3566
3567 for (int i = 0; i < bodies().size(); i += 2) {
3568 Body predicateBody = bodies().get(i);
3569 Block.Builder predicateBlock = blocks.get(i);
3570 Body actionBody = bodies().get(i + 1);
3571 Block.Builder actionBlock = blocks.get(i + 1);
3572
3573 // Lower predicate body for non-default cases
3574 if (i != defaultCaseIndex) {
3575 int nextPredicateIdx = i + 2;
3576 if (nextPredicateIdx == defaultCaseIndex) {
3577 nextPredicateIdx += 2;
3578 }
3579
3580 Block.Builder noMatchBlock;
3581 if (nextPredicateIdx < bodies().size()) {
3582 noMatchBlock = blocks.get(nextPredicateIdx);
3583 } else if (defaultCaseIndex != -1) {
3584 noMatchBlock = blocks.get(defaultCaseIndex + 1);
3585 } else if (this instanceof SwitchExpressionOp) {
3586 // If switch expression, the last predicate body should be unconditional
3587 // and no conditional branch should be required. Rather than verifying
3588 // that create a no match block that terminates with unreachable
3589 noMatchBlock = b.block();
3590 noMatchBlock.add(unreachable());
3591 } else {
3592 noMatchBlock = exit;
3593 }
3594
3595 ControlFlowBooleanExpressionOp.lowerBooleanBody(predicateBlock, predicateBody, List.of(selectorExpression),
3596 new ControlFlowBooleanExpressionOp.ConditionalBranchContinuation(actionBlock.reference(), noMatchBlock.reference()),
3597 inherited);
3598 }
3599
3600 if (isBooleanExpression) {
3601 ControlFlowBooleanExpressionOp.lowerBooleanBody(actionBlock, actionBody, List.of(), continuation, inherited);
3602 } else {
3603 // Lower action body for all cases
3604 actionBlock.transformBody(actionBody, List.of(), loweringTransformer(inherited,
3605 (block, op) -> switch (op) {
3606 case CoreOp.YieldOp yop -> {
3607 List<Value> args = yop.yieldValue() == null
3608 ? List.of()
3609 : List.of(block.context().getValue(yop.yieldValue()));
3610 block.add(branch(exit.reference(args)));
3611 yield block;
3612 }
3613 default -> null;
3614 }));
3615 }
3616 }
3617
3618 return exit;
3619 }
3620
3621 /**
3622 * {@return {@code true} if this switch operation handles nulls}
3623 */
3624 public boolean handleNulls() {
3625 return handleNulls;
3626 }
3627
3628 private boolean inferNullCase() {
3629 /*
3630 case null is modeled like this:
3631 (%4 : T)boolean -> {
3632 %5 : java.lang.Object = constant @null;
3633 %6 : boolean = invoke %4 %5 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
3634 yield %6;
3635 }
3636 * */
3637 for (int i = 0; i < bodies().size() - 2; i+=2) {
3638 Body labelBody = bodies().get(i);
3639 if (labelBody.blocks().size() != 1) {
3640 continue; // we skip, for now
3641 }
3642 Op terminatingOp = bodies().get(i).entryBlock().terminatingOp();
3643 //@@@ when op pattern matching is ready, we can use it
3644 if (terminatingOp instanceof CoreOp.YieldOp yieldOp &&
3645 yieldOp.yieldValue() instanceof Op.Result opr &&
3646 opr.op() instanceof InvokeOp invokeOp &&
3647 invokeOp.invokeReference().equals(
3648 MethodRef.method(Objects.class, "equals", boolean.class, Object.class, Object.class)) &&
3649 invokeOp.operands().stream().anyMatch(o -> o instanceof Op.Result r &&
3650 r.op() instanceof ConstantOp cop && cop.value() == null)) {
3651 return true;
3652 }
3653 }
3654 return false;
3655 }
3656 }
3657
3658 /**
3659 * The switch expression operation, that can model Java language switch expressions.
3660 * <p>
3661 * For switch expression operations, action bodies yield a value of type {@code T}, where {@code T} is also the
3662 * type of the switch expression operation.
3663 *
3664 * @jls 15.28 {@code switch} Expressions
3665 */
3666 @OpDeclaration(SwitchExpressionOp.NAME)
3667 public static final class SwitchExpressionOp extends SwitchOp
3668 implements ControlFlowBooleanExpressionOp, JavaExpression {
3669 static final String NAME = "java.switch.expression";
3670
3671 final CodeType resultType;
3672
3673 SwitchExpressionOp(ExternalizedOp def) {
3674 this(def.resultType(), requireSingleOperand(def), SwitchNullHandling.of(def), def.bodyDefinitions());
3675 }
3676
3677 SwitchExpressionOp(SwitchExpressionOp that, CodeContext cc, CodeTransformer ct) {
3678 super(that, cc, ct);
3679
3680 this.resultType = that.resultType;
3681 }
3682
3683 @Override
3684 public SwitchExpressionOp transform(CodeContext cc, CodeTransformer ct) {
3685 return new SwitchExpressionOp(this, cc, ct);
3686 }
3687
3688 SwitchExpressionOp(CodeType resultType, Value target, SwitchNullHandling nullHandling, List<Body.Builder> bodyCs) {
3689 super(target, nullHandling, requireBodyPairs(NAME, bodyCs));
3690 this.resultType = resultType == null ? bodies.get(1).yieldType() : resultType;
3691 }
3692
3693 @Override
3694 public CodeType resultType() {
3695 return resultType;
3696 }
3697 }
3698
3699 /**
3700 * The switch statement operation, that can model Java language switch statement.
3701 * <p>
3702 * For switch statement operations, action bodies yield {@linkplain JavaType#VOID no value}.
3703 * <p>
3704 * The result type of a switch statement operation is {@link JavaType#VOID}.
3705 *
3706 * @jls 14.11 The switch Statement
3707 */
3708 @OpDeclaration(SwitchStatementOp.NAME)
3709 public static final class SwitchStatementOp extends SwitchOp
3710 implements JavaStatement {
3711 static final String NAME = "java.switch.statement";
3712
3713 SwitchStatementOp(ExternalizedOp def) {
3714 this(requireSingleOperand(def), SwitchNullHandling.of(def), def.bodyDefinitions());
3715 }
3716
3717 SwitchStatementOp(SwitchStatementOp that, CodeContext cc, CodeTransformer ct) {
3718 super(that, cc, ct);
3719 }
3720
3721 @Override
3722 public SwitchStatementOp transform(CodeContext cc, CodeTransformer ct) {
3723 return new SwitchStatementOp(this, cc, ct);
3724 }
3725
3726 SwitchStatementOp(Value target, SwitchNullHandling nullHandling, List<Body.Builder> bodyCs) {
3727 super(target, nullHandling, requireBodyPairs(NAME, bodyCs));
3728 }
3729
3730 @Override
3731 public CodeType resultType() {
3732 return VOID;
3733 }
3734 }
3735
3736 /**
3737 * The switch fall-through operation, that can model fall-through to the next statement in the switch block after
3738 * the last statement of the current switch label.
3739 * <p>
3740 * A switch fall-through operation is a body terminating operation.
3741 */
3742 @OpDeclaration(SwitchFallthroughOp.NAME)
3743 public static final class SwitchFallthroughOp extends AbstractOp.Terminating
3744 implements JavaOp, Op.Lowerable {
3745 static final String NAME = "java.switch.fallthrough";
3746
3747 SwitchFallthroughOp(ExternalizedOp def) {
3748 this();
3749 }
3750
3751 SwitchFallthroughOp(SwitchFallthroughOp that, CodeContext cc) {
3752 super(that, cc);
3753 }
3754
3755 @Override
3756 public SwitchFallthroughOp transform(CodeContext cc, CodeTransformer ct) {
3757 return new SwitchFallthroughOp(this, cc);
3758 }
3759
3760 SwitchFallthroughOp() {
3761 super(List.of());
3762 }
3763
3764 @Override
3765 public CodeType resultType() {
3766 return VOID;
3767 }
3768
3769 @Override
3770 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
3771 return lower(b, BranchTarget::continueBlock);
3772 }
3773
3774 Block.Builder lower(Block.Builder b, Function<BranchTarget, Block.Builder> f) {
3775 BranchTarget t = BranchTarget.getBranchTarget(b.context(), ancestorBody());
3776 if (t != null) {
3777 b.add(branch(f.apply(t).reference()));
3778 } else {
3779 throw new IllegalStateException("No branch target for operation: " + this);
3780 }
3781 return b;
3782 }
3783 }
3784
3785 /**
3786 * The for operation, that can model a Java language basic for statement.
3787 * <p>
3788 * For operations feature four bodies that model a basic {@code for} statement:
3789 * an <em>initialization body</em>, a <em>predicate body</em>, an <em>update body</em>, and a <em>loop body</em>.
3790 * <p>
3791 * The initialization body accepts no arguments and yields the loop state, of type {@code S}. For instance,
3792 * a loop with a single loop variable of type {@code T} might use a loop state of type {@code T}.
3793 * A loop with two loop variables of type {@code X} and {@code Y} might use a loop state whose type is
3794 * a {@linkplain TupleType tuple type}, such as {@code (X, Y)}. A loop with no loop variables might use
3795 * a loop state of type {@link JavaType#VOID}, and have its initialization body yield no value.
3796 * <p>
3797 * The predicate body accepts an argument of type {@code S} and yields a {@link JavaType#BOOLEAN} value.
3798 * The update and loop bodies accept an argument of type {@code S} and yield {@linkplain JavaType#VOID no value}.
3799 * <p>
3800 * The result type of a for operation is {@link JavaType#VOID}.
3801 *
3802 * @jls 14.14.1 The basic for Statement
3803 */
3804 @OpDeclaration(ForOp.NAME)
3805 public static final class ForOp extends AbstractOp
3806 implements JavaOp, Op.Loop, Op.Lowerable, JavaStatement {
3807
3808 /**
3809 * Builder for the initialization body of a for operation.
3810 */
3811 public static final class InitBuilder {
3812 final Body.Builder connectedAncestorBody;
3813 final List<? extends CodeType> initTypes;
3814
3815 InitBuilder(Body.Builder connectedAncestorBody,
3816 List<? extends CodeType> initTypes) {
3817 this.connectedAncestorBody = connectedAncestorBody;
3818 this.initTypes = initTypes.stream().map(CoreType::varType).toList();
3819 }
3820
3821 /**
3822 * Builds the initialization body of a for-loop.
3823 *
3824 * @param c a consumer that populates the initialization body
3825 * @return a builder for specifying the loop predicate body
3826 */
3827 public ForOp.CondBuilder init(Consumer<Block.Builder> c) {
3828 Body.Builder init = Body.Builder.of(connectedAncestorBody,
3829 CoreType.functionType(CoreType.tupleType(initTypes)));
3830 c.accept(init.entryBlock());
3831
3832 return new CondBuilder(connectedAncestorBody, initTypes, init);
3833 }
3834 }
3835
3836 /**
3837 * Builder for the predicate body of a for operation.
3838 */
3839 public static final class CondBuilder {
3840 final Body.Builder connectedAncestorBody;
3841 final List<? extends CodeType> initTypes;
3842 final Body.Builder init;
3843
3844 CondBuilder(Body.Builder connectedAncestorBody,
3845 List<? extends CodeType> initTypes,
3846 Body.Builder init) {
3847 this.connectedAncestorBody = connectedAncestorBody;
3848 this.initTypes = initTypes;
3849 this.init = init;
3850 }
3851
3852 /**
3853 * Builds the predicate body of a for-loop.
3854 *
3855 * @param c a consumer that populates the predicate body
3856 * @return a builder for specifying the update body
3857 */
3858 public ForOp.UpdateBuilder cond(Consumer<Block.Builder> c) {
3859 Body.Builder cond = Body.Builder.of(connectedAncestorBody,
3860 CoreType.functionType(BOOLEAN, initTypes));
3861 c.accept(cond.entryBlock());
3862
3863 return new UpdateBuilder(connectedAncestorBody, initTypes, init, cond);
3864 }
3865 }
3866
3867 /**
3868 * Builder for the update body of a for operation.
3869 */
3870 public static final class UpdateBuilder {
3871 final Body.Builder connectedAncestorBody;
3872 final List<? extends CodeType> initTypes;
3873 final Body.Builder init;
3874 final Body.Builder cond;
3875
3876 UpdateBuilder(Body.Builder connectedAncestorBody,
3877 List<? extends CodeType> initTypes,
3878 Body.Builder init, Body.Builder cond) {
3879 this.connectedAncestorBody = connectedAncestorBody;
3880 this.initTypes = initTypes;
3881 this.init = init;
3882 this.cond = cond;
3883 }
3884
3885 /**
3886 * Builds the update body of a for-loop.
3887 *
3888 * @param c a consumer that populates the update body
3889 * @return a builder for specifying the loop body
3890 */
3891 public ForOp.BodyBuilder update(Consumer<Block.Builder> c) {
3892 Body.Builder update = Body.Builder.of(connectedAncestorBody,
3893 CoreType.functionType(VOID, initTypes));
3894 c.accept(update.entryBlock());
3895
3896 return new BodyBuilder(connectedAncestorBody, initTypes, init, cond, update);
3897 }
3898 }
3899
3900 /**
3901 * Builder for the body (main logic) portion of a for-loop.
3902 */
3903 public static final class BodyBuilder {
3904 final Body.Builder connectedAncestorBody;
3905 final List<? extends CodeType> initTypes;
3906 final Body.Builder init;
3907 final Body.Builder cond;
3908 final Body.Builder update;
3909
3910 BodyBuilder(Body.Builder connectedAncestorBody,
3911 List<? extends CodeType> initTypes,
3912 Body.Builder init, Body.Builder cond, Body.Builder update) {
3913 this.connectedAncestorBody = connectedAncestorBody;
3914 this.initTypes = initTypes;
3915 this.init = init;
3916 this.cond = cond;
3917 this.update = update;
3918 }
3919
3920 /**
3921 * Completes for operation by adding the loop body.
3922 *
3923 * @param c a consumer that populates the loop body
3924 * @return the completed for-loop operation
3925 */
3926 public ForOp body(Consumer<Block.Builder> c) {
3927 Body.Builder body = Body.Builder.of(connectedAncestorBody,
3928 CoreType.functionType(VOID, initTypes));
3929 c.accept(body.entryBlock());
3930
3931 return new ForOp(init, cond, update, body);
3932 }
3933 }
3934
3935 static final String NAME = "java.for";
3936
3937 final Body initBody;
3938 final Body condBody;
3939 final Body updateBody;
3940 final Body loopBody;
3941
3942 ForOp(ExternalizedOp def) {
3943 List<Body.Builder> bodies = requireBodies(def, 4);
3944 this(bodies.get(0), bodies.get(1), bodies.get(2), bodies.get(3));
3945 }
3946
3947 ForOp(ForOp that, CodeContext cc, CodeTransformer ct) {
3948 super(that, cc);
3949
3950 this.initBody = that.initBody.transform(cc, ct).build(this);
3951 this.condBody = that.condBody.transform(cc, ct).build(this);
3952 this.updateBody = that.updateBody.transform(cc, ct).build(this);
3953 this.loopBody = that.loopBody.transform(cc, ct).build(this);
3954 }
3955
3956 @Override
3957 public ForOp transform(CodeContext cc, CodeTransformer ct) {
3958 return new ForOp(this, cc, ct);
3959 }
3960
3961 ForOp(Body.Builder initC,
3962 Body.Builder condC,
3963 Body.Builder updateC,
3964 Body.Builder bodyC) {
3965 super(List.of());
3966
3967 List<CodeType> varTypes = switch (initC.bodySignature().returnType()) {
3968 case TupleType tt -> tt.componentTypes();
3969 case PrimitiveType pt when pt.equals(VOID) -> List.of();
3970 case CodeType t -> List.of(t);
3971 };
3972 FunctionType condType = CoreType.functionType(BOOLEAN, varTypes);
3973 FunctionType bodyType = CoreType.functionType(VOID, varTypes);
3974
3975 this.initBody = requireNoParameters(NAME + " init", initC).build(this);
3976 this.condBody = requireBodySignature(NAME + " predicate", condC, condType).build(this);
3977 this.updateBody = requireBodySignature(NAME + " update", updateC, bodyType).build(this);
3978 this.loopBody = requireBodySignature(NAME + " loop", bodyC, bodyType).build(this);
3979 }
3980
3981 @Override
3982 public List<Body> bodies() {
3983 return List.of(initBody, condBody, updateBody, loopBody);
3984 }
3985
3986 /**
3987 * {@return the initialization body}
3988 */
3989 public Body initBody() {
3990 return initBody;
3991 }
3992
3993 /**
3994 * {@return the loop condition (predicate) body}
3995 */
3996 public Body condBody() {
3997 return condBody;
3998 }
3999
4000 /**
4001 * {@return the update body}
4002 */
4003 public Body updateBody() {
4004 return updateBody;
4005 }
4006
4007 @Override
4008 public Body loopBody() {
4009 return loopBody;
4010 }
4011
4012 @Override
4013 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
4014 Block.Builder header = b.block();
4015 Block.Builder body = b.block();
4016 Block.Builder update = b.block();
4017 Block.Builder exit = b.block();
4018
4019 List<Value> initValues = new ArrayList<>();
4020 // @@@ Init body has one yield operation yielding
4021 // void, a single variable, or a tuple of one or more variables
4022 b.transformBody(initBody, List.of(), loweringTransformer(inherited, (block, op) -> switch (op) {
4023 case TupleOp _ -> {
4024 // Drop Tuple if a yielded
4025 boolean isResult = op.result().uses().size() == 1 &&
4026 op.result().uses().stream().allMatch(r -> r.op() instanceof CoreOp.YieldOp);
4027 if (!isResult) {
4028 block.add(op);
4029 }
4030 yield block;
4031 }
4032 case CoreOp.YieldOp yop -> {
4033 if (yop.yieldValue() == null) {
4034 block.add(branch(header.reference()));
4035 yield block;
4036 } else if (yop.yieldValue() instanceof Result or) {
4037 if (or.op() instanceof TupleOp top) {
4038 initValues.addAll(block.context().getValues(top.operands()));
4039 } else {
4040 initValues.addAll(block.context().getValues(yop.operands()));
4041 }
4042 block.add(branch(header.reference()));
4043 yield block;
4044 }
4045
4046 throw new IllegalStateException("Bad yield operation");
4047 }
4048 default -> null;
4049 }));
4050
4051 ControlFlowBooleanExpressionOp.lowerBooleanBody(header, condBody, initValues,
4052 new ControlFlowBooleanExpressionOp.ConditionalBranchContinuation(body.reference(), exit.reference()),
4053 inherited);
4054
4055 BranchTarget.setBranchTarget(b.context(), this, exit, update);
4056
4057 body.transformBody(loopBody, initValues, loweringTransformer(inherited, (_, _) -> null));
4058
4059 update.transformBody(updateBody, initValues, loweringTransformer(inherited, (block, op) -> {
4060 if (op instanceof CoreOp.YieldOp) {
4061 block.add(branch(header.reference()));
4062 return block;
4063 } else {
4064 return null;
4065 }
4066 }));
4067
4068 return exit;
4069 }
4070
4071 @Override
4072 public CodeType resultType() {
4073 return VOID;
4074 }
4075 }
4076
4077 /**
4078 * The enhanced for operation, that can model a Java language enhanced for statement.
4079 * <p>
4080 * Enhanced-for operations feature three bodies. The <em>expression body</em> models the expression to be
4081 * iterated. The <em>definition body</em> models the definition of the loop variable. The <em>loop body</em>
4082 * models the statements to execute.
4083 * <p>
4084 * The expression body accepts no arguments and yields a value of type {@code I}, corresponding to the type of the
4085 * expression to be iterated. The definition body accepts one argument of type {@code E}, corresponding to an element
4086 * type derived from {@code I}, and yields a value of type {@code V}, the type of the loop variable. Finally, the loop
4087 * body accepts that value and yields {@linkplain JavaType#VOID no value}.
4088 * <p>
4089 * The result type of an enhanced-for operation is {@link JavaType#VOID}.
4090 *
4091 * @jls 14.14.2 The enhanced for statement
4092 */
4093 @OpDeclaration(EnhancedForOp.NAME)
4094 public static final class EnhancedForOp extends AbstractOp
4095 implements JavaOp, Op.Loop, Op.Lowerable, JavaStatement {
4096
4097 /**
4098 * Builder for the expression body of an enhanced-for operation.
4099 */
4100 public static final class ExpressionBuilder {
4101 final Body.Builder connectedAncestorBody;
4102 final CodeType iterableType;
4103 final CodeType elementType;
4104
4105 ExpressionBuilder(Body.Builder connectedAncestorBody,
4106 CodeType iterableType, CodeType elementType) {
4107 this.connectedAncestorBody = connectedAncestorBody;
4108 this.iterableType = iterableType;
4109 this.elementType = elementType;
4110 }
4111
4112 /**
4113 * Builds the expression body of an enhanced-for operation.
4114 *
4115 * @param c a consumer that populates the expression body
4116 * @return a builder for specifying the definition body
4117 */
4118 public DefinitionBuilder expression(Consumer<Block.Builder> c) {
4119 Body.Builder expression = Body.Builder.of(connectedAncestorBody,
4120 CoreType.functionType(iterableType));
4121 c.accept(expression.entryBlock());
4122
4123 return new DefinitionBuilder(connectedAncestorBody, elementType, expression);
4124 }
4125 }
4126
4127 /**
4128 * Builder for the definition body of an enhanced-for operation.
4129 */
4130 public static final class DefinitionBuilder {
4131 final Body.Builder connectedAncestorBody;
4132 final CodeType elementType;
4133 final Body.Builder expression;
4134
4135 DefinitionBuilder(Body.Builder connectedAncestorBody,
4136 CodeType elementType, Body.Builder expression) {
4137 this.connectedAncestorBody = connectedAncestorBody;
4138 this.elementType = elementType;
4139 this.expression = expression;
4140 }
4141
4142 /**
4143 * Builds the definition body of an enhanced-for operation, using a type derived from the type
4144 * of the loop expression.
4145 *
4146 * @param c a consumer that populates the definition body
4147 * @return a builder for specifying the loop body
4148 */
4149 public BodyBuilder definition(Consumer<Block.Builder> c) {
4150 return definition(elementType, c);
4151 }
4152
4153 /**
4154 * Builds the definition body of an enhanced-for operation with the provided type.
4155 *
4156 * @param bodyElementType the type to provide to the loop body
4157 * @param c a consumer that populates the definition body
4158 * @return a builder for specifying the loop body
4159 */
4160 public BodyBuilder definition(CodeType bodyElementType, Consumer<Block.Builder> c) {
4161 Body.Builder definition = Body.Builder.of(connectedAncestorBody,
4162 CoreType.functionType(bodyElementType, elementType));
4163 c.accept(definition.entryBlock());
4164
4165 return new BodyBuilder(connectedAncestorBody, elementType, expression, definition);
4166 }
4167 }
4168
4169 /**
4170 * Builder for the loop body of an enhanced-for operation.
4171 */
4172 public static final class BodyBuilder {
4173 final Body.Builder connectedAncestorBody;
4174 final CodeType elementType;
4175 final Body.Builder expression;
4176 final Body.Builder definition;
4177
4178 BodyBuilder(Body.Builder connectedAncestorBody,
4179 CodeType elementType, Body.Builder expression, Body.Builder definition) {
4180 this.connectedAncestorBody = connectedAncestorBody;
4181 this.elementType = elementType;
4182 this.expression = expression;
4183 this.definition = definition;
4184 }
4185
4186 /**
4187 * Completes the enhanced-for operation by adding the loop body.
4188 *
4189 * @param c a consumer that populates the loop body
4190 * @return the completed enhanced-for operation
4191 */
4192 public EnhancedForOp body(Consumer<Block.Builder> c) {
4193 Body.Builder body = Body.Builder.of(connectedAncestorBody,
4194 CoreType.functionType(VOID, elementType));
4195 c.accept(body.entryBlock());
4196
4197 return new EnhancedForOp(expression, definition, body);
4198 }
4199 }
4200
4201 static final String NAME = "java.enhancedFor";
4202
4203 final Body exprBody;
4204 final Body initBody;
4205 final Body loopBody;
4206
4207 EnhancedForOp(ExternalizedOp def) {
4208 List<Body.Builder> bodies = requireBodies(def, 3);
4209 this(bodies.get(0), bodies.get(1), bodies.get(2));
4210 }
4211
4212 EnhancedForOp(EnhancedForOp that, CodeContext cc, CodeTransformer ct) {
4213 super(that, cc);
4214
4215 this.exprBody = that.exprBody.transform(cc, ct).build(this);
4216 this.initBody = that.initBody.transform(cc, ct).build(this);
4217 this.loopBody = that.loopBody.transform(cc, ct).build(this);
4218 }
4219
4220 @Override
4221 public EnhancedForOp transform(CodeContext cc, CodeTransformer ct) {
4222 return new EnhancedForOp(this, cc, ct);
4223 }
4224
4225 EnhancedForOp(Body.Builder expressionC, Body.Builder initC, Body.Builder bodyC) {
4226 super(List.of());
4227
4228 this.exprBody = requireNonVoidReturnType(NAME + " expression", expressionC, 0).build(this);
4229 this.initBody = requireNonVoidReturnType(NAME + " initialization", initC, 1).build(this);
4230 this.loopBody = requireVoidReturnType(NAME + " loop", bodyC, 1).build(this);
4231 }
4232
4233 @Override
4234 public List<Body> bodies() {
4235 return List.of(exprBody, initBody, loopBody);
4236 }
4237
4238 /**
4239 * {@return the expression body}
4240 */
4241 public Body exprBody() {
4242 return exprBody;
4243 }
4244
4245 /**
4246 * {@return the initialization body}
4247 */
4248 public Body initBody() {
4249 return initBody;
4250 }
4251
4252 @Override
4253 public Body loopBody() {
4254 return loopBody;
4255 }
4256
4257 static final MethodRef ITERABLE_ITERATOR = MethodRef.method(Iterable.class, "iterator", Iterator.class);
4258 static final MethodRef ITERATOR_HAS_NEXT = MethodRef.method(Iterator.class, "hasNext", boolean.class);
4259 static final MethodRef ITERATOR_NEXT = MethodRef.method(Iterator.class, "next", Object.class);
4260
4261 @Override
4262 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
4263 JavaType elementType = (JavaType) initBody.entryBlock().parameters().get(0).type();
4264 boolean isArray = exprBody.bodySignature().returnType() instanceof ArrayType;
4265
4266 Block.Builder preHeader = b.block(exprBody.bodySignature().returnType());
4267 Block.Builder header = b.block(isArray ? List.of(INT) : List.of());
4268 Block.Builder init = b.block();
4269 Block.Builder body = b.block();
4270 Block.Builder exit = b.block();
4271
4272 b.transformBody(exprBody, List.of(), loweringTransformer(inherited, (block, op) -> {
4273 if (op instanceof CoreOp.YieldOp yop) {
4274 Value loopSource = block.context().getValue(yop.yieldValue());
4275 block.add(branch(preHeader.reference(loopSource)));
4276 return block;
4277 } else {
4278 return null;
4279 }
4280 }));
4281
4282 if (isArray) {
4283 Value array = preHeader.parameters().get(0);
4284 Value arrayLength = preHeader.add(arrayLength(array));
4285 Value i = preHeader.add(constant(INT, 0));
4286 preHeader.add(branch(header.reference(i)));
4287
4288 i = header.parameters().get(0);
4289 Value p = header.add(lt(i, arrayLength));
4290 header.add(conditionalBranch(p, init.reference(), exit.reference()));
4291
4292 Value e = init.add(arrayLoadOp(array, i));
4293 List<Value> initValues = new ArrayList<>();
4294 init.transformBody(this.initBody, List.of(e), loweringTransformer(inherited, (block, op) -> {
4295 if (op instanceof CoreOp.YieldOp yop) {
4296 initValues.addAll(block.context().getValues(yop.operands()));
4297 block.add(branch(body.reference()));
4298 return block;
4299 } else {
4300 return null;
4301 }
4302 }));
4303
4304 Block.Builder update = b.block();
4305 BranchTarget.setBranchTarget(b.context(), this, exit, update);
4306
4307 body.transformBody(this.loopBody, initValues, loweringTransformer(inherited, (_, _) -> null));
4308
4309 i = update.add(add(i, update.add(constant(INT, 1))));
4310 update.add(branch(header.reference(i)));
4311 } else {
4312 JavaType iterable = parameterized(type(Iterator.class), elementType);
4313 Value iterator = preHeader.add(invoke(iterable, ITERABLE_ITERATOR, preHeader.parameters().get(0)));
4314 preHeader.add(branch(header.reference()));
4315
4316 Value p = header.add(invoke(ITERATOR_HAS_NEXT, iterator));
4317 header.add(conditionalBranch(p, init.reference(), exit.reference()));
4318
4319 Value e = init.add(invoke(elementType, ITERATOR_NEXT, iterator));
4320 List<Value> initValues = new ArrayList<>();
4321 init.transformBody(this.initBody, List.of(e), loweringTransformer(inherited, (block, op) -> {
4322 if (op instanceof CoreOp.YieldOp yop) {
4323 initValues.addAll(block.context().getValues(yop.operands()));
4324 block.add(branch(body.reference()));
4325 return block;
4326 } else {
4327 return null;
4328 }
4329 }));
4330
4331 BranchTarget.setBranchTarget(b.context(), this, exit, header);
4332
4333 body.transformBody(this.loopBody, initValues, loweringTransformer(inherited, (_, _) -> null));
4334 }
4335
4336 return exit;
4337 }
4338
4339 @Override
4340 public CodeType resultType() {
4341 return VOID;
4342 }
4343 }
4344
4345 /**
4346 * The while operation, that can model a Java language while statement.
4347 * <p>
4348 * While operations feature two bodies. The <em>predicate body</em> models the loop condition.
4349 * The <em>loop body</em> models the statements to execute.
4350 * <p>
4351 * The predicate body should accept no arguments and yield a {@link JavaType#BOOLEAN} value.
4352 * The loop body should accept no arguments, and yield {@linkplain JavaType#VOID no value}.
4353 * <p>
4354 * The result type of a while operation is {@link JavaType#VOID}.
4355 *
4356 * @jls 14.12 The while Statement
4357 */
4358 @OpDeclaration(WhileOp.NAME)
4359 public static final class WhileOp extends AbstractOp
4360 implements JavaOp, Op.Loop, Op.Lowerable, JavaStatement {
4361
4362 /**
4363 * Builder for the predicate body of a while operation.
4364 */
4365 public static class PredicateBuilder {
4366 final Body.Builder connectedAncestorBody;
4367
4368 PredicateBuilder(Body.Builder connectedAncestorBody) {
4369 this.connectedAncestorBody = connectedAncestorBody;
4370 }
4371
4372 /**
4373 * Builds the predicate body of a while operation.
4374 *
4375 * @param c a consumer that populates the predicate body
4376 * @return a builder for specifying the loop body
4377 */
4378 public WhileOp.BodyBuilder predicate(Consumer<Block.Builder> c) {
4379 Body.Builder body = Body.Builder.of(connectedAncestorBody, CoreType.functionType(BOOLEAN));
4380 c.accept(body.entryBlock());
4381
4382 return new WhileOp.BodyBuilder(connectedAncestorBody, body);
4383 }
4384 }
4385
4386 /**
4387 * Builder for the loop body of a while operation.
4388 */
4389 public static class BodyBuilder {
4390 final Body.Builder connectedAncestorBody;
4391 private final Body.Builder predicate;
4392
4393 BodyBuilder(Body.Builder connectedAncestorBody, Body.Builder predicate) {
4394 this.connectedAncestorBody = connectedAncestorBody;
4395 this.predicate = predicate;
4396 }
4397
4398 /**
4399 * Completes the while operation by adding the loop body.
4400 *
4401 * @param c a consumer that populates the loop body
4402 * @return the completed while operation
4403 */
4404 public WhileOp body(Consumer<Block.Builder> c) {
4405 Body.Builder body = Body.Builder.of(connectedAncestorBody, CoreType.FUNCTION_TYPE_VOID);
4406 c.accept(body.entryBlock());
4407
4408 return new WhileOp(predicate, body);
4409 }
4410 }
4411
4412 private static final String NAME = "java.while";
4413
4414 private final List<Body> bodies;
4415
4416 WhileOp(ExternalizedOp def) {
4417 List<Body.Builder> bodies = requireBodies(def, 2);
4418 this(bodies.get(0), bodies.get(1));
4419 }
4420
4421 WhileOp(Body.Builder predicate, Body.Builder body) {
4422 super(List.of());
4423 this.bodies = List.of(requireBodySignature(NAME + " predicate", predicate, CoreType.functionType(BOOLEAN)).build(this),
4424 requireVoidBodySignature(NAME + " body", body).build(this));
4425 }
4426
4427 WhileOp(WhileOp that, CodeContext cc, CodeTransformer ct) {
4428 super(that, cc);
4429
4430 this.bodies = that.bodies.stream()
4431 .map(b -> b.transform(cc, ct).build(this)).toList();
4432 }
4433
4434 @Override
4435 public WhileOp transform(CodeContext cc, CodeTransformer ct) {
4436 return new WhileOp(this, cc, ct);
4437 }
4438
4439 @Override
4440 public List<Body> bodies() {
4441 return bodies;
4442 }
4443
4444 /**
4445 * {@return the loop condition body}
4446 */
4447 public Body predicateBody() {
4448 return bodies.get(0);
4449 }
4450
4451 @Override
4452 public Body loopBody() {
4453 return bodies.get(1);
4454 }
4455
4456 @Override
4457 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
4458 Block.Builder header = b.block();
4459 Block.Builder body = b.block();
4460 Block.Builder exit = b.block();
4461
4462 b.add(branch(header.reference()));
4463
4464 ControlFlowBooleanExpressionOp.lowerBooleanBody(header, predicateBody(), List.of(),
4465 new ControlFlowBooleanExpressionOp.ConditionalBranchContinuation(body.reference(), exit.reference()),
4466 inherited);
4467
4468 BranchTarget.setBranchTarget(b.context(), this, exit, header);
4469
4470 body.transformBody(loopBody(), List.of(), loweringTransformer(inherited, (_, _) -> null));
4471
4472 return exit;
4473 }
4474
4475 @Override
4476 public CodeType resultType() {
4477 return VOID;
4478 }
4479 }
4480
4481 /**
4482 * The do-while operation, that can model a Java language do statement.
4483 * <p>
4484 * Do-while operations feature two bodies. The <em>loop body</em> models the statements to execute.
4485 * The <em>predicate body</em> models the loop condition.
4486 * <p>
4487 * The loop body should accept no arguments, and yield {@linkplain JavaType#VOID no value}. The predicate body
4488 * should accept no arguments, and yield a {@link JavaType#BOOLEAN} value.
4489 * <p>
4490 * The result type of a do-while operation is {@link JavaType#VOID}.
4491 *
4492 * @jls 14.13 The do Statement
4493 */
4494 // @@@ Unify JavaDoWhileOp and JavaWhileOp with common abstract superclass
4495 @OpDeclaration(DoWhileOp.NAME)
4496 public static final class DoWhileOp extends AbstractOp
4497 implements JavaOp, Op.Loop, Op.Lowerable, JavaStatement {
4498
4499 /**
4500 * Builder for the predicate body of a do-while operation.
4501 */
4502 public static class PredicateBuilder {
4503 final Body.Builder connectedAncestorBody;
4504 private final Body.Builder body;
4505
4506 PredicateBuilder(Body.Builder connectedAncestorBody, Body.Builder body) {
4507 this.connectedAncestorBody = connectedAncestorBody;
4508 this.body = body;
4509 }
4510
4511 /**
4512 * Completes the do-while operation by adding the predicate body.
4513 *
4514 * @param c a consumer that populates the predicate body
4515 * @return the completed do-while operation
4516 */
4517 public DoWhileOp predicate(Consumer<Block.Builder> c) {
4518 Body.Builder predicate = Body.Builder.of(connectedAncestorBody, CoreType.functionType(BOOLEAN));
4519 c.accept(predicate.entryBlock());
4520 return new DoWhileOp(body, predicate);
4521 }
4522 }
4523
4524 /**
4525 * Builder for the loop body of a do-while operation.
4526 */
4527 public static class BodyBuilder {
4528 final Body.Builder connectedAncestorBody;
4529
4530 BodyBuilder(Body.Builder connectedAncestorBody) {
4531 this.connectedAncestorBody = connectedAncestorBody;
4532 }
4533
4534 /**
4535 * Builds the loop body of a do-while operation.
4536 *
4537 * @param c a consumer that populates the loop body
4538 * @return a builder for specifying the predicate body
4539 */
4540 public DoWhileOp.PredicateBuilder body(Consumer<Block.Builder> c) {
4541 Body.Builder body = Body.Builder.of(connectedAncestorBody, CoreType.FUNCTION_TYPE_VOID);
4542 c.accept(body.entryBlock());
4543
4544 return new DoWhileOp.PredicateBuilder(connectedAncestorBody, body);
4545 }
4546 }
4547
4548 private static final String NAME = "java.do.while";
4549
4550 private final List<Body> bodies;
4551
4552 DoWhileOp(ExternalizedOp def) {
4553 List<Body.Builder> bodies = requireBodies(def, 2);
4554 this(bodies.get(0), bodies.get(1));
4555 }
4556
4557 DoWhileOp(Body.Builder body, Body.Builder predicate) {
4558 super(List.of());
4559
4560 Objects.requireNonNull(body);
4561
4562 this.bodies = List.of(requireVoidBodySignature(NAME + " body", body).build(this),
4563 requireBodySignature(NAME + " predicate", predicate, CoreType.functionType(BOOLEAN)).build(this));
4564 }
4565
4566 DoWhileOp(DoWhileOp that, CodeContext cc, CodeTransformer ct) {
4567 super(that, cc);
4568
4569 this.bodies = that.bodies.stream()
4570 .map(b -> b.transform(cc, ct).build(this)).toList();
4571 }
4572
4573 @Override
4574 public DoWhileOp transform(CodeContext cc, CodeTransformer ct) {
4575 return new DoWhileOp(this, cc, ct);
4576 }
4577
4578 @Override
4579 public List<Body> bodies() {
4580 return bodies;
4581 }
4582
4583 /**
4584 * {@return the predicate body for the do-while operation}
4585 */
4586 public Body predicateBody() {
4587 return bodies.get(1);
4588 }
4589
4590 @Override
4591 public Body loopBody() {
4592 return bodies.get(0);
4593 }
4594
4595 @Override
4596 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
4597 Block.Builder body = b.block();
4598 Block.Builder header = b.block();
4599 Block.Builder exit = b.block();
4600
4601 b.add(branch(body.reference()));
4602
4603 BranchTarget.setBranchTarget(b.context(), this, exit, header);
4604
4605 body.transformBody(loopBody(), List.of(), loweringTransformer(inherited, (_, _) -> null));
4606
4607 ControlFlowBooleanExpressionOp.lowerBooleanBody(header, predicateBody(), List.of(),
4608 new ControlFlowBooleanExpressionOp.ConditionalBranchContinuation(body.reference(), exit.reference()),
4609 inherited);
4610
4611 return exit;
4612 }
4613
4614 @Override
4615 public CodeType resultType() {
4616 return VOID;
4617 }
4618 }
4619
4620 /**
4621 * The conditional operation, that can model Java language conditional-and and conditional-or expressions.
4622 * <p>
4623 * Conditional operations feature two or more predicate bodies, each yielding a {@link JavaType#BOOLEAN} value.
4624 *
4625 * @jls 15.23 Conditional-And Operator {@code &&}
4626 * @jls 15.24 Conditional-Or Operator {@code ||}
4627 */
4628 public sealed static abstract class ConditionalOp extends AbstractOp
4629 implements JavaOp, Op.Nested, ControlFlowBooleanExpressionOp, JavaExpression
4630 permits ConditionalAndOp, ConditionalOrOp {
4631
4632 static final FunctionType BODY_TYPE = CoreType.functionType(BOOLEAN);
4633
4634 // 2 or more bodies
4635 // See use for modeling multi-label cases of switch statements/expressions
4636 final List<Body> bodies;
4637
4638 ConditionalOp(ConditionalOp that, CodeContext cc, CodeTransformer ct) {
4639 super(that, cc);
4640
4641 this.bodies = that.bodies.stream().map(b -> b.transform(cc, ct).build(this)).toList();
4642 }
4643
4644 ConditionalOp(List<Body.Builder> bodyCs) {
4645 super(List.of());
4646
4647 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList();
4648 }
4649
4650 @Override
4651 public List<Body> bodies() {
4652 return bodies;
4653 }
4654
4655 @Override
4656 public CodeType resultType() {
4657 return BOOLEAN;
4658 }
4659
4660 @Override
4661 public Block.Builder lower(Block.Builder lhs, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
4662 // Poll for implicit boolean continuation parameter
4663 BooleanResultContinuation continuation = BOOLEAN_CONTINUATION_ARG.poll(lhs.context());
4664 Block.Builder exit = lhs;
4665 if (continuation == null) {
4666 exit = lhs.block();
4667 lhs.context().mapValue(result(), exit.parameter(resultType()));
4668 continuation = new BranchWithArgumentContinuation(exit);
4669 }
4670 lowerTo(lhs, inherited, continuation);
4671 return exit;
4672 }
4673
4674 void lowerTo(Block.Builder lhs, BiFunction<Block.Builder, Op, Block.Builder> inherited,
4675 BooleanResultContinuation continuation) {
4676 boolean isAnd = this instanceof ConditionalAndOp;
4677 Block.Reference shortCircuitRef = continuation.referenceFor(lhs, !isAnd);
4678
4679 // Lower all but the last body
4680 for (int i = 0; i < bodies().size() - 1; i++) {
4681 Block.Builder rhs = lhs.block();
4682
4683 ConditionalBranchContinuation bodyContinuation = isAnd
4684 ? new ConditionalBranchContinuation(rhs.reference(), shortCircuitRef)
4685 : new ConditionalBranchContinuation(shortCircuitRef, rhs.reference());
4686
4687 ControlFlowBooleanExpressionOp.lowerBooleanBody(lhs, bodies().get(i), List.of(), bodyContinuation, inherited);
4688
4689 lhs = rhs;
4690 }
4691
4692 // Lower the last body
4693 ControlFlowBooleanExpressionOp.lowerBooleanBody(lhs, bodies().getLast(),List.of(), continuation, inherited);
4694 }
4695 }
4696
4697 /**
4698 * The conditional-and operation, that can model Java language conditional-and expressions.
4699 *
4700 * @jls 15.23 Conditional-And Operator {@code &&}
4701 */
4702 @OpDeclaration(ConditionalAndOp.NAME)
4703 public static final class ConditionalAndOp extends ConditionalOp {
4704
4705 /**
4706 * Builder for conditional-and operations.
4707 */
4708 public static class Builder {
4709 final Body.Builder connectedAncestorBody;
4710 final List<Body.Builder> bodies;
4711
4712 Builder(Body.Builder connectedAncestorBody, Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) {
4713 this.connectedAncestorBody = connectedAncestorBody;
4714 this.bodies = new ArrayList<>();
4715 and(lhs);
4716 and(rhs);
4717 }
4718
4719 /**
4720 * Adds a predicate body to this conditional-and operation.
4721 *
4722 * @param c a consumer that populates the predicate body
4723 * @return this builder
4724 */
4725 public Builder and(Consumer<Block.Builder> c) {
4726 Body.Builder body = Body.Builder.of(connectedAncestorBody, CoreType.functionType(BOOLEAN));
4727 c.accept(body.entryBlock());
4728 bodies.add(body);
4729
4730 return this;
4731 }
4732
4733 /**
4734 * {@return the completed conditional-and operation}
4735 */
4736 public ConditionalAndOp build() {
4737 return new ConditionalAndOp(bodies);
4738 }
4739 }
4740
4741 static final String NAME = "java.cand";
4742
4743 ConditionalAndOp(ExternalizedOp def) {
4744 this(def.bodyDefinitions());
4745 }
4746
4747 ConditionalAndOp(ConditionalAndOp that, CodeContext cc, CodeTransformer ct) {
4748 super(that, cc, ct);
4749 }
4750
4751 @Override
4752 public ConditionalAndOp transform(CodeContext cc, CodeTransformer ct) {
4753 return new ConditionalAndOp(this, cc, ct);
4754 }
4755
4756 ConditionalAndOp(List<Body.Builder> bodyCs) {
4757 bodyCs.forEach(b -> requireBodySignature(NAME, b, BODY_TYPE));
4758 super(requireMinBodies(NAME, bodyCs, 2));
4759 }
4760 }
4761
4762 /**
4763 * The conditional-or operation, that can model Java language conditional-or expressions.
4764 *
4765 * @jls 15.24 Conditional-Or Operator {@code ||}
4766 */
4767 @OpDeclaration(ConditionalOrOp.NAME)
4768 public static final class ConditionalOrOp extends ConditionalOp {
4769
4770 /**
4771 * Builder for conditional-or operations.
4772 */
4773 public static class Builder {
4774 final Body.Builder connectedAncestorBody;
4775 final List<Body.Builder> bodies;
4776
4777 Builder(Body.Builder connectedAncestorBody, Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) {
4778 this.connectedAncestorBody = connectedAncestorBody;
4779 this.bodies = new ArrayList<>();
4780 or(lhs);
4781 or(rhs);
4782 }
4783
4784 /**
4785 * Adds a predicate body to this conditional-or operation.
4786 *
4787 * @param c a consumer that populates the predicate body
4788 * @return this builder
4789 */
4790 public Builder or(Consumer<Block.Builder> c) {
4791 Body.Builder body = Body.Builder.of(connectedAncestorBody, CoreType.functionType(BOOLEAN));
4792 c.accept(body.entryBlock());
4793 bodies.add(body);
4794
4795 return this;
4796 }
4797
4798 /**
4799 * {@return the completed conditional-or operation}
4800 */
4801 public ConditionalOrOp build() {
4802 return new ConditionalOrOp(bodies);
4803 }
4804 }
4805
4806 static final String NAME = "java.cor";
4807
4808 ConditionalOrOp(ExternalizedOp def) {
4809 this(def.bodyDefinitions());
4810 }
4811
4812 ConditionalOrOp(ConditionalOrOp that, CodeContext cc, CodeTransformer ct) {
4813 super(that, cc, ct);
4814 }
4815
4816 @Override
4817 public ConditionalOrOp transform(CodeContext cc, CodeTransformer ct) {
4818 return new ConditionalOrOp(this, cc, ct);
4819 }
4820
4821 ConditionalOrOp(List<Body.Builder> bodyCs) {
4822 bodyCs.forEach(b -> requireBodySignature(NAME, b, BODY_TYPE));
4823 super(requireMinBodies(NAME, bodyCs, 2));
4824 }
4825 }
4826
4827 /**
4828 * The conditional operation, that can model Java language conditional operator {@code ?} expressions.
4829 * <p>
4830 * Conditional expression operations feature three bodies: the predicate body, the true body, and the false body.
4831 * <p>
4832 * The predicate body accepts no arguments and yields a {@link JavaType#BOOLEAN} value.
4833 * The true and false bodies accepts no arguments and yield a value.
4834 *
4835 * @jls 15.25 Conditional Operator {@code ? :}
4836 */
4837 @OpDeclaration(ConditionalExpressionOp.NAME)
4838 public static final class ConditionalExpressionOp extends AbstractOp
4839 implements JavaOp, Op.Nested, ControlFlowBooleanExpressionOp, JavaExpression {
4840
4841 static final String NAME = "java.cexpression";
4842
4843 final CodeType resultType;
4844 // {cond, truepart, falsepart}
4845 final List<Body> bodies;
4846
4847 ConditionalExpressionOp(ExternalizedOp def) {
4848 List<Body.Builder> bodies = requireBodies(def, 3);
4849 this(def.resultType(), bodies.get(0), bodies.get(1), bodies.get(2));
4850 }
4851
4852 ConditionalExpressionOp(ConditionalExpressionOp that, CodeContext cc, CodeTransformer ct) {
4853 super(that, cc);
4854
4855 // Copy body
4856 this.bodies = that.bodies.stream()
4857 .map(b -> b.transform(cc, ct).build(this)).toList();
4858 this.resultType = that.resultType;
4859 }
4860
4861 @Override
4862 public ConditionalExpressionOp transform(CodeContext cc, CodeTransformer ct) {
4863 return new ConditionalExpressionOp(this, cc, ct);
4864 }
4865
4866 ConditionalExpressionOp(CodeType expressionType, Body.Builder predicateBody, Body.Builder trueBody, Body.Builder falseBody) {
4867 super(List.of());
4868
4869 this.bodies = List.of(requireBodySignature(NAME + " predicate", predicateBody, CoreType.functionType(BOOLEAN)).build(this),
4870 requireNoParameters(NAME + " true body", trueBody).build(this),
4871 requireNoParameters(NAME + " false body", falseBody).build(this));
4872 // @@@ when expressionType is null, we assume truepart and falsepart have the same yieldType
4873 this.resultType = expressionType == null ? bodies.get(1).yieldType() : expressionType;
4874 }
4875
4876 @Override
4877 public List<Body> bodies() {
4878 return bodies;
4879 }
4880
4881 /**
4882 * {@return the predicate body}
4883 */
4884 public Body predicateBody() {
4885 return bodies.get(0);
4886 }
4887
4888 /**
4889 * {@return the true body}
4890 */
4891 public Body trueBody() {
4892 return bodies.get(1);
4893 }
4894
4895 /**
4896 * {@return the false body}
4897 */
4898 public Body falseBody() {
4899 return bodies.get(2);
4900 }
4901
4902 @Override
4903 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
4904 boolean isBooleanExpression = resultType().equals(BOOLEAN);
4905 // Poll for implicit boolean continuation parameter
4906 BooleanResultContinuation continuation = isBooleanExpression
4907 ? BOOLEAN_CONTINUATION_ARG.poll(b.context())
4908 : null;
4909
4910 // Lower predicate body
4911 Block.Builder trueBlock = b.block();
4912 Block.Builder falseBlock = b.block();
4913 ControlFlowBooleanExpressionOp.lowerBooleanBody(b, predicateBody(), List.of(),
4914 new ConditionalBranchContinuation(trueBlock.reference(), falseBlock.reference()),
4915 inherited);
4916
4917 // Lower true/false bodies depending on if a boolean expression or a value expression
4918 if (isBooleanExpression) {
4919 Block.Builder exit;
4920 if (continuation == null) {
4921 exit = b.block();
4922 b.context().mapValue(result(), exit.parameter(resultType()));
4923 continuation = new BranchWithArgumentContinuation(exit);
4924 } else {
4925 exit = b;
4926 }
4927
4928 ControlFlowBooleanExpressionOp.lowerBooleanBody(trueBlock, trueBody(), List.of(), continuation, inherited);
4929 ControlFlowBooleanExpressionOp.lowerBooleanBody(falseBlock, falseBody(), List.of(), continuation, inherited);
4930 return exit;
4931 } else {
4932 Block.Builder exit = b.block();
4933 b.context().mapValue(result(), exit.parameter(resultType()));
4934
4935 BranchTarget.setBranchTarget(b.context(), this, exit, null);
4936
4937 CodeTransformer exitTransformer = loweringTransformer(inherited, (block, op) -> {
4938 if (op instanceof CoreOp.YieldOp yop) {
4939 block.add(branch(exit.reference(block.context().getValue(yop.yieldValue()))));
4940 return block;
4941 } else {
4942 return null;
4943 }
4944 });
4945 trueBlock.transformBody(trueBody(), List.of(), exitTransformer);
4946 falseBlock.transformBody(falseBody(), List.of(), exitTransformer);
4947 return exit;
4948 }
4949 }
4950
4951 @Override
4952 public CodeType resultType() {
4953 return resultType;
4954 }
4955 }
4956
4957 /**
4958 * The try operation, that can model Java language try statements.
4959 * <p>
4960 * Try operations feature a <em>try body</em>, zero or more <em>catch bodies</em>, and an optional
4961 * <em>finally body</em>. Try operations may also feature zero or more <em>resources bodies</em>, modeling a
4962 * try-with-resources statement.
4963 * <p>
4964 * Each resource body yields a value. The first resource body accepts no arguments. A second resource body accepts
4965 * an argument whose type is the same as the yield type of the first resource body. A subsequent resource accepts,
4966 * in order, arguments whose types are the same as all the prior resource body yield types.
4967 * <p>
4968 * The try body yields {@linkplain JavaType#VOID no value}. If one or more resources bodies are present then
4969 * the try body accepts, in order, arguments whose types are the same as the resource bodies yield types.
4970 * <p>
4971 * Each catch body should accept an exception value and yield {@linkplain JavaType#VOID no value}. The
4972 * finally body, if present, should accept no arguments and yield {@linkplain JavaType#VOID no value}.
4973 * <p>
4974 * The result type of a try operation is {@link JavaType#VOID}.
4975 *
4976 * @jls 14.20 The try statement
4977 * @jls 14.20.3 try-with-resources
4978 */
4979 @OpDeclaration(TryOp.NAME)
4980 public static final class TryOp extends AbstractOp
4981 implements JavaOp, Op.Nested, Op.Lowerable, JavaStatement {
4982
4983 // Represents a source targeting operation in a staged model, where the source targeting operation is not in
4984 // the staged model.
4985 // A staged model is a synthetic function operation that contains a normalized try operation extracted from a
4986 // source model.
4987 // The translation that produces the staged model translates a source targeting operation to a staged targeting
4988 // operation when the source targeting operation attempts to exit the extracted try operation. Therefore, a
4989 // staged targeting operation attempts to exit all of its ancestor operations.
4990 // The lowering of a staged targeting operation behaves almost identically to the lowering of its source,
4991 // except that where necessary the staged operation is unstaged to an equivalent source operation and the source
4992 // target is used to obtain branch targets.
4993 private static abstract sealed class AbstractStagedTargetingOp<T extends Op & TargetingOp> extends Terminating
4994 implements JavaOp, Lowerable, TargetingOp
4995 permits StagedReturnOp, StagedYieldOp, StagedStatementTargetingOp {
4996 // The source targeting operation in the source model
4997 // ReturnOp | YieldOp | ContinueOp | BreakOp
4998 final T source;
4999
5000 AbstractStagedTargetingOp(T source, List<Value> operands) {
5001 super(operands);
5002
5003 assert source instanceof ReturnOp || source instanceof YieldOp || source instanceof StatementTargetingOp;
5004 this.source = source;
5005 }
5006
5007 AbstractStagedTargetingOp(AbstractStagedTargetingOp<T> that, CodeContext cc) {
5008 super(that, cc);
5009
5010 this.source = that.source;
5011 }
5012
5013 @Override
5014 public final Op target() {
5015 return source.target();
5016 }
5017
5018 @Override
5019 public final boolean targetsOrAttemptsToExit(Op op) {
5020 // If the given operation is an ancestor of this staged targeting operation, then the staged targeting
5021 // operation attempts to exit the given operation
5022 // Otherwise, the given operation is in the source model, so test the given operation against the
5023 // source. This can occur for an ancestor try operation that is not normalized, or an ancestor
5024 // synchronized operation. Specifically, when lowering such an operation all descendant targeting
5025 // operations that attempt to exit the operation need to be processed. Some of those descendant
5026 // targeting operations may be staged.
5027 return op.isAncestorOf(this) || source.targetsOrAttemptsToExit(op);
5028 }
5029
5030 @Override
5031 public final CodeType resultType() {
5032 return VOID;
5033 }
5034 }
5035
5036 @OpDeclaration("staged.return")
5037 private static final class StagedReturnOp extends AbstractStagedTargetingOp<ReturnOp> {
5038 StagedReturnOp(ReturnOp delegate, Value returnValue) {
5039 super(delegate, returnValue == null ? List.of() : List.of(returnValue));
5040 }
5041
5042 StagedReturnOp(StagedReturnOp that, CodeContext cc) {
5043 super(that, cc);
5044 }
5045
5046 @Override
5047 public StagedReturnOp transform(CodeContext cc, CodeTransformer ct) {
5048 return new StagedReturnOp(this, cc);
5049 }
5050
5051 @Override
5052 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
5053 if (operands().isEmpty()) {
5054 b.add(CoreOp.return_());
5055 } else {
5056 b.add(CoreOp.return_(b.context().getValue(operands().getFirst())));
5057 }
5058 return b;
5059 }
5060 }
5061
5062 @OpDeclaration("staged.java.yield")
5063 private static final class StagedYieldOp extends AbstractStagedTargetingOp<YieldOp> {
5064 StagedYieldOp(YieldOp delegate, Value operand) {
5065 super(delegate, List.of(Objects.requireNonNull(operand)));
5066 }
5067
5068 StagedYieldOp(StagedYieldOp that, CodeContext cc) {
5069 super(that, cc);
5070 }
5071
5072 @Override
5073 public StagedYieldOp transform(CodeContext cc, CodeTransformer ct) {
5074 return new StagedYieldOp(this, cc);
5075 }
5076
5077 @Override
5078 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
5079 // for now, we will use breakBlock field to indicate java.yield target block
5080 return lower(b, BranchTarget::breakBlock);
5081 }
5082
5083 Block.Builder lower(Block.Builder b, Function<BranchTarget, Block.Builder> f) {
5084 Op opt = target();
5085 BranchTarget t = BranchTarget.getBranchTarget(b.context(), opt);
5086 if (t != null) {
5087 b.add(branch(f.apply(t).reference(b.context().getValue(operands().getFirst()))));
5088 } else {
5089 throw new IllegalStateException("No branch target for operation: " + opt);
5090 }
5091 return b;
5092 }
5093 }
5094
5095 @OpDeclaration("staged.java.statement")
5096 private static final class StagedStatementTargetingOp extends AbstractStagedTargetingOp<StatementTargetingOp> {
5097 StagedStatementTargetingOp(StatementTargetingOp delegate) {
5098 super(delegate, List.of());
5099 }
5100
5101 StagedStatementTargetingOp(StagedStatementTargetingOp that, CodeContext cc) {
5102 super(that, cc);
5103 }
5104
5105 @Override
5106 public StagedStatementTargetingOp transform(CodeContext cc, CodeTransformer ct) {
5107 return new StagedStatementTargetingOp(this, cc);
5108 }
5109
5110 @Override
5111 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
5112 return source.lower(b, inherited);
5113 }
5114 }
5115
5116 private static final boolean SHARED_FINALIZER_DISPATCH =
5117 "sharedDispatch".equalsIgnoreCase(System.getProperty("babylon.tryFinally"));
5118
5119 /**
5120 * Builder for the resource bodies and the try body of a try operation.
5121 */
5122 public static final class BodyBuilder {
5123 final Body.Builder connectedAncestorBody;
5124 final List<Body.Builder> resources;
5125
5126 BodyBuilder(Body.Builder connectedAncestorBody) {
5127 this.connectedAncestorBody = connectedAncestorBody;
5128 this.resources = new ArrayList<>();
5129 }
5130
5131 /**
5132 * Adds a resource body to a try-with-resources operation.
5133 *
5134 * @param yieldType the resource type for a resource expression, or the Var type for a resource declaration
5135 * @param c a consumer that populates the resource body
5136 * @return this builder
5137 */
5138 public BodyBuilder resource(CodeType yieldType, Consumer<Block.Builder> c) {
5139 List<CodeType> paramTypes = resources.stream().map(r -> r.bodySignature().returnType()).toList();
5140 Body.Builder resource = Body.Builder.of(connectedAncestorBody,
5141 CoreType.functionType(yieldType, paramTypes));
5142 c.accept(resource.entryBlock());
5143 resources.add(resource);
5144 return this;
5145 }
5146
5147 /**
5148 * Builds the try body of the try operation.
5149 *
5150 * @param c a consumer that populates the try body
5151 * @return a builder for specifying catch bodies and an optional finalizer
5152 */
5153 public CatchBuilder body(Consumer<Block.Builder> c) {
5154 Body.Builder body = Body.Builder.of(connectedAncestorBody,
5155 CoreType.functionType(VOID, resources.stream().map(bb -> bb.bodySignature().returnType()).toList()));
5156 c.accept(body.entryBlock());
5157
5158 return new CatchBuilder(connectedAncestorBody, resources, body);
5159 }
5160 }
5161
5162 /**
5163 * Builder for specifying catch bodies and an optional finalizer body of a try operation.
5164 */
5165 public static final class CatchBuilder {
5166 final Body.Builder connectedAncestorBody;
5167 final List<Body.Builder> resources;
5168 final Body.Builder body;
5169 final List<CodeType> catchTypes;
5170 final List<Body.Builder> handlers;
5171
5172 CatchBuilder(Body.Builder connectedAncestorBody, List<Body.Builder> resources, Body.Builder body) {
5173 this.connectedAncestorBody = connectedAncestorBody;
5174 this.resources = resources;
5175 this.body = body;
5176 this.catchTypes = new ArrayList<>();
5177 this.handlers = new ArrayList<>();
5178 }
5179
5180 /**
5181 * Adds a catch body for handling exceptions of a specific type.
5182 *
5183 * @param handlerExceptionType the type of exception handled
5184 * @param c a consumer that populates the catch body
5185 * @return this builder
5186 */
5187 public CatchBuilder catch_(CodeType handlerExceptionType, Consumer<Block.Builder> c) {
5188 return catch_(handlerExceptionType, handlerExceptionType, c);
5189 }
5190
5191 /**
5192 * Adds a catch body for handling exceptions of a specific catch type and a handler type.
5193 *
5194 * @param catchType the type of exception(s) caught, use {@link TupleType} for a multi-catch
5195 * @param handlerExceptionType the type of exception handled by the catch body
5196 * @param c a consumer that populates the catch body
5197 * @return this builder
5198 */
5199 public CatchBuilder catch_(CodeType catchType, CodeType handlerExceptionType, Consumer<Block.Builder> c) {
5200 Body.Builder _catch = Body.Builder.of(connectedAncestorBody,
5201 CoreType.functionType(VOID, handlerExceptionType));
5202 c.accept(_catch.entryBlock());
5203 handlers.add(_catch);
5204 catchTypes.add(catchType);
5205 return this;
5206 }
5207
5208 /**
5209 * Completes the try operation by adding the finalizer body.
5210 *
5211 * @param c a consumer that populates the finalizer body
5212 * @return the completed try operation
5213 */
5214 public TryOp finally_(Consumer<Block.Builder> c) {
5215 Body.Builder _finally = Body.Builder.of(connectedAncestorBody, CoreType.FUNCTION_TYPE_VOID);
5216 c.accept(_finally.entryBlock());
5217
5218 return new TryOp(resources, body, catchTypes, handlers, _finally);
5219 }
5220
5221 /**
5222 * Completes the try operation without a finalizer body.
5223 *
5224 * @return the completed try operation
5225 */
5226 public TryOp noFinalizer() {
5227 return new TryOp(resources, body, catchTypes, handlers, null);
5228 }
5229 }
5230
5231 static final String NAME = "java.try";
5232 static final String ATTRIBUTE_CATCH_TYPES = NAME + ".catchTypes";
5233 static final MethodRef AUTO_CLOSEABLE_CLOSE_METHOD = MethodRef.method(AutoCloseable.class, "close", void.class);
5234 static final MethodRef THROWABLE_ADD_SUPPRESSED_METHOD = MethodRef.method(Throwable.class, "addSuppressed", void.class, Throwable.class);
5235
5236 final List<Body> resourcesBodies;
5237 final Body body;
5238 final List<CodeType> explicitCatchTypes;
5239 final List<Body> handlers;
5240 final Body finallyBody;
5241
5242 TryOp(ExternalizedOp def) {
5243 List<Body.Builder> bodies = def.bodyDefinitions();
5244 if (bodies.size() < 1) {
5245 throw structuralException(def.name(), "requires at least 1 body");
5246 }
5247 int bodyIndex = 0;
5248 while (bodyIndex < bodies.size() && !bodies.get(bodyIndex).bodySignature().returnType().equals(VOID)) {
5249 bodyIndex++;
5250 }
5251 if (bodyIndex == bodies.size()) {
5252 throw structuralException(def.name(), "no void try body found");
5253 }
5254 List<Body.Builder> resources = bodies.subList(0, bodyIndex);
5255 Body.Builder body = bodies.get(bodyIndex);
5256 Body.Builder last = bodies.getLast();
5257 Body.Builder finalizer;
5258 if (last != body && last.bodySignature().parameterTypes().isEmpty()) {
5259 finalizer = last;
5260 } else {
5261 finalizer = null;
5262 }
5263 List<CodeType> catchTypes = optionalAttribute(def, ATTRIBUTE_CATCH_TYPES, true, TupleType.class)
5264 .map(TupleType::componentTypes).orElse(null);
5265 List<Body.Builder> handlers = bodies.subList(
5266 bodyIndex + 1,
5267 bodies.size() - (finalizer == null ? 0 : 1));
5268
5269 this(resources, body, catchTypes, handlers, finalizer);
5270 }
5271
5272 TryOp(TryOp that, CodeContext cc, CodeTransformer ct) {
5273 super(that, cc);
5274
5275 this.resourcesBodies = that.resourcesBodies.stream()
5276 .map(b -> b.transform(cc, ct).build(this))
5277 .toList();
5278 this.body = that.body.transform(cc, ct).build(this);
5279 this.explicitCatchTypes = that.explicitCatchTypes;
5280 this.handlers = that.handlers.stream()
5281 .map(b -> b.transform(cc, ct).build(this))
5282 .toList();
5283 if (that.finallyBody != null) {
5284 this.finallyBody = that.finallyBody.transform(cc, ct).build(this);
5285 } else {
5286 this.finallyBody = null;
5287 }
5288 }
5289
5290 @Override
5291 public TryOp transform(CodeContext cc, CodeTransformer ct) {
5292 return new TryOp(this, cc, ct);
5293 }
5294
5295 TryOp(List<Body.Builder> resourcesC,
5296 Body.Builder bodyC,
5297 List<CodeType> catchTypes,
5298 List<Body.Builder> handlersC,
5299 Body.Builder finalizerC) {
5300 super(List.of());
5301
5302 List<CodeType> resourceTypes = new ArrayList<>();
5303 for (Body.Builder _resource : resourcesC) {
5304 requireNonVoidReturnType(NAME + " resource", _resource, resourceTypes.size());
5305 if (!_resource.bodySignature().parameterTypes().equals(resourceTypes)) {
5306 throw structuralException(NAME, "resource #%d requires %s parameter types, found %s"
5307 .formatted(resourceTypes.size(), resourceTypes, _resource.bodySignature().parameterTypes()));
5308 }
5309 resourceTypes.add(_resource.bodySignature().returnType());
5310 }
5311 this.resourcesBodies = resourcesC.stream().map(r -> r.build(this)).toList();
5312 this.body = requireBodySignature(NAME + " try",
5313 bodyC, CoreType.functionType(VOID, resourceTypes)).build(this);
5314 this.explicitCatchTypes = catchTypes == null ? null : List.copyOf(catchTypes);
5315 this.handlers = handlersC.stream().map(
5316 c -> requireVoidReturnType(NAME + " catch", c, 1).build(this)).toList();
5317 if (explicitCatchTypes != null && explicitCatchTypes.size() != handlers.size()) {
5318 throw structuralException(NAME, "catch types %s require %d catch bodies, found %d"
5319 .formatted(explicitCatchTypes, explicitCatchTypes.size(), handlers.size()));
5320 }
5321 if (finalizerC != null) {
5322 this.finallyBody = requireVoidBodySignature(NAME + " finalizer", finalizerC).build(this);
5323 } else {
5324 this.finallyBody = null;
5325 }
5326 }
5327
5328 @Override
5329 public Map<String, Object> externalize() {
5330 // avoid storing explicit catch types if they all match the handlers
5331 return explicitCatchTypes == null || explicitCatchTypes.equals(implicitCatchTypes())
5332 ? Map.of()
5333 : Map.of("", CoreType.tupleType(explicitCatchTypes));
5334 }
5335
5336 @Override
5337 public List<Body> bodies() {
5338 ArrayList<Body> bodies = new ArrayList<>();
5339 bodies.addAll(resourcesBodies);
5340 bodies.add(body);
5341 bodies.addAll(handlers);
5342 if (finallyBody != null) {
5343 bodies.add(finallyBody);
5344 }
5345 return bodies;
5346 }
5347
5348 /**
5349 * {@return the resources bodies}
5350 */
5351 public List<Body> resourceBodies() {
5352 return resourcesBodies;
5353 }
5354
5355 /**
5356 * {@return the body of the try operation}
5357 */
5358 public Body body() {
5359 return body;
5360 }
5361
5362 /**
5363 * {@return the catch types}
5364 */
5365 public List<CodeType> catchTypes() {
5366 return explicitCatchTypes == null ? implicitCatchTypes() : explicitCatchTypes;
5367 }
5368
5369 private List<CodeType> implicitCatchTypes() {
5370 return handlers.stream().map(h -> h.entryBlock().parameterTypes().getFirst()).toList();
5371 }
5372
5373 /**
5374 * {@return the catch bodies}
5375 */
5376 public List<Body> catchBodies() {
5377 return handlers;
5378 }
5379
5380 /**
5381 * {@return the finally body, or {@code null} if this try operation has no finally body}
5382 */
5383 public Body finallyBody() {
5384 return finallyBody;
5385 }
5386
5387 @Override
5388 public Block.Builder lower(Block.Builder b, final BiFunction<Block.Builder, Op, Block.Builder> inherited) {
5389 Block.Builder exit = b.block();
5390 BranchTarget.setBranchTarget(b.context(), this, exit, null);
5391
5392 if (!resourcesBodies.isEmpty() || SHARED_FINALIZER_DISPATCH && finallyBody != null) {
5393 List<Value> captures = normalizationCaptures();
5394 Op normalized = normalize(captures);
5395 CodeContext ctx = CodeContext.create(b.context());
5396 ctx.mapValues(normalized.ancestorBody().entryBlock().parameters(), b.context().getValues(captures));
5397 CodeTransformer lowering = loweringTransformer(inherited, (_, _) -> null);
5398 // acceptOp invokes TryOp.lower, but only with normalized try ops so it should never enter here again
5399 lowering.acceptOp(b.withContextAndTransformer(ctx, lowering), normalized)
5400 .add(branch(exit.reference()));
5401 return exit;
5402 }
5403
5404 // Simple case with no catch and finally bodies
5405 if (handlers.isEmpty() && finallyBody == null) {
5406 b.transformBody(body, List.of(), loweringTransformer(inherited, (block, op) -> {
5407 if (op instanceof CoreOp.YieldOp) {
5408 block.add(branch(exit.reference()));
5409 return block;
5410 } else {
5411 return null;
5412 }
5413 }));
5414 return exit;
5415 }
5416
5417 Block.Builder tryRegionEnter = b.block();
5418 Block.Builder tryRegionExit = b.block();
5419
5420 // Construct the catcher block builders
5421 List<Block.Builder> catchers = catchBodies().stream()
5422 .map(catcher -> b.block())
5423 .toList();
5424 List<Block.Reference> exitHandlers = new ArrayList<>();
5425 for (int i = 0; i < catchers.size(); i++) {
5426 Value arg = b.add(constant(catchBodies().get(i).bodySignature().parameterTypes().getFirst(), null));
5427 exitHandlers.add(catchers.get(i).reference(arg));
5428 }
5429 List<CodeType> catchTypes = catchTypes();
5430 Block.Builder catcherFinally;
5431 Op.Result nullThrowable;
5432 if (finallyBody == null) {
5433 catcherFinally = null;
5434 nullThrowable = null;
5435 } else {
5436 catcherFinally = b.block();
5437 catchers = new ArrayList<>(catchers);
5438 catchers.add(catcherFinally);
5439 nullThrowable = b.add(constant(type(Throwable.class), null));
5440 exitHandlers.add(catcherFinally.reference(nullThrowable));
5441 catchTypes = new ArrayList<>(catchTypes);
5442 catchTypes.add(VOID);
5443 }
5444
5445 // Enter the try exception region
5446 Op.Result enter = b.add(exceptionRegionEnter(
5447 catchTypes.reversed(), tryRegionEnter.reference(), exitHandlers.reversed()));
5448
5449 BiFunction<Block.Builder, Op, Block.Builder> tryExitTransformer;
5450 if (finallyBody != null) {
5451 assert !SHARED_FINALIZER_DISPATCH;
5452
5453 tryExitTransformer = composeFirst(inherited, (block, op) -> {
5454 if (op instanceof TargetingOp top && top.targetsOrAttemptsToExit(this)) {
5455 return inlineFinalizer(block, enter, inherited);
5456 } else {
5457 return block;
5458 }
5459 });
5460 } else {
5461 tryExitTransformer = composeFirst(inherited, (block, op) -> {
5462 if (op instanceof TargetingOp top && top.targetsOrAttemptsToExit(this)) {
5463 Block.Builder tryRegionReturnExit = block.block();
5464 block.add(exceptionRegionExit(enter, tryRegionReturnExit.reference()));
5465 return tryRegionReturnExit;
5466 } else {
5467 return block;
5468 }
5469 });
5470 }
5471 // Inline the try body
5472 AtomicBoolean hasTryRegionExit = new AtomicBoolean();
5473 tryRegionEnter.transformBody(body, List.of(), loweringTransformer(tryExitTransformer, (block, op) -> {
5474 if (op instanceof CoreOp.YieldOp) {
5475 hasTryRegionExit.set(true);
5476 block.add(branch(tryRegionExit.reference()));
5477 return block;
5478 } else {
5479 return null;
5480 }
5481 }));
5482
5483 Block.Builder finallyEnter = null;
5484 if (finallyBody != null) {
5485 assert !SHARED_FINALIZER_DISPATCH;
5486
5487 finallyEnter = b.block();
5488 if (hasTryRegionExit.get()) {
5489 // Exit the try exception region
5490 tryRegionExit.add(exceptionRegionExit(enter, finallyEnter.reference()));
5491 }
5492 } else if (hasTryRegionExit.get()) {
5493 // Exit the try exception region
5494 tryRegionExit.add(exceptionRegionExit(enter, exit.reference()));
5495 }
5496
5497 // Inline the catch bodies
5498 for (int i = 0; i < this.handlers.size(); i++) {
5499 Block.Builder catcher = catchers.get(i);
5500 Body catcherBody = this.handlers.get(i);
5501 // Create the throwable argument
5502 Block.Parameter t = catcher.parameter(catcherBody.bodySignature().parameterTypes().get(0));
5503
5504 if (finallyBody != null) {
5505 assert !SHARED_FINALIZER_DISPATCH;
5506
5507 Block.Builder catchRegionEnter = b.block();
5508 Block.Builder catchRegionExit = b.block();
5509
5510 // Enter the catch exception region
5511 Result catchExceptionRegion = catcher.add(
5512 exceptionRegionEnter(catchRegionEnter.reference(),
5513 catcherFinally.reference(nullThrowable)));
5514
5515 BiFunction<Block.Builder, Op, Block.Builder> catchExitTransformer = composeFirst(inherited, (block, op) -> {
5516 if (op instanceof TargetingOp top && top.targetsOrAttemptsToExit(this)) {
5517 return inlineFinalizer(block, catchExceptionRegion, inherited);
5518 } else {
5519 return block;
5520 }
5521 });
5522
5523 // Inline the catch body
5524 AtomicBoolean hasCatchRegionExit = new AtomicBoolean();
5525 catchRegionEnter.transformBody(catcherBody, List.of(t), loweringTransformer(catchExitTransformer, (block, op) -> {
5526 if (op instanceof CoreOp.YieldOp) {
5527 hasCatchRegionExit.set(true);
5528 block.add(branch(catchRegionExit.reference()));
5529 return block;
5530 } else {
5531 return null;
5532 }
5533 }));
5534
5535 // Exit the catch exception region
5536 if (hasCatchRegionExit.get()) {
5537 hasTryRegionExit.set(true);
5538 catchRegionExit.add(exceptionRegionExit(catchExceptionRegion, finallyEnter.reference()));
5539 }
5540 } else {
5541 // Inline the catch body for normal completion
5542 catcher.transformBody(catcherBody, List.of(t), loweringTransformer(inherited, (block, op) -> {
5543 if (op instanceof CoreOp.YieldOp) {
5544 block.add(branch(exit.reference()));
5545 return block;
5546 } else {
5547 return null;
5548 }
5549 }));
5550 }
5551 }
5552
5553 // Inline the finally body as a catcher of Throwable and adjusting to throw
5554 if (finallyBody != null && hasTryRegionExit.get()) {
5555 assert !SHARED_FINALIZER_DISPATCH;
5556
5557 // Inline the finally body for exceptional completion and rethrow
5558 finallyEnter.transformBody(finallyBody, List.of(), loweringTransformer(inherited, (block, op) -> {
5559 if (op instanceof CoreOp.YieldOp) {
5560 block.add(branch(exit.reference()));
5561 return block;
5562 } else {
5563 return null;
5564 }
5565 }));
5566 }
5567
5568 if (finallyBody != null) {
5569 assert !SHARED_FINALIZER_DISPATCH;
5570
5571 // Inline the finally body for exceptional completion and rethrow
5572 Block.Parameter t = catcherFinally.parameter(type(Throwable.class));
5573 catcherFinally.transformBody(finallyBody, List.of(), loweringTransformer(inherited, (block, op) -> {
5574 if (op instanceof CoreOp.YieldOp) {
5575 block.add(throw_(t));
5576 return block;
5577 } else {
5578 return null;
5579 }
5580 }));
5581 }
5582 return exit;
5583 }
5584
5585 /// Normalize try-with-resources in two stages.
5586 ///
5587 /// First normalize an extended form to nested basic forms, one resource per
5588 /// level, left to right.
5589 ///
5590 /// ```
5591 /// extended TWR -> basic TWR -> try/catch/finally
5592 /// ```
5593 Op normalize(List<Value> captures) {
5594 Body.Builder body = Body.Builder.of(null, CoreType.functionType(VOID, captures.stream().map(Value::type).toList()));
5595 Block.Builder entry = body.entryBlock();
5596 entry.context().mapValues(captures, entry.parameters());
5597 entry.context().mapBlock(ancestorBody().entryBlock(), entry);
5598 entry.withContextAndTransformer(entry.context(), this::stageTargetingOps).add(this);
5599 entry.add(return_());
5600
5601 CoreOp.FuncOp root = func("$", body);
5602 root = normalize(root, TryOp::isExtendedTryWithResources, TryOp::normalizeExtendedTryWithResources);
5603 root = normalize(root, TryOp::isBasicTryWithResources, TryOp::normalizeBasicTryWithResources);
5604 if (SHARED_FINALIZER_DISPATCH) {
5605 root = normalize(root, tryOp -> tryOp.finallyBody != null, TryOp::normalizeFinalizer);
5606 }
5607
5608 return root.body().entryBlock().ops().getFirst();
5609 }
5610
5611 static CoreOp.FuncOp normalize(CoreOp.FuncOp root,
5612 Predicate<TryOp> requiresNormalization,
5613 BiFunction<TryOp, Block.Builder, Op.Result> normalizer) {
5614 // normalization repeats until no operations left to normalize
5615 while (root.elements().anyMatch(element -> element instanceof TryOp tryOp && requiresNormalization.test(tryOp))) {
5616 root = root.transform(CodeContext.create(), (block, op) -> {
5617 if (op instanceof TryOp tryOp && requiresNormalization.test(tryOp)) {
5618 block.context().mapValue(tryOp.result(), normalizer.apply(tryOp, block));
5619 } else {
5620 block.add(op);
5621 }
5622 return block;
5623 });
5624 }
5625 return root;
5626 }
5627
5628 boolean isExtendedTryWithResources() {
5629 return !resourcesBodies.isEmpty() && (resourcesBodies.size() != 1 || !handlers.isEmpty() || finallyBody != null);
5630 }
5631
5632 boolean isBasicTryWithResources() {
5633 return resourcesBodies.size() == 1 && handlers.isEmpty() && finallyBody == null;
5634 }
5635
5636 /// Normalize an extended try-with-resources form to nested basic forms, one resource per level.
5637 ///
5638 /// ```
5639 /// try (r1; r2; ...; rn) { body } catch (...) { catches } finally { finalizer }
5640 ///
5641 /// =>
5642 ///
5643 /// try (r1) {
5644 /// try (r2) {
5645 /// ...
5646 /// try (rn) { body }
5647 /// ...
5648 /// }
5649 /// } catch (...) {
5650 /// catches
5651 /// } finally {
5652 /// finalizer
5653 /// }
5654 /// ```
5655 ///
5656 /// @jls 14.20.3 try-with-resources
5657 /// @jls 14.20.3.2 Extended try-with-resources
5658 Op.Result normalizeExtendedTryWithResources(Block.Builder b) {
5659 if (handlers.isEmpty() && finallyBody == null) {
5660 return b.add(normalizeExtendedTryWithResources(b.parentBody(), new ArrayList<>()));
5661 }
5662
5663 CatchBuilder catchBuilder = try_(b.parentBody(), tryBlock -> {
5664 tryBlock.add(normalizeExtendedTryWithResources(tryBlock.parentBody(), new ArrayList<>()));
5665 tryBlock.add(core_yield());
5666 });
5667 List<CodeType> catchTypes = catchTypes();
5668 for (int i = 0; i < handlers.size(); i++) {
5669 Body catcher = handlers.get(i);
5670 catchBuilder.catch_(catchTypes.get(i), catcher.bodySignature().parameterTypes().getFirst(), catchBlock ->
5671 catchBlock.transformBody(catcher, catchBlock.parameters()));
5672 }
5673 return b.add(finallyBody == null
5674 ? catchBuilder.noFinalizer()
5675 : catchBuilder.finally_(finallyBlock ->
5676 finallyBlock.transformBody(finallyBody, List.of())));
5677 }
5678
5679 /// Recursive step for extended try-with-resources.
5680 ///
5681 /// The next resource becomes the current outer basic try-with-resources.
5682 ///
5683 /// @jls 14.20.3.2 Extended try-with-resources
5684 TryOp normalizeExtendedTryWithResources(Body.Builder anc, List<Value> res) {
5685 Body resource = resourcesBodies.get(res.size());
5686 Body.Builder resourceBody = Body.Builder.of(anc, CoreType.functionType(resource.yieldType()));
5687 resourceBody.entryBlock().transformBody(resource, res, resourceBody.entryBlock().context(), resourceBody.entryBlock().transformer());
5688 Body.Builder basicBody = Body.Builder.of(anc, CoreType.functionType(VOID, List.of(resource.yieldType())));
5689 Block.Builder bodyBlock = basicBody.entryBlock();
5690 res.add(bodyBlock.parameters().getFirst());
5691 if (res.size() < resourcesBodies.size()) {
5692 bodyBlock.add(normalizeExtendedTryWithResources(basicBody, res));
5693 bodyBlock.add(core_yield());
5694 } else {
5695 bodyBlock.transformBody(body, res, bodyBlock.context(), bodyBlock.transformer());
5696 }
5697 return try_(List.of(resourceBody), basicBody, List.of(), null);
5698 }
5699
5700 /// Normalize basic try-with-resources to `try / catch / finally`.
5701 ///
5702 /// ```
5703 /// resource = acquire()
5704 /// primary = null
5705 /// try {
5706 /// body(resources)
5707 /// } catch (e) {
5708 /// primary = e
5709 /// throw t
5710 /// } finally {
5711 /// if (resource != null) {
5712 /// if (primary != null) {
5713 /// try { resource.close(); }
5714 /// catch (closeExc) { primary.addSuppressed(closeExc); }
5715 /// } else {
5716 /// resource.close();
5717 /// }
5718 /// }
5719 /// }
5720 /// ```
5721 ///
5722 /// @jls 14.20.3.1 Basic try-with-resources
5723 Op.Result normalizeBasicTryWithResources(Block.Builder b) {
5724 assert resourcesBodies.size() == 1;
5725 Body.Builder normalizedBody = Body.Builder.of(b.parentBody(), CoreType.functionType(VOID));
5726 Block.Builder entryBlock = normalizedBody.entryBlock();
5727 Body resourceBody = resourcesBodies.getFirst();
5728 CodeType resourceType = resourceBody.bodySignature().returnType();
5729 Block.Builder afterAcquire = entryBlock.block(resourceType);
5730 entryBlock.transformBody(resourceBody, List.of(), entryBlock.context(), (block, op) -> {
5731 if (op instanceof CoreOp.YieldOp yop && op.ancestorBody() == resourceBody) {
5732 block.add(branch(afterAcquire.reference(block.context().getValue(yop.yieldValue()))));
5733 } else {
5734 block.add(op);
5735 }
5736 return block;
5737 });
5738 // resource may be a var value if a resource declaration such as
5739 // try (AutoCloseable resource = open()) { ... }
5740 // or a value if an existing resource such as
5741 // AutoCloseable resource = open()
5742 // try (resource) { ... }
5743 // Operations in the resource need to distinguish between them and require
5744 // a load operation for the former
5745 Value resourceArgument = afterAcquire.parameters().getFirst();
5746 Value primaryExceptionVar = afterAcquire.add(var(afterAcquire.add(constant(type(Throwable.class), null))));
5747 // @@@ following builder code may be refactored into a reflected template method transformation
5748 afterAcquire.add(try_(entryBlock.parentBody(), tryEntry -> {
5749 tryEntry.transformBody(body, List.of(resourceArgument), tryEntry.context(), tryEntry.transformer());
5750 }).catch_(type(Throwable.class), catchB -> {
5751 Block.Parameter thrown = catchB.parameters().getFirst();
5752 catchB.add(varStore(primaryExceptionVar, thrown));
5753 catchB.add(throw_(thrown));
5754 }).finally_(finB -> {
5755 Value nullObj = finB.add(constant(J_L_OBJECT, null));
5756 Value resource = resourceArgument.type() instanceof VarType
5757 ? finB.add(varLoad(resourceArgument))
5758 : resourceArgument;
5759 finB.add(if_(finB.parentBody()).if_(predB -> {
5760 predB.add(core_yield(predB.add(neq(resource, nullObj))));
5761 }).then(closeB -> {
5762 Value primaryException = closeB.add(varLoad(primaryExceptionVar));
5763 closeB.add(if_(closeB.parentBody()).if_(predB -> {
5764 predB.add(core_yield(predB.add(neq(primaryException, nullObj))));
5765 }).then(suppB -> {
5766 suppB.add(try_(suppB.parentBody(), tryB -> {
5767 tryB.add(invoke(AUTO_CLOSEABLE_CLOSE_METHOD, resource));
5768 tryB.add(core_yield());
5769 }).catch_(type(Throwable.class), catchB -> {
5770 Block.Parameter closeException = catchB.parameters().getFirst();
5771 catchB.add(invoke(THROWABLE_ADD_SUPPRESSED_METHOD, primaryException, closeException));
5772 catchB.add(core_yield());
5773 }).noFinalizer());
5774 suppB.add(core_yield());
5775 }).else_(normB -> {
5776 normB.add(invoke(AUTO_CLOSEABLE_CLOSE_METHOD, resource));
5777 normB.add(core_yield());
5778 }));
5779 closeB.add(core_yield());
5780 }).noElse());
5781 finB.add(core_yield());
5782 }));
5783 afterAcquire.add(core_yield());
5784 return b.add(try_(List.of(), normalizedBody, List.of(), null));
5785 }
5786
5787 private record FinallyExit(Op op, Value valueVar) {
5788 }
5789
5790 /// Normalize `try / catch / finally` to elemental `try / catch`
5791 ///
5792 /// ```
5793 /// completion = normal
5794 /// pending = null
5795 /// finalizerExit: {
5796 /// try {
5797 /// try { body } catch (...) { catches }
5798 /// record normal, return, break, continue, or yield
5799 /// break finalizerExit
5800 /// } catch (t) {
5801 /// pending = t
5802 /// completion = throw
5803 /// break finalizerExit
5804 /// }
5805 /// }
5806 /// finalizer
5807 /// replay(completion, pending)
5808 /// ```
5809 ///
5810 /// @jls 14.20.2 Execution of try-finally and try-catch-finally
5811 private Op.Result normalizeFinalizer(Block.Builder b) {
5812 Body.Builder normalizedBody = Body.Builder.of(b.parentBody(), CoreType.functionType(VOID));
5813 Block.Builder output = normalizedBody.entryBlock();
5814 Value completionVar = output.add(var(output.add(constant(INT, 0))));
5815 Value exceptionVar = output.add(var(output.add(constant(type(Throwable.class), null))));
5816 List<FinallyExit> exits = new ArrayList<>();
5817
5818 Body.Builder labeledBody = Body.Builder.of(output.parentBody(), CoreType.functionType(VOID));
5819 Block.Builder labeledBlock = labeledBody.entryBlock();
5820 Value exitLabel = labeledBlock.add(constant(J_L_STRING, "$finally"));
5821
5822 CatchBuilder protectedTry = try_(labeledBody, tryBlock -> {
5823 if (handlers.isEmpty()) {
5824 tryBlock.transformBody(body, List.of(),
5825 finalizerExitTransformer(body, exitLabel, completionVar, exits, output));
5826 } else {
5827 CatchBuilder innerTry = try_(tryBlock.parentBody(), innerBlock ->
5828 innerBlock.transformBody(body, List.of(),
5829 finalizerExitTransformer(body, exitLabel, completionVar, exits, output)));
5830 List<CodeType> catchTypes = catchTypes();
5831 for (int i = 0; i < handlers.size(); i++) {
5832 Body catcher = handlers.get(i);
5833 innerTry.catch_(catchTypes.get(i), catcher.bodySignature().parameterTypes().getFirst(),
5834 catchBlock -> catchBlock.transformBody(catcher, catchBlock.parameters(),
5835 finalizerExitTransformer(catcher, exitLabel, completionVar, exits, output)));
5836 }
5837 tryBlock.add(innerTry.noFinalizer());
5838 tryBlock.add(core_yield());
5839 }
5840 });
5841 labeledBlock.add(protectedTry.catch_(type(Throwable.class), catchBlock -> {
5842 catchBlock.add(varStore(exceptionVar, catchBlock.parameters().getFirst()));
5843 completeFinalizer(catchBlock, exitLabel, completionVar, 1);
5844 }).noFinalizer());
5845 labeledBlock.add(core_yield());
5846 output.add(labeled(labeledBody));
5847
5848 Block.Builder afterFinalizer = output.block();
5849 output.transformBody(finallyBody, List.of(), (current, op) -> {
5850 if (op instanceof CoreOp.YieldOp && op.ancestorBody() == finallyBody) {
5851 current.add(branch(afterFinalizer.reference()));
5852 return current;
5853 }
5854 current.add(op);
5855 return current;
5856 });
5857
5858 for (int i = 0; i < exits.size(); i++) {
5859 FinallyExit exit = exits.get(i);
5860 int completion = i + 2;
5861 afterFinalizer.add(if_(afterFinalizer.parentBody()).if_(predicate -> {
5862 Value value = predicate.add(varLoad(completionVar));
5863 predicate.add(core_yield(predicate.add(eq(value, predicate.add(constant(INT, completion))))));
5864 }).then(action -> {
5865 Op exitOp = exit.op();
5866 if (exitOp instanceof TargetingOp && exit.valueVar() != null) {
5867 assert exitOp.operands().size() == 1;
5868
5869 Value returnValue = action.add(varLoad(exit.valueVar()));
5870 action.context().mapValue(exitOp.operands().getFirst(), returnValue);
5871 }
5872 action.add(exitOp);
5873 }).noElse());
5874 }
5875 afterFinalizer.add(if_(afterFinalizer.parentBody()).if_(predicate -> {
5876 Value value = predicate.add(varLoad(completionVar));
5877 predicate.add(core_yield(predicate.add(eq(value, predicate.add(constant(INT, 1))))));
5878 }).then(action -> {
5879 action.add(throw_(action.add(varLoad(exceptionVar))));
5880 }).noElse());
5881 afterFinalizer.add(core_yield());
5882 return b.add(try_(List.of(), normalizedBody, List.of(), null));
5883 }
5884
5885 @SuppressWarnings("fallthrough")
5886 private CodeTransformer finalizerExitTransformer(Body sourceBody, Value exitLabel, Value completionVar,
5887 List<FinallyExit> exits, Block.Builder output) {
5888 return (b, op) -> {
5889 switch (op) {
5890 case CoreOp.YieldOp _ when op.ancestorBody() == sourceBody -> {
5891 completeFinalizer(b, exitLabel, completionVar, 0);
5892 }
5893 case TargetingOp top when top.targetsOrAttemptsToExit(this) -> {
5894 Value valueVar = null;
5895 switch (top) {
5896 case ReturnOp _, StagedReturnOp _ when op.operands().size() == 1 :
5897 case YieldOp _, StagedYieldOp _ : {
5898 Value yieldValue = b.context().getValue(op.operands().getFirst());
5899 valueVar = output.add(var(yieldValue.type()));
5900 b.add(varStore(valueVar, yieldValue));
5901 }
5902 // Fallthrough for all targeting ops
5903 // Including StatementTargetingOp which may have an unmapped operand for its label
5904 default: {
5905 exits.add(new FinallyExit(op, valueVar));
5906 completeFinalizer(b, exitLabel, completionVar, exits.size() + 1);
5907 }
5908 }
5909 }
5910 default -> {
5911 b.add(op);
5912 }
5913 };
5914 return b;
5915 };
5916 }
5917
5918 private static void completeFinalizer(Block.Builder b, Value exitLabel, Value completionVar, int completion) {
5919 b.add(varStore(completionVar, b.add(constant(INT, completion))));
5920 b.add(break_(exitLabel));
5921 }
5922
5923 // Replace targeting operations whose targets are outside the staged model with staged forms.
5924 private Block.Builder stageTargetingOps(Block.Builder block, Op op) {
5925 block.add(switch (op) {
5926 case StatementTargetingOp st when st.targetsOrAttemptsToExit(this) ->
5927 new StagedStatementTargetingOp(st);
5928 case JavaOp.YieldOp yop when yop.targetsOrAttemptsToExit(this) ->
5929 new StagedYieldOp(yop, block.context().getValue(yop.yieldOperand()));
5930 case CoreOp.ReturnOp rop when rop.targetsOrAttemptsToExit(this) -> {
5931 Value returnValue = rop.returnValue() != null
5932 ? block.context().getValue(rop.returnValue())
5933 : null;
5934 yield new StagedReturnOp(rop, returnValue);
5935 }
5936 default -> op;
5937 });
5938 return block;
5939 }
5940
5941 private List<Value> normalizationCaptures() {
5942 return capturedValues().stream()
5943 .filter(value -> !(value instanceof Result result
5944 && result.op().ancestorOp() instanceof LabeledOp labeled
5945 && labeled.labelIdentifier() == result))
5946 .toList();
5947 }
5948
5949 Block.Builder inlineFinalizer(Block.Builder block1, Value enter, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
5950 Block.Builder finallyEnter = block1.block();
5951 Block.Builder finallyExit = block1.block();
5952
5953 block1.add(exceptionRegionExit(enter, finallyEnter.reference()));
5954
5955 // Inline the finally body
5956 finallyEnter.transformBody(finallyBody, List.of(), loweringTransformer(inherited, (block2, op2) -> {
5957 if (op2 instanceof CoreOp.YieldOp) {
5958 block2.add(branch(finallyExit.reference()));
5959 return block2;
5960 } else {
5961 return null;
5962 }
5963 }));
5964
5965 return finallyExit;
5966 }
5967
5968 @Override
5969 public CodeType resultType() {
5970 return VOID;
5971 }
5972 }
5973
5974 //
5975 // Patterns
5976
5977 // Reified pattern nodes
5978
5979 /**
5980 * Synthetic pattern types
5981 * // @@@ Replace with types extending from CodeType
5982 */
5983 public sealed interface Pattern {
5984
5985 /**
5986 * Synthetic type pattern type.
5987 *
5988 * @param <T> the type of values that are bound
5989 */
5990 final class Type<T> implements Pattern {
5991 Type() {
5992 }
5993 }
5994
5995 /**
5996 * Synthetic record pattern type.
5997 *
5998 * @param <T> the type of records that are bound
5999 */
6000 final class Record<T> implements Pattern {
6001 Record() {
6002 }
6003 }
6004
6005 /**
6006 * A synthetic match-all pattern type representing an unconditional pattern.
6007 */
6008 final class MatchAll implements Pattern {
6009 MatchAll() {
6010 }
6011 }
6012
6013 // @@@ Pattern types
6014
6015 /** The synthetic type of a type test pattern. */
6016 JavaType PATTERN_BINDING_TYPE = JavaType.type(Type.class);
6017
6018 /** The synthetic type of a record pattern. */
6019 JavaType PATTERN_RECORD_TYPE = JavaType.type(Record.class);
6020
6021 /** The synthetic type of an unconditional pattern. */
6022 JavaType PATTERN_MATCH_ALL_TYPE = JavaType.type(MatchAll.class);
6023
6024 /**
6025 * {@return a synthetic type for a type test pattern with the provided type}
6026 * @param t the type of the type test pattern
6027 */
6028 static JavaType bindingType(CodeType t) {
6029 return parameterized(PATTERN_BINDING_TYPE, (JavaType) t);
6030 }
6031
6032 /**
6033 * {@return a synthetic type for a record pattern with the provided record type}
6034 * @param t the record type
6035 */
6036 static JavaType recordType(CodeType t) {
6037 return parameterized(PATTERN_RECORD_TYPE, (JavaType) t);
6038 }
6039
6040 /**
6041 * {@return a synthetic type for an unconditional pattern}
6042 */
6043 static JavaType matchAllType() {
6044 return PATTERN_MATCH_ALL_TYPE;
6045 }
6046
6047 /**
6048 * {@return the type bound by a synthetic type test/record pattern}
6049 * @param t the synthetic pattern type
6050 */
6051 static CodeType targetType(CodeType t) {
6052 return ((ClassType) t).typeArguments().get(0);
6053 }
6054 }
6055
6056 /**
6057 * Pattern operations.
6058 *
6059 * @jls 14.30 Patterns
6060 */
6061 public static final class PatternOps {
6062 PatternOps() {
6063 }
6064
6065 /**
6066 * The pattern operation.
6067 * <p>
6068 * The result type of a pattern operation is a synthetic {@linkplain Pattern pattern type}.
6069 * Pattern operations are used in pattern bodies of {@link MatchOp} and as nested pattern operands of
6070 * {@link RecordPatternOp}.
6071 */
6072 public sealed static abstract class PatternOp extends AbstractOp
6073 implements JavaOp, Op.Pure {
6074 PatternOp(PatternOp that, CodeContext cc) {
6075 super(that, cc);
6076 }
6077
6078 PatternOp(List<Value> operands) {
6079 super(operands);
6080 }
6081 }
6082
6083 /**
6084 * The type pattern operation, that can model Java language type test patterns.
6085 * <p>
6086 * Type pattern operations are associated with a target type (a {@link JavaType})
6087 * and an optional binding name.
6088 *
6089 * @jls 14.30.1 Kinds of Patterns
6090 * @jls 15.20.2 The instanceof Operator
6091 */
6092 @OpDeclaration(TypePatternOp.NAME)
6093 public static final class TypePatternOp extends PatternOp {
6094 static final String NAME = "pattern.type";
6095
6096 /**
6097 * The externalized attribute key for a pattern binding name in a type pattern operation.
6098 */
6099 static final String ATTRIBUTE_BINDING_NAME = NAME + ".binding.name";
6100
6101 final CodeType resultType;
6102 final String bindingName;
6103
6104 TypePatternOp(ExternalizedOp def) {
6105 super(List.of());
6106 this.bindingName = optionalAttribute(def, ATTRIBUTE_BINDING_NAME, true, String.class).orElse(null);
6107 // @@@ Cannot use canonical constructor because it wraps the given type
6108 this.resultType = def.resultType();
6109 }
6110
6111 TypePatternOp(TypePatternOp that, CodeContext cc) {
6112 super(that, cc);
6113
6114 this.bindingName = that.bindingName;
6115 this.resultType = that.resultType;
6116 }
6117
6118 @Override
6119 public TypePatternOp transform(CodeContext cc, CodeTransformer ct) {
6120 return new TypePatternOp(this, cc);
6121 }
6122
6123 TypePatternOp(CodeType targetType, String bindingName) {
6124 super(List.of());
6125
6126 this.bindingName = bindingName;
6127 this.resultType = Pattern.bindingType(targetType);
6128 }
6129
6130 @Override
6131 public Map<String, Object> externalize() {
6132 return bindingName == null ? Map.of() : Map.of("", bindingName);
6133 }
6134
6135 /**
6136 * {@return the variable name bound by this type test pattern, or {@code null} if none}
6137 */
6138 public String bindingName() {
6139 return bindingName;
6140 }
6141
6142 /**
6143 * {@return the type matched by this type test pattern}
6144 */
6145 public CodeType targetType() {
6146 return Pattern.targetType(resultType());
6147 }
6148
6149 @Override
6150 public CodeType resultType() {
6151 return resultType;
6152 }
6153 }
6154
6155 /**
6156 * The record pattern operation, that can model Java language record patterns.
6157 * <p>
6158 * Record pattern operations are associated with a {@linkplain RecordTypeRef record reference}.
6159 * The operands are nested pattern values.
6160 *
6161 * @jls 14.30.1 Kinds of Patterns
6162 */
6163 @OpDeclaration(RecordPatternOp.NAME)
6164 public static final class RecordPatternOp extends PatternOp {
6165 static final String NAME = "pattern.record";
6166
6167 /**
6168 * The externalized attribute key for a record reference in a record pattern operation.
6169 */
6170 static final String ATTRIBUTE_RECORD_REF = NAME + ".ref";
6171
6172 final RecordTypeRef recordReference;
6173
6174 RecordPatternOp(ExternalizedOp def) {
6175 this(requireAttribute(def, ATTRIBUTE_RECORD_REF, true, RecordTypeRef.class), def.operands());
6176 }
6177
6178 RecordPatternOp(RecordPatternOp that, CodeContext cc) {
6179 super(that, cc);
6180
6181 this.recordReference = that.recordReference;
6182 }
6183
6184 @Override
6185 public RecordPatternOp transform(CodeContext cc, CodeTransformer ct) {
6186 return new RecordPatternOp(this, cc);
6187 }
6188
6189 RecordPatternOp(RecordTypeRef recordReference, List<Value> nestedPatterns) {
6190 // The type of each value is a subtype of Pattern
6191 // The number of values corresponds to the number of components of the record
6192 if (recordReference.components().size() != nestedPatterns.size()) {
6193 throw structuralException(NAME, "requires %d nested pattern operands, found %d".formatted(recordReference.components().size(), nestedPatterns.size()));
6194 }
6195 super(List.copyOf(nestedPatterns));
6196
6197 this.recordReference = recordReference;
6198 }
6199
6200 @Override
6201 public Map<String, Object> externalize() {
6202 return Map.of("", recordReference());
6203 }
6204
6205 /**
6206 * {@return the record reference associated with this record pattern}
6207 */
6208 public RecordTypeRef recordReference() {
6209 return recordReference;
6210 }
6211
6212 /**
6213 * {@return the type matched by this record pattern}
6214 */
6215 public CodeType targetType() {
6216 return Pattern.targetType(resultType());
6217 }
6218
6219 @Override
6220 public CodeType resultType() {
6221 return Pattern.recordType(recordReference.recordType());
6222 }
6223 }
6224
6225 /**
6226 * A pattern operation representing a match-all (unconditional) pattern.
6227 *
6228 * @jls 14.30.1 Kinds of Patterns
6229 */
6230 @OpDeclaration(MatchAllPatternOp.NAME)
6231 public static final class MatchAllPatternOp extends PatternOp {
6232
6233 // @@@ we may need to add info about the type of the record component
6234 // this info can be used when lowering
6235
6236 static final String NAME = "pattern.match.all";
6237
6238 MatchAllPatternOp(ExternalizedOp def) {
6239 this();
6240 }
6241
6242 MatchAllPatternOp(MatchAllPatternOp that, CodeContext cc) {
6243 super(that, cc);
6244 }
6245
6246 MatchAllPatternOp() {
6247 super(List.of());
6248 }
6249
6250 @Override
6251 public Op transform(CodeContext cc, CodeTransformer ct) {
6252 return new MatchAllPatternOp(this, cc);
6253 }
6254
6255 @Override
6256 public CodeType resultType() {
6257 return Pattern.matchAllType();
6258 }
6259 }
6260
6261 /**
6262 * The match operation, that can model Java language pattern matching.
6263 * <p>
6264 * Match operations can be used to model instanceof expressions with a pattern match operator, or
6265 * case labels with case patterns in switch statements and switch expressions.
6266 * <p>
6267 * Match operations feature one operand, the target value being matched, and two bodies: the pattern body and
6268 * the match body.
6269 * <p>
6270 * The pattern body should accept no arguments and yield a pattern value.
6271 * The match body accepts the values bound by the pattern body and yields {@linkplain JavaType#VOID no value}.
6272 * The result type of a match operation is {@link JavaType#BOOLEAN}.
6273 *
6274 * @jls 14.30.2 Pattern Matching
6275 * @jls 14.11 The switch Statement
6276 * @jls 15.28 switch Expressions
6277 * @jls 15.20.2 The instanceof Operator
6278 */
6279 @OpDeclaration(MatchOp.NAME)
6280 public static final class MatchOp extends AbstractOp
6281 implements JavaOp, Op.Isolated, ControlFlowBooleanExpressionOp {
6282 static final String NAME = "pattern.match";
6283
6284 final Body patternBody;
6285 final Body matchBody;
6286
6287 MatchOp(ExternalizedOp def) {
6288 List<Body.Builder> bodies = requireBodies(def, 2);
6289 this(requireSingleOperand(def), bodies.get(0), bodies.get(1));
6290 }
6291
6292 MatchOp(MatchOp that, CodeContext cc, CodeTransformer ct) {
6293 super(that, cc);
6294
6295 this.patternBody = that.patternBody.transform(cc, ct).build(this);
6296 this.matchBody = that.matchBody.transform(cc, ct).build(this);
6297 }
6298
6299 @Override
6300 public MatchOp transform(CodeContext cc, CodeTransformer ct) {
6301 return new MatchOp(this, cc, ct);
6302 }
6303
6304 MatchOp(Value target, Body.Builder patternC, Body.Builder matchC) {
6305 super(List.of(target));
6306
6307 this.patternBody = requireNoParameters(NAME + " pattern", patternC).build(this);
6308 this.matchBody = matchC.build(this);
6309 }
6310
6311 @Override
6312 public List<Body> bodies() {
6313 return List.of(patternBody, matchBody);
6314 }
6315
6316 /**
6317 * Returns the pattern body for this match operation.
6318 *
6319 * @return the pattern body
6320 */
6321 public Body patternBody() {
6322 return patternBody;
6323 }
6324
6325 /**
6326 * Returns the match body for this match operation.
6327 *
6328 * @return the match body
6329 */
6330 public Body matchBody() {
6331 return matchBody;
6332 }
6333
6334 /**
6335 * Returns the target value being matched in this match operation.
6336 *
6337 * @return the match target value
6338 */
6339 public Value targetOperand() {
6340 return operands().get(0);
6341 }
6342
6343 @Override
6344 public Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited) {
6345 // Poll for implicit boolean continuation parameter
6346 BooleanResultContinuation continuation = BOOLEAN_CONTINUATION_ARG.poll(b.context());
6347 Block.Builder exit = b;
6348 if (continuation == null) {
6349 exit = b.block();
6350 b.context().mapValue(result(), exit.parameter(resultType()));
6351 continuation = new BranchWithArgumentContinuation(exit);
6352 }
6353 lowerTo(b, inherited, continuation);
6354 return exit;
6355 }
6356
6357 void lowerTo(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited,
6358 BooleanResultContinuation continuation) {
6359 Block.Reference trueRef = continuation.referenceFor(b, true);
6360 Block.Reference falseRef = continuation.referenceFor(b, false);
6361
6362 List<Value> patternValues = new ArrayList<>();
6363 Op patternYieldOp = patternBody.entryBlock().terminatingOp();
6364 Op.Result rootPatternValue = (Op.Result) patternYieldOp.operands().get(0);
6365 Block.Builder matchedBlock = lower(
6366 falseRef,
6367 b,
6368 patternValues,
6369 rootPatternValue.op(),
6370 b.context().getValue(targetOperand()));
6371
6372 matchedBlock.transformBody(matchBody, patternValues, loweringTransformer(inherited, (block, op) -> {
6373 if (op instanceof CoreOp.YieldOp) {
6374 block.add(branch(trueRef));
6375 return block;
6376 } else {
6377 return null;
6378 }
6379 }));
6380 }
6381
6382 static Block.Builder lower(Block.Reference falseRef, Block.Builder currentBlock,
6383 List<Value> bindings,
6384 Op pattern, Value target) {
6385 return switch (pattern) {
6386 case RecordPatternOp rp -> lowerRecordPattern(falseRef, currentBlock, bindings, rp, target);
6387 case TypePatternOp tp -> lowerTypePattern(falseRef, currentBlock, bindings, tp, target);
6388 case MatchAllPatternOp map -> lowerMatchAllPattern(currentBlock);
6389 case null, default -> throw new UnsupportedOperationException("Unknown pattern op: " + pattern);
6390 };
6391 }
6392
6393 static Block.Builder lowerRecordPattern(Block.Reference falseRef, Block.Builder currentBlock,
6394 List<Value> bindings,
6395 JavaOp.PatternOps.RecordPatternOp rpOp, Value target) {
6396 CodeType targetType = rpOp.targetType();
6397
6398 Block.Builder nextBlock = currentBlock.block();
6399
6400 // Check if instance of target type
6401 Op.Result isInstance = currentBlock.add(instanceOf(targetType, target));
6402 currentBlock.add(conditionalBranch(isInstance, nextBlock.reference(), falseRef));
6403
6404 currentBlock = nextBlock;
6405
6406 target = currentBlock.add(cast(targetType, target));
6407
6408 // Access component values of record and match on each as nested target
6409 List<Value> dArgs = rpOp.operands();
6410 for (int i = 0; i < dArgs.size(); i++) {
6411 Op.Result nestedPattern = (Op.Result) dArgs.get(i);
6412 // @@@ Handle exceptions?
6413 Value nestedTarget = currentBlock.add(invoke(rpOp.recordReference().methodForComponent(i), target));
6414
6415 currentBlock = lower(falseRef, currentBlock, bindings, nestedPattern.op(), nestedTarget);
6416 }
6417
6418 return currentBlock;
6419 }
6420
6421 static Block.Builder lowerTypePattern(Block.Reference falseRef, Block.Builder currentBlock,
6422 List<Value> bindings,
6423 TypePatternOp tpOp, Value target) {
6424 CodeType s = target.type();
6425 CodeType t = tpOp.targetType();
6426 if (t instanceof PrimitiveType pt) {
6427 if (s instanceof ClassType cs) {
6428 ClassType box;
6429 if (cs.unbox().isEmpty()) { // s not a boxed type
6430 // e.g. Number -> int, narrowing + unboxing
6431 box = pt.box().orElseThrow();
6432 currentBlock = appendTypeTestOp(instanceOf(box, target), currentBlock, falseRef);
6433 // e.g. Object -> int, on true path we need to cast Object to Integer
6434 target = currentBlock.add(cast(box, target));
6435 } else {
6436 // e.g. Float -> float, unboxing
6437 // e.g. Integer -> long, unboxing + widening
6438 box = cs;
6439 Op p = neq(target, currentBlock.add(constant(s, null)));
6440 currentBlock = appendTypeTestOp(p, currentBlock, falseRef);
6441 }
6442 target = currentBlock.add(invoke(MethodRef.method(box, t + "Value", t), target));
6443 } else {
6444 // primitive to primitive conversion
6445 PrimitiveType ps = ((PrimitiveType) s);
6446 if (isNarrowingPrimitiveConv(ps, pt) || isWideningPrimitiveConvWithCheck(ps, pt)
6447 || isWideningAndNarrowingPrimitiveConv(ps, pt)) {
6448 // e.g. int -> byte, narrowing
6449 // e,g. int -> float, widening with check
6450 // e.g. byte -> char, widening and narrowing
6451 MethodRef mref = convMethodRef(s, t);
6452 currentBlock = appendTypeTestOp(invoke(mref, target), currentBlock, falseRef);
6453 }
6454 target = currentBlock.add(conv(t, target));
6455 }
6456 } else if (s instanceof PrimitiveType ps) {
6457 // boxing conversions
6458 // e.g. int -> Number, boxing + widening
6459 // e.g. byte -> Byte, boxing
6460 ClassType box = ps.box().orElseThrow();
6461 target = currentBlock.add(invoke(MethodRef.method(box, "valueOf", box, ps), target));
6462 } else {
6463 // reference to reference
6464 // e.g. Character -> Character
6465 // e.g. Number -> Double, narrowing
6466 // e.g. Short -> Object, widening
6467 currentBlock = appendTypeTestOp(instanceOf(t, target), currentBlock, falseRef);
6468 if (!s.equals(t)) {
6469 target = currentBlock.add(cast(t, target));
6470 }
6471 }
6472
6473 bindings.add(target);
6474
6475 return currentBlock;
6476 }
6477
6478 private static Block.Builder appendTypeTestOp(Op p, Block.Builder b, Block.Reference falseBlock) {
6479 Block.Builder trueBlock = b.block();
6480 b.add(conditionalBranch(b.add(p), trueBlock.reference(), falseBlock));
6481 return trueBlock;
6482 }
6483
6484 private static boolean isWideningAndNarrowingPrimitiveConv(PrimitiveType s, PrimitiveType t) {
6485 return BYTE.equals(s) && CHAR.equals(t);
6486 }
6487
6488 private static boolean isWideningPrimitiveConvWithCheck(PrimitiveType s, PrimitiveType t) {
6489 return (INT.equals(s) && FLOAT.equals(t))
6490 || (LONG.equals(s) && FLOAT.equals(t))
6491 || (LONG.equals(s) && DOUBLE.equals(t));
6492 }
6493
6494 // s -> t is narrowing if order(t) <= order(s)
6495 private final static Map<PrimitiveType, Integer> narrowingOrder = Map.of(
6496 BYTE, 1,
6497 SHORT, 2,
6498 CHAR, 2,
6499 INT, 3,
6500 LONG, 4,
6501 FLOAT, 5,
6502 DOUBLE, 6
6503 );
6504 private static boolean isNarrowingPrimitiveConv(PrimitiveType s, PrimitiveType t) {
6505 return narrowingOrder.get(t) <= narrowingOrder.get(s) && !s.equals(t); // need to be strict, to not consider int -> int as narrowing
6506 }
6507
6508 private static MethodRef convMethodRef(CodeType s, CodeType t) {
6509 if (BYTE.equals(s) || SHORT.equals(s) || CHAR.equals(s)) {
6510 s = INT;
6511 }
6512 String sn = capitalize(s.toString());
6513 String tn = capitalize(t.toString());
6514 String mn = "is%sTo%sExact".formatted(sn, tn);
6515 JavaType exactConversionSupport = JavaType.type(ClassDesc.of("java.lang.runtime.ExactConversionsSupport"));
6516 return MethodRef.method(exactConversionSupport, mn, BOOLEAN, s);
6517 }
6518
6519 private static String capitalize(String s) {
6520 return s.substring(0, 1).toUpperCase() + s.substring(1);
6521 }
6522
6523 static Block.Builder lowerMatchAllPattern(Block.Builder currentBlock) {
6524 return currentBlock;
6525 }
6526
6527 @Override
6528 public CodeType resultType() {
6529 return BOOLEAN;
6530 }
6531 }
6532 }
6533
6534 /**
6535 * Returns a composed function that composes {@code g} into the first argument of {@code f}.
6536 * <p>
6537 * if {@code f} is {@code null} then this method returns {@code g}.
6538 *
6539 * @param f the outer function
6540 * @param g the inner function
6541 * @return the composed
6542 */
6543 private static <T, U> BiFunction<T, U, T> composeFirst(
6544 BiFunction<T, U, T> f,
6545 BiFunction<T, U, T> g) {
6546 Objects.requireNonNull(g);
6547 return f == null
6548 ? g
6549 : (builder, op) -> f.apply(g.apply(builder, op), op);
6550 }
6551
6552 private static Op createOp(ExternalizedOp def) {
6553 Op op = switch (def.name()) {
6554 case "add" -> new AddOp(def);
6555 case "and" -> new AndOp(def);
6556 case "array.length" -> new ArrayLengthOp(def);
6557 case "array.load" -> new ArrayAccessOp.ArrayLoadOp(def);
6558 case "array.store" -> new ArrayAccessOp.ArrayStoreOp(def);
6559 case "ashr" -> new AshrOp(def);
6560 case "assert" -> new AssertOp(def);
6561 case "cast" -> new CastOp(def);
6562 case "compl" -> new ComplOp(def);
6563 case "concat" -> new ConcatOp(def);
6564 case "conv" -> new ConvOp(def);
6565 case "div" -> new DivOp(def);
6566 case "eq" -> new EqOp(def);
6567 case "exception.region.enter" -> new ExceptionRegionEnter(def);
6568 case "exception.region.exit" -> new ExceptionRegionExit(def);
6569 case "field.load" -> new FieldAccessOp.FieldLoadOp(def);
6570 case "field.store" -> new FieldAccessOp.FieldStoreOp(def);
6571 case "ge" -> new GeOp(def);
6572 case "gt" -> new GtOp(def);
6573 case "instanceof" -> new InstanceOfOp(def);
6574 case "invoke" -> new InvokeOp(def);
6575 case "java.block" -> new BlockOp(def);
6576 case "java.break" -> new BreakOp(def);
6577 case "java.cand" -> new ConditionalAndOp(def);
6578 case "java.cexpression" -> new ConditionalExpressionOp(def);
6579 case "java.continue" -> new ContinueOp(def);
6580 case "java.cor" -> new ConditionalOrOp(def);
6581 case "java.do.while" -> new DoWhileOp(def);
6582 case "java.enhancedFor" -> new EnhancedForOp(def);
6583 case "java.for" -> new ForOp(def);
6584 case "java.if" -> new IfOp(def);
6585 case "java.labeled" -> new LabeledOp(def);
6586 case "java.switch.expression" -> new SwitchExpressionOp(def);
6587 case "java.switch.fallthrough" -> new SwitchFallthroughOp(def);
6588 case "java.switch.statement" -> new SwitchStatementOp(def);
6589 case "java.synchronized" -> new SynchronizedOp(def);
6590 case "java.try" -> new TryOp(def);
6591 case "java.while" -> new WhileOp(def);
6592 case "java.yield" -> new YieldOp(def);
6593 case "lambda" -> new LambdaOp(def);
6594 case "le" -> new LeOp(def);
6595 case "lshl" -> new LshlOp(def);
6596 case "lshr" -> new LshrOp(def);
6597 case "lt" -> new LtOp(def);
6598 case "mod" -> new ModOp(def);
6599 case "monitor.enter" -> new MonitorOp.MonitorEnterOp(def);
6600 case "monitor.exit" -> new MonitorOp.MonitorExitOp(def);
6601 case "mul" -> new MulOp(def);
6602 case "neg" -> new NegOp(def);
6603 case "neq" -> new NeqOp(def);
6604 case "new" -> new NewOp(def);
6605 case "not" -> new NotOp(def);
6606 case "or" -> new OrOp(def);
6607 case "pattern.match" -> new PatternOps.MatchOp(def);
6608 case "pattern.match.all" -> new PatternOps.MatchAllPatternOp(def);
6609 case "pattern.record" -> new PatternOps.RecordPatternOp(def);
6610 case "pattern.type" -> new PatternOps.TypePatternOp(def);
6611 case "sub" -> new SubOp(def);
6612 case "throw" -> new ThrowOp(def);
6613 case "xor" -> new XorOp(def);
6614 default -> null;
6615 };
6616 if (op != null) {
6617 op.setLocation(def.location());
6618 }
6619 return op;
6620 }
6621
6622 /**
6623 * An operation factory for core operations composed with Java operations.
6624 */
6625 public static final OpFactory JAVA_OP_FACTORY = CoreOp.CORE_OP_FACTORY.andThen(JavaOp::createOp);
6626
6627 /**
6628 * A Java dialect factory, for constructing core and Java operations and constructing
6629 * core types and Java types, where the core types can refer to Java
6630 * types.
6631 */
6632 public static final DialectFactory JAVA_DIALECT_FACTORY = new DialectFactory(
6633 JAVA_OP_FACTORY,
6634 JAVA_TYPE_FACTORY);
6635
6636 /**
6637 * Creates a lambda operation.
6638 *
6639 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
6640 * connected, or {@code null} if they are isolated
6641 * @param signature the lambda operation's signature, represented as a function type
6642 * @param functionalInterface the lambda operation's functional interface type
6643 * @return the lambda operation
6644 */
6645 public static LambdaOp.Builder lambda(Body.Builder connectedAncestorBody,
6646 FunctionType signature, CodeType functionalInterface) {
6647 return new LambdaOp.Builder(connectedAncestorBody, signature, functionalInterface);
6648 }
6649
6650 /**
6651 * Creates a lambda operation.
6652 *
6653 * @param functionalInterface the lambda operation's functional interface type
6654 * @param body the body of the lambda operation
6655 * @return the lambda operation
6656 */
6657 public static LambdaOp lambda(CodeType functionalInterface, Body.Builder body) {
6658 return new LambdaOp(functionalInterface, body, false);
6659 }
6660
6661 /**
6662 * Creates a lambda operation.
6663 *
6664 * @param functionalInterface the lambda operation's functional interface type
6665 * @param body the body of the lambda operation
6666 * @param isReflectable true if the lambda is reflectable
6667 * @return the lambda operation
6668 */
6669 public static LambdaOp lambda(CodeType functionalInterface, Body.Builder body, boolean isReflectable) {
6670 return new LambdaOp(functionalInterface, body, isReflectable);
6671 }
6672
6673 /**
6674 * Creates an exception region enter operation
6675 *
6676 * @param start the reference to the block that enters the exception region
6677 * @param catchers the references to blocks handling exceptions thrown by blocks within the exception region
6678 * @return the exception region enter operation
6679 */
6680 public static ExceptionRegionEnter exceptionRegionEnter(Block.Reference start, Block.Reference... catchers) {
6681 return exceptionRegionEnter(null, start, List.of(catchers));
6682 }
6683
6684 /**
6685 * Creates an exception region enter operation
6686 *
6687 * @param start the reference to the block that enters the exception region
6688 * @param catchers the references to blocks handling exceptions thrown by blocks within the exception region
6689 * @return the exception region enter operation
6690 */
6691 public static ExceptionRegionEnter exceptionRegionEnter(Block.Reference start, List<Block.Reference> catchers) {
6692 return exceptionRegionEnter(null, start, catchers);
6693 }
6694
6695 /**
6696 * Creates an exception region enter operation
6697 *
6698 * @param catchTypes the explicit catch types, may be {@code null}
6699 * @param start the reference to the block that enters the exception region
6700 * @param catchers the references to blocks handling exceptions thrown by blocks within the exception region
6701 * @return the exception region enter operation
6702 */
6703 public static ExceptionRegionEnter exceptionRegionEnter(List<CodeType> catchTypes,
6704 Block.Reference start,
6705 List<Block.Reference> catchers) {
6706 List<Block.Reference> s = new ArrayList<>();
6707 s.add(start);
6708 s.addAll(catchers);
6709 return new ExceptionRegionEnter(catchTypes, s);
6710 }
6711
6712 /**
6713 * Creates an exception region exit operation
6714 *
6715 * @param enter the result of the dominant {@link ExceptionRegionEnter}
6716 * @param end the reference to the block reached after exiting the exception region
6717 * @return the exception region exit operation
6718 */
6719 public static ExceptionRegionExit exceptionRegionExit(Value enter, Block.Reference end) {
6720 return new ExceptionRegionExit(enter, end);
6721 }
6722
6723 /**
6724 * Creates a throw operation.
6725 *
6726 * @param exceptionValue the thrown value
6727 * @return the throw operation
6728 */
6729 public static ThrowOp throw_(Value exceptionValue) {
6730 return new ThrowOp(exceptionValue);
6731 }
6732
6733 /**
6734 * Creates an assert operation.
6735 *
6736 * @param bodies the nested bodies
6737 * @return the assert operation
6738 */
6739 public static AssertOp assert_(List<Body.Builder> bodies) {
6740 return new AssertOp(bodies);
6741 }
6742
6743 /**
6744 * Creates a monitor enter operation.
6745 * @param monitor the monitor value
6746 * @return the monitor enter operation
6747 */
6748 public static MonitorOp.MonitorEnterOp monitorEnter(Value monitor) {
6749 return new MonitorOp.MonitorEnterOp(monitor);
6750 }
6751
6752 /**
6753 * Creates a monitor exit operation.
6754 * @param monitor the monitor value
6755 * @return the monitor exit operation
6756 */
6757 public static MonitorOp.MonitorExitOp monitorExit(Value monitor) {
6758 return new MonitorOp.MonitorExitOp(monitor);
6759 }
6760
6761 /**
6762 * Creates an invoke operation modeling an invocation to an
6763 * instance or static (class) method with no variable arguments.
6764 * <p>
6765 * The invoke kind of the invoke operation is determined by
6766 * comparing the argument count with the method reference's
6767 * parameter count. If they are equal then the invoke kind is
6768 * {@link InvokeOp.InvokeKind#STATIC static}. If the parameter count
6769 * plus one is equal to the argument count then the invoke kind
6770 * is {@link InvokeOp.InvokeKind#INSTANCE instance}.
6771 * <p>
6772 * The result type of the invoke operation is the method reference's return type.
6773 *
6774 * @param invokeRef the method reference
6775 * @param args the invoke arguments
6776 * @return the invoke operation
6777 */
6778 public static InvokeOp invoke(MethodRef invokeRef, Value... args) {
6779 return invoke(invokeRef, List.of(args));
6780 }
6781
6782 /**
6783 * Creates an invoke operation modeling an invocation to an
6784 * instance or static (class) method with no variable arguments.
6785 * <p>
6786 * The invoke kind of the invoke operation is determined by
6787 * comparing the argument count with the method reference's
6788 * parameter count. If they are equal then the invoke kind is
6789 * {@link InvokeOp.InvokeKind#STATIC static}. If the parameter count
6790 * plus one is equal to the argument count then the invoke kind
6791 * is {@link InvokeOp.InvokeKind#INSTANCE instance}.
6792 * <p>
6793 * The result type of the invoke operation is the method reference's return type.
6794 *
6795 * @param invokeRef the method reference
6796 * @param args the invoke arguments
6797 * @return the invoke operation
6798 */
6799 public static InvokeOp invoke(MethodRef invokeRef, List<Value> args) {
6800 return invoke(invokeRef.signature().returnType(), invokeRef, args);
6801 }
6802
6803 /**
6804 * Creates an invoke operation modeling an invocation to an
6805 * instance or static (class) method with no variable arguments.
6806 * <p>
6807 * The invoke kind of the invoke operation is determined by
6808 * comparing the argument count with the method reference's
6809 * parameter count. If they are equal then the invoke kind is
6810 * {@link InvokeOp.InvokeKind#STATIC static}. If the parameter count
6811 * plus one is equal to the argument count then the invoke kind
6812 * is {@link InvokeOp.InvokeKind#INSTANCE instance}.
6813 *
6814 * @param returnType the result type of the invoke operation
6815 * @param invokeRef the method reference
6816 * @param args the invoke arguments
6817 * @return the invoke operation
6818 */
6819 public static InvokeOp invoke(CodeType returnType, MethodRef invokeRef, Value... args) {
6820 return invoke(returnType, invokeRef, List.of(args));
6821 }
6822
6823 /**
6824 * Creates an invoke operation modeling an invocation to an
6825 * instance or static (class) method with no variable arguments.
6826 * <p>
6827 * The invoke kind of the invoke operation is determined by
6828 * comparing the argument count with the method reference's
6829 * parameter count. If they are equal then the invoke kind is
6830 * {@link InvokeOp.InvokeKind#STATIC static}. If the parameter count
6831 * plus one is equal to the argument count then the invoke kind
6832 * is {@link InvokeOp.InvokeKind#INSTANCE instance}.
6833 *
6834 * @param returnType the result type of the invoke operation
6835 * @param invokeRef the method reference
6836 * @param args the invoke arguments
6837 * @return the invoke super operation
6838 */
6839 public static InvokeOp invoke(CodeType returnType, MethodRef invokeRef, List<Value> args) {
6840 int paramCount = invokeRef.signature().parameterTypes().size();
6841 int argCount = args.size();
6842 InvokeOp.InvokeKind ik = (argCount == paramCount + 1)
6843 ? InvokeOp.InvokeKind.INSTANCE
6844 : InvokeOp.InvokeKind.STATIC;
6845 return new InvokeOp(ik, false, returnType, invokeRef, args);
6846 }
6847
6848 /**
6849 * Creates an invoke operation modeling an invocation to a method.
6850 *
6851 * @param invokeKind the invoke kind
6852 * @param isVarArgs true if an invocation to a variable argument method
6853 * @param returnType the result type of the invoke operation
6854 * @param invokeRef the method reference
6855 * @param args the invoke arguments
6856 * @return the invoke operation
6857 * @throws IllegalArgumentException if there is a mismatch between the argument count
6858 * and the method reference's parameter count.
6859 */
6860 public static InvokeOp invoke(InvokeOp.InvokeKind invokeKind, boolean isVarArgs,
6861 CodeType returnType, MethodRef invokeRef, Value... args) {
6862 return new InvokeOp(invokeKind, isVarArgs, returnType, invokeRef, List.of(args));
6863 }
6864
6865 /**
6866 * Creates an invoke operation modeling an invocation to a method.
6867 *
6868 * @param invokeKind the invoke kind
6869 * @param isVarArgs true if an invocation to a variable argument method
6870 * @param returnType the result type of the invoke operation
6871 * @param invokeRef the method reference
6872 * @param args the invoke arguments
6873 * @return the invoke operation
6874 * @throws IllegalArgumentException if there is a mismatch between the argument count
6875 * and the method reference's parameter count.
6876 */
6877 public static InvokeOp invoke(InvokeOp.InvokeKind invokeKind, boolean isVarArgs,
6878 CodeType returnType, MethodRef invokeRef, List<Value> args) {
6879 return new InvokeOp(invokeKind, isVarArgs, returnType, invokeRef, args);
6880 }
6881
6882 /**
6883 * Creates a conversion operation.
6884 *
6885 * @param to the conversion target type
6886 * @param from the value to be converted
6887 * @return the conversion operation
6888 */
6889 public static ConvOp conv(CodeType to, Value from) {
6890 return new ConvOp(to, from);
6891 }
6892
6893 /**
6894 * Creates an instance creation operation.
6895 *
6896 * @param constructorRef the constructor reference
6897 * @param args the constructor arguments
6898 * @return the instance creation operation
6899 */
6900 public static NewOp new_(MethodRef constructorRef, Value... args) {
6901 return new_(constructorRef, List.of(args));
6902 }
6903
6904 /**
6905 * Creates an instance creation operation.
6906 *
6907 * @param constructorRef the constructor reference
6908 * @param args the constructor arguments
6909 * @return the instance creation operation
6910 */
6911 public static NewOp new_(MethodRef constructorRef, List<Value> args) {
6912 return new NewOp(false, constructorRef.refType(), constructorRef, args);
6913 }
6914
6915 /**
6916 * Creates an instance creation operation.
6917 *
6918 * @param returnType the result type of the instance creation operation
6919 * @param constructorRef the constructor reference
6920 * @param args the constructor arguments
6921 * @return the instance creation operation
6922 */
6923 public static NewOp new_(CodeType returnType, MethodRef constructorRef,
6924 Value... args) {
6925 return new_(returnType, constructorRef, List.of(args));
6926 }
6927
6928 /**
6929 * Creates an instance creation operation.
6930 *
6931 * @param returnType the result type of the instance creation operation
6932 * @param constructorRef the constructor reference
6933 * @param args the constructor arguments
6934 * @return the instance creation operation
6935 */
6936 public static NewOp new_(CodeType returnType, MethodRef constructorRef,
6937 List<Value> args) {
6938 return new NewOp(false, returnType, constructorRef, args);
6939 }
6940
6941 /**
6942 * Creates an instance creation operation.
6943 *
6944 * @param isVarargs {@code true} if calling a varargs constructor
6945 * @param returnType the result type of the instance creation operation
6946 * @param constructorRef the constructor reference
6947 * @param args the constructor arguments
6948 * @return the instance creation operation
6949 */
6950 public static NewOp new_(boolean isVarargs, CodeType returnType, MethodRef constructorRef,
6951 List<Value> args) {
6952 return new NewOp(isVarargs, returnType, constructorRef, args);
6953 }
6954
6955 /**
6956 * Creates an array creation operation.
6957 *
6958 * @param arrayType the array type
6959 * @param length the array size
6960 * @return the array creation operation
6961 */
6962 public static NewOp newArray(CodeType arrayType, Value length) {
6963 MethodRef constructorRef = MethodRef.constructor(arrayType, INT);
6964 return new_(constructorRef, length);
6965 }
6966
6967 /**
6968 * Creates a field load operation to a non-static field.
6969 *
6970 * @param fieldRef the field reference
6971 * @param receiver the receiver value
6972 * @return the field load operation
6973 */
6974 public static FieldAccessOp.FieldLoadOp fieldLoad(FieldRef fieldRef, Value receiver) {
6975 return new FieldAccessOp.FieldLoadOp(fieldRef.type(), fieldRef, receiver);
6976 }
6977
6978 /**
6979 * Creates a field load operation to a non-static field.
6980 *
6981 * @param resultType the result type of the operation
6982 * @param fieldRef the field reference
6983 * @param receiver the receiver value
6984 * @return the field load operation
6985 */
6986 public static FieldAccessOp.FieldLoadOp fieldLoad(CodeType resultType, FieldRef fieldRef, Value receiver) {
6987 return new FieldAccessOp.FieldLoadOp(resultType, fieldRef, receiver);
6988 }
6989
6990 /**
6991 * Creates a field load operation to a static field.
6992 *
6993 * @param fieldRef the field reference
6994 * @return the field load operation
6995 */
6996 public static FieldAccessOp.FieldLoadOp fieldLoad(FieldRef fieldRef) {
6997 return new FieldAccessOp.FieldLoadOp(fieldRef.type(), fieldRef);
6998 }
6999
7000 /**
7001 * Creates a field load operation to a static field.
7002 *
7003 * @param resultType the result type of the operation
7004 * @param fieldRef the field reference
7005 * @return the field load operation
7006 */
7007 public static FieldAccessOp.FieldLoadOp fieldLoad(CodeType resultType, FieldRef fieldRef) {
7008 return new FieldAccessOp.FieldLoadOp(resultType, fieldRef);
7009 }
7010
7011 /**
7012 * Creates a field store operation to a non-static field.
7013 *
7014 * @param fieldRef the field reference
7015 * @param receiver the receiver value
7016 * @param v the value to store
7017 * @return the field store operation
7018 */
7019 public static FieldAccessOp.FieldStoreOp fieldStore(FieldRef fieldRef, Value receiver, Value v) {
7020 return new FieldAccessOp.FieldStoreOp(fieldRef, receiver, v);
7021 }
7022
7023 /**
7024 * Creates a field load operation to a static field.
7025 *
7026 * @param fieldRef the field reference
7027 * @param v the value to store
7028 * @return the field store operation
7029 */
7030 public static FieldAccessOp.FieldStoreOp fieldStore(FieldRef fieldRef, Value v) {
7031 return new FieldAccessOp.FieldStoreOp(fieldRef, v);
7032 }
7033
7034 /**
7035 * Creates an array length operation.
7036 *
7037 * @param array the array value
7038 * @return the array length operation
7039 */
7040 public static ArrayLengthOp arrayLength(Value array) {
7041 return new ArrayLengthOp(array);
7042 }
7043
7044 /**
7045 * Creates an array load operation.
7046 *
7047 * @param array the array value
7048 * @param index the index value
7049 * @return the array load operation
7050 */
7051 public static ArrayAccessOp.ArrayLoadOp arrayLoadOp(Value array, Value index) {
7052 return new ArrayAccessOp.ArrayLoadOp(array, index);
7053 }
7054
7055 /**
7056 * Creates an array load operation.
7057 *
7058 * @param array the array value
7059 * @param index the index value
7060 * @param componentType the type of the array component
7061 * @return the array load operation
7062 */
7063 public static ArrayAccessOp.ArrayLoadOp arrayLoadOp(Value array, Value index, CodeType componentType) {
7064 return new ArrayAccessOp.ArrayLoadOp(array, index, componentType);
7065 }
7066
7067 /**
7068 * Creates an array store operation.
7069 *
7070 * @param array the array value
7071 * @param index the index value
7072 * @param v the value to store
7073 * @return the array store operation
7074 */
7075 public static ArrayAccessOp.ArrayStoreOp arrayStoreOp(Value array, Value index, Value v) {
7076 return new ArrayAccessOp.ArrayStoreOp(array, index, v);
7077 }
7078
7079 /**
7080 * Creates an instanceof operation.
7081 *
7082 * @param t the type to test against
7083 * @param v the value to test
7084 * @return the instanceof operation
7085 */
7086 public static InstanceOfOp instanceOf(CodeType t, Value v) {
7087 return new InstanceOfOp(t, v);
7088 }
7089
7090 /**
7091 * Creates a cast operation.
7092 *
7093 * @param resultType the result type of the operation
7094 * @param v the value to cast
7095 * @return the cast operation
7096 */
7097 public static CastOp cast(CodeType resultType, Value v) {
7098 return new CastOp(resultType, resultType, v);
7099 }
7100
7101 /**
7102 * Creates a cast operation.
7103 *
7104 * @param resultType the result type of the operation
7105 * @param t the type to cast to
7106 * @param v the value to cast
7107 * @return the cast operation
7108 */
7109 public static CastOp cast(CodeType resultType, JavaType t, Value v) {
7110 return new CastOp(resultType, t, v);
7111 }
7112
7113 /**
7114 * Creates an add operation.
7115 *
7116 * @param lhs the first operand
7117 * @param rhs the second operand
7118 * @return the add operation
7119 */
7120 public static AddOp add(Value lhs, Value rhs) {
7121 return new AddOp(lhs, rhs);
7122 }
7123
7124 /**
7125 * Creates a sub operation.
7126 *
7127 * @param lhs the first operand
7128 * @param rhs the second operand
7129 * @return the sub operation
7130 */
7131 public static SubOp sub(Value lhs, Value rhs) {
7132 return new SubOp(lhs, rhs);
7133 }
7134
7135 /**
7136 * Creates a mul operation.
7137 *
7138 * @param lhs the first operand
7139 * @param rhs the second operand
7140 * @return the mul operation
7141 */
7142 public static MulOp mul(Value lhs, Value rhs) {
7143 return new MulOp(lhs, rhs);
7144 }
7145
7146 /**
7147 * Creates a div operation.
7148 *
7149 * @param lhs the first operand
7150 * @param rhs the second operand
7151 * @return the div operation
7152 */
7153 public static DivOp div(Value lhs, Value rhs) {
7154 return new DivOp(lhs, rhs);
7155 }
7156
7157 /**
7158 * Creates a mod operation.
7159 *
7160 * @param lhs the first operand
7161 * @param rhs the second operand
7162 * @return the mod operation
7163 */
7164 public static ModOp mod(Value lhs, Value rhs) {
7165 return new ModOp(lhs, rhs);
7166 }
7167
7168 /**
7169 * Creates a bitwise/logical or operation.
7170 *
7171 * @param lhs the first operand
7172 * @param rhs the second operand
7173 * @return the or operation
7174 */
7175 public static OrOp or(Value lhs, Value rhs) {
7176 return new OrOp(lhs, rhs);
7177 }
7178
7179 /**
7180 * Creates a bitwise/logical and operation.
7181 *
7182 * @param lhs the first operand
7183 * @param rhs the second operand
7184 * @return the and operation
7185 */
7186 public static AndOp and(Value lhs, Value rhs) {
7187 return new AndOp(lhs, rhs);
7188 }
7189
7190 /**
7191 * Creates a bitwise/logical xor operation.
7192 *
7193 * @param lhs the first operand
7194 * @param rhs the second operand
7195 * @return the xor operation
7196 */
7197 public static XorOp xor(Value lhs, Value rhs) {
7198 return new XorOp(lhs, rhs);
7199 }
7200
7201 /**
7202 * Creates a left shift operation.
7203 *
7204 * @param lhs the first operand
7205 * @param rhs the second operand
7206 * @return the left shift operation
7207 */
7208 public static LshlOp lshl(Value lhs, Value rhs) {
7209 return new LshlOp(lhs, rhs);
7210 }
7211
7212 /**
7213 * Creates a right shift operation.
7214 *
7215 * @param lhs the first operand
7216 * @param rhs the second operand
7217 * @return the right shift operation
7218 */
7219 public static AshrOp ashr(Value lhs, Value rhs) {
7220 return new AshrOp(lhs, rhs);
7221 }
7222
7223 /**
7224 * Creates an unsigned right shift operation.
7225 *
7226 * @param lhs the first operand
7227 * @param rhs the second operand
7228 * @return the unsigned right shift operation
7229 */
7230 public static LshrOp lshr(Value lhs, Value rhs) {
7231 return new LshrOp(lhs, rhs);
7232 }
7233
7234 /**
7235 * Creates a neg operation.
7236 *
7237 * @param v the operand
7238 * @return the neg operation
7239 */
7240 public static NegOp neg(Value v) {
7241 return new NegOp(v);
7242 }
7243
7244 /**
7245 * Creates a bitwise complement operation.
7246 *
7247 * @param v the operand
7248 * @return the bitwise complement operation
7249 */
7250 public static ComplOp compl(Value v) {
7251 return new ComplOp(v);
7252 }
7253
7254 /**
7255 * Creates a not operation.
7256 *
7257 * @param v the operand
7258 * @return the not operation
7259 */
7260 public static NotOp not(Value v) {
7261 return new NotOp(v);
7262 }
7263
7264 /**
7265 * Creates an equals comparison operation.
7266 *
7267 * @param lhs the first operand
7268 * @param rhs the second operand
7269 * @return the equals comparison operation
7270 */
7271 public static EqOp eq(Value lhs, Value rhs) {
7272 return new EqOp(lhs, rhs);
7273 }
7274
7275 /**
7276 * Creates a not equals comparison operation.
7277 *
7278 * @param lhs the first operand
7279 * @param rhs the second operand
7280 * @return the not equals comparison operation
7281 */
7282 public static NeqOp neq(Value lhs, Value rhs) {
7283 return new NeqOp(lhs, rhs);
7284 }
7285
7286 /**
7287 * Creates a greater than comparison operation.
7288 *
7289 * @param lhs the first operand
7290 * @param rhs the second operand
7291 * @return the greater than comparison operation
7292 */
7293 public static GtOp gt(Value lhs, Value rhs) {
7294 return new GtOp(lhs, rhs);
7295 }
7296
7297 /**
7298 * Creates a greater than or equals to comparison operation.
7299 *
7300 * @param lhs the first operand
7301 * @param rhs the second operand
7302 * @return the greater than or equals to comparison operation
7303 */
7304 public static GeOp ge(Value lhs, Value rhs) {
7305 return new GeOp(lhs, rhs);
7306 }
7307
7308 /**
7309 * Creates a less than comparison operation.
7310 *
7311 * @param lhs the first operand
7312 * @param rhs the second operand
7313 * @return the less than comparison operation
7314 */
7315 public static LtOp lt(Value lhs, Value rhs) {
7316 return new LtOp(lhs, rhs);
7317 }
7318
7319 /**
7320 * Creates a less than or equals to comparison operation.
7321 *
7322 * @param lhs the first operand
7323 * @param rhs the second operand
7324 * @return the less than or equals to comparison operation
7325 */
7326 public static LeOp le(Value lhs, Value rhs) {
7327 return new LeOp(lhs, rhs);
7328 }
7329
7330 /**
7331 * Creates a string concatenation operation.
7332 *
7333 * @param lhs the first operand
7334 * @param rhs the second operand
7335 * @return the string concatenation operation
7336 */
7337 public static ConcatOp concat(Value lhs, Value rhs) {
7338 return new ConcatOp(lhs, rhs);
7339 }
7340
7341 /**
7342 * Creates a continue operation.
7343 *
7344 * @return the continue operation
7345 */
7346 public static ContinueOp continue_() {
7347 return continue_(null);
7348 }
7349
7350 /**
7351 * Creates a continue operation.
7352 *
7353 * @param label the value associated with where to continue from
7354 * @return the continue operation
7355 */
7356 public static ContinueOp continue_(Value label) {
7357 return new ContinueOp(label);
7358 }
7359
7360 /**
7361 * Creates a break operation.
7362 *
7363 * @return the break operation
7364 */
7365 public static BreakOp break_() {
7366 return break_(null);
7367 }
7368
7369 /**
7370 * Creates a break operation.
7371 *
7372 * @param label the label identifier
7373 * @return the break operation
7374 */
7375 public static BreakOp break_(Value label) {
7376 return new BreakOp(label);
7377 }
7378
7379 /**
7380 * Creates a yield operation.
7381 *
7382 * @param operand the value to yield
7383 * @return the yield operation
7384 */
7385 public static YieldOp java_yield(Value operand) {
7386 return new YieldOp(operand);
7387 }
7388
7389 /**
7390 * Creates a block operation.
7391 *
7392 * @param body the statements body builder
7393 * @return the block operation
7394 */
7395 public static BlockOp block(Body.Builder body) {
7396 return new BlockOp(body);
7397 }
7398
7399 /**
7400 * Creates a synchronized operation.
7401 *
7402 * @param expr the expression body builder
7403 * @param blockBody the block body builder
7404 * @return the synchronized operation
7405 */
7406 public static SynchronizedOp synchronized_(Body.Builder expr, Body.Builder blockBody) {
7407 return new SynchronizedOp(expr, blockBody);
7408 }
7409
7410 /**
7411 * Creates a labeled operation.
7412 *
7413 * @param body the labeled body builder
7414 * @return the labeled operation
7415 */
7416 public static LabeledOp labeled(Body.Builder body) {
7417 return new LabeledOp(body);
7418 }
7419
7420 /**
7421 * Creates an if operation builder.
7422 *
7423 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7424 * connected, or {@code null} if they are isolated
7425 * @return the if operation builder
7426 */
7427 public static IfOp.IfBuilder if_(Body.Builder connectedAncestorBody) {
7428 return new IfOp.IfBuilder(connectedAncestorBody);
7429 }
7430
7431 // Pairs of
7432 // predicate ()boolean, body ()void
7433 // And one optional body ()void at the end
7434
7435 /**
7436 * Creates an if operation.
7437 *
7438 * @param bodies the body builders for the predicate and action bodies
7439 * @return the if operation
7440 */
7441 public static IfOp if_(List<Body.Builder> bodies) {
7442 return new IfOp(bodies);
7443 }
7444
7445 /**
7446 * Creates a switch expression operation.
7447 * <p>
7448 * Case bodies are provided as pairs of bodies, where the first body of each pair is the predicate body and the
7449 * second is the corresponding action body. The result type of the operation will be derived from the yield type of
7450 * the first action body.
7451 * <p>
7452 * The returned switch expression operation handles nulls if this factory can determine that at least one of the
7453 * predicate bodies accepts null selector values. For more explicit selection of null-handling policy, please
7454 * use {@link #switchExpression(CodeType, Value, boolean, List)}.</p>
7455 *
7456 * @param target the switch target value
7457 * @param bodies the body builders for the predicate and action bodies
7458 * @return the switch expression operation
7459 */
7460 public static SwitchExpressionOp switchExpression(Value target, List<Body.Builder> bodies) {
7461 return new SwitchExpressionOp(null, target, SwitchNullHandling.INFER, bodies);
7462 }
7463
7464 /**
7465 * Creates a switch expression operation.
7466 * <p>
7467 * Case bodies are provided as pairs of bodies, where the first body of each pair is the predicate body and the
7468 * second is the corresponding action body.
7469 * <p>
7470 * The returned switch expression operation handles nulls if this factory can determine that at least one of the
7471 * predicate bodies accepts null selector values. For more explicit selection of null-handling policy, please
7472 * use {@link #switchExpression(CodeType, Value, boolean, List)}.</p>
7473 *
7474 * @param resultType the result type of the expression
7475 * @param target the switch target value
7476 * @param bodies the body builders for the predicate and action bodies
7477 * @return the switch expression operation
7478 */
7479 public static SwitchExpressionOp switchExpression(CodeType resultType, Value target,
7480 List<Body.Builder> bodies) {
7481 Objects.requireNonNull(resultType);
7482 return new SwitchExpressionOp(resultType, target, SwitchNullHandling.INFER, bodies);
7483 }
7484
7485 /**
7486 * Creates a switch expression operation.
7487 * <p>
7488 * Case bodies are provided as pairs of bodies, where the first body of each pair is the predicate body and the
7489 * second is the corresponding action body.
7490 *
7491 * @param resultType the result type of the expression
7492 * @param target the switch target value
7493 * @param handleNulls whether the switch expression handles nulls
7494 * @param bodies the body builders for the predicate and action bodies
7495 * @return the switch expression operation
7496 */
7497 public static SwitchExpressionOp switchExpression(CodeType resultType, Value target,
7498 boolean handleNulls,
7499 List<Body.Builder> bodies) {
7500 Objects.requireNonNull(resultType);
7501 return new SwitchExpressionOp(resultType, target, SwitchNullHandling.of(handleNulls), bodies);
7502 }
7503
7504 /**
7505 * Creates a switch statement operation.
7506 * <p>
7507 * Case bodies are provided as pairs of bodies, where the first body of each pair is the predicate body and the
7508 * second is the corresponding action body.
7509 * <p>
7510 * The returned switch statement operation handles nulls if this factory can determine that at least one of the
7511 * predicate bodies accepts null selector values. For more explicit selection of null-handling policy, please
7512 * use {@link #switchStatement(Value, boolean, List)}.</p>
7513 *
7514 * @param target the switch target value
7515 * @param bodies the body builders for the predicate and action bodies
7516 * @return the switch statement operation
7517 */
7518 public static SwitchStatementOp switchStatement(Value target, List<Body.Builder> bodies) {
7519 return new SwitchStatementOp(target, SwitchNullHandling.INFER, bodies);
7520 }
7521
7522 /**
7523 * Creates a switch statement operation.
7524 * <p>
7525 * Case bodies are provided as pairs of bodies, where the first body of each pair is the predicate body and the
7526 * second is the corresponding action body.
7527 *
7528 * @param target the switch target value
7529 * @param handleNulls whether the switch statement handles nulls
7530 * @param bodies the body builders for the predicate and action bodies
7531 * @return the switch statement operation
7532 */
7533 public static SwitchStatementOp switchStatement(Value target, boolean handleNulls, List<Body.Builder> bodies) {
7534 return new SwitchStatementOp(target, SwitchNullHandling.of(handleNulls), bodies);
7535 }
7536
7537 /**
7538 * Creates a switch fallthrough operation.
7539 *
7540 * @return the switch fallthrough operation
7541 */
7542 public static SwitchFallthroughOp switchFallthroughOp() {
7543 return new SwitchFallthroughOp();
7544 }
7545
7546 /**
7547 * Creates a for operation builder.
7548 *
7549 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7550 * connected, or {@code null} if they are isolated
7551 * @param initTypes the types of initialized variables
7552 * @return the for operation builder
7553 */
7554 public static ForOp.InitBuilder for_(Body.Builder connectedAncestorBody, CodeType... initTypes) {
7555 return for_(connectedAncestorBody, List.of(initTypes));
7556 }
7557
7558 /**
7559 * Creates a for operation builder.
7560 *
7561 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7562 * connected, or {@code null} if they are isolated
7563 * @param initTypes the types of initialized variables
7564 * @return the for operation builder
7565 */
7566 public static ForOp.InitBuilder for_(Body.Builder connectedAncestorBody, List<? extends CodeType> initTypes) {
7567 return new ForOp.InitBuilder(connectedAncestorBody, initTypes);
7568 }
7569
7570
7571 /**
7572 * Creates a for operation.
7573 *
7574 * @param initBody the initialization body builder
7575 * @param condBody the predicate body builder
7576 * @param updateBody the update body builder
7577 * @param loopBody the loop body builder
7578 * @return the for operation
7579 */
7580 // initBody ()Tuple<Var<T1>, Var<T2>, ..., Var<TN>>, or initBody ()Var<T1>, or initBody ()void
7581 // condBody (Var<T1>, Var<T2>, ..., Var<TN>)boolean
7582 // updateBody (Var<T1>, Var<T2>, ..., Var<TN>)void
7583 // loopBody (Var<T1>, Var<T2>, ..., Var<TN>)void
7584 public static ForOp for_(Body.Builder initBody,
7585 Body.Builder condBody,
7586 Body.Builder updateBody,
7587 Body.Builder loopBody) {
7588 return new ForOp(initBody, condBody, updateBody, loopBody);
7589 }
7590
7591 /**
7592 * Creates an enhanced for operation builder.
7593 *
7594 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7595 * connected, or {@code null} if they are isolated
7596 * @param iterableType the iterable type
7597 * @param elementType the element type
7598 * @return the enhanced for operation builder
7599 */
7600 public static EnhancedForOp.ExpressionBuilder enhancedFor(Body.Builder connectedAncestorBody,
7601 CodeType iterableType, CodeType elementType) {
7602 return new EnhancedForOp.ExpressionBuilder(connectedAncestorBody, iterableType, elementType);
7603 }
7604
7605 /**
7606 * Creates an enhanced for operation.
7607 *
7608 * @param exprBody the expression body builder
7609 * @param initBody the initialization body builder
7610 * @param loopBody the loop body builder
7611 * @return the enhanced for operation
7612 */
7613 // expression ()I<E>
7614 // init (E )Var<T>
7615 // body (Var<T> )void
7616 public static EnhancedForOp enhancedFor(Body.Builder exprBody,
7617 Body.Builder initBody,
7618 Body.Builder loopBody) {
7619 return new EnhancedForOp(exprBody, initBody, loopBody);
7620 }
7621
7622 /**
7623 * Creates a while operation builder.
7624 *
7625 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7626 * connected, or {@code null} if they are isolated
7627 * @return the while operation builder
7628 */
7629 public static WhileOp.PredicateBuilder while_(Body.Builder connectedAncestorBody) {
7630 return new WhileOp.PredicateBuilder(connectedAncestorBody);
7631 }
7632
7633 /**
7634 * Creates a while operation.
7635 *
7636 * @param predicateBody the predicate body builder
7637 * @param loopBody the loop body builder
7638 * @return the while operation
7639 */
7640 // predicateBody, ()boolean, may be null for predicateBody returning true
7641 // loopBody, ()void
7642 public static WhileOp while_(Body.Builder predicateBody, Body.Builder loopBody) {
7643 return new WhileOp(predicateBody, loopBody);
7644 }
7645
7646 /**
7647 * Creates a do operation builder.
7648 *
7649 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7650 * connected, or {@code null} if they are isolated
7651 * @return the do operation builder
7652 */
7653 public static DoWhileOp.BodyBuilder doWhile(Body.Builder connectedAncestorBody) {
7654 return new DoWhileOp.BodyBuilder(connectedAncestorBody);
7655 }
7656
7657 /**
7658 * Creates a do operation.
7659 *
7660 * @param loopBody the loop body builder
7661 * @param predicateBody the predicate body builder
7662 * @return the do operation
7663 */
7664 public static DoWhileOp doWhile(Body.Builder loopBody, Body.Builder predicateBody) {
7665 return new DoWhileOp(loopBody, predicateBody);
7666 }
7667
7668 /**
7669 * Creates a conditional-and operation builder.
7670 *
7671 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7672 * connected, or {@code null} if they are isolated
7673 * @param lhs a consumer that populates the first predicate body
7674 * @param rhs a consumer that populates the second predicate body
7675 * @return the conditional-and operation builder
7676 */
7677 public static ConditionalAndOp.Builder conditionalAnd(Body.Builder connectedAncestorBody,
7678 Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) {
7679 return new ConditionalAndOp.Builder(connectedAncestorBody, lhs, rhs);
7680 }
7681
7682 /**
7683 * Creates a conditional-or operation builder.
7684 *
7685 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7686 * connected, or {@code null} if they are isolated
7687 * @param lhs a consumer that populates the first predicate body
7688 * @param rhs a consumer that populates the second predicate body
7689 * @return the conditional-or operation builder
7690 */
7691 public static ConditionalOrOp.Builder conditionalOr(Body.Builder connectedAncestorBody,
7692 Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) {
7693 return new ConditionalOrOp.Builder(connectedAncestorBody, lhs, rhs);
7694 }
7695
7696 /**
7697 * Creates a conditional-and operation
7698 *
7699 * @param bodies the body builders for the predicate bodies
7700 * @return the conditional-and operation
7701 */
7702 // predicates, ()boolean
7703 public static ConditionalAndOp conditionalAnd(List<Body.Builder> bodies) {
7704 return new ConditionalAndOp(bodies);
7705 }
7706
7707 /**
7708 * Creates a conditional-or operation
7709 *
7710 * @param bodies the body builders for the predicate bodies
7711 * @return the conditional-or operation
7712 */
7713 // predicates, ()boolean
7714 public static ConditionalOrOp conditionalOr(List<Body.Builder> bodies) {
7715 return new ConditionalOrOp(bodies);
7716 }
7717
7718 /**
7719 * Creates a conditional operation
7720 *
7721 * @param expressionType the result type of the expression
7722 * @param predicateBody the body builder for the predicate body
7723 * @param trueBody the body builder for the true body
7724 * @param falseBody the body builder for the false body
7725 * @return the conditional operation
7726 */
7727 public static ConditionalExpressionOp conditionalExpression(CodeType expressionType,
7728 Body.Builder predicateBody,
7729 Body.Builder trueBody,
7730 Body.Builder falseBody) {
7731 Objects.requireNonNull(expressionType);
7732 return new ConditionalExpressionOp(expressionType, predicateBody, trueBody, falseBody);
7733 }
7734
7735 /**
7736 * Creates a conditional operation
7737 * <p>
7738 * The result type of the operation will be derived from the yield type of the true body.
7739 *
7740 * @param predicateBody the body builder for the predicate body
7741 * @param trueBody the body builder for the true body
7742 * @param falseBody the body builder for the false body
7743 * @return the conditional operation
7744 */
7745 public static ConditionalExpressionOp conditionalExpression(Body.Builder predicateBody,
7746 Body.Builder trueBody,
7747 Body.Builder falseBody) {
7748 return new ConditionalExpressionOp(null, predicateBody, trueBody, falseBody);
7749 }
7750
7751 /**
7752 * Creates try operation builder.
7753 *
7754 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7755 * connected, or {@code null} if they are isolated
7756 * @param c a consumer that populates the try body
7757 * @return the try operation builder
7758 */
7759 public static TryOp.CatchBuilder try_(Body.Builder connectedAncestorBody, Consumer<Block.Builder> c) {
7760 Body.Builder _try = Body.Builder.of(connectedAncestorBody, CoreType.FUNCTION_TYPE_VOID);
7761 c.accept(_try.entryBlock());
7762 return new TryOp.CatchBuilder(connectedAncestorBody, List.of(), _try);
7763 }
7764
7765 /**
7766 * Creates try-with-resources operation builder.
7767 *
7768 * @param connectedAncestorBody the nearest ancestor body builder to which body builders for this operation are
7769 * connected, or {@code null} if they are isolated
7770 * @return the try-with-resources operation builder
7771 */
7772 public static TryOp.BodyBuilder tryWithResources(Body.Builder connectedAncestorBody) {
7773 return new TryOp.BodyBuilder(connectedAncestorBody);
7774 }
7775
7776 // resources: ()T1, (T1)T2, ..., (T1, T2, ..., T{N-1})TN, or empty
7777 // Ti is Ri for a resource expression, or Var<Ri> for a resource declaration
7778 // try (T1, T2, ..., TN)void, or try ()void
7779 // catch (E )void, where E <: Throwable
7780 // finally ()void, or null
7781
7782 /**
7783 * Creates a try or try-with-resources operation.
7784 *
7785 * @param resourceBodies the resources body builders
7786 * @param body the try body builder
7787 * @param catchBodies the catch body builders
7788 * @param finallyBody the finalizer body builder, may be {@code null}
7789 * @return the try or try-with-resources operation
7790 */
7791 public static TryOp try_(List<Body.Builder> resourceBodies,
7792 Body.Builder body,
7793 List<Body.Builder> catchBodies,
7794 Body.Builder finallyBody) {
7795 return try_(resourceBodies, body, null, catchBodies, finallyBody);
7796 }
7797
7798 /**
7799 * Creates a try or try-with-resources operation.
7800 *
7801 * @param resourceBodies the resources body builders
7802 * @param body the try body builder
7803 * @param catchTypes the explicit catch types, may be {@code null}
7804 * @param catchBodies the catch body builders
7805 * @param finallyBody the finalizer body builder, may be {@code null}
7806 * @return the try or try-with-resources operation
7807 */
7808 public static TryOp try_(List<Body.Builder> resourceBodies,
7809 Body.Builder body,
7810 List<CodeType> catchTypes,
7811 List<Body.Builder> catchBodies,
7812 Body.Builder finallyBody) {
7813 return new TryOp(resourceBodies, body, catchTypes, catchBodies, finallyBody);
7814 }
7815
7816 //
7817 // Patterns
7818
7819 /**
7820 * Creates a pattern match operation.
7821 *
7822 * @param target the target value
7823 * @param patternBody the pattern body builder
7824 * @param matchBody the match body builder
7825 * @return the pattern match operation
7826 */
7827 public static PatternOps.MatchOp match(Value target,
7828 Body.Builder patternBody, Body.Builder matchBody) {
7829 return new PatternOps.MatchOp(target, patternBody, matchBody);
7830 }
7831
7832 /**
7833 * Creates a pattern binding operation.
7834 *
7835 * @param type the type of value to be bound
7836 * @param bindingName the binding name
7837 * @return the pattern binding operation
7838 */
7839 public static PatternOps.TypePatternOp typePattern(CodeType type, String bindingName) {
7840 return new PatternOps.TypePatternOp(type, bindingName);
7841 }
7842
7843 /**
7844 * Creates a record pattern operation.
7845 *
7846 * @param recordRef the record reference
7847 * @param nestedPatterns the nested pattern values
7848 * @return the record pattern operation
7849 */
7850 public static PatternOps.RecordPatternOp recordPattern(RecordTypeRef recordRef, Value... nestedPatterns) {
7851 return recordPattern(recordRef, List.of(nestedPatterns));
7852 }
7853
7854 /**
7855 * Creates a record pattern operation.
7856 *
7857 * @param recordRef the record reference
7858 * @param nestedPatterns the nested pattern values
7859 * @return the record pattern operation
7860 */
7861 public static PatternOps.RecordPatternOp recordPattern(RecordTypeRef recordRef, List<Value> nestedPatterns) {
7862 return new PatternOps.RecordPatternOp(recordRef, nestedPatterns);
7863 }
7864
7865 /**
7866 * Creates a match-all pattern operation.
7867 *
7868 * @return a match-all pattern
7869 */
7870 public static PatternOps.MatchAllPatternOp matchAllPattern() {
7871 return new PatternOps.MatchAllPatternOp();
7872 }
7873 }