1 /*
   2  * Copyright (c) 2024, 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 java.lang.reflect.code.op;
  27 
  28 import java.lang.constant.ClassDesc;
  29 import java.lang.reflect.code.*;
  30 import java.lang.reflect.code.type.FieldRef;
  31 import java.lang.reflect.code.type.MethodRef;
  32 import java.lang.reflect.code.type.*;
  33 import java.util.*;
  34 import java.util.function.Consumer;
  35 import java.util.function.Function;
  36 import java.util.function.Predicate;
  37 
  38 /**
  39  * The top-level operation class for the set of enclosed core operations.
  40  * <p>
  41  * A code model, produced by the Java compiler from Java program source, may consist of extended operations and core
  42  * operations. Such a model represents the same Java program and preserves the program meaning as defined by the
  43  * Java Language Specification
  44  */
  45 public sealed abstract class CoreOp extends ExternalizableOp {
  46     /**
  47      * An operation that models a Java expression
  48      */
  49     sealed interface JavaExpression permits
  50             ArithmeticOperation,
  51             ArrayAccessOp.ArrayLoadOp,
  52             ArrayAccessOp.ArrayStoreOp,
  53             ArrayLengthOp,
  54             CastOp,
  55             ConvOp,
  56             ClosureOp,
  57             ConcatOp,
  58             ConstantOp,
  59             FieldAccessOp.FieldLoadOp,
  60             FieldAccessOp.FieldStoreOp,
  61             InstanceOfOp,
  62             InvokeOp,
  63             LambdaOp,
  64             NewOp,
  65             TestOperation,
  66             VarAccessOp.VarLoadOp,
  67             VarAccessOp.VarStoreOp,
  68             ExtendedOp.JavaConditionalExpressionOp,
  69             ExtendedOp.JavaConditionalOp,
  70             ExtendedOp.JavaSwitchExpressionOp {
  71     }
  72 
  73     /**
  74      * An operation that models a Java statement
  75      */
  76     sealed interface JavaStatement permits
  77             ArrayAccessOp.ArrayStoreOp,
  78             AssertOp,
  79             FieldAccessOp.FieldStoreOp,
  80             InvokeOp,
  81             NewOp,
  82             ReturnOp,
  83             ThrowOp,
  84             VarAccessOp.VarStoreOp,
  85             VarOp,
  86             ExtendedOp.JavaBlockOp,
  87             ExtendedOp.JavaDoWhileOp,
  88             ExtendedOp.JavaEnhancedForOp,
  89             ExtendedOp.JavaForOp,
  90             ExtendedOp.JavaIfOp,
  91             ExtendedOp.JavaLabelOp,
  92             ExtendedOp.JavaLabeledOp,
  93             ExtendedOp.JavaTryOp,
  94             ExtendedOp.JavaWhileOp,
  95             ExtendedOp.JavaYieldOp {
  96     }
  97 
  98     // Split string to ensure the name does not get rewritten
  99     // when the script copies this source to the jdk.compiler module
 100     static final String PACKAGE_NAME = "java.lang" + ".reflect.code";
 101 
 102     static final String CoreOp_CLASS_NAME = PACKAGE_NAME + "." + CoreOp.class.getSimpleName();
 103 
 104     protected CoreOp(Op that, CopyContext cc) {
 105         super(that, cc);
 106     }
 107 
 108     protected CoreOp(String name, List<? extends Value> operands) {
 109         super(name, operands);
 110     }
 111 
 112     protected CoreOp(ExternalizedOp def) {
 113         super(def);
 114     }
 115 
 116     /**
 117      * The function operation, that can model a Java method declaration.
 118      */
 119     @OpFactory.OpDeclaration(FuncOp.NAME)
 120     public static final class FuncOp extends CoreOp
 121             implements Op.Invokable, Op.Isolated, Op.Lowerable {
 122 
 123         public static class Builder {
 124             final Body.Builder ancestorBody;
 125             final String funcName;
 126             final FunctionType funcType;
 127 
 128             Builder(Body.Builder ancestorBody, String funcName, FunctionType funcType) {
 129                 this.ancestorBody = ancestorBody;
 130                 this.funcName = funcName;
 131                 this.funcType = funcType;
 132             }
 133 
 134             public FuncOp body(Consumer<Block.Builder> c) {
 135                 Body.Builder body = Body.Builder.of(ancestorBody, funcType);
 136                 c.accept(body.entryBlock());
 137                 return new FuncOp(funcName, body);
 138             }
 139         }
 140 
 141         public static final String NAME = "func";
 142         public static final String ATTRIBUTE_FUNC_NAME = NAME + ".name";
 143 
 144         final String funcName;
 145         final Body body;
 146 
 147         public static FuncOp create(ExternalizedOp def) {
 148             if (!def.operands().isEmpty()) {
 149                 throw new IllegalStateException("Bad op " + def.name());
 150             }
 151 
 152             String funcName = def.extractAttributeValue(ATTRIBUTE_FUNC_NAME, true,
 153                     v -> switch (v) {
 154                         case String s -> s;
 155                         default -> throw new UnsupportedOperationException("Unsupported func name value:" + v);
 156                     });
 157             return new FuncOp(def, funcName);
 158         }
 159 
 160         FuncOp(ExternalizedOp def, String funcName) {
 161             super(def);
 162 
 163             this.funcName = funcName;
 164             this.body = def.bodyDefinitions().get(0).build(this);
 165         }
 166 
 167         FuncOp(FuncOp that, CopyContext cc, OpTransformer oa) {
 168             this(that, that.funcName, cc, oa);
 169         }
 170 
 171         FuncOp(FuncOp that, String funcName, CopyContext cc, OpTransformer ot) {
 172             super(that, cc);
 173 
 174             this.funcName = funcName;
 175             this.body = that.body.transform(cc, ot).build(this);
 176         }
 177 
 178         @Override
 179         public FuncOp transform(CopyContext cc, OpTransformer ot) {
 180             return new FuncOp(this, cc, ot);
 181         }
 182 
 183         public FuncOp transform(OpTransformer ot) {
 184             return new FuncOp(this, CopyContext.create(), ot);
 185         }
 186 
 187         public FuncOp transform(String funcName, OpTransformer ot) {
 188             return new FuncOp(this, funcName, CopyContext.create(), ot);
 189         }
 190 
 191         FuncOp(String funcName, Body.Builder bodyBuilder) {
 192             super(NAME,
 193                     List.of());
 194 
 195             this.funcName = funcName;
 196             this.body = bodyBuilder.build(this);
 197         }
 198 
 199         @Override
 200         public List<Body> bodies() {
 201             return List.of(body);
 202         }
 203 
 204         @Override
 205         public Map<String, Object> attributes() {
 206             HashMap<String, Object> m = new HashMap<>(super.attributes());
 207             m.put("", funcName);
 208             return Collections.unmodifiableMap(m);
 209         }
 210 
 211         @Override
 212         public FunctionType invokableType() {
 213             return body.bodyType();
 214         }
 215 
 216         public String funcName() {
 217             return funcName;
 218         }
 219 
 220         @Override
 221         public Body body() {
 222             return body;
 223         }
 224 
 225         @Override
 226         public Block.Builder lower(Block.Builder b, OpTransformer _ignore) {
 227             // Isolate body with respect to ancestor transformations
 228             // and copy directly without lowering descendant operations
 229             b.op(this, OpTransformer.COPYING_TRANSFORMER);
 230             return b;
 231         }
 232 
 233         @Override
 234         public TypeElement resultType() {
 235             return JavaType.VOID;
 236         }
 237     }
 238 
 239     /**
 240      * The function call operation, that models a call to a function, by name, declared in the module op that is also an
 241      * ancestor of this operation.
 242      */
 243     // @@@ stack effects equivalent to the call operation as if the function were a Java method?
 244     @OpFactory.OpDeclaration(FuncCallOp.NAME)
 245     public static final class FuncCallOp extends CoreOp {
 246         public static final String NAME = "func.call";
 247         public static final String ATTRIBUTE_FUNC_NAME = NAME + ".name";
 248 
 249         final String funcName;
 250         final TypeElement resultType;
 251 
 252         public static FuncCallOp create(ExternalizedOp def) {
 253             String funcName = def.extractAttributeValue(ATTRIBUTE_FUNC_NAME, true,
 254                     v -> switch (v) {
 255                         case String s -> s;
 256                         default -> throw new UnsupportedOperationException("Unsupported func name value:" + v);
 257                     });
 258 
 259             return new FuncCallOp(def, funcName);
 260         }
 261 
 262         FuncCallOp(ExternalizedOp def, String funcName) {
 263             super(def);
 264 
 265             this.funcName = funcName;
 266             this.resultType = def.resultType();
 267         }
 268 
 269         FuncCallOp(FuncCallOp that, CopyContext cc) {
 270             super(that, cc);
 271 
 272             this.funcName = that.funcName;
 273             this.resultType = that.resultType;
 274         }
 275 
 276         @Override
 277         public FuncCallOp transform(CopyContext cc, OpTransformer ot) {
 278             return new FuncCallOp(this, cc);
 279         }
 280 
 281         FuncCallOp(String funcName, TypeElement resultType, List<Value> args) {
 282             super(NAME, args);
 283 
 284             this.funcName = funcName;
 285             this.resultType = resultType;
 286         }
 287 
 288         @Override
 289         public Map<String, Object> attributes() {
 290             HashMap<String, Object> m = new HashMap<>(super.attributes());
 291             m.put("", funcName);
 292             return Collections.unmodifiableMap(m);
 293         }
 294 
 295         public String funcName() {
 296             return funcName;
 297         }
 298 
 299         @Override
 300         public TypeElement resultType() {
 301             return resultType;
 302         }
 303     }
 304 
 305     /**
 306      * The module operation, modeling a collection of functions,
 307      * and creating a symbol table of function name to function
 308      */
 309     @OpFactory.OpDeclaration(ModuleOp.NAME)
 310     public static final class ModuleOp extends CoreOp
 311             implements Op.Isolated {
 312 
 313         public static final String NAME = "module";
 314 
 315         final Map<String, FuncOp> table;
 316         final Body body;
 317 
 318         public static ModuleOp create(ExternalizedOp def) {
 319             if (!def.operands().isEmpty()) {
 320                 throw new IllegalStateException("Bad op " + def.name());
 321             }
 322 
 323             return new ModuleOp(def);
 324         }
 325 
 326         ModuleOp(ExternalizedOp def) {
 327             super(def);
 328 
 329             this.body = def.bodyDefinitions().get(0).build(this);
 330             this.table = createTable(body);
 331         }
 332 
 333         ModuleOp(ModuleOp that, CopyContext cc, OpTransformer ot) {
 334             super(that, cc);
 335 
 336             this.body = that.body.transform(cc, ot).build(this);
 337             this.table = createTable(body);
 338         }
 339 
 340         static Map<String, FuncOp> createTable(Body body) {
 341             Map<String, FuncOp> table = new HashMap<>();
 342             for (var op : body.entryBlock().ops()) {
 343                 if (op instanceof FuncOp fop) {
 344                     table.put(fop.funcName(), fop);
 345                 } else {
 346                     throw new IllegalArgumentException("Bad operation in module: " + op);
 347                 }
 348             }
 349             return Collections.unmodifiableMap(table);
 350         }
 351 
 352         @Override
 353         public ModuleOp transform(CopyContext cc, OpTransformer ot) {
 354             return new ModuleOp(this, cc, ot);
 355         }
 356 
 357         public ModuleOp transform(OpTransformer ot) {
 358             return new ModuleOp(this, CopyContext.create(), ot);
 359         }
 360 
 361         ModuleOp(List<FuncOp> functions) {
 362             super(NAME,
 363                     List.of());
 364 
 365             Body.Builder bodyC = Body.Builder.of(null, FunctionType.VOID);
 366             Block.Builder entryBlock = bodyC.entryBlock();
 367             Map<String, FuncOp> table = new HashMap<>();
 368             for (FuncOp f : functions) {
 369                 entryBlock.op(f);
 370                 table.put(f.funcName(), f);
 371             }
 372             this.table = Collections.unmodifiableMap(table);
 373             this.body = bodyC.build(this);
 374         }
 375 
 376         @Override
 377         public List<Body> bodies() {
 378             return List.of(body);
 379         }
 380 
 381         public Map<String, FuncOp> functionTable() {
 382             return table;
 383         }
 384 
 385         @Override
 386         public TypeElement resultType() {
 387             return JavaType.VOID;
 388         }
 389     }
 390 
 391     /**
 392      * The quoted operation, that models the quoting of an operation.
 393      */
 394     @OpFactory.OpDeclaration(QuotedOp.NAME)
 395     public static final class QuotedOp extends CoreOp
 396             implements Op.Nested, Op.Lowerable, Op.Pure {
 397         public static final String NAME = "quoted";
 398 
 399         // Type name must be the same in the java.base and jdk.compiler module
 400         static final String Quoted_CLASS_NAME = PACKAGE_NAME +
 401                 "." + Quoted.class.getSimpleName();
 402         public static final JavaType QUOTED_TYPE = JavaType.type(ClassDesc.of(Quoted_CLASS_NAME));
 403 
 404         final Body quotedBody;
 405 
 406         final Op quotedOp;
 407 
 408         public QuotedOp(ExternalizedOp def) {
 409             super(def);
 410 
 411             this.quotedBody = def.bodyDefinitions().get(0).build(this);
 412 
 413             if (quotedBody.entryBlock().terminatingOp() instanceof YieldOp brk &&
 414                     brk.yieldValue() instanceof Result quotedOpResult) {
 415                 this.quotedOp = quotedOpResult.op();
 416             } else {
 417                 throw new IllegalArgumentException();
 418             }
 419         }
 420 
 421         QuotedOp(QuotedOp that, CopyContext cc, OpTransformer ot) {
 422             super(that, cc);
 423 
 424             this.quotedBody = that.quotedBody.transform(cc, ot).build(this);
 425             this.quotedOp = that.quotedOp;
 426         }
 427 
 428         @Override
 429         public QuotedOp transform(CopyContext cc, OpTransformer ot) {
 430             return new QuotedOp(this, cc, ot);
 431         }
 432 
 433         QuotedOp(Body.Builder bodyC) {
 434             super(NAME,
 435                     List.of());
 436 
 437             this.quotedBody = bodyC.build(this);
 438             if (quotedBody.blocks().size() > 1) {
 439                 throw new IllegalArgumentException();
 440             }
 441             if (!(quotedBody.entryBlock().terminatingOp() instanceof YieldOp yop)) {
 442                 throw new IllegalArgumentException();
 443             }
 444             if (!(yop.yieldValue() instanceof Result r)) {
 445                 throw new IllegalArgumentException();
 446             }
 447             this.quotedOp = r.op();
 448         }
 449 
 450         @Override
 451         public List<Body> bodies() {
 452             return List.of(quotedBody);
 453         }
 454 
 455         public Op quotedOp() {
 456             return quotedOp;
 457         }
 458 
 459         @Override
 460         public List<Value> capturedValues() {
 461             return quotedBody.capturedValues();
 462         }
 463 
 464         @Override
 465         public Block.Builder lower(Block.Builder b, OpTransformer _ignore) {
 466             // Isolate body with respect to ancestor transformations
 467             // and copy directly without lowering descendant operations
 468             b.op(this, OpTransformer.COPYING_TRANSFORMER);
 469             return b;
 470         }
 471 
 472         @Override
 473         public TypeElement resultType() {
 474             return QUOTED_TYPE;
 475         }
 476     }
 477 
 478     /**
 479      * The lambda operation, that can model a Java lambda expression.
 480      */
 481     @OpFactory.OpDeclaration(LambdaOp.NAME)
 482     public static final class LambdaOp extends CoreOp
 483             implements Op.Invokable, Op.Lowerable, JavaExpression {
 484 
 485         public static class Builder {
 486             final Body.Builder ancestorBody;
 487             final FunctionType funcType;
 488             final TypeElement functionalInterface;
 489 
 490             Builder(Body.Builder ancestorBody, FunctionType funcType, TypeElement functionalInterface) {
 491                 this.ancestorBody = ancestorBody;
 492                 this.funcType = funcType;
 493                 this.functionalInterface = functionalInterface;
 494             }
 495 
 496             public LambdaOp body(Consumer<Block.Builder> c) {
 497                 Body.Builder body = Body.Builder.of(ancestorBody, funcType);
 498                 c.accept(body.entryBlock());
 499                 return new LambdaOp(functionalInterface, body);
 500             }
 501         }
 502 
 503         public static final String NAME = "lambda";
 504 
 505         final TypeElement functionalInterface;
 506         final Body body;
 507 
 508         public LambdaOp(ExternalizedOp def) {
 509             super(def);
 510 
 511             this.functionalInterface = def.resultType();
 512             this.body = def.bodyDefinitions().get(0).build(this);
 513         }
 514 
 515         LambdaOp(LambdaOp that, CopyContext cc, OpTransformer ot) {
 516             super(that, cc);
 517 
 518             this.functionalInterface = that.functionalInterface;
 519             this.body = that.body.transform(cc, ot).build(this);
 520         }
 521 
 522         @Override
 523         public LambdaOp transform(CopyContext cc, OpTransformer ot) {
 524             return new LambdaOp(this, cc, ot);
 525         }
 526 
 527         LambdaOp(TypeElement functionalInterface, Body.Builder bodyC) {
 528             super(NAME,
 529                     List.of());
 530 
 531             this.functionalInterface = functionalInterface;
 532             this.body = bodyC.build(this);
 533         }
 534 
 535         @Override
 536         public List<Body> bodies() {
 537             return List.of(body);
 538         }
 539 
 540         @Override
 541         public FunctionType invokableType() {
 542             return body.bodyType();
 543         }
 544 
 545         public TypeElement functionalInterface() {
 546             return functionalInterface;
 547         }
 548 
 549         @Override
 550         public Body body() {
 551             return body;
 552         }
 553 
 554         @Override
 555         public List<Value> capturedValues() {
 556             return body.capturedValues();
 557         }
 558 
 559         @Override
 560         public Block.Builder lower(Block.Builder b, OpTransformer _ignore) {
 561             // Isolate body with respect to ancestor transformations
 562             b.op(this, OpTransformer.LOWERING_TRANSFORMER);
 563             return b;
 564         }
 565 
 566         @Override
 567         public TypeElement resultType() {
 568             return functionalInterface();
 569         }
 570 
 571         /**
 572          * Determines if this lambda operation could have originated from a
 573          * method reference declared in Java source code.
 574          * <p>
 575          * Such a lambda operation is one with the following constraints:
 576          * <ol>
 577          *     <li>Zero or one captured value (assuming correspondence to the {@code this} variable).
 578          *     <li>A body with only one (entry) block that contains only variable declaration
 579          *     operations, variable load operations, invoke operations to box or unbox
 580          *     primitive values, a single invoke operation to the method that is
 581          *     referenced, and a return operation.
 582          *     <li>if the return operation returns a non-void result then that result is,
 583          *     or uniquely depends on, the result of the referencing invoke operation.
 584          *     <li>If the lambda operation captures one value then the first operand corresponds
 585          *     to captured the value, and subsequent operands of the referencing invocation
 586          *     operation are, or uniquely depend on, the lambda operation's parameters, in order.
 587          *     Otherwise, the first and subsequent operands of the referencing invocation
 588          *     operation are, or uniquely depend on, the lambda operation's parameters, in order.
 589          * </ol>
 590          * A value, V2, uniquely depends on another value, V1, if the graph of what V2 depends on
 591          * contains only nodes with single edges terminating in V1, and the graph of what depends on V1
 592          * is bidirectionally equal to the graph of what V2 depends on.
 593          *
 594          * @return the invocation operation to the method referenced by the lambda
 595          * operation, otherwise empty.
 596          */
 597         public Optional<InvokeOp> methodReference() {
 598             // Single block
 599             if (body().blocks().size() > 1) {
 600                 return Optional.empty();
 601             }
 602 
 603             // Zero or one (this) capture
 604             List<Value> cvs = capturedValues();
 605             if (cvs.size() > 1) {
 606                 return Optional.empty();
 607             }
 608 
 609             Map<Value, Value> valueMapping = new HashMap<>();
 610             CoreOp.InvokeOp methodRefInvokeOp = extractMethodInvoke(valueMapping, body().entryBlock().ops());
 611             if (methodRefInvokeOp == null) {
 612                 return Optional.empty();
 613             }
 614 
 615             // Lambda's parameters map in encounter order with the invocation's operands
 616             List<Value> lambdaParameters = new ArrayList<>();
 617             if (cvs.size() == 1) {
 618                 lambdaParameters.add(cvs.getFirst());
 619             }
 620             lambdaParameters.addAll(parameters());
 621             List<Value> methodRefOperands = methodRefInvokeOp.operands().stream().map(valueMapping::get).toList();
 622             if (!lambdaParameters.equals(methodRefOperands)) {
 623                 return Optional.empty();
 624             }
 625 
 626             return Optional.of(methodRefInvokeOp);
 627         }
 628 
 629         static CoreOp.InvokeOp extractMethodInvoke(Map<Value, Value> valueMapping, List<Op> ops) {
 630             CoreOp.InvokeOp methodRefInvokeOp = null;
 631             for (Op op : ops) {
 632                 switch (op) {
 633                     case CoreOp.VarOp varOp -> {
 634                         if (isValueUsedWithOp(varOp.result(), o -> o instanceof CoreOp.VarAccessOp.VarStoreOp)) {
 635                             return null;
 636                         }
 637                     }
 638                     case CoreOp.VarAccessOp.VarLoadOp varLoadOp -> {
 639                         Value v = varLoadOp.varOp().operands().getFirst();
 640                         valueMapping.put(varLoadOp.result(), valueMapping.getOrDefault(v, v));
 641                     }
 642                     case CoreOp.InvokeOp iop when isBoxOrUnboxInvocation(iop) -> {
 643                         Value v = iop.operands().getFirst();
 644                         valueMapping.put(iop.result(), valueMapping.getOrDefault(v, v));
 645                     }
 646                     case CoreOp.InvokeOp iop -> {
 647                         if (methodRefInvokeOp != null) {
 648                             return null;
 649                         }
 650 
 651                         for (Value o : iop.operands()) {
 652                             valueMapping.put(o, valueMapping.getOrDefault(o, o));
 653                         }
 654                         methodRefInvokeOp = iop;
 655                     }
 656                     case CoreOp.ReturnOp rop -> {
 657                         if (methodRefInvokeOp == null) {
 658                             return null;
 659                         }
 660                         Value r = rop.returnValue();
 661                         if (!(valueMapping.getOrDefault(r, r) instanceof Op.Result invokeResult)) {
 662                             return null;
 663                         }
 664                         if (invokeResult.op() != methodRefInvokeOp) {
 665                             return null;
 666                         }
 667                         assert methodRefInvokeOp.result().uses().size() == 1;
 668                     }
 669                     default -> {
 670                         return null;
 671                     }
 672                 }
 673             }
 674 
 675             return methodRefInvokeOp;
 676         }
 677 
 678         private static boolean isValueUsedWithOp(Value value, Predicate<Op> opPredicate) {
 679             for (Op.Result user : value.uses()) {
 680                 if (opPredicate.test(user.op())) {
 681                     return true;
 682                 }
 683             }
 684             return false;
 685         }
 686 
 687         // @@@ Move to functionality on JavaType(s)
 688         static final Set<String> UNBOX_NAMES = Set.of(
 689                 "byteValue",
 690                 "shortValue",
 691                 "charValue",
 692                 "intValue",
 693                 "longValue",
 694                 "floatValue",
 695                 "doubleValue",
 696                 "booleanValue");
 697 
 698         private static boolean isBoxOrUnboxInvocation(CoreOp.InvokeOp iop) {
 699             MethodRef mr = iop.invokeDescriptor();
 700             return mr.refType() instanceof ClassType ct && ct.unbox().isPresent() &&
 701                     (UNBOX_NAMES.contains(mr.name()) || mr.name().equals("valueOf"));
 702         }
 703     }
 704 
 705     /**
 706      * The closure operation, that can model a structured Java lambda expression
 707      * that has no target type (a functional interface).
 708      */
 709     @OpFactory.OpDeclaration(ClosureOp.NAME)
 710     public static final class ClosureOp extends CoreOp
 711             implements Op.Invokable, Op.Lowerable, JavaExpression {
 712 
 713         public static class Builder {
 714             final Body.Builder ancestorBody;
 715             final FunctionType funcType;
 716 
 717             Builder(Body.Builder ancestorBody, FunctionType funcType) {
 718                 this.ancestorBody = ancestorBody;
 719                 this.funcType = funcType;
 720             }
 721 
 722             public ClosureOp body(Consumer<Block.Builder> c) {
 723                 Body.Builder body = Body.Builder.of(ancestorBody, funcType);
 724                 c.accept(body.entryBlock());
 725                 return new ClosureOp(body);
 726             }
 727         }
 728 
 729         public static final String NAME = "closure";
 730 
 731         final Body body;
 732 
 733         public ClosureOp(ExternalizedOp def) {
 734             super(def);
 735 
 736             this.body = def.bodyDefinitions().get(0).build(this);
 737         }
 738 
 739         ClosureOp(ClosureOp that, CopyContext cc, OpTransformer ot) {
 740             super(that, cc);
 741 
 742             this.body = that.body.transform(cc, ot).build(this);
 743         }
 744 
 745         @Override
 746         public ClosureOp transform(CopyContext cc, OpTransformer ot) {
 747             return new ClosureOp(this, cc, ot);
 748         }
 749 
 750         ClosureOp(Body.Builder bodyC) {
 751             super(NAME,
 752                     List.of());
 753 
 754             this.body = bodyC.build(this);
 755         }
 756 
 757         @Override
 758         public List<Body> bodies() {
 759             return List.of(body);
 760         }
 761 
 762         @Override
 763         public FunctionType invokableType() {
 764             return body.bodyType();
 765         }
 766 
 767         @Override
 768         public Body body() {
 769             return body;
 770         }
 771 
 772         @Override
 773         public List<Value> capturedValues() {
 774             return body.capturedValues();
 775         }
 776 
 777         @Override
 778         public Block.Builder lower(Block.Builder b, OpTransformer _ignore) {
 779             // Isolate body with respect to ancestor transformations
 780             b.op(this, OpTransformer.LOWERING_TRANSFORMER);
 781             return b;
 782         }
 783 
 784         @Override
 785         public TypeElement resultType() {
 786             return body.bodyType();
 787         }
 788     }
 789 
 790     /**
 791      * The closure call operation, that models a call to a closure, by reference
 792      */
 793 //  @@@ stack effects equivalent to the invocation of an SAM of on an instance of an anonymous functional interface
 794 //  that is the target of the closures lambda expression.
 795     @OpFactory.OpDeclaration(ClosureCallOp.NAME)
 796     public static final class ClosureCallOp extends CoreOp {
 797         public static final String NAME = "closure.call";
 798 
 799         public ClosureCallOp(ExternalizedOp def) {
 800             super(def);
 801         }
 802 
 803         ClosureCallOp(ClosureCallOp that, CopyContext cc) {
 804             super(that, cc);
 805         }
 806 
 807         @Override
 808         public ClosureCallOp transform(CopyContext cc, OpTransformer ot) {
 809             return new ClosureCallOp(this, cc);
 810         }
 811 
 812         ClosureCallOp(List<Value> args) {
 813             super(NAME, args);
 814         }
 815 
 816         @Override
 817         public TypeElement resultType() {
 818             FunctionType ft = (FunctionType) operands().getFirst().type();
 819             return ft.returnType();
 820         }
 821     }
 822 
 823     /**
 824      * The terminating return operation, that can model the Java language return statement.
 825      * <p>
 826      * This operation exits an isolated body.
 827      */
 828     @OpFactory.OpDeclaration(ReturnOp.NAME)
 829     public static final class ReturnOp extends CoreOp
 830             implements Op.BodyTerminating, JavaStatement {
 831         public static final String NAME = "return";
 832 
 833         public ReturnOp(ExternalizedOp def) {
 834             super(def);
 835 
 836             if (def.operands().size() > 1) {
 837                 throw new IllegalArgumentException("Operation must have zero or one operand " + def.name());
 838             }
 839         }
 840 
 841         ReturnOp(ReturnOp that, CopyContext cc) {
 842             super(that, cc);
 843         }
 844 
 845         @Override
 846         public ReturnOp transform(CopyContext cc, OpTransformer ot) {
 847             return new ReturnOp(this, cc);
 848         }
 849 
 850         ReturnOp() {
 851             super(NAME, List.of());
 852         }
 853 
 854         ReturnOp(Value operand) {
 855             super(NAME, List.of(operand));
 856         }
 857 
 858         public Value returnValue() {
 859             if (operands().size() == 1) {
 860                 return operands().get(0);
 861             } else {
 862                 // @@@
 863                 return null;
 864             }
 865         }
 866 
 867         @Override
 868         public TypeElement resultType() {
 869             return JavaType.VOID;
 870         }
 871     }
 872 
 873     /**
 874      * The terminating throw operation, that can model the Java language throw statement.
 875      */
 876     @OpFactory.OpDeclaration(ThrowOp.NAME)
 877     public static final class ThrowOp extends CoreOp
 878             implements Op.BodyTerminating, JavaStatement {
 879         public static final String NAME = "throw";
 880 
 881         public ThrowOp(ExternalizedOp def) {
 882             super(def);
 883 
 884             if (def.operands().size() != 1) {
 885                 throw new IllegalArgumentException("Operation must have one operand " + def.name());
 886             }
 887         }
 888 
 889         ThrowOp(ThrowOp that, CopyContext cc) {
 890             super(that, cc);
 891         }
 892 
 893         @Override
 894         public ThrowOp transform(CopyContext cc, OpTransformer ot) {
 895             return new ThrowOp(this, cc);
 896         }
 897 
 898         ThrowOp(Value e) {
 899             super(NAME, List.of(e));
 900         }
 901 
 902         public Value argument() {
 903             return operands().get(0);
 904         }
 905 
 906         @Override
 907         public TypeElement resultType() {
 908             return JavaType.VOID;
 909         }
 910     }
 911 
 912     /**
 913      * The assertion operation. Supporting assertions in statement form.
 914      */
 915     @OpFactory.OpDeclaration(AssertOp.NAME)
 916     public static final class AssertOp extends CoreOp
 917             implements Op.Nested, JavaStatement {
 918         public static final String NAME = "assert";
 919         public final List<Body> bodies;
 920 
 921         public AssertOp(ExternalizedOp def) {
 922             super(def);
 923             var bodies = def.bodyDefinitions().stream().map(b -> b.build(this)).toList();
 924             checkBodies(bodies);
 925             this.bodies = bodies;
 926         }
 927 
 928         public AssertOp(List<Body.Builder> bodies) {
 929             super(NAME, List.of());
 930             checkBodies(bodies);
 931             this.bodies = bodies.stream().map(b -> b.build(this)).toList();
 932         }
 933 
 934         AssertOp(AssertOp that, CopyContext cc, OpTransformer ot) {
 935 
 936             super(that, cc);
 937             this.bodies = that.bodies.stream().map(b -> b.transform(cc, ot).build(this)).toList();
 938         }
 939 
 940         private void checkBodies(List<?> bodies) {
 941             if (bodies.size() != 1 && bodies.size() != 2) {
 942                 throw new IllegalArgumentException("Assert must have one or two bodies.");
 943             }
 944         }
 945 
 946         @Override
 947         public Op transform(CopyContext cc, OpTransformer ot) {
 948             return new AssertOp(this, cc, ot);
 949         }
 950 
 951         @Override
 952         public TypeElement resultType() {
 953             return JavaType.VOID;
 954         }
 955 
 956         @Override
 957         public List<Body> bodies() {
 958             return this.bodies;
 959         }
 960     }
 961 
 962     /**
 963      * The terminating unreachable operation.
 964      * <p>
 965      * This operation models termination that is unreachable.
 966      */
 967     @OpFactory.OpDeclaration(UnreachableOp.NAME)
 968     public static final class UnreachableOp extends CoreOp
 969             implements Op.BodyTerminating {
 970         public static final String NAME = "unreachable";
 971 
 972         public UnreachableOp(ExternalizedOp def) {
 973             super(def);
 974 
 975             if (!def.operands().isEmpty()) {
 976                 throw new IllegalArgumentException("Operation must zero operands " + def.name());
 977             }
 978         }
 979 
 980         UnreachableOp(UnreachableOp that, CopyContext cc) {
 981             super(that, cc);
 982         }
 983 
 984         @Override
 985         public UnreachableOp transform(CopyContext cc, OpTransformer ot) {
 986             return new UnreachableOp(this, cc);
 987         }
 988 
 989         UnreachableOp() {
 990             super(NAME, List.of());
 991         }
 992 
 993         @Override
 994         public TypeElement resultType() {
 995             return JavaType.VOID;
 996         }
 997     }
 998 
 999     /**
1000      * The terminating yield operation.
1001      * <p>
1002      * This operation models exits from its parent body, yielding at most one value (zero value for yielding unit
1003      * or void)
1004      */
1005     @OpFactory.OpDeclaration(YieldOp.NAME)
1006     public static final class YieldOp extends CoreOp
1007             implements Op.BodyTerminating {
1008         public static final String NAME = "yield";
1009 
1010         public YieldOp(ExternalizedOp def) {
1011             super(def);
1012 
1013             if (def.operands().size() > 1) {
1014                 throw new IllegalArgumentException("Operation must have zero or one operand " + def.name());
1015             }
1016         }
1017 
1018         YieldOp(YieldOp that, CopyContext cc) {
1019             super(that, cc);
1020         }
1021 
1022         @Override
1023         public YieldOp transform(CopyContext cc, OpTransformer ot) {
1024             return new YieldOp(this, cc);
1025         }
1026 
1027         YieldOp() {
1028             super(NAME, List.of());
1029         }
1030 
1031         YieldOp(List<Value> operands) {
1032             super(NAME, operands);
1033         }
1034 
1035         public Value yieldValue() {
1036             if (operands().size() == 1) {
1037                 return operands().get(0);
1038             } else {
1039                 // @@@
1040                 return null;
1041             }
1042         }
1043 
1044         @Override
1045         public TypeElement resultType() {
1046             return JavaType.VOID;
1047         }
1048     }
1049 
1050     /**
1051      * The terminating unconditional branch operation.
1052      * <p>
1053      * This operation accepts a successor to the next block to branch to.
1054      */
1055     @OpFactory.OpDeclaration(BranchOp.NAME)
1056     public static final class BranchOp extends CoreOp
1057             implements Op.BlockTerminating {
1058         public static final String NAME = "branch";
1059 
1060         final Block.Reference b;
1061 
1062         public BranchOp(ExternalizedOp def) {
1063             super(def);
1064 
1065             if (!def.operands().isEmpty() || def.successors().size() != 1) {
1066                 throw new IllegalArgumentException("Operation must have zero arguments and one successor" + def.name());
1067             }
1068 
1069             this.b = def.successors().get(0);
1070         }
1071 
1072         BranchOp(BranchOp that, CopyContext cc) {
1073             super(that, cc);
1074 
1075             this.b = cc.getSuccessorOrCreate(that.b);
1076         }
1077 
1078         @Override
1079         public BranchOp transform(CopyContext cc, OpTransformer ot) {
1080             return new BranchOp(this, cc);
1081         }
1082 
1083         BranchOp(Block.Reference successor) {
1084             super(NAME, List.of());
1085 
1086             this.b = successor;
1087         }
1088 
1089         @Override
1090         public List<Block.Reference> successors() {
1091             return List.of(b);
1092         }
1093 
1094         public Block.Reference branch() {
1095             return b;
1096         }
1097 
1098         @Override
1099         public TypeElement resultType() {
1100             return JavaType.VOID;
1101         }
1102     }
1103 
1104     /**
1105      * The terminating conditional branch operation.
1106      * <p>
1107      * This operation accepts a boolean operand and two successors, the true successor and false successor.
1108      * When the operand is true the  true successor is selected, otherwise the false successor is selected.
1109      * The selected successor refers to the next block to branch to.
1110      */
1111     @OpFactory.OpDeclaration(ConditionalBranchOp.NAME)
1112     public static final class ConditionalBranchOp extends CoreOp
1113             implements Op.BlockTerminating {
1114         public static final String NAME = "cbranch";
1115 
1116         final Block.Reference t;
1117         final Block.Reference f;
1118 
1119         public ConditionalBranchOp(ExternalizedOp def) {
1120             super(def);
1121 
1122             if (def.operands().size() != 1 || def.successors().size() != 2) {
1123                 throw new IllegalArgumentException("Operation must one operand and two successors" + def.name());
1124             }
1125 
1126             this.t = def.successors().get(0);
1127             this.f = def.successors().get(1);
1128         }
1129 
1130         ConditionalBranchOp(ConditionalBranchOp that, CopyContext cc) {
1131             super(that, cc);
1132 
1133             this.t = cc.getSuccessorOrCreate(that.t);
1134             this.f = cc.getSuccessorOrCreate(that.f);
1135         }
1136 
1137         @Override
1138         public ConditionalBranchOp transform(CopyContext cc, OpTransformer ot) {
1139             return new ConditionalBranchOp(this, cc);
1140         }
1141 
1142         ConditionalBranchOp(Value p, Block.Reference t, Block.Reference f) {
1143             super(NAME, List.of(p));
1144 
1145             this.t = t;
1146             this.f = f;
1147         }
1148 
1149         @Override
1150         public List<Block.Reference> successors() {
1151             return List.of(t, f);
1152         }
1153 
1154         public Value predicate() {
1155             return operands().get(0);
1156         }
1157 
1158         public Block.Reference trueBranch() {
1159             return t;
1160         }
1161 
1162         public Block.Reference falseBranch() {
1163             return f;
1164         }
1165 
1166         @Override
1167         public TypeElement resultType() {
1168             return JavaType.VOID;
1169         }
1170     }
1171 
1172     /**
1173      * The constant operation, that can model Java language literal and constant expressions.
1174      */
1175     @OpFactory.OpDeclaration(ConstantOp.NAME)
1176     public static final class ConstantOp extends CoreOp
1177             implements Op.Pure, JavaExpression {
1178         public static final String NAME = "constant";
1179 
1180         public static final String ATTRIBUTE_CONSTANT_VALUE = NAME + ".value";
1181 
1182         final Object value;
1183         final TypeElement type;
1184 
1185         public static ConstantOp create(ExternalizedOp def) {
1186             if (!def.operands().isEmpty()) {
1187                 throw new IllegalArgumentException("Operation must have zero operands");
1188             }
1189 
1190             Object value = def.extractAttributeValue(ATTRIBUTE_CONSTANT_VALUE, true,
1191                     v -> processConstantValue(def.resultType(), v));
1192             return new ConstantOp(def, value);
1193         }
1194 
1195         static Object processConstantValue(TypeElement t, Object value) {
1196             if (t.equals(JavaType.BOOLEAN)) {
1197                 if (value instanceof String s) {
1198                     return Boolean.valueOf(s);
1199                 } else if (value instanceof Boolean) {
1200                     return value;
1201                 }
1202             } else if (t.equals(JavaType.BYTE)) {
1203                 if (value instanceof String s) {
1204                     return Byte.valueOf(s);
1205                 } else if (value instanceof Number n) {
1206                     return n.byteValue();
1207                 }
1208             } else if (t.equals(JavaType.SHORT)) {
1209                 if (value instanceof String s) {
1210                     return Short.valueOf(s);
1211                 } else if (value instanceof Number n) {
1212                     return n.shortValue();
1213                 }
1214             } else if (t.equals(JavaType.CHAR)) {
1215                 if (value instanceof String s) {
1216                     return s.charAt(0);
1217                 } else if (value instanceof Character) {
1218                     return value;
1219                 }
1220             } else if (t.equals(JavaType.INT)) {
1221                 if (value instanceof String s) {
1222                     return Integer.valueOf(s);
1223                 } else if (value instanceof Number n) {
1224                     return n.intValue();
1225                 }
1226             } else if (t.equals(JavaType.LONG)) {
1227                 if (value instanceof String s) {
1228                     return Long.valueOf(s);
1229                 } else if (value instanceof Number n) {
1230                     return n.longValue();
1231                 }
1232             } else if (t.equals(JavaType.FLOAT)) {
1233                 if (value instanceof String s) {
1234                     return Float.valueOf(s);
1235                 } else if (value instanceof Number n) {
1236                     return n.floatValue();
1237                 }
1238             } else if (t.equals(JavaType.DOUBLE)) {
1239                 if (value instanceof String s) {
1240                     return Double.valueOf(s);
1241                 } else if (value instanceof Number n) {
1242                     return n.doubleValue();
1243                 }
1244             } else if (t.equals(JavaType.J_L_STRING)) {
1245                 return value == NULL_ATTRIBUTE_VALUE ? null :
1246                         value.toString();
1247             } else if (t.equals(JavaType.J_L_CLASS)) {
1248                 return value == NULL_ATTRIBUTE_VALUE ? null : JavaType.ofString(value.toString());
1249             } else if (value == NULL_ATTRIBUTE_VALUE) {
1250                 return null; // null constant
1251             }
1252 
1253             throw new UnsupportedOperationException("Unsupported constant type and value: " + t + " " + value);
1254         }
1255 
1256         ConstantOp(ExternalizedOp def, Object value) {
1257             super(def);
1258 
1259             this.type = def.resultType();
1260             this.value = value;
1261         }
1262 
1263         ConstantOp(ConstantOp that, CopyContext cc) {
1264             super(that, cc);
1265 
1266             this.type = that.type;
1267             this.value = that.value;
1268         }
1269 
1270         @Override
1271         public ConstantOp transform(CopyContext cc, OpTransformer ot) {
1272             return new ConstantOp(this, cc);
1273         }
1274 
1275         ConstantOp(TypeElement type, Object value) {
1276             super(NAME, List.of());
1277 
1278             this.type = type;
1279             this.value = value;
1280         }
1281 
1282         @Override
1283         public Map<String, Object> attributes() {
1284             HashMap<String, Object> attrs = new HashMap<>(super.attributes());
1285             attrs.put("", value == null ? NULL_ATTRIBUTE_VALUE : value);
1286             return attrs;
1287         }
1288 
1289         public Object value() {
1290             return value;
1291         }
1292 
1293         @Override
1294         public TypeElement resultType() {
1295             return type;
1296         }
1297     }
1298 
1299     /**
1300      * An operation characteristic indicating the operation's behavior may be emulated using Java reflection.
1301      * A descriptor is derived from or declared by the operation that can be resolved at runtime to
1302      * an instance of a reflective handle or member. That handle or member can be operated on to
1303      * emulate the operation's behavior, specifically as bytecode behavior.
1304      */
1305     public sealed interface ReflectiveOp {
1306     }
1307 
1308     /**
1309      * The invoke operation, that can model Java language method invocation expressions.
1310      */
1311     @OpFactory.OpDeclaration(InvokeOp.NAME)
1312     public static final class InvokeOp extends CoreOp
1313             implements ReflectiveOp, JavaExpression, JavaStatement {
1314         public static final String NAME = "invoke";
1315         public static final String ATTRIBUTE_INVOKE_DESCRIPTOR = NAME + ".descriptor";
1316 
1317         final MethodRef invokeDescriptor;
1318         final TypeElement resultType;
1319 
1320         public static InvokeOp create(ExternalizedOp def) {
1321             MethodRef invokeDescriptor = def.extractAttributeValue(ATTRIBUTE_INVOKE_DESCRIPTOR,
1322                     true, v -> switch (v) {
1323                         case String s -> MethodRef.ofString(s);
1324                         case MethodRef md -> md;
1325                         default -> throw new UnsupportedOperationException("Unsupported invoke descriptor value:" + v);
1326                     });
1327 
1328             return new InvokeOp(def, invokeDescriptor);
1329         }
1330 
1331         InvokeOp(ExternalizedOp def, MethodRef invokeDescriptor) {
1332             super(def);
1333 
1334             this.invokeDescriptor = invokeDescriptor;
1335             this.resultType = def.resultType();
1336         }
1337 
1338         InvokeOp(InvokeOp that, CopyContext cc) {
1339             super(that, cc);
1340 
1341             this.invokeDescriptor = that.invokeDescriptor;
1342             this.resultType = that.resultType;
1343         }
1344 
1345         @Override
1346         public InvokeOp transform(CopyContext cc, OpTransformer ot) {
1347             return new InvokeOp(this, cc);
1348         }
1349 
1350         InvokeOp(MethodRef invokeDescriptor, List<Value> args) {
1351             this(invokeDescriptor.type().returnType(), invokeDescriptor, args);
1352         }
1353 
1354         InvokeOp(TypeElement resultType, MethodRef invokeDescriptor, List<Value> args) {
1355             super(NAME, args);
1356 
1357             this.invokeDescriptor = invokeDescriptor;
1358             this.resultType = resultType;
1359         }
1360 
1361         @Override
1362         public Map<String, Object> attributes() {
1363             HashMap<String, Object> m = new HashMap<>(super.attributes());
1364             m.put("", invokeDescriptor);
1365             return Collections.unmodifiableMap(m);
1366         }
1367 
1368         public MethodRef invokeDescriptor() {
1369             return invokeDescriptor;
1370         }
1371 
1372         public boolean hasReceiver() {
1373             return operands().size() != invokeDescriptor().type().parameterTypes().size();
1374         }
1375 
1376         @Override
1377         public TypeElement resultType() {
1378             return resultType;
1379         }
1380     }
1381 
1382     /**
1383      * The conversion operation, that can model Java language cast expressions
1384      * for numerical conversion, or such implicit conversion.
1385      */
1386     @OpFactory.OpDeclaration(ConvOp.NAME)
1387     public static final class ConvOp extends CoreOp
1388             implements Op.Pure, JavaExpression {
1389         public static final String NAME = "conv";
1390 
1391         final TypeElement resultType;
1392 
1393         public ConvOp(ExternalizedOp def) {
1394             super(def);
1395 
1396             this.resultType = def.resultType();
1397         }
1398 
1399         ConvOp(ConvOp that, CopyContext cc) {
1400             super(that, cc);
1401 
1402             this.resultType = that.resultType;
1403         }
1404 
1405         @Override
1406         public Op transform(CopyContext cc, OpTransformer ot) {
1407             return new ConvOp(this, cc);
1408         }
1409 
1410         ConvOp(TypeElement resultType, Value arg) {
1411             super(NAME, List.of(arg));
1412 
1413             this.resultType = resultType;
1414         }
1415 
1416         @Override
1417         public TypeElement resultType() {
1418             return resultType;
1419         }
1420     }
1421 
1422     /**
1423      * The new operation, that can models Java language instance creation expressions.
1424      */
1425     @OpFactory.OpDeclaration(NewOp.NAME)
1426     public static final class NewOp extends CoreOp
1427             implements ReflectiveOp, JavaExpression, JavaStatement {
1428         public static final String NAME = "new";
1429         public static final String ATTRIBUTE_NEW_DESCRIPTOR = NAME + ".descriptor";
1430 
1431         final FunctionType constructorType;
1432         final TypeElement resultType;
1433 
1434         public static NewOp create(ExternalizedOp def) {
1435             FunctionType constructorType = def.extractAttributeValue(ATTRIBUTE_NEW_DESCRIPTOR, true,
1436                     v -> switch (v) {
1437                         case String s -> {
1438                             TypeElement te = CoreTypeFactory.CORE_TYPE_FACTORY
1439                                     .constructType(TypeElement.ExternalizedTypeElement.ofString(s));
1440                             if (!(te instanceof FunctionType ft)) {
1441                                 throw new UnsupportedOperationException("Unsupported new descriptor value:" + v);
1442                             }
1443                             yield ft;
1444                         }
1445                         case FunctionType ct -> ct;
1446                         default -> throw new UnsupportedOperationException("Unsupported new descriptor value:" + v);
1447                     });
1448             return new NewOp(def, constructorType);
1449         }
1450 
1451         NewOp(ExternalizedOp def, FunctionType constructorType) {
1452             super(def);
1453 
1454             this.constructorType = constructorType;
1455             this.resultType = def.resultType();
1456         }
1457 
1458         NewOp(NewOp that, CopyContext cc) {
1459             super(that, cc);
1460 
1461             this.constructorType = that.constructorType;
1462             this.resultType = that.resultType;
1463         }
1464 
1465         @Override
1466         public NewOp transform(CopyContext cc, OpTransformer ot) {
1467             return new NewOp(this, cc);
1468         }
1469 
1470         NewOp(FunctionType constructorType, List<Value> args) {
1471             this(constructorType.returnType(), constructorType, args);
1472         }
1473 
1474         NewOp(TypeElement resultType, FunctionType constructorType, List<Value> args) {
1475             super(NAME, args);
1476 
1477             this.constructorType = constructorType;
1478             this.resultType = resultType;
1479         }
1480 
1481         @Override
1482         public Map<String, Object> attributes() {
1483             HashMap<String, Object> m = new HashMap<>(super.attributes());
1484             m.put("", constructorType);
1485             return Collections.unmodifiableMap(m);
1486         }
1487 
1488         public TypeElement type() {
1489             return opType().returnType();
1490         }
1491 
1492         public FunctionType constructorType() {
1493             return constructorType;
1494         }
1495 
1496         @Override
1497         public TypeElement resultType() {
1498             return resultType;
1499         }
1500     }
1501 
1502     /**
1503      * An operation that performs access.
1504      */
1505     public sealed interface AccessOp {
1506     }
1507 
1508     /**
1509      * A field access operation, that can model Java langauge field access expressions.
1510      */
1511     public sealed abstract static class FieldAccessOp extends CoreOp
1512             implements AccessOp, ReflectiveOp {
1513         public static final String ATTRIBUTE_FIELD_DESCRIPTOR = "field.descriptor";
1514 
1515         final FieldRef fieldDescriptor;
1516 
1517         FieldAccessOp(ExternalizedOp def, FieldRef fieldDescriptor) {
1518             super(def);
1519 
1520             this.fieldDescriptor = fieldDescriptor;
1521         }
1522 
1523         FieldAccessOp(FieldAccessOp that, CopyContext cc) {
1524             super(that, cc);
1525 
1526             this.fieldDescriptor = that.fieldDescriptor;
1527         }
1528 
1529         FieldAccessOp(String name, List<Value> operands,
1530                       FieldRef fieldDescriptor) {
1531             super(name, operands);
1532 
1533             this.fieldDescriptor = fieldDescriptor;
1534         }
1535 
1536         @Override
1537         public Map<String, Object> attributes() {
1538             HashMap<String, Object> m = new HashMap<>(super.attributes());
1539             m.put("", fieldDescriptor);
1540             return Collections.unmodifiableMap(m);
1541         }
1542 
1543         public final FieldRef fieldDescriptor() {
1544             return fieldDescriptor;
1545         }
1546 
1547         /**
1548          * The field load operation, that can model Java language field access expressions combined with load access to
1549          * field instance variables.
1550          */
1551         @OpFactory.OpDeclaration(FieldLoadOp.NAME)
1552         public static final class FieldLoadOp extends FieldAccessOp
1553                 implements Op.Pure, JavaExpression {
1554             public static final String NAME = "field.load";
1555 
1556             final TypeElement resultType;
1557 
1558             public static FieldLoadOp create(ExternalizedOp def) {
1559                 if (def.operands().size() > 1) {
1560                     throw new IllegalArgumentException("Operation must accept zero or one operand");
1561                 }
1562 
1563                 FieldRef fieldDescriptor = def.extractAttributeValue(ATTRIBUTE_FIELD_DESCRIPTOR, true,
1564                         v -> switch (v) {
1565                             case String s -> FieldRef.ofString(s);
1566                             case FieldRef fd -> fd;
1567                             default ->
1568                                     throw new UnsupportedOperationException("Unsupported field descriptor value:" + v);
1569                         });
1570                 return new FieldLoadOp(def, fieldDescriptor);
1571             }
1572 
1573             FieldLoadOp(ExternalizedOp opdef, FieldRef fieldDescriptor) {
1574                 super(opdef, fieldDescriptor);
1575 
1576                 resultType = opdef.resultType();
1577             }
1578 
1579             FieldLoadOp(FieldLoadOp that, CopyContext cc) {
1580                 super(that, cc);
1581 
1582                 resultType = that.resultType();
1583             }
1584 
1585             @Override
1586             public FieldLoadOp transform(CopyContext cc, OpTransformer ot) {
1587                 return new FieldLoadOp(this, cc);
1588             }
1589 
1590             // instance
1591             FieldLoadOp(TypeElement resultType, FieldRef descriptor, Value receiver) {
1592                 super(NAME, List.of(receiver), descriptor);
1593 
1594                 this.resultType = resultType;
1595             }
1596 
1597             // static
1598             FieldLoadOp(TypeElement resultType, FieldRef descriptor) {
1599                 super(NAME, List.of(), descriptor);
1600 
1601                 this.resultType = resultType;
1602             }
1603 
1604             @Override
1605             public TypeElement resultType() {
1606                 return resultType;
1607             }
1608         }
1609 
1610         /**
1611          * The field store operation, that can model Java language field access expressions combined with store access
1612          * to field instance variables.
1613          */
1614         @OpFactory.OpDeclaration(FieldStoreOp.NAME)
1615         public static final class FieldStoreOp extends FieldAccessOp
1616                 implements JavaExpression, JavaStatement {
1617             public static final String NAME = "field.store";
1618 
1619             public static FieldStoreOp create(ExternalizedOp def) {
1620                 if (def.operands().size() > 2) {
1621                     throw new IllegalArgumentException("Operation must accept one or two operands");
1622                 }
1623 
1624                 FieldRef fieldDescriptor = def.extractAttributeValue(ATTRIBUTE_FIELD_DESCRIPTOR, true,
1625                         v -> switch (v) {
1626                             case String s -> FieldRef.ofString(s);
1627                             case FieldRef fd -> fd;
1628                             default ->
1629                                     throw new UnsupportedOperationException("Unsupported field descriptor value:" + v);
1630                         });
1631                 return new FieldStoreOp(def, fieldDescriptor);
1632             }
1633 
1634             FieldStoreOp(ExternalizedOp opdef, FieldRef fieldDescriptor) {
1635                 super(opdef, fieldDescriptor);
1636             }
1637 
1638             FieldStoreOp(FieldStoreOp that, CopyContext cc) {
1639                 super(that, cc);
1640             }
1641 
1642             @Override
1643             public FieldStoreOp transform(CopyContext cc, OpTransformer ot) {
1644                 return new FieldStoreOp(this, cc);
1645             }
1646 
1647             // instance
1648             FieldStoreOp(FieldRef descriptor, Value receiver, Value v) {
1649                 super(NAME,
1650                         List.of(receiver, v), descriptor);
1651             }
1652 
1653             // static
1654             FieldStoreOp(FieldRef descriptor, Value v) {
1655                 super(NAME,
1656                         List.of(v), descriptor);
1657             }
1658 
1659             @Override
1660             public TypeElement resultType() {
1661                 return JavaType.VOID;
1662             }
1663         }
1664     }
1665 
1666     /**
1667      * The array length operation, that can model Java language field access expressions to the length field of an
1668      * array.
1669      */
1670     @OpFactory.OpDeclaration(ArrayLengthOp.NAME)
1671     public static final class ArrayLengthOp extends CoreOp
1672             implements ReflectiveOp, JavaExpression {
1673         public static final String NAME = "array.length";
1674 
1675         public ArrayLengthOp(ExternalizedOp def) {
1676             super(def);
1677         }
1678 
1679         ArrayLengthOp(ArrayLengthOp that, CopyContext cc) {
1680             super(that, cc);
1681         }
1682 
1683         @Override
1684         public ArrayLengthOp transform(CopyContext cc, OpTransformer ot) {
1685             return new ArrayLengthOp(this, cc);
1686         }
1687 
1688         ArrayLengthOp(Value array) {
1689             super(NAME, List.of(array));
1690         }
1691 
1692         @Override
1693         public TypeElement resultType() {
1694             return JavaType.INT;
1695         }
1696     }
1697 
1698     /**
1699      * The array access operation, that can model Java language array access expressions.
1700      */
1701     public sealed abstract static class ArrayAccessOp extends CoreOp
1702             implements AccessOp, ReflectiveOp {
1703         ArrayAccessOp(ExternalizedOp def) {
1704             super(def);
1705 
1706             if (def.operands().size() != 2 && def.operands().size() != 3) {
1707                 throw new IllegalArgumentException("Operation must have 2 or 3 operands");
1708             }
1709 
1710             // @@@ validate first operand is an array
1711         }
1712 
1713         ArrayAccessOp(ArrayAccessOp that, CopyContext cc) {
1714             this(that, cc.getValues(that.operands()));
1715         }
1716 
1717         ArrayAccessOp(ArrayAccessOp that, List<Value> operands) {
1718             super(that.opName(), operands);
1719         }
1720 
1721         ArrayAccessOp(String name,
1722                       Value array, Value index, Value v) {
1723             super(name, operands(array, index, v));
1724         }
1725 
1726         static List<Value> operands(Value array, Value index, Value v) {
1727             return v == null
1728                     ? List.of(array, index)
1729                     : List.of(array, index, v);
1730         }
1731 
1732         static TypeElement resultType(Value array, Value v) {
1733             if (!(array.type() instanceof ArrayType arrayType)) {
1734                 throw new IllegalArgumentException("Type is not an array type: " + array.type());
1735             }
1736 
1737             // @@@ restrict to indexes of int?
1738             TypeElement componentType = arrayType.componentType();
1739             if (v == null) {
1740                 return componentType;
1741             } else {
1742                 return JavaType.VOID;
1743             }
1744         }
1745 
1746         /**
1747          * The array load operation, that can model Java language array expressions combined with load access to the
1748          * components of an array.
1749          */
1750         @OpFactory.OpDeclaration(ArrayLoadOp.NAME)
1751         public static final class ArrayLoadOp extends ArrayAccessOp
1752                 implements Op.Pure, JavaExpression {
1753             public static final String NAME = "array.load";
1754 
1755             public ArrayLoadOp(ExternalizedOp def) {
1756                 super(def);
1757             }
1758 
1759             ArrayLoadOp(ArrayLoadOp that, CopyContext cc) {
1760                 super(that, cc);
1761             }
1762 
1763             @Override
1764             public ArrayLoadOp transform(CopyContext cc, OpTransformer ot) {
1765                 return new ArrayLoadOp(this, cc);
1766             }
1767 
1768             ArrayLoadOp(Value array, Value index) {
1769                 super(NAME, array, index, null);
1770             }
1771 
1772             @Override
1773             public TypeElement resultType() {
1774                 Value array = operands().get(0);
1775                 ArrayType t = (ArrayType) array.type();
1776                 return t.componentType();
1777             }
1778         }
1779 
1780         /**
1781          * The array store operation, that can model Java language array expressions combined with store access to the
1782          * components of an array.
1783          */
1784         @OpFactory.OpDeclaration(ArrayStoreOp.NAME)
1785         public static final class ArrayStoreOp extends ArrayAccessOp
1786                 implements JavaExpression, JavaStatement {
1787             public static final String NAME = "array.store";
1788 
1789             public ArrayStoreOp(ExternalizedOp def) {
1790                 super(def);
1791             }
1792 
1793             ArrayStoreOp(ArrayStoreOp that, CopyContext cc) {
1794                 super(that, cc);
1795             }
1796 
1797             @Override
1798             public ArrayStoreOp transform(CopyContext cc, OpTransformer ot) {
1799                 return new ArrayStoreOp(this, cc);
1800             }
1801 
1802             ArrayStoreOp(Value array, Value index, Value v) {
1803                 super(NAME, array, index, v);
1804             }
1805 
1806             @Override
1807             public TypeElement resultType() {
1808                 return JavaType.VOID;
1809             }
1810         }
1811     }
1812 
1813     /**
1814      * The instanceof operation, that can model Java language instanceof expressions when the instanceof keyword is a
1815      * type comparison operator.
1816      */
1817     @OpFactory.OpDeclaration(InstanceOfOp.NAME)
1818     public static final class InstanceOfOp extends CoreOp
1819             implements Op.Pure, ReflectiveOp, JavaExpression {
1820         public static final String NAME = "instanceof";
1821         public static final String ATTRIBUTE_TYPE_DESCRIPTOR = NAME + ".descriptor";
1822 
1823         final TypeElement typeDescriptor;
1824 
1825         public static InstanceOfOp create(ExternalizedOp def) {
1826             if (def.operands().size() != 1) {
1827                 throw new IllegalArgumentException("Operation must have one operand " + def.name());
1828             }
1829 
1830             TypeElement typeDescriptor = def.extractAttributeValue(ATTRIBUTE_TYPE_DESCRIPTOR, true,
1831                     v -> switch (v) {
1832                         case String s -> JavaType.ofString(s);
1833                         case JavaType td -> td;
1834                         default -> throw new UnsupportedOperationException("Unsupported type descriptor value:" + v);
1835                     });
1836             return new InstanceOfOp(def, typeDescriptor);
1837         }
1838 
1839         InstanceOfOp(ExternalizedOp def, TypeElement typeDescriptor) {
1840             super(def);
1841 
1842             this.typeDescriptor = typeDescriptor;
1843         }
1844 
1845         InstanceOfOp(InstanceOfOp that, CopyContext cc) {
1846             super(that, cc);
1847 
1848             this.typeDescriptor = that.typeDescriptor;
1849         }
1850 
1851         @Override
1852         public InstanceOfOp transform(CopyContext cc, OpTransformer ot) {
1853             return new InstanceOfOp(this, cc);
1854         }
1855 
1856         InstanceOfOp(TypeElement t, Value v) {
1857             super(NAME,
1858                     List.of(v));
1859 
1860             this.typeDescriptor = t;
1861         }
1862 
1863         @Override
1864         public Map<String, Object> attributes() {
1865             HashMap<String, Object> m = new HashMap<>(super.attributes());
1866             m.put("", typeDescriptor);
1867             return Collections.unmodifiableMap(m);
1868         }
1869 
1870         public TypeElement type() {
1871             return typeDescriptor;
1872         }
1873 
1874         @Override
1875         public TypeElement resultType() {
1876             return JavaType.BOOLEAN;
1877         }
1878     }
1879 
1880     /**
1881      * The cast operation, that can model Java language cast expressions for reference types.
1882      */
1883     @OpFactory.OpDeclaration(CastOp.NAME)
1884     public static final class CastOp extends CoreOp
1885             implements Op.Pure, ReflectiveOp, JavaExpression {
1886         public static final String NAME = "cast";
1887         public static final String ATTRIBUTE_TYPE_DESCRIPTOR = NAME + ".descriptor";
1888 
1889         final TypeElement resultType;
1890         final TypeElement typeDescriptor;
1891 
1892         public static CastOp create(ExternalizedOp def) {
1893             if (def.operands().size() != 1) {
1894                 throw new IllegalArgumentException("Operation must have one operand " + def.name());
1895             }
1896 
1897             TypeElement type = def.extractAttributeValue(ATTRIBUTE_TYPE_DESCRIPTOR, true,
1898                     v -> switch (v) {
1899                         case String s -> JavaType.ofString(s);
1900                         case JavaType td -> td;
1901                         default -> throw new UnsupportedOperationException("Unsupported type descriptor value:" + v);
1902                     });
1903             return new CastOp(def, type);
1904         }
1905 
1906         CastOp(ExternalizedOp def, TypeElement typeDescriptor) {
1907             super(def);
1908 
1909             this.resultType = def.resultType();
1910             this.typeDescriptor = typeDescriptor;
1911         }
1912 
1913         CastOp(CastOp that, CopyContext cc) {
1914             super(that, cc);
1915 
1916             this.resultType = that.resultType;
1917             this.typeDescriptor = that.typeDescriptor;
1918         }
1919 
1920         @Override
1921         public CastOp transform(CopyContext cc, OpTransformer ot) {
1922             return new CastOp(this, cc);
1923         }
1924 
1925         CastOp(TypeElement resultType, TypeElement t, Value v) {
1926             super(NAME, List.of(v));
1927 
1928             this.resultType = resultType;
1929             this.typeDescriptor = t;
1930         }
1931 
1932         @Override
1933         public Map<String, Object> attributes() {
1934             HashMap<String, Object> m = new HashMap<>(super.attributes());
1935             m.put("", typeDescriptor);
1936             return Collections.unmodifiableMap(m);
1937         }
1938 
1939         public TypeElement type() {
1940             return typeDescriptor;
1941         }
1942 
1943         @Override
1944         public TypeElement resultType() {
1945             return resultType;
1946         }
1947     }
1948 
1949 
1950     /**
1951      * A runtime representation of a variable.
1952      *
1953      * @param <T> the type of the var's value.
1954      * @@@ Ideally should never be exposed
1955      * @@@ Move to interpreter?
1956      */
1957     public interface Var<T> {
1958         /**
1959          * {@return the value of a var}
1960          */
1961         T value();
1962 
1963         /**
1964          * Constructs an instance of a var.
1965          *
1966          * @param value the initial value of the var.
1967          * @param <T>   the type of the var's value.
1968          * @return the var
1969          */
1970         static <T> Var<T> of(T value) {
1971             return () -> value;
1972         }
1973     }
1974 
1975     /**
1976      * The variable operation, that can model declarations of Java language local variables, method parameters, or
1977      * lambda parameters.
1978      */
1979     @OpFactory.OpDeclaration(VarOp.NAME)
1980     public static final class VarOp extends CoreOp
1981             implements JavaStatement {
1982         public static final String NAME = "var";
1983         public static final String ATTRIBUTE_NAME = NAME + ".name";
1984 
1985         final String varName;
1986         final TypeElement resultType;
1987 
1988         public static VarOp create(ExternalizedOp def) {
1989             if (def.operands().size() != 1) {
1990                 throw new IllegalStateException("Operation must have one operand");
1991             }
1992 
1993             String name = def.extractAttributeValue(ATTRIBUTE_NAME, true,
1994                     v -> switch (v) {
1995                         case String s -> s;
1996                         default -> throw new UnsupportedOperationException("Unsupported var name value:" + v);
1997                     });
1998             return new VarOp(def, name);
1999         }
2000 
2001         VarOp(ExternalizedOp def, String varName) {
2002             super(def);
2003 
2004             this.varName = varName;
2005             this.resultType = def.resultType();
2006         }
2007 
2008         VarOp(VarOp that, CopyContext cc) {
2009             super(that, cc);
2010 
2011             this.varName = that.varName;
2012             this.resultType = that.resultType;
2013         }
2014 
2015         @Override
2016         public VarOp transform(CopyContext cc, OpTransformer ot) {
2017             return new VarOp(this, cc);
2018         }
2019 
2020         VarOp(String varName, Value init) {
2021             this(varName, init.type(), init);
2022         }
2023 
2024         VarOp(String varName, TypeElement type, Value init) {
2025             super(NAME, List.of(init));
2026 
2027             this.varName = varName;
2028             this.resultType = VarType.varType(type);
2029         }
2030 
2031         @Override
2032         public Map<String, Object> attributes() {
2033             if (varName == null) {
2034                 return super.attributes();
2035             }
2036 
2037             HashMap<String, Object> m = new HashMap<>(super.attributes());
2038             m.put("", varName);
2039             return Collections.unmodifiableMap(m);
2040         }
2041 
2042         public String varName() {
2043             return varName;
2044         }
2045 
2046         public TypeElement varType() {
2047             return ((VarType) resultType).valueType();
2048         }
2049 
2050         @Override
2051         public TypeElement resultType() {
2052             return resultType;
2053         }
2054     }
2055 
2056     /**
2057      * The var access operation, that can model access to Java language local variables, method parameters, or
2058      * lambda parameters.
2059      */
2060     public sealed abstract static class VarAccessOp extends CoreOp
2061             implements AccessOp {
2062         VarAccessOp(ExternalizedOp opdef) {
2063             super(opdef);
2064         }
2065 
2066         VarAccessOp(VarAccessOp that, CopyContext cc) {
2067             super(that, cc);
2068         }
2069 
2070         VarAccessOp(String name, List<Value> operands) {
2071             super(name, operands);
2072         }
2073 
2074         public VarOp varOp() {
2075             // @@@ At a high-level a Var value occur as a BlockArgument.
2076             // Lowering should remove such cases and the var definition should emerge
2077             // @@@ This method is used when transforming to pure SSA
2078             Result variable = (Result) operands().get(0);
2079             return (VarOp) variable.op();
2080         }
2081 
2082         static Value checkIsVarOp(Value varValue) {
2083             if (!(varValue.type() instanceof VarType)) {
2084                 throw new IllegalArgumentException("Value's type is not a variable type: " + varValue);
2085             }
2086             return varValue;
2087         }
2088 
2089         /**
2090          * The variable load operation, that models a reading variable.
2091          */
2092         @OpFactory.OpDeclaration(VarLoadOp.NAME)
2093         public static final class VarLoadOp extends VarAccessOp
2094                 implements JavaExpression {
2095             public static final String NAME = "var.load";
2096 
2097             public VarLoadOp(ExternalizedOp opdef) {
2098                 super(opdef);
2099 
2100                 if (opdef.operands().size() != 1) {
2101                     throw new IllegalArgumentException("Operation must have one operand");
2102                 }
2103                 checkIsVarOp(opdef.operands().get(0));
2104             }
2105 
2106             VarLoadOp(VarLoadOp that, CopyContext cc) {
2107                 super(that, cc);
2108             }
2109 
2110             VarLoadOp(List<Value> varValue) {
2111                 super(NAME, varValue);
2112             }
2113 
2114             @Override
2115             public VarLoadOp transform(CopyContext cc, OpTransformer ot) {
2116                 return new VarLoadOp(this, cc);
2117             }
2118 
2119             // (Variable)VarType
2120             VarLoadOp(Value varValue) {
2121                 super(NAME, List.of(varValue));
2122             }
2123 
2124             @Override
2125             public TypeElement resultType() {
2126                 VarType vt = (VarType) operands().get(0).type();
2127                 return vt.valueType();
2128             }
2129         }
2130 
2131         /**
2132          * The variable store operation, that can model a variable assignment.
2133          */
2134         @OpFactory.OpDeclaration(VarStoreOp.NAME)
2135         public static final class VarStoreOp extends VarAccessOp
2136                 implements JavaExpression, JavaStatement {
2137             public static final String NAME = "var.store";
2138 
2139             public VarStoreOp(ExternalizedOp opdef) {
2140                 super(opdef);
2141 
2142                 if (opdef.operands().size() != 2) {
2143                     throw new IllegalArgumentException("Operation must have two operands");
2144                 }
2145                 checkIsVarOp(opdef.operands().get(0));
2146             }
2147 
2148             VarStoreOp(VarStoreOp that, CopyContext cc) {
2149                 super(that, cc);
2150             }
2151 
2152             VarStoreOp(List<Value> values) {
2153                 super(NAME,
2154                         values);
2155             }
2156 
2157             @Override
2158             public VarStoreOp transform(CopyContext cc, OpTransformer ot) {
2159                 return new VarStoreOp(this, cc);
2160             }
2161 
2162             // (Variable, VarType)void
2163             VarStoreOp(Value varValue, Value v) {
2164                 super(NAME,
2165                         List.of(varValue, v));
2166             }
2167 
2168             @Override
2169             public TypeElement resultType() {
2170                 return JavaType.VOID;
2171             }
2172         }
2173     }
2174 
2175     // Tuple operations, for modelling return with multiple values
2176 
2177     /**
2178      * The tuple operation. A tuple contain a fixed set of values accessible by their component index.
2179      */
2180     @OpFactory.OpDeclaration(TupleOp.NAME)
2181     public static final class TupleOp extends CoreOp {
2182         public static final String NAME = "tuple";
2183 
2184         public TupleOp(ExternalizedOp def) {
2185             super(def);
2186         }
2187 
2188         TupleOp(TupleOp that, CopyContext cc) {
2189             this(cc.getValues(that.operands()));
2190         }
2191 
2192         @Override
2193         public TupleOp transform(CopyContext cc, OpTransformer ot) {
2194             return new TupleOp(this, cc);
2195         }
2196 
2197         TupleOp(List<? extends Value> componentValues) {
2198             super(NAME, componentValues);
2199         }
2200 
2201         @Override
2202         public TypeElement resultType() {
2203             return TupleType.tupleTypeFromValues(operands());
2204         }
2205     }
2206 
2207     /**
2208      * The tuple component load operation, that access the component of a tuple at a given, constant, component index.
2209      */
2210     @OpFactory.OpDeclaration(TupleLoadOp.NAME)
2211     public static final class TupleLoadOp extends CoreOp {
2212         public static final String NAME = "tuple.load";
2213         public static final String ATTRIBUTE_INDEX = NAME + ".index";
2214 
2215         final int index;
2216 
2217         public static TupleLoadOp create(ExternalizedOp def) {
2218             if (def.operands().size() != 1) {
2219                 throw new IllegalStateException("Operation must have one operand");
2220             }
2221 
2222             int index = def.extractAttributeValue(ATTRIBUTE_INDEX, true,
2223                     v -> switch (v) {
2224                         case String s -> Integer.valueOf(s);
2225                         case Integer i -> i;
2226                         default -> throw new UnsupportedOperationException("Unsupported tuple index value:" + v);
2227                     });
2228             return new TupleLoadOp(def, index);
2229         }
2230 
2231         TupleLoadOp(ExternalizedOp def, int index) {
2232             super(def);
2233 
2234             // @@@ Validate tuple type and index
2235             this.index = index;
2236         }
2237 
2238         TupleLoadOp(TupleLoadOp that, CopyContext cc) {
2239             this(that, cc.getValues(that.operands()));
2240         }
2241 
2242         TupleLoadOp(TupleLoadOp that, List<Value> values) {
2243             super(NAME, values);
2244 
2245             this.index = that.index;
2246         }
2247 
2248         @Override
2249         public TupleLoadOp transform(CopyContext cc, OpTransformer ot) {
2250             return new TupleLoadOp(this, cc);
2251         }
2252 
2253         TupleLoadOp(Value tupleValue, int index) {
2254             super(NAME, List.of(tupleValue));
2255 
2256             this.index = index;
2257         }
2258 
2259         @Override
2260         public Map<String, Object> attributes() {
2261             HashMap<String, Object> m = new HashMap<>(super.attributes());
2262             m.put("", index);
2263             return Collections.unmodifiableMap(m);
2264         }
2265 
2266         public int index() {
2267             return index;
2268         }
2269 
2270         @Override
2271         public TypeElement resultType() {
2272             Value tupleValue = operands().get(0);
2273             TupleType t = (TupleType) tupleValue.type();
2274             return t.componentTypes().get(index);
2275         }
2276     }
2277 
2278     /**
2279      * The tuple component set operation, that access the component of a tuple at a given, constant, component index.
2280      */
2281     @OpFactory.OpDeclaration(TupleWithOp.NAME)
2282     public static final class TupleWithOp extends CoreOp {
2283         public static final String NAME = "tuple.with";
2284         public static final String ATTRIBUTE_INDEX = NAME + ".index";
2285 
2286         final int index;
2287 
2288         public static TupleWithOp create(ExternalizedOp def) {
2289             if (def.operands().size() != 2) {
2290                 throw new IllegalStateException("Operation must have two operands");
2291             }
2292 
2293             int index = def.extractAttributeValue(ATTRIBUTE_INDEX, true,
2294                     v -> switch (v) {
2295                         case String s -> Integer.valueOf(s);
2296                         case Integer i -> i;
2297                         default -> throw new UnsupportedOperationException("Unsupported tuple index value:" + v);
2298                     });
2299             return new TupleWithOp(def, index);
2300         }
2301 
2302         TupleWithOp(ExternalizedOp def, int index) {
2303             super(def);
2304 
2305             // @@@ Validate tuple type and index
2306             this.index = index;
2307         }
2308 
2309         TupleWithOp(TupleWithOp that, CopyContext cc) {
2310             this(that, cc.getValues(that.operands()));
2311         }
2312 
2313         TupleWithOp(TupleWithOp that, List<Value> values) {
2314             super(NAME, values);
2315 
2316             this.index = that.index;
2317         }
2318 
2319         @Override
2320         public TupleWithOp transform(CopyContext cc, OpTransformer ot) {
2321             return new TupleWithOp(this, cc);
2322         }
2323 
2324         TupleWithOp(Value tupleValue, int index, Value value) {
2325             super(NAME, List.of(tupleValue, value));
2326 
2327             // @@@ Validate tuple type and index
2328             this.index = index;
2329         }
2330 
2331         @Override
2332         public Map<String, Object> attributes() {
2333             HashMap<String, Object> m = new HashMap<>(super.attributes());
2334             m.put("", index);
2335             return Collections.unmodifiableMap(m);
2336         }
2337 
2338         public int index() {
2339             return index;
2340         }
2341 
2342         @Override
2343         public TypeElement resultType() {
2344             Value tupleValue = operands().get(0);
2345             TupleType tupleType = (TupleType) tupleValue.type();
2346             Value value = operands().get(2);
2347 
2348             List<TypeElement> tupleComponentTypes = new ArrayList<>(tupleType.componentTypes());
2349             tupleComponentTypes.set(index, value.type());
2350             return TupleType.tupleType(tupleComponentTypes);
2351         }
2352     }
2353 
2354     // @@@ Sealed
2355     // Synthetic/hidden type that is the result type of an ExceptionRegionStart operation
2356     // and is an operand of an ExceptionRegionEnd operation
2357 
2358     /**
2359      * A synthetic exception region type, that is the operation result-type of an exception region
2360      * start operation.
2361      */
2362     // @@@: Create as new type element
2363     public interface ExceptionRegion {
2364         TypeElement EXCEPTION_REGION_TYPE = JavaType.type(ExceptionRegion.class);
2365     }
2366 
2367     /**
2368      * The exception region start operation.
2369      */
2370     @OpFactory.OpDeclaration(ExceptionRegionEnter.NAME)
2371     public static final class ExceptionRegionEnter extends CoreOp
2372             implements Op.BlockTerminating {
2373         public static final String NAME = "exception.region.enter";
2374 
2375         // First successor is the non-exceptional successor whose target indicates
2376         // the first block in the exception region.
2377         // One or more subsequent successors target the exception catching blocks
2378         // each of which have one block argument whose type is an exception type.
2379         final List<Block.Reference> s;
2380 
2381         public ExceptionRegionEnter(ExternalizedOp def) {
2382             super(def);
2383 
2384             if (def.successors().size() < 2) {
2385                 throw new IllegalArgumentException("Operation must have two or more successors" + def.name());
2386             }
2387 
2388             this.s = List.copyOf(def.successors());
2389         }
2390 
2391         ExceptionRegionEnter(ExceptionRegionEnter that, CopyContext cc) {
2392             super(that, cc);
2393 
2394             this.s = that.s.stream().map(cc::getSuccessorOrCreate).toList();
2395         }
2396 
2397         @Override
2398         public ExceptionRegionEnter transform(CopyContext cc, OpTransformer ot) {
2399             return new ExceptionRegionEnter(this, cc);
2400         }
2401 
2402         ExceptionRegionEnter(List<Block.Reference> s) {
2403             super(NAME, List.of());
2404 
2405             if (s.size() < 2) {
2406                 throw new IllegalArgumentException("Operation must have two or more successors" + opName());
2407             }
2408 
2409             this.s = List.copyOf(s);
2410         }
2411 
2412         @Override
2413         public List<Block.Reference> successors() {
2414             return s;
2415         }
2416 
2417         public Block.Reference start() {
2418             return s.get(0);
2419         }
2420 
2421         public List<Block.Reference> catchBlocks() {
2422             return s.subList(1, s.size());
2423         }
2424 
2425         @Override
2426         public TypeElement resultType() {
2427             return ExceptionRegion.EXCEPTION_REGION_TYPE;
2428         }
2429     }
2430 
2431     /**
2432      * The exception region end operation.
2433      */
2434     @OpFactory.OpDeclaration(ExceptionRegionExit.NAME)
2435     public static final class ExceptionRegionExit extends CoreOp
2436             implements Op.BlockTerminating {
2437         public static final String NAME = "exception.region.exit";
2438 
2439         final Block.Reference end;
2440 
2441         public ExceptionRegionExit(ExternalizedOp def) {
2442             super(def);
2443 
2444             if (def.operands().size() != 1) {
2445                 throw new IllegalArgumentException("Operation must have one operand" + def.name());
2446             }
2447 
2448             if (def.successors().size() != 1) {
2449                 throw new IllegalArgumentException("Operation must have one successor" + def.name());
2450             }
2451 
2452             this.end = def.successors().get(0);
2453         }
2454 
2455         ExceptionRegionExit(ExceptionRegionExit that, CopyContext cc) {
2456             super(that, cc);
2457 
2458             this.end = cc.getSuccessorOrCreate(that.end);
2459         }
2460 
2461         @Override
2462         public ExceptionRegionExit transform(CopyContext cc, OpTransformer ot) {
2463             return new ExceptionRegionExit(this, cc);
2464         }
2465 
2466         ExceptionRegionExit(Value exceptionRegion, Block.Reference end) {
2467             super(NAME, checkValue(exceptionRegion));
2468 
2469             this.end = end;
2470         }
2471 
2472         static List<Value> checkValue(Value er) {
2473             if (!(er instanceof Result or && or.op() instanceof ExceptionRegionEnter)) {
2474                 throw new IllegalArgumentException(
2475                         "Operand not the result of an exception.region.start operation: " + er);
2476             }
2477 
2478             return List.of(er);
2479         }
2480 
2481         @Override
2482         public List<Block.Reference> successors() {
2483             return List.of(end);
2484         }
2485 
2486         public Block.Reference end() {
2487             return end;
2488         }
2489 
2490         public ExceptionRegionEnter regionStart() {
2491             if (operands().get(0) instanceof Result or &&
2492                     or.op() instanceof ExceptionRegionEnter ers) {
2493                 return ers;
2494             }
2495             throw new InternalError("Should not reach here");
2496         }
2497 
2498         @Override
2499         public TypeElement resultType() {
2500             return JavaType.VOID;
2501         }
2502     }
2503 
2504     /**
2505      * The String Concatenation Operation
2506      */
2507 
2508     @OpFactory.OpDeclaration(ConcatOp.NAME)
2509     public static final class ConcatOp extends CoreOp
2510             implements Op.Pure, JavaExpression {
2511         public static final String NAME = "concat";
2512 
2513         public ConcatOp(ConcatOp that, CopyContext cc) {
2514             super(that, cc);
2515         }
2516 
2517         public ConcatOp(ExternalizedOp def) {
2518             super(def);
2519             if (def.operands().size() != 2) {
2520                 throw new IllegalArgumentException("Concatenation Operation must have two operands.");
2521             }
2522         }
2523 
2524         public ConcatOp(Value lhs, Value rhs) {
2525             super(ConcatOp.NAME, List.of(lhs, rhs));
2526         }
2527 
2528         @Override
2529         public Op transform(CopyContext cc, OpTransformer ot) {
2530             return new ConcatOp(this, cc);
2531         }
2532 
2533         @Override
2534         public TypeElement resultType() {
2535             return JavaType.J_L_STRING;
2536         }
2537     }
2538 
2539     //
2540     // Arithmetic ops
2541 
2542     /**
2543      * The arithmetic operation.
2544      */
2545     public sealed static abstract class ArithmeticOperation extends CoreOp
2546             implements Op.Pure, JavaExpression {
2547         protected ArithmeticOperation(ExternalizedOp def) {
2548             super(def);
2549 
2550             if (def.operands().isEmpty()) {
2551                 throw new IllegalArgumentException("Operation must have one or more operands");
2552             }
2553         }
2554 
2555         protected ArithmeticOperation(ArithmeticOperation that, CopyContext cc) {
2556             super(that, cc);
2557         }
2558 
2559         protected ArithmeticOperation(String name, List<Value> operands) {
2560             super(name, operands);
2561         }
2562     }
2563 
2564     /**
2565      * The test operation.
2566      */
2567     public sealed static abstract class TestOperation extends CoreOp
2568             implements Op.Pure, JavaExpression {
2569         protected TestOperation(ExternalizedOp def) {
2570             super(def);
2571 
2572             if (def.operands().isEmpty()) {
2573                 throw new IllegalArgumentException("Operation must have one or more operands");
2574             }
2575         }
2576 
2577         protected TestOperation(TestOperation that, CopyContext cc) {
2578             super(that, cc);
2579         }
2580 
2581         protected TestOperation(String name, List<Value> operands) {
2582             super(name, operands);
2583         }
2584     }
2585 
2586     /**
2587      * The binary arithmetic operation.
2588      */
2589     public sealed static abstract class BinaryOp extends ArithmeticOperation {
2590         protected BinaryOp(ExternalizedOp def) {
2591             super(def);
2592 
2593             if (def.operands().size() != 2) {
2594                 throw new IllegalArgumentException("Number of operands must be 2: " + def.operands().size());
2595             }
2596         }
2597 
2598         protected BinaryOp(BinaryOp that, CopyContext cc) {
2599             super(that, cc);
2600         }
2601 
2602         protected BinaryOp(String name, Value lhs, Value rhs) {
2603             super(name, List.of(lhs, rhs));
2604         }
2605 
2606         @Override
2607         public TypeElement resultType() {
2608             return operands().get(0).type();
2609         }
2610     }
2611 
2612     /**
2613      * The unary arithmetic operation.
2614      */
2615     public sealed static abstract class UnaryOp extends ArithmeticOperation {
2616         protected UnaryOp(ExternalizedOp def) {
2617             super(def);
2618 
2619             if (def.operands().size() != 1) {
2620                 throw new IllegalArgumentException("Number of operands must be 1: " + def.operands().size());
2621             }
2622         }
2623 
2624         protected UnaryOp(UnaryOp that, CopyContext cc) {
2625             super(that, cc);
2626         }
2627 
2628         protected UnaryOp(String name, Value v) {
2629             super(name, List.of(v));
2630         }
2631 
2632         @Override
2633         public TypeElement resultType() {
2634             return operands().get(0).type();
2635         }
2636     }
2637 
2638     /**
2639      * The binary test operation.
2640      */
2641     public sealed static abstract class BinaryTestOp extends TestOperation {
2642         protected BinaryTestOp(ExternalizedOp def) {
2643             super(def);
2644 
2645             if (def.operands().size() != 2) {
2646                 throw new IllegalArgumentException("Number of operands must be 2: " + def.operands().size());
2647             }
2648         }
2649 
2650         protected BinaryTestOp(BinaryTestOp that, CopyContext cc) {
2651             super(that, cc);
2652         }
2653 
2654         protected BinaryTestOp(String name, Value lhs, Value rhs) {
2655             super(name, List.of(lhs, rhs));
2656         }
2657 
2658         @Override
2659         public TypeElement resultType() {
2660             return JavaType.BOOLEAN;
2661         }
2662     }
2663 
2664     /**
2665      * The add operation, that can model the Java language binary {@code +} operator for numeric types
2666      */
2667     @OpFactory.OpDeclaration(AddOp.NAME)
2668     public static final class AddOp extends BinaryOp {
2669         public static final String NAME = "add";
2670 
2671         public AddOp(ExternalizedOp def) {
2672             super(def);
2673         }
2674 
2675         AddOp(AddOp that, CopyContext cc) {
2676             super(that, cc);
2677         }
2678 
2679         @Override
2680         public AddOp transform(CopyContext cc, OpTransformer ot) {
2681             return new AddOp(this, cc);
2682         }
2683 
2684         AddOp(Value lhs, Value rhs) {
2685             super(NAME, lhs, rhs);
2686         }
2687     }
2688 
2689     /**
2690      * The sub operation, that can model the Java language binary {@code -} operator for numeric types
2691      */
2692     @OpFactory.OpDeclaration(SubOp.NAME)
2693     public static final class SubOp extends BinaryOp {
2694         public static final String NAME = "sub";
2695 
2696         public SubOp(ExternalizedOp opdef) {
2697             super(opdef);
2698         }
2699 
2700         SubOp(SubOp that, CopyContext cc) {
2701             super(that, cc);
2702         }
2703 
2704         @Override
2705         public SubOp transform(CopyContext cc, OpTransformer ot) {
2706             return new SubOp(this, cc);
2707         }
2708 
2709         SubOp(Value lhs, Value rhs) {
2710             super(NAME, lhs, rhs);
2711         }
2712     }
2713 
2714     /**
2715      * The mul operation, that can model the Java language binary {@code *} operator for numeric types
2716      */
2717     @OpFactory.OpDeclaration(MulOp.NAME)
2718     public static final class MulOp extends BinaryOp {
2719         public static final String NAME = "mul";
2720 
2721         public MulOp(ExternalizedOp opdef) {
2722             super(opdef);
2723         }
2724 
2725         MulOp(MulOp that, CopyContext cc) {
2726             super(that, cc);
2727         }
2728 
2729         @Override
2730         public MulOp transform(CopyContext cc, OpTransformer ot) {
2731             return new MulOp(this, cc);
2732         }
2733 
2734         MulOp(Value lhs, Value rhs) {
2735             super(NAME, lhs, rhs);
2736         }
2737     }
2738 
2739     /**
2740      * The div operation, that can model the Java language binary {@code /} operator for numeric types
2741      */
2742     @OpFactory.OpDeclaration(DivOp.NAME)
2743     public static final class DivOp extends BinaryOp {
2744         public static final String NAME = "div";
2745 
2746         public DivOp(ExternalizedOp opdef) {
2747             super(opdef);
2748         }
2749 
2750         DivOp(DivOp that, CopyContext cc) {
2751             super(that, cc);
2752         }
2753 
2754         @Override
2755         public DivOp transform(CopyContext cc, OpTransformer ot) {
2756             return new DivOp(this, cc);
2757         }
2758 
2759         DivOp(Value lhs, Value rhs) {
2760             super(NAME, lhs, rhs);
2761         }
2762     }
2763 
2764     /**
2765      * The mod operation, that can model the Java language binary {@code %} operator for numeric types
2766      */
2767     @OpFactory.OpDeclaration(ModOp.NAME)
2768     public static final class ModOp extends BinaryOp {
2769         public static final String NAME = "mod";
2770 
2771         public ModOp(ExternalizedOp opdef) {
2772             super(opdef);
2773         }
2774 
2775         ModOp(ModOp that, CopyContext cc) {
2776             super(that, cc);
2777         }
2778 
2779         @Override
2780         public ModOp transform(CopyContext cc, OpTransformer ot) {
2781             return new ModOp(this, cc);
2782         }
2783 
2784         ModOp(Value lhs, Value rhs) {
2785             super(NAME, lhs, rhs);
2786         }
2787     }
2788 
2789     /**
2790      * The bitwise/logical or operation, that can model the Java language binary {@code |} operator for integral types
2791      * and booleans
2792      */
2793     @OpFactory.OpDeclaration(OrOp.NAME)
2794     public static final class OrOp extends BinaryOp {
2795         public static final String NAME = "or";
2796 
2797         public OrOp(ExternalizedOp opdef) {
2798             super(opdef);
2799         }
2800 
2801         OrOp(OrOp that, CopyContext cc) {
2802             super(that, cc);
2803         }
2804 
2805         @Override
2806         public OrOp transform(CopyContext cc, OpTransformer ot) {
2807             return new OrOp(this, cc);
2808         }
2809 
2810         OrOp(Value lhs, Value rhs) {
2811             super(NAME, lhs, rhs);
2812         }
2813     }
2814 
2815     /**
2816      * The bitwise/logical and operation, that can model the Java language binary {@code &} operator for integral types
2817      * and booleans
2818      */
2819     @OpFactory.OpDeclaration(AndOp.NAME)
2820     public static final class AndOp extends BinaryOp {
2821         public static final String NAME = "and";
2822 
2823         public AndOp(ExternalizedOp opdef) {
2824             super(opdef);
2825         }
2826 
2827         AndOp(AndOp that, CopyContext cc) {
2828             super(that, cc);
2829         }
2830 
2831         @Override
2832         public AndOp transform(CopyContext cc, OpTransformer ot) {
2833             return new AndOp(this, cc);
2834         }
2835 
2836         AndOp(Value lhs, Value rhs) {
2837             super(NAME, lhs, rhs);
2838         }
2839     }
2840 
2841     /**
2842      * The xor operation, that can model the Java language binary {@code ^} operator for integral types
2843      * and booleans
2844      */
2845     @OpFactory.OpDeclaration(XorOp.NAME)
2846     public static final class XorOp extends BinaryOp {
2847         public static final String NAME = "xor";
2848 
2849         public XorOp(ExternalizedOp opdef) {
2850             super(opdef);
2851         }
2852 
2853         XorOp(XorOp that, CopyContext cc) {
2854             super(that, cc);
2855         }
2856 
2857         @Override
2858         public XorOp transform(CopyContext cc, OpTransformer ot) {
2859             return new XorOp(this, cc);
2860         }
2861 
2862         XorOp(Value lhs, Value rhs) {
2863             super(NAME, lhs, rhs);
2864         }
2865     }
2866 
2867     /**
2868      * The (logical) shift left operation, that can model the Java language binary {@code <<} operator for integral types
2869      */
2870     @OpFactory.OpDeclaration(LshlOp.NAME)
2871     public static final class LshlOp extends BinaryOp {
2872         public static final String NAME = "lshl";
2873 
2874         public LshlOp(ExternalizedOp opdef) {
2875             super(opdef);
2876         }
2877 
2878         LshlOp(LshlOp that, CopyContext cc) {
2879             super(that, cc);
2880         }
2881 
2882         @Override
2883         public LshlOp transform(CopyContext cc, OpTransformer ot) {
2884             return new LshlOp(this, cc);
2885         }
2886 
2887         LshlOp(Value lhs, Value rhs) {
2888             super(NAME, lhs, rhs);
2889         }
2890     }
2891 
2892     /**
2893      * The (arithmetic) shift right operation, that can model the Java language binary {@code >>} operator for integral types
2894      */
2895     @OpFactory.OpDeclaration(AshrOp.NAME)
2896     public static final class AshrOp extends CoreOp.BinaryOp {
2897         public static final String NAME = "ashr";
2898 
2899         public AshrOp(ExternalizedOp opdef) {
2900             super(opdef);
2901         }
2902 
2903         AshrOp(AshrOp that, CopyContext cc) {
2904             super(that, cc);
2905         }
2906 
2907         @Override
2908         public AshrOp transform(CopyContext cc, OpTransformer ot) {
2909             return new AshrOp(this, cc);
2910         }
2911 
2912         AshrOp(Value lhs, Value rhs) {
2913             super(NAME, lhs, rhs);
2914         }
2915     }
2916 
2917     /**
2918      * The unsigned (logical) shift right operation, that can model the Java language binary {@code >>>} operator for integral types
2919      */
2920     @OpFactory.OpDeclaration(LshrOp.NAME)
2921     public static final class LshrOp extends CoreOp.BinaryOp {
2922         public static final String NAME = "lshr";
2923 
2924         public LshrOp(ExternalizedOp opdef) {
2925             super(opdef);
2926         }
2927 
2928         LshrOp(LshrOp that, CopyContext cc) {
2929             super(that, cc);
2930         }
2931 
2932         @Override
2933         public LshrOp transform(CopyContext cc, OpTransformer ot) {
2934             return new LshrOp(this, cc);
2935         }
2936 
2937         LshrOp(Value lhs, Value rhs) {
2938             super(NAME, lhs, rhs);
2939         }
2940     }
2941 
2942     /**
2943      * The neg operation, that can model the Java language unary {@code -} operator for numeric types
2944      */
2945     @OpFactory.OpDeclaration(NegOp.NAME)
2946     public static final class NegOp extends UnaryOp {
2947         public static final String NAME = "neg";
2948 
2949         public NegOp(ExternalizedOp opdef) {
2950             super(opdef);
2951         }
2952 
2953         NegOp(NegOp that, CopyContext cc) {
2954             super(that, cc);
2955         }
2956 
2957         @Override
2958         public NegOp transform(CopyContext cc, OpTransformer ot) {
2959             return new NegOp(this, cc);
2960         }
2961 
2962         NegOp(Value v) {
2963             super(NAME, v);
2964         }
2965     }
2966 
2967     /**
2968      * The not operation, that can model the Java language unary {@code !} operator for boolean types
2969      */
2970     @OpFactory.OpDeclaration(NotOp.NAME)
2971     public static final class NotOp extends UnaryOp {
2972         public static final String NAME = "not";
2973 
2974         public NotOp(ExternalizedOp opdef) {
2975             super(opdef);
2976         }
2977 
2978         NotOp(NotOp that, CopyContext cc) {
2979             super(that, cc);
2980         }
2981 
2982         @Override
2983         public NotOp transform(CopyContext cc, OpTransformer ot) {
2984             return new NotOp(this, cc);
2985         }
2986 
2987         NotOp(Value v) {
2988             super(NAME, v);
2989         }
2990     }
2991 
2992     /**
2993      * The equals operation, that can model the Java language equality {@code ==} operator for numeric, boolean
2994      * and reference types
2995      */
2996     @OpFactory.OpDeclaration(EqOp.NAME)
2997     public static final class EqOp extends BinaryTestOp {
2998         public static final String NAME = "eq";
2999 
3000         public EqOp(ExternalizedOp opdef) {
3001             super(opdef);
3002         }
3003 
3004         EqOp(EqOp that, CopyContext cc) {
3005             super(that, cc);
3006         }
3007 
3008         @Override
3009         public EqOp transform(CopyContext cc, OpTransformer ot) {
3010             return new EqOp(this, cc);
3011         }
3012 
3013         EqOp(Value lhs, Value rhs) {
3014             super(NAME, lhs, rhs);
3015         }
3016     }
3017 
3018     /**
3019      * The not equals operation, that can model the Java language equality {@code !=} operator for numeric, boolean
3020      * and reference types
3021      */
3022     @OpFactory.OpDeclaration(NeqOp.NAME)
3023     public static final class NeqOp extends BinaryTestOp {
3024         public static final String NAME = "neq";
3025 
3026         public NeqOp(ExternalizedOp opdef) {
3027             super(opdef);
3028         }
3029 
3030         NeqOp(NeqOp that, CopyContext cc) {
3031             super(that, cc);
3032         }
3033 
3034         @Override
3035         public NeqOp transform(CopyContext cc, OpTransformer ot) {
3036             return new NeqOp(this, cc);
3037         }
3038 
3039         NeqOp(Value lhs, Value rhs) {
3040             super(NAME, lhs, rhs);
3041         }
3042     }
3043 
3044     /**
3045      * The greater than operation, that can model the Java language relational {@code >} operator for numeric types
3046      */
3047     @OpFactory.OpDeclaration(GtOp.NAME)
3048     public static final class GtOp extends BinaryTestOp {
3049         public static final String NAME = "gt";
3050 
3051         public GtOp(ExternalizedOp opdef) {
3052             super(opdef);
3053         }
3054 
3055         GtOp(GtOp that, CopyContext cc) {
3056             super(that, cc);
3057         }
3058 
3059         @Override
3060         public GtOp transform(CopyContext cc, OpTransformer ot) {
3061             return new GtOp(this, cc);
3062         }
3063 
3064         GtOp(Value lhs, Value rhs) {
3065             super(NAME, lhs, rhs);
3066         }
3067     }
3068 
3069     /**
3070      * The greater than or equal to operation, that can model the Java language relational {@code >=} operator for
3071      * numeric types
3072      */
3073     @OpFactory.OpDeclaration(GeOp.NAME)
3074     public static final class GeOp extends BinaryTestOp {
3075         public static final String NAME = "ge";
3076 
3077         public GeOp(ExternalizedOp opdef) {
3078             super(opdef);
3079         }
3080 
3081         GeOp(GeOp that, CopyContext cc) {
3082             super(that, cc);
3083         }
3084 
3085         @Override
3086         public GeOp transform(CopyContext cc, OpTransformer ot) {
3087             return new GeOp(this, cc);
3088         }
3089 
3090         GeOp(Value lhs, Value rhs) {
3091             super(NAME, lhs, rhs);
3092         }
3093     }
3094 
3095     /**
3096      * The less than operation, that can model the Java language relational {@code <} operator for
3097      * numeric types
3098      */
3099     @OpFactory.OpDeclaration(LtOp.NAME)
3100     public static final class LtOp extends BinaryTestOp {
3101         public static final String NAME = "lt";
3102 
3103         public LtOp(ExternalizedOp opdef) {
3104             super(opdef);
3105         }
3106 
3107         LtOp(LtOp that, CopyContext cc) {
3108             super(that, cc);
3109         }
3110 
3111         @Override
3112         public LtOp transform(CopyContext cc, OpTransformer ot) {
3113             return new LtOp(this, cc);
3114         }
3115 
3116         LtOp(Value lhs, Value rhs) {
3117             super(NAME, lhs, rhs);
3118         }
3119     }
3120 
3121     /**
3122      * The less than or equal to operation, that can model the Java language relational {@code <=} operator for
3123      * numeric types
3124      */
3125     @OpFactory.OpDeclaration(LeOp.NAME)
3126     public static final class LeOp extends BinaryTestOp {
3127         public static final String NAME = "le";
3128 
3129         public LeOp(ExternalizedOp opdef) {
3130             super(opdef);
3131         }
3132 
3133         LeOp(LeOp that, CopyContext cc) {
3134             super(that, cc);
3135         }
3136 
3137         @Override
3138         public LeOp transform(CopyContext cc, OpTransformer ot) {
3139             return new LeOp(this, cc);
3140         }
3141 
3142         LeOp(Value lhs, Value rhs) {
3143             super(NAME, lhs, rhs);
3144         }
3145     }
3146 
3147 
3148     /**
3149      * A factory for core operations.
3150      */
3151     // @@@ Compute lazily
3152     public static final OpFactory FACTORY = OpFactory.OP_FACTORY.get(CoreOp.class);
3153 
3154     /**
3155      * Creates a function operation builder
3156      *
3157      * @param funcName the function name
3158      * @param funcType the function type
3159      * @return the function operation builder
3160      */
3161     public static FuncOp.Builder func(String funcName, FunctionType funcType) {
3162         return new FuncOp.Builder(null, funcName, funcType);
3163     }
3164 
3165     /**
3166      * Creates a function operation
3167      *
3168      * @param funcName the function name
3169      * @param body     the function body
3170      * @return the function operation
3171      */
3172     public static FuncOp func(String funcName, Body.Builder body) {
3173         return new FuncOp(funcName, body);
3174     }
3175 
3176     /**
3177      * Creates a function call operation
3178      *
3179      * @param funcName the name of the function operation
3180      * @param funcType the function type
3181      * @param args     the function arguments
3182      * @return the function call operation
3183      */
3184     public static FuncCallOp funcCall(String funcName, FunctionType funcType, Value... args) {
3185         return funcCall(funcName, funcType, List.of(args));
3186     }
3187 
3188     /**
3189      * Creates a function call operation
3190      *
3191      * @param funcName the name of the function operation
3192      * @param funcType the function type
3193      * @param args     the function arguments
3194      * @return the function call operation
3195      */
3196     public static FuncCallOp funcCall(String funcName, FunctionType funcType, List<Value> args) {
3197         return new FuncCallOp(funcName, funcType.returnType(), args);
3198     }
3199 
3200     /**
3201      * Creates a function call operation
3202      *
3203      * @param func the target function
3204      * @param args the function arguments
3205      * @return the function call operation
3206      */
3207     public static FuncCallOp funcCall(FuncOp func, Value... args) {
3208         return funcCall(func, List.of(args));
3209     }
3210 
3211     /**
3212      * Creates a function call operation
3213      *
3214      * @param func the target function
3215      * @param args the function argments
3216      * @return the function call operation
3217      */
3218     public static FuncCallOp funcCall(FuncOp func, List<Value> args) {
3219         return new FuncCallOp(func.funcName(), func.invokableType().returnType(), args);
3220     }
3221 
3222     /**
3223      * Creates a module operation.
3224      *
3225      * @param functions the functions of the module operation
3226      * @return the module operation
3227      */
3228     public static ModuleOp module(FuncOp... functions) {
3229         return module(List.of(functions));
3230     }
3231 
3232     /**
3233      * Creates a module operation.
3234      *
3235      * @param functions the functions of the module operation
3236      * @return the module operation
3237      */
3238     public static ModuleOp module(List<FuncOp> functions) {
3239         return new ModuleOp(List.copyOf(functions));
3240     }
3241 
3242     /**
3243      * Creates a quoted operation.
3244      *
3245      * @param ancestorBody the ancestor of the body of the quoted operation
3246      * @param opFunc       a function that accepts the body of the quoted operation and returns the operation to be quoted
3247      * @return the quoted operation
3248      */
3249     public static QuotedOp quoted(Body.Builder ancestorBody,
3250                                   Function<Block.Builder, Op> opFunc) {
3251         Body.Builder body = Body.Builder.of(ancestorBody, FunctionType.VOID);
3252         Block.Builder block = body.entryBlock();
3253         block.op(_yield(
3254                 block.op(opFunc.apply(block))));
3255         return new QuotedOp(body);
3256     }
3257 
3258     /**
3259      * Creates a quoted operation.
3260      *
3261      * @param body quoted operation body
3262      * @return the quoted operation
3263      */
3264     public static QuotedOp quoted(Body.Builder body) {
3265         return new QuotedOp(body);
3266     }
3267 
3268     /**
3269      * Creates a lambda operation.
3270      *
3271      * @param ancestorBody        the ancestor of the body of the lambda operation
3272      * @param funcType            the lambda operation's function type
3273      * @param functionalInterface the lambda operation's functional interface type
3274      * @return the lambda operation
3275      */
3276     public static LambdaOp.Builder lambda(Body.Builder ancestorBody,
3277                                           FunctionType funcType, TypeElement functionalInterface) {
3278         return new LambdaOp.Builder(ancestorBody, funcType, functionalInterface);
3279     }
3280 
3281     /**
3282      * Creates a lambda operation.
3283      *
3284      * @param functionalInterface the lambda operation's functional interface type
3285      * @param body                the body of the lambda operation
3286      * @return the lambda operation
3287      */
3288     public static LambdaOp lambda(TypeElement functionalInterface, Body.Builder body) {
3289         return new LambdaOp(functionalInterface, body);
3290     }
3291 
3292     /**
3293      * Creates a closure operation.
3294      *
3295      * @param ancestorBody the ancestor of the body of the closure operation
3296      * @param funcType     the closure operation's function type
3297      * @return the closure operation
3298      */
3299     public static ClosureOp.Builder closure(Body.Builder ancestorBody,
3300                                             FunctionType funcType) {
3301         return new ClosureOp.Builder(ancestorBody, funcType);
3302     }
3303 
3304     /**
3305      * Creates a closure operation.
3306      *
3307      * @param body the body of the closure operation
3308      * @return the closure operation
3309      */
3310     public static ClosureOp closure(Body.Builder body) {
3311         return new ClosureOp(body);
3312     }
3313 
3314     /**
3315      * Creates a closure call operation.
3316      *
3317      * @param args the closure arguments. The first argument is the closure operation to be called
3318      * @return the closure call operation
3319      */
3320     // @@@: Is this the right signature?
3321     public static ClosureCallOp closureCall(Value... args) {
3322         return closureCall(List.of(args));
3323     }
3324 
3325     /**
3326      * Creates a closure call operation.
3327      *
3328      * @param args the closure arguments. The first argument is the closure operation to be called
3329      * @return the closure call operation
3330      */
3331     // @@@: Is this the right signature?
3332     public static ClosureCallOp closureCall(List<Value> args) {
3333         return new ClosureCallOp(args);
3334     }
3335 
3336     /**
3337      * Creates an exception region enter operation
3338      *
3339      * @param start    the exception region block
3340      * @param catchers the blocks handling exceptions thrown by the region block
3341      * @return the exception region enter operation
3342      */
3343     public static ExceptionRegionEnter exceptionRegionEnter(Block.Reference start, Block.Reference... catchers) {
3344         return exceptionRegionEnter(start, List.of(catchers));
3345     }
3346 
3347     /**
3348      * Creates an exception region enter operation
3349      *
3350      * @param start    the exception region block
3351      * @param catchers the blocks handling exceptions thrown by the region block
3352      * @return the exception region enter operation
3353      */
3354     public static ExceptionRegionEnter exceptionRegionEnter(Block.Reference start, List<Block.Reference> catchers) {
3355         List<Block.Reference> s = new ArrayList<>();
3356         s.add(start);
3357         s.addAll(catchers);
3358         return new ExceptionRegionEnter(s);
3359     }
3360 
3361     /**
3362      * Creates an exception region exit operation
3363      *
3364      * @param exceptionRegion the exception region to be exited
3365      * @param end             the block to which control is transferred after the exception region is exited
3366      * @return the exception region exit operation
3367      */
3368     public static ExceptionRegionExit exceptionRegionExit(Value exceptionRegion, Block.Reference end) {
3369         return new ExceptionRegionExit(exceptionRegion, end);
3370     }
3371 
3372     /**
3373      * Creates a return operation.
3374      *
3375      * @return the return operation
3376      */
3377     public static ReturnOp _return() {
3378         return new ReturnOp();
3379     }
3380 
3381     /**
3382      * Creates a return operation.
3383      *
3384      * @param returnValue the return value
3385      * @return the return operation
3386      */
3387     public static ReturnOp _return(Value returnValue) {
3388         return new ReturnOp(returnValue);
3389     }
3390 
3391     /**
3392      * Creates a throw operation.
3393      *
3394      * @param exceptionValue the thrown value
3395      * @return the throw operation
3396      */
3397     public static ThrowOp _throw(Value exceptionValue) {
3398         return new ThrowOp(exceptionValue);
3399     }
3400 
3401     /**
3402      * Creates an unreachable operation.
3403      *
3404      * @return the unreachable operation
3405      */
3406     public static UnreachableOp unreachable() {
3407         return new UnreachableOp();
3408     }
3409 
3410     /**
3411      * Creates a yield operation.
3412      *
3413      * @return the yield operation
3414      */
3415     public static YieldOp _yield() {
3416         return new YieldOp();
3417     }
3418 
3419     /**
3420      * Creates a yield operation.
3421      *
3422      * @param yieldValue the yielded value
3423      * @return the yield operation
3424      */
3425     public static YieldOp _yield(Value yieldValue) {
3426         return new YieldOp(List.of(yieldValue));
3427     }
3428 
3429     /**
3430      * Creates an assert operation.
3431      *
3432      * @param bodies the nested bodies
3433      * @return the assert operation
3434      */
3435     public static AssertOp _assert(List<Body.Builder> bodies) {
3436         return new AssertOp(bodies);
3437     }
3438 
3439     /**
3440      * Creates an unconditional break operation.
3441      *
3442      * @param target the jump target
3443      * @return the unconditional break operation
3444      */
3445     public static BranchOp branch(Block.Reference target) {
3446         return new BranchOp(target);
3447     }
3448 
3449     /**
3450      * Creates a conditional break operation.
3451      *
3452      * @param condValue   the test value of the conditional break operation
3453      * @param trueTarget  the jump target when the test value evaluates to true
3454      * @param falseTarget the jump target when the test value evaluates to false
3455      * @return the conditional break operation
3456      */
3457     public static ConditionalBranchOp conditionalBranch(Value condValue,
3458                                                         Block.Reference trueTarget, Block.Reference falseTarget) {
3459         return new ConditionalBranchOp(condValue, trueTarget, falseTarget);
3460     }
3461 
3462     /**
3463      * Creates a constant operation.
3464      *
3465      * @param type  the constant type
3466      * @param value the constant value
3467      * @return the constant operation
3468      */
3469     public static ConstantOp constant(TypeElement type, Object value) {
3470         return new ConstantOp(type, value);
3471     }
3472 
3473     /**
3474      * Creates an invoke operation.
3475      *
3476      * @param invokeDescriptor the invocation descriptor
3477      * @param args             the invoke parameters
3478      * @return the invoke operation
3479      */
3480     public static InvokeOp invoke(MethodRef invokeDescriptor, Value... args) {
3481         return new InvokeOp(invokeDescriptor, List.of(args));
3482     }
3483 
3484     /**
3485      * Creates an invoke operation.
3486      *
3487      * @param invokeDescriptor the invocation descriptor
3488      * @param args             the invoke parameters
3489      * @return the invoke operation
3490      */
3491     public static InvokeOp invoke(MethodRef invokeDescriptor, List<Value> args) {
3492         return new InvokeOp(invokeDescriptor, args);
3493     }
3494 
3495     /**
3496      * Creates an invoke operation.
3497      *
3498      * @param returnType       the invocation return type
3499      * @param invokeDescriptor the invocation descriptor
3500      * @param args             the invoke parameters
3501      * @return the invoke operation
3502      */
3503     public static InvokeOp invoke(TypeElement returnType, MethodRef invokeDescriptor, Value... args) {
3504         return new InvokeOp(returnType, invokeDescriptor, List.of(args));
3505     }
3506 
3507     /**
3508      * Creates an invoke operation.
3509      *
3510      * @param returnType       the invocation return type
3511      * @param invokeDescriptor the invocation descriptor
3512      * @param args             the invoke parameters
3513      * @return the invoke operation
3514      */
3515     public static InvokeOp invoke(TypeElement returnType, MethodRef invokeDescriptor, List<Value> args) {
3516         return new InvokeOp(returnType, invokeDescriptor, args);
3517     }
3518 
3519     /**
3520      * Creates a conversion operation.
3521      *
3522      * @param to   the conversion target type
3523      * @param from the value to be converted
3524      * @return the conversion operation
3525      */
3526     public static ConvOp conv(TypeElement to, Value from) {
3527         return new ConvOp(to, from);
3528     }
3529 
3530     /**
3531      * Creates an instance creation operation.
3532      *
3533      * @param constructorType the constructor type
3534      * @param args            the constructor arguments
3535      * @return the instance creation operation
3536      */
3537     public static NewOp _new(FunctionType constructorType, Value... args) {
3538         return _new(constructorType, List.of(args));
3539     }
3540 
3541     /**
3542      * Creates an instance creation operation.
3543      *
3544      * @param constructorType the constructor type
3545      * @param args            the constructor arguments
3546      * @return the instance creation operation
3547      */
3548     public static NewOp _new(FunctionType constructorType, List<Value> args) {
3549         return new NewOp(constructorType, args);
3550     }
3551 
3552     /**
3553      * Creates an instance creation operation.
3554      *
3555      * @param returnType      the instance type
3556      * @param constructorType the constructor type
3557      * @param args            the constructor arguments
3558      * @return the instance creation operation
3559      */
3560     public static NewOp _new(TypeElement returnType, FunctionType constructorType,
3561                              Value... args) {
3562         return _new(returnType, constructorType, List.of(args));
3563     }
3564 
3565     /**
3566      * Creates an instance creation operation.
3567      *
3568      * @param returnType      the instance type
3569      * @param constructorType the constructor type
3570      * @param args            the constructor arguments
3571      * @return the instance creation operation
3572      */
3573     public static NewOp _new(TypeElement returnType, FunctionType constructorType,
3574                              List<Value> args) {
3575         return new NewOp(returnType, constructorType, args);
3576     }
3577 
3578     /**
3579      * Creates an array creation operation.
3580      *
3581      * @param arrayType the array type
3582      * @param length    the array size
3583      * @return the array creation operation
3584      */
3585     public static NewOp newArray(TypeElement arrayType, Value length) {
3586         return _new(FunctionType.functionType(arrayType, JavaType.INT), length);
3587     }
3588 
3589     // @@@ Add field load/store overload with explicit fieldType
3590 
3591     /**
3592      * Creates a field load operation to a non-static field.
3593      *
3594      * @param descriptor the field descriptor
3595      * @param receiver   the receiver value
3596      * @return the field load operation
3597      */
3598     public static FieldAccessOp.FieldLoadOp fieldLoad(FieldRef descriptor, Value receiver) {
3599         return new FieldAccessOp.FieldLoadOp(descriptor.type(), descriptor, receiver);
3600     }
3601 
3602     /**
3603      * Creates a field load operation to a non-static field.
3604      *
3605      * @param resultType the result type of the operation
3606      * @param descriptor the field descriptor
3607      * @param receiver   the receiver value
3608      * @return the field load operation
3609      */
3610     public static FieldAccessOp.FieldLoadOp fieldLoad(TypeElement resultType, FieldRef descriptor, Value receiver) {
3611         return new FieldAccessOp.FieldLoadOp(resultType, descriptor, receiver);
3612     }
3613 
3614     /**
3615      * Creates a field load operation to a static field.
3616      *
3617      * @param descriptor the field descriptor
3618      * @return the field load operation
3619      */
3620     public static FieldAccessOp.FieldLoadOp fieldLoad(FieldRef descriptor) {
3621         return new FieldAccessOp.FieldLoadOp(descriptor.type(), descriptor);
3622     }
3623 
3624     /**
3625      * Creates a field load operation to a static field.
3626      *
3627      * @param resultType the result type of the operation
3628      * @param descriptor the field descriptor
3629      * @return the field load operation
3630      */
3631     public static FieldAccessOp.FieldLoadOp fieldLoad(TypeElement resultType, FieldRef descriptor) {
3632         return new FieldAccessOp.FieldLoadOp(resultType, descriptor);
3633     }
3634 
3635     /**
3636      * Creates a field store operation to a non-static field.
3637      *
3638      * @param descriptor the field descriptor
3639      * @param receiver   the receiver value
3640      * @param v          the value to store
3641      * @return the field store operation
3642      */
3643     public static FieldAccessOp.FieldStoreOp fieldStore(FieldRef descriptor, Value receiver, Value v) {
3644         return new FieldAccessOp.FieldStoreOp(descriptor, receiver, v);
3645     }
3646 
3647     /**
3648      * Creates a field load operation to a static field.
3649      *
3650      * @param descriptor the field descriptor
3651      * @param v          the value to store
3652      * @return the field store operation
3653      */
3654     public static FieldAccessOp.FieldStoreOp fieldStore(FieldRef descriptor, Value v) {
3655         return new FieldAccessOp.FieldStoreOp(descriptor, v);
3656     }
3657 
3658     /**
3659      * Creates an array length operation.
3660      *
3661      * @param array the array value
3662      * @return the array length operation
3663      */
3664     public static ArrayLengthOp arrayLength(Value array) {
3665         return new ArrayLengthOp(array);
3666     }
3667 
3668     /**
3669      * Creates an array load operation.
3670      *
3671      * @param array the array value
3672      * @param index the index value
3673      * @return the array load operation
3674      */
3675     public static ArrayAccessOp.ArrayLoadOp arrayLoadOp(Value array, Value index) {
3676         return new ArrayAccessOp.ArrayLoadOp(array, index);
3677     }
3678 
3679     /**
3680      * Creates an array store operation.
3681      *
3682      * @param array the array value
3683      * @param index the index value
3684      * @param v     the value to store
3685      * @return the array store operation
3686      */
3687     public static ArrayAccessOp.ArrayStoreOp arrayStoreOp(Value array, Value index, Value v) {
3688         return new ArrayAccessOp.ArrayStoreOp(array, index, v);
3689     }
3690 
3691     /**
3692      * Creates an instanceof operation.
3693      *
3694      * @param t the type to test against
3695      * @param v the value to test
3696      * @return the instanceof operation
3697      */
3698     public static InstanceOfOp instanceOf(TypeElement t, Value v) {
3699         return new InstanceOfOp(t, v);
3700     }
3701 
3702     /**
3703      * Creates a cast operation.
3704      *
3705      * @param resultType the result type of the operation
3706      * @param v          the value to cast
3707      * @return the cast operation
3708      */
3709     public static CastOp cast(TypeElement resultType, Value v) {
3710         return new CastOp(resultType, resultType, v);
3711     }
3712 
3713     /**
3714      * Creates a cast operation.
3715      *
3716      * @param resultType the result type of the operation
3717      * @param t          the type to cast to
3718      * @param v          the value to cast
3719      * @return the cast operation
3720      */
3721     public static CastOp cast(TypeElement resultType, JavaType t, Value v) {
3722         return new CastOp(resultType, t, v);
3723     }
3724 
3725     /**
3726      * Creates a var operation.
3727      *
3728      * @param init the initial value of the var
3729      * @return the var operation
3730      */
3731     public static VarOp var(Value init) {
3732         return var(null, init);
3733     }
3734 
3735     /**
3736      * Creates a var operation.
3737      *
3738      * @param name the name of the var
3739      * @param init the initial value of the var
3740      * @return the var operation
3741      */
3742     public static VarOp var(String name, Value init) {
3743         return new VarOp(name, init);
3744     }
3745 
3746     /**
3747      * Creates a var operation.
3748      *
3749      * @param name the name of the var
3750      * @param type the type of the var's value
3751      * @param init the initial value of the var
3752      * @return the var operation
3753      */
3754     public static VarOp var(String name, TypeElement type, Value init) {
3755         return new VarOp(name, type, init);
3756     }
3757 
3758     /**
3759      * Creates a var load operation.
3760      *
3761      * @param varValue the var value
3762      * @return the var load operation
3763      */
3764     public static VarAccessOp.VarLoadOp varLoad(Value varValue) {
3765         return new VarAccessOp.VarLoadOp(varValue);
3766     }
3767 
3768     /**
3769      * Creates a var store operation.
3770      *
3771      * @param varValue the var value
3772      * @param v        the value to store in the var
3773      * @return the var store operation
3774      */
3775     public static VarAccessOp.VarStoreOp varStore(Value varValue, Value v) {
3776         return new VarAccessOp.VarStoreOp(varValue, v);
3777     }
3778 
3779     /**
3780      * Creates a tuple operation.
3781      *
3782      * @param componentValues the values of tuple (in order)
3783      * @return the tuple operation
3784      */
3785     public static TupleOp tuple(Value... componentValues) {
3786         return tuple(List.of(componentValues));
3787     }
3788 
3789     /**
3790      * Creates a tuple operation.
3791      *
3792      * @param componentValues the values of tuple (in order)
3793      * @return the tuple operation
3794      */
3795     public static TupleOp tuple(List<? extends Value> componentValues) {
3796         return new TupleOp(componentValues);
3797     }
3798 
3799     /**
3800      * Creates a tuple load operation.
3801      *
3802      * @param tuple the tuple value
3803      * @param index the component index value
3804      * @return the tuple load operation
3805      */
3806     public static TupleLoadOp tupleLoad(Value tuple, int index) {
3807         return new TupleLoadOp(tuple, index);
3808     }
3809 
3810     /**
3811      * Creates a tuple with operation.
3812      *
3813      * @param tuple the tuple value
3814      * @param index the component index value
3815      * @param value the component value
3816      * @return the tuple with operation
3817      */
3818     public static TupleWithOp tupleWith(Value tuple, int index, Value value) {
3819         return new TupleWithOp(tuple, index, value);
3820     }
3821 
3822     //
3823     // Arithmetic ops
3824 
3825     /**
3826      * Creates an add operation.
3827      *
3828      * @param lhs the first operand
3829      * @param rhs the second operand
3830      * @return the add operation
3831      */
3832     public static BinaryOp add(Value lhs, Value rhs) {
3833         return new AddOp(lhs, rhs);
3834     }
3835 
3836     /**
3837      * Creates a sub operation.
3838      *
3839      * @param lhs the first operand
3840      * @param rhs the second operand
3841      * @return the sub operation
3842      */
3843     public static BinaryOp sub(Value lhs, Value rhs) {
3844         return new SubOp(lhs, rhs);
3845     }
3846 
3847     /**
3848      * Creates a mul operation.
3849      *
3850      * @param lhs the first operand
3851      * @param rhs the second operand
3852      * @return the mul operation
3853      */
3854     public static BinaryOp mul(Value lhs, Value rhs) {
3855         return new MulOp(lhs, rhs);
3856     }
3857 
3858     /**
3859      * Creates a div operation.
3860      *
3861      * @param lhs the first operand
3862      * @param rhs the second operand
3863      * @return the div operation
3864      */
3865     public static BinaryOp div(Value lhs, Value rhs) {
3866         return new DivOp(lhs, rhs);
3867     }
3868 
3869     /**
3870      * Creates a mod operation.
3871      *
3872      * @param lhs the first operand
3873      * @param rhs the second operand
3874      * @return the mod operation
3875      */
3876     public static BinaryOp mod(Value lhs, Value rhs) {
3877         return new ModOp(lhs, rhs);
3878     }
3879 
3880     /**
3881      * Creates a bitwise/logical or operation.
3882      *
3883      * @param lhs the first operand
3884      * @param rhs the second operand
3885      * @return the or operation
3886      */
3887     public static BinaryOp or(Value lhs, Value rhs) {
3888         return new OrOp(lhs, rhs);
3889     }
3890 
3891     /**
3892      * Creates a bitwise/logical and operation.
3893      *
3894      * @param lhs the first operand
3895      * @param rhs the second operand
3896      * @return the and operation
3897      */
3898     public static BinaryOp and(Value lhs, Value rhs) {
3899         return new AndOp(lhs, rhs);
3900     }
3901 
3902     /**
3903      * Creates a bitwise/logical xor operation.
3904      *
3905      * @param lhs the first operand
3906      * @param rhs the second operand
3907      * @return the xor operation
3908      */
3909     public static BinaryOp xor(Value lhs, Value rhs) {
3910         return new XorOp(lhs, rhs);
3911     }
3912 
3913     /**
3914      * Creates a left shift operation.
3915      *
3916      * @param lhs the first operand
3917      * @param rhs the second operand
3918      * @return the xor operation
3919      */
3920     public static BinaryOp lshl(Value lhs, Value rhs) {
3921         return new LshlOp(lhs, rhs);
3922     }
3923 
3924     /**
3925      * Creates a right shift operation.
3926      *
3927      * @param lhs the first operand
3928      * @param rhs the second operand
3929      * @return the xor operation
3930      */
3931     public static BinaryOp ashr(Value lhs, Value rhs) {
3932         return new AshrOp(lhs, rhs);
3933     }
3934 
3935     /**
3936      * Creates an unsigned right shift operation.
3937      *
3938      * @param lhs the first operand
3939      * @param rhs the second operand
3940      * @return the xor operation
3941      */
3942     public static BinaryOp lshr(Value lhs, Value rhs) {
3943         return new LshrOp(lhs, rhs);
3944     }
3945 
3946     /**
3947      * Creates a neg operation.
3948      *
3949      * @param v the operand
3950      * @return the neg operation
3951      */
3952     public static UnaryOp neg(Value v) {
3953         return new NegOp(v);
3954     }
3955 
3956     /**
3957      * Creates a not operation.
3958      *
3959      * @param v the operand
3960      * @return the not operation
3961      */
3962     public static UnaryOp not(Value v) {
3963         return new NotOp(v);
3964     }
3965 
3966 
3967     /**
3968      * Creates an equals comparison operation.
3969      *
3970      * @param lhs the first operand
3971      * @param rhs the second operand
3972      * @return the equals comparison operation
3973      */
3974     public static BinaryTestOp eq(Value lhs, Value rhs) {
3975         return new EqOp(lhs, rhs);
3976     }
3977 
3978     /**
3979      * Creates a not equals comparison operation.
3980      *
3981      * @param lhs the first operand
3982      * @param rhs the second operand
3983      * @return the not equals comparison operation
3984      */
3985     public static BinaryTestOp neq(Value lhs, Value rhs) {
3986         return new NeqOp(lhs, rhs);
3987     }
3988 
3989     /**
3990      * Creates a greater than comparison operation.
3991      *
3992      * @param lhs the first operand
3993      * @param rhs the second operand
3994      * @return the greater than comparison operation
3995      */
3996     public static BinaryTestOp gt(Value lhs, Value rhs) {
3997         return new GtOp(lhs, rhs);
3998     }
3999 
4000     /**
4001      * Creates a greater than or equals to comparison operation.
4002      *
4003      * @param lhs the first operand
4004      * @param rhs the second operand
4005      * @return the greater than or equals to comparison operation
4006      */
4007     public static BinaryTestOp ge(Value lhs, Value rhs) {
4008         return new GeOp(lhs, rhs);
4009     }
4010 
4011     /**
4012      * Creates a less than comparison operation.
4013      *
4014      * @param lhs the first operand
4015      * @param rhs the second operand
4016      * @return the less than comparison operation
4017      */
4018     public static BinaryTestOp lt(Value lhs, Value rhs) {
4019         return new LtOp(lhs, rhs);
4020     }
4021 
4022     /**
4023      * Creates a less than or equals to comparison operation.
4024      *
4025      * @param lhs the first operand
4026      * @param rhs the second operand
4027      * @return the less than or equals to comparison operation
4028      */
4029     public static BinaryTestOp le(Value lhs, Value rhs) {
4030         return new LeOp(lhs, rhs);
4031     }
4032 
4033     /**
4034      * Creates a string concatenation operation.
4035      *
4036      * @param lhs the first operand
4037      * @param rhs the second operand
4038      * @return the string concatenation operation
4039      */
4040     public static ConcatOp concat(Value lhs, Value rhs) {
4041         return new ConcatOp(lhs, rhs);
4042     }
4043 }