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