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