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