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