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