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