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