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