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