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