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