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