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