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