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