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