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.ArrayType; 31 import java.lang.reflect.code.type.ClassType; 32 import java.lang.reflect.code.type.MethodRef; 33 import java.lang.reflect.code.type.RecordTypeRef; 34 import java.lang.reflect.code.type.FunctionType; 35 import java.lang.reflect.code.type.JavaType; 36 import java.lang.reflect.code.type.TupleType; 37 import java.lang.reflect.code.TypeElement; 38 import java.lang.reflect.code.type.VarType; 39 import java.util.*; 40 import java.util.concurrent.atomic.AtomicBoolean; 41 import java.util.function.Consumer; 42 import java.util.function.Function; 43 import java.util.stream.Stream; 44 45 import static java.lang.reflect.code.op.CoreOp.*; 46 import static java.lang.reflect.code.type.JavaType.*; 47 48 /** 49 * The top-level operation class for the enclosed set of extended operations. 50 * <p> 51 * A code model, produced by the Java compiler from Java program source, may consist of extended operations and core 52 * operations. Such a model represents the same Java program and preserves the program meaning as defined by the 53 * Java Language Specification 54 * <p> 55 * Extended operations model specific Java language constructs, often those with structured control flow and nested 56 * code. Each operation is transformable into a sequence of core operations, commonly referred to as lowering. Those 57 * that implement {@link Op.Lowerable} can transform themselves and will transform associated extended operations 58 * that are not explicitly lowerable. 59 * <p> 60 * A code model, produced by the Java compiler from source, and consisting of extended operations and core operations 61 * can be transformed to one consisting only of core operations, where all extended operations are lowered. This 62 * transformation preserves programing meaning. The resulting lowered code model also represents the same Java program. 63 */ 64 public sealed abstract class ExtendedOp extends ExternalizableOp { 65 // Split string to ensure the name does not get rewritten 66 // when the script copies this source to the jdk.compiler module 67 static final String PACKAGE_NAME = "java.lang" + ".reflect.code"; 68 69 static final String ExtendedOp_CLASS_NAME = PACKAGE_NAME + "." + ExtendedOp.class.getSimpleName(); 70 71 protected ExtendedOp(Op that, CopyContext cc) { 72 super(that, cc); 73 } 74 75 protected ExtendedOp(String name, List<? extends Value> operands) { 76 super(name, operands); 77 } 78 79 protected ExtendedOp(ExternalizableOp.ExternalizedOp def) { 80 super(def); 81 } 82 83 84 /** 85 * The label operation, that can model Java language statements with label identifiers. 86 */ 87 public sealed static abstract class JavaLabelOp extends ExtendedOp 88 implements Op.Lowerable, Op.BodyTerminating, JavaStatement { 89 JavaLabelOp(ExternalizedOp def) { 90 super(def); 91 92 if (def.operands().size() > 1) { 93 throw new IllegalArgumentException("Operation must have zero or one operand " + def.name()); 94 } 95 } 96 97 JavaLabelOp(JavaLabelOp that, CopyContext cc) { 98 super(that, cc); 99 } 100 101 JavaLabelOp(String name, Value label) { 102 super(name, checkLabel(label)); 103 } 104 105 static List<Value> checkLabel(Value label) { 106 return label == null ? List.of() : List.of(label); 107 } 108 109 Op innerMostEnclosingTarget() { 110 /* 111 A break statement with no label attempts to transfer control to the 112 innermost enclosing switch, while, do, or for statement; this enclosing statement, 113 which is called the break target, then immediately completes normally. 114 115 A break statement with label Identifier attempts to transfer control to the 116 enclosing labeled statement (14.7) that has the same Identifier as its label; 117 this enclosing statement, which is called the break target, then immediately completes normally. 118 In this case, the break target need not be a switch, while, do, or for statement. 119 */ 120 121 // No label 122 // Get innermost enclosing loop operation 123 // @@@ expand to support innermost enclosing switch operation 124 Op op = this; 125 Body b; 126 do { 127 b = op.ancestorBody(); 128 op = b.parentOp(); 129 if (op == null) { 130 throw new IllegalStateException("No enclosing loop"); 131 } 132 } while (!(op instanceof Op.Loop)); 133 // } while (!(op instanceof Op.Loop lop)); 134 // error: variable lop might not have been initialized 135 Op.Loop lop = (Op.Loop) op; 136 return lop.loopBody() == b ? op : null; 137 } 138 139 boolean isUnlabeled() { 140 return operands().isEmpty(); 141 } 142 143 Op target() { 144 // If unlabeled then find the nearest enclosing op 145 // Otherwise obtain the label target 146 if (isUnlabeled()) { 147 return innerMostEnclosingTarget(); 148 } 149 150 Value value = operands().get(0); 151 if (value instanceof Result r && r.op().ancestorBody().parentOp() instanceof JavaLabeledOp lop) { 152 return lop.target(); 153 } else { 154 throw new IllegalStateException("Bad label value: " + value + " " + ((Result) value).op()); 155 } 156 } 157 158 Block.Builder lower(Block.Builder b, Function<BranchTarget, Block.Builder> f) { 159 Op opt = target(); 160 BranchTarget t = getBranchTarget(b.context(), opt); 161 if (t != null) { 162 b.op(branch(f.apply(t).successor())); 163 } else { 164 throw new IllegalStateException("No branch target for operation: " + opt); 165 } 166 return b; 167 } 168 169 @Override 170 public TypeElement resultType() { 171 return VOID; 172 } 173 } 174 175 /** 176 * The break operation, that can model Java language break statements with label identifiers. 177 */ 178 @OpFactory.OpDeclaration(JavaBreakOp.NAME) 179 public static final class JavaBreakOp extends JavaLabelOp { 180 public static final String NAME = "java.break"; 181 182 public JavaBreakOp(ExternalizedOp def) { 183 super(def); 184 } 185 186 JavaBreakOp(JavaBreakOp that, CopyContext cc) { 187 super(that, cc); 188 } 189 190 @Override 191 public JavaBreakOp transform(CopyContext cc, OpTransformer ot) { 192 return new JavaBreakOp(this, cc); 193 } 194 195 JavaBreakOp(Value label) { 196 super(NAME, label); 197 } 198 199 @Override 200 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 201 return lower(b, BranchTarget::breakBlock); 202 } 203 } 204 205 /** 206 * The break operation, that can model Java language continue statements with label identifiers. 207 */ 208 @OpFactory.OpDeclaration(JavaContinueOp.NAME) 209 public static final class JavaContinueOp extends JavaLabelOp { 210 public static final String NAME = "java.continue"; 211 212 public JavaContinueOp(ExternalizedOp def) { 213 super(def); 214 } 215 216 JavaContinueOp(JavaContinueOp that, CopyContext cc) { 217 super(that, cc); 218 } 219 220 @Override 221 public JavaContinueOp transform(CopyContext cc, OpTransformer ot) { 222 return new JavaContinueOp(this, cc); 223 } 224 225 JavaContinueOp(Value label) { 226 super(NAME, label); 227 } 228 229 @Override 230 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 231 return lower(b, BranchTarget::continueBlock); 232 } 233 } 234 235 record BranchTarget(Block.Builder breakBlock, Block.Builder continueBlock) { 236 } 237 238 static final String BRANCH_TARGET_MAP_PROPERTY_KEY = "BRANCH_TARGET_MAP"; 239 240 static BranchTarget getBranchTarget(CopyContext cc, Op op) { 241 @SuppressWarnings("unchecked") 242 Map<Op, BranchTarget> m = (Map<Op, BranchTarget>) cc.getProperty(BRANCH_TARGET_MAP_PROPERTY_KEY); 243 if (m != null) { 244 return m.get(op); 245 } 246 247 return null; 248 } 249 250 static void setBranchTarget(CopyContext cc, Op label, BranchTarget t) { 251 @SuppressWarnings("unchecked") 252 Map<Op, BranchTarget> x = (Map<Op, BranchTarget>) cc.computePropertyIfAbsent( 253 BRANCH_TARGET_MAP_PROPERTY_KEY, k -> new HashMap<>()); 254 x.put(label, t); 255 } 256 257 /** 258 * The yield operation, that can model Java language yield statements. 259 */ 260 @OpFactory.OpDeclaration(JavaYieldOp.NAME) 261 public static final class JavaYieldOp extends ExtendedOp 262 implements Op.BodyTerminating, JavaStatement { 263 public static final String NAME = "java.yield"; 264 265 public JavaYieldOp(ExternalizedOp def) { 266 super(def); 267 } 268 269 JavaYieldOp(JavaYieldOp that, CopyContext cc) { 270 super(that, cc); 271 } 272 273 @Override 274 public JavaYieldOp transform(CopyContext cc, OpTransformer ot) { 275 return new JavaYieldOp(this, cc); 276 } 277 278 JavaYieldOp() { 279 super(NAME, 280 List.of()); 281 } 282 283 JavaYieldOp(Value operand) { 284 super(NAME, List.of(operand)); 285 } 286 287 public Value yieldValue() { 288 if (operands().size() == 1) { 289 return operands().get(0); 290 } else { 291 // @@@ 292 return null; 293 } 294 } 295 296 @Override 297 public TypeElement resultType() { 298 return VOID; 299 } 300 } 301 302 /** 303 * The block operation, that can model Java language blocks. 304 */ 305 @OpFactory.OpDeclaration(JavaBlockOp.NAME) 306 // @@@ Support synchronized attribute 307 public static final class JavaBlockOp extends ExtendedOp 308 implements Op.Nested, Op.Lowerable, JavaStatement { 309 public static final String NAME = "java.block"; 310 311 final Body body; 312 313 public JavaBlockOp(ExternalizedOp def) { 314 super(def); 315 316 if (!def.operands().isEmpty()) { 317 throw new IllegalStateException("Operation must have no operands"); 318 } 319 320 this.body = def.bodyDefinitions().get(0).build(this); 321 } 322 323 JavaBlockOp(JavaBlockOp that, CopyContext cc, OpTransformer ot) { 324 super(that, cc); 325 326 // Copy body 327 this.body = that.body.transform(cc, ot).build(this); 328 } 329 330 @Override 331 public JavaBlockOp transform(CopyContext cc, OpTransformer ot) { 332 return new JavaBlockOp(this, cc, ot); 333 } 334 335 // @@@ Support non-void result type 336 JavaBlockOp(Body.Builder bodyC) { 337 super(NAME, List.of()); 338 339 this.body = bodyC.build(this); 340 if (!body.bodyType().returnType().equals(VOID)) { 341 throw new IllegalArgumentException("Body should return void: " + body.bodyType()); 342 } 343 if (!body.bodyType().parameterTypes().isEmpty()) { 344 throw new IllegalArgumentException("Body should have zero parameters: " + body.bodyType()); 345 } 346 } 347 348 @Override 349 public List<Body> bodies() { 350 return List.of(body); 351 } 352 353 public Body body() { 354 return body; 355 } 356 357 @Override 358 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 359 Block.Builder exit = b.block(); 360 setBranchTarget(b.context(), this, new BranchTarget(exit, null)); 361 362 b.transformBody(body, List.of(), opT.andThen((block, op) -> { 363 if (op instanceof YieldOp) { 364 block.op(branch(exit.successor())); 365 } else { 366 // @@@ Composition of lowerable ops 367 if (op instanceof Lowerable lop) { 368 block = lop.lower(block, opT); 369 } else { 370 block.op(op); 371 } 372 } 373 return block; 374 })); 375 376 return exit; 377 } 378 379 @Override 380 public TypeElement resultType() { 381 return VOID; 382 } 383 } 384 385 /** 386 * The labeled operation, that can model Java language labeled statements. 387 */ 388 @OpFactory.OpDeclaration(JavaLabeledOp.NAME) 389 public static final class JavaLabeledOp extends ExtendedOp 390 implements Op.Nested, Op.Lowerable, JavaStatement { 391 public static final String NAME = "java.labeled"; 392 393 final Body body; 394 395 public JavaLabeledOp(ExternalizedOp def) { 396 super(def); 397 398 if (!def.operands().isEmpty()) { 399 throw new IllegalStateException("Operation must have no operands"); 400 } 401 402 this.body = def.bodyDefinitions().get(0).build(this); 403 } 404 405 JavaLabeledOp(JavaLabeledOp that, CopyContext cc, OpTransformer ot) { 406 super(that, cc); 407 408 // Copy body 409 this.body = that.body.transform(cc, ot).build(this); 410 } 411 412 @Override 413 public JavaLabeledOp transform(CopyContext cc, OpTransformer ot) { 414 return new JavaLabeledOp(this, cc, ot); 415 } 416 417 JavaLabeledOp(Body.Builder bodyC) { 418 super(NAME, List.of()); 419 420 this.body = bodyC.build(this); 421 if (!body.bodyType().returnType().equals(VOID)) { 422 throw new IllegalArgumentException("Body should return void: " + body.bodyType()); 423 } 424 if (!body.bodyType().parameterTypes().isEmpty()) { 425 throw new IllegalArgumentException("Body should have zero parameters: " + body.bodyType()); 426 } 427 } 428 429 @Override 430 public List<Body> bodies() { 431 return List.of(body); 432 } 433 434 public Op label() { 435 return body.entryBlock().firstOp(); 436 } 437 438 public Op target() { 439 return body.entryBlock().nextOp(label()); 440 } 441 442 @Override 443 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 444 Block.Builder exit = b.block(); 445 setBranchTarget(b.context(), this, new BranchTarget(exit, null)); 446 447 AtomicBoolean first = new AtomicBoolean(); 448 b.transformBody(body, List.of(), opT.andThen((block, op) -> { 449 // Drop first operation that corresponds to the label 450 if (!first.get()) { 451 first.set(true); 452 return block; 453 } 454 455 if (op instanceof YieldOp) { 456 block.op(branch(exit.successor())); 457 } else { 458 // @@@ Composition of lowerable ops 459 if (op instanceof Lowerable lop) { 460 block = lop.lower(block, opT); 461 } else { 462 block.op(op); 463 } 464 } 465 return block; 466 })); 467 468 return exit; 469 } 470 471 @Override 472 public TypeElement resultType() { 473 return VOID; 474 } 475 } 476 477 /** 478 * The if operation, that can model Java language if, if-then, and if-then-else statements. 479 */ 480 @OpFactory.OpDeclaration(JavaIfOp.NAME) 481 public static final class JavaIfOp extends ExtendedOp 482 implements Op.Nested, Op.Lowerable, JavaStatement { 483 484 static final FunctionType PREDICATE_TYPE = FunctionType.functionType(BOOLEAN); 485 486 static final FunctionType ACTION_TYPE = FunctionType.VOID; 487 488 public static class IfBuilder { 489 final Body.Builder ancestorBody; 490 final List<Body.Builder> bodies; 491 492 IfBuilder(Body.Builder ancestorBody) { 493 this.ancestorBody = ancestorBody; 494 this.bodies = new ArrayList<>(); 495 } 496 497 public ThenBuilder _if(Consumer<Block.Builder> c) { 498 Body.Builder body = Body.Builder.of(ancestorBody, PREDICATE_TYPE); 499 c.accept(body.entryBlock()); 500 bodies.add(body); 501 502 return new ThenBuilder(ancestorBody, bodies); 503 } 504 } 505 506 public static class ThenBuilder { 507 final Body.Builder ancestorBody; 508 final List<Body.Builder> bodies; 509 510 public ThenBuilder(Body.Builder ancestorBody, List<Body.Builder> bodies) { 511 this.ancestorBody = ancestorBody; 512 this.bodies = bodies; 513 } 514 515 public ElseIfBuilder then(Consumer<Block.Builder> c) { 516 Body.Builder body = Body.Builder.of(ancestorBody, ACTION_TYPE); 517 c.accept(body.entryBlock()); 518 bodies.add(body); 519 520 return new ElseIfBuilder(ancestorBody, bodies); 521 } 522 523 public ElseIfBuilder then() { 524 Body.Builder body = Body.Builder.of(ancestorBody, ACTION_TYPE); 525 body.entryBlock().op(_yield()); 526 bodies.add(body); 527 528 return new ElseIfBuilder(ancestorBody, bodies); 529 } 530 } 531 532 public static class ElseIfBuilder { 533 final Body.Builder ancestorBody; 534 final List<Body.Builder> bodies; 535 536 public ElseIfBuilder(Body.Builder ancestorBody, List<Body.Builder> bodies) { 537 this.ancestorBody = ancestorBody; 538 this.bodies = bodies; 539 } 540 541 public ThenBuilder elseif(Consumer<Block.Builder> c) { 542 Body.Builder body = Body.Builder.of(ancestorBody, PREDICATE_TYPE); 543 c.accept(body.entryBlock()); 544 bodies.add(body); 545 546 return new ThenBuilder(ancestorBody, bodies); 547 } 548 549 public JavaIfOp _else(Consumer<Block.Builder> c) { 550 Body.Builder body = Body.Builder.of(ancestorBody, ACTION_TYPE); 551 c.accept(body.entryBlock()); 552 bodies.add(body); 553 554 return new JavaIfOp(bodies); 555 } 556 557 public JavaIfOp _else() { 558 Body.Builder body = Body.Builder.of(ancestorBody, ACTION_TYPE); 559 body.entryBlock().op(_yield()); 560 bodies.add(body); 561 562 return new JavaIfOp(bodies); 563 } 564 } 565 566 public static final String NAME = "java.if"; 567 568 final List<Body> bodies; 569 570 public JavaIfOp(ExternalizedOp def) { 571 super(def); 572 573 if (!def.operands().isEmpty()) { 574 throw new IllegalStateException("Operation must have no operands"); 575 } 576 577 // @@@ Validate 578 579 this.bodies = def.bodyDefinitions().stream().map(bd -> bd.build(this)).toList(); 580 } 581 582 JavaIfOp(JavaIfOp that, CopyContext cc, OpTransformer ot) { 583 super(that, cc); 584 585 // Copy body 586 this.bodies = that.bodies.stream() 587 .map(b -> b.transform(cc, ot).build(this)).toList(); 588 } 589 590 @Override 591 public JavaIfOp transform(CopyContext cc, OpTransformer ot) { 592 return new JavaIfOp(this, cc, ot); 593 } 594 595 JavaIfOp(List<Body.Builder> bodyCs) { 596 super(NAME, List.of()); 597 598 // Normalize by adding an empty else action 599 // @@@ Is this needed? 600 if (bodyCs.size() % 2 == 0) { 601 bodyCs = new ArrayList<>(bodyCs); 602 Body.Builder end = Body.Builder.of(bodyCs.get(0).ancestorBody(), 603 FunctionType.VOID); 604 end.entryBlock().op(_yield()); 605 bodyCs.add(end); 606 } 607 608 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList(); 609 610 if (bodies.size() < 2) { 611 throw new IllegalArgumentException("Incorrect number of bodies: " + bodies.size()); 612 } 613 for (int i = 0; i < bodies.size(); i += 2) { 614 Body action; 615 if (i == bodies.size() - 1) { 616 action = bodies.get(i); 617 } else { 618 action = bodies.get(i + 1); 619 Body fromPred = bodies.get(i); 620 if (!fromPred.bodyType().equals(FunctionType.functionType(BOOLEAN))) { 621 throw new IllegalArgumentException("Illegal predicate body descriptor: " + fromPred.bodyType()); 622 } 623 } 624 if (!action.bodyType().equals(FunctionType.VOID)) { 625 throw new IllegalArgumentException("Illegal action body descriptor: " + action.bodyType()); 626 } 627 } 628 } 629 630 @Override 631 public List<Body> bodies() { 632 return bodies; 633 } 634 635 @Override 636 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 637 Block.Builder exit = b.block(); 638 setBranchTarget(b.context(), this, new BranchTarget(exit, null)); 639 640 // Create predicate and action blocks 641 List<Block.Builder> builders = new ArrayList<>(); 642 for (int i = 0; i < bodies.size(); i += 2) { 643 if (i == bodies.size() - 1) { 644 builders.add(b.block()); 645 } else { 646 builders.add(i == 0 ? b : b.block()); 647 builders.add(b.block()); 648 } 649 } 650 651 for (int i = 0; i < bodies.size(); i += 2) { 652 Body actionBody; 653 Block.Builder action; 654 if (i == bodies.size() - 1) { 655 actionBody = bodies.get(i); 656 action = builders.get(i); 657 } else { 658 Body predBody = bodies.get(i); 659 actionBody = bodies.get(i + 1); 660 661 Block.Builder pred = builders.get(i); 662 action = builders.get(i + 1); 663 Block.Builder next = builders.get(i + 2); 664 665 pred.transformBody(predBody, List.of(), opT.andThen((block, op) -> { 666 if (op instanceof YieldOp yo) { 667 block.op(conditionalBranch(block.context().getValue(yo.yieldValue()), 668 action.successor(), next.successor())); 669 } else if (op instanceof Lowerable lop) { 670 // @@@ Composition of lowerable ops 671 block = lop.lower(block, opT); 672 } else { 673 block.op(op); 674 } 675 return block; 676 })); 677 } 678 679 action.transformBody(actionBody, List.of(), opT.andThen((block, op) -> { 680 if (op instanceof YieldOp) { 681 block.op(branch(exit.successor())); 682 } else { 683 // @@@ Composition of lowerable ops 684 if (op instanceof Lowerable lop) { 685 block = lop.lower(block, opT); 686 } else { 687 block.op(op); 688 } 689 } 690 return block; 691 })); 692 } 693 694 return exit; 695 } 696 697 @Override 698 public TypeElement resultType() { 699 return VOID; 700 } 701 } 702 703 /** 704 * The switch expression operation, that can model Java language switch expressions. 705 */ 706 @OpFactory.OpDeclaration(JavaSwitchExpressionOp.NAME) 707 public static final class JavaSwitchExpressionOp extends ExtendedOp 708 implements Op.Nested, Op.Lowerable, JavaExpression { 709 public static final String NAME = "java.switch.expression"; 710 711 final TypeElement resultType; 712 final List<Body> bodies; 713 714 public JavaSwitchExpressionOp(ExternalizedOp def) { 715 super(def); 716 717 if (def.operands().size() != 1) { 718 throw new IllegalStateException("Operation must have one operand"); 719 } 720 721 // @@@ Validate 722 723 this.bodies = def.bodyDefinitions().stream().map(bd -> bd.build(this)).toList(); 724 this.resultType = def.resultType(); 725 } 726 727 JavaSwitchExpressionOp(JavaSwitchExpressionOp that, CopyContext cc, OpTransformer ot) { 728 super(that, cc); 729 730 // Copy body 731 this.bodies = that.bodies.stream() 732 .map(b -> b.transform(cc, ot).build(this)).toList(); 733 this.resultType = that.resultType; 734 } 735 736 @Override 737 public JavaSwitchExpressionOp transform(CopyContext cc, OpTransformer ot) { 738 return new JavaSwitchExpressionOp(this, cc, ot); 739 } 740 741 JavaSwitchExpressionOp(TypeElement resultType, Value target, List<Body.Builder> bodyCs) { 742 super(NAME, List.of(target)); 743 744 // Each case is modelled as a contiguous pair of bodies 745 // The first body models the case labels, and the second models the case expression or statements 746 // The labels body has a parameter whose type is target operand's type and returns a boolean value 747 // The statements/expression body has no parameters and returns the result whose type is the result of 748 // the switch expression 749 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList(); 750 // @@@ when resultType is null, we assume statements/expressions bodies have the same yieldType 751 this.resultType = resultType == null ? bodies.get(1).yieldType() : resultType; 752 } 753 754 @Override 755 public List<Body> bodies() { 756 return bodies; 757 } 758 759 @Override 760 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 761 throw new UnsupportedOperationException(); 762 } 763 764 @Override 765 public TypeElement resultType() { 766 return resultType; 767 } 768 } 769 770 /** 771 * The switch fall-through operation, that can model fall-through to the next statement in the switch block after 772 * the last statement of the current switch label. 773 */ 774 @OpFactory.OpDeclaration(JavaSwitchFallthroughOp.NAME) 775 public static final class JavaSwitchFallthroughOp extends ExtendedOp 776 implements Op.BodyTerminating { 777 public static final String NAME = "java.switch.fallthrough"; 778 779 public JavaSwitchFallthroughOp(ExternalizedOp def) { 780 super(def); 781 } 782 783 JavaSwitchFallthroughOp(JavaSwitchFallthroughOp that, CopyContext cc) { 784 super(that, cc); 785 } 786 787 @Override 788 public JavaSwitchFallthroughOp transform(CopyContext cc, OpTransformer ot) { 789 return new JavaSwitchFallthroughOp(this, cc); 790 } 791 792 JavaSwitchFallthroughOp() { 793 super(NAME, List.of()); 794 } 795 796 @Override 797 public TypeElement resultType() { 798 return VOID; 799 } 800 } 801 802 /** 803 * The for operation, that can model a Java language for statement. 804 */ 805 @OpFactory.OpDeclaration(JavaForOp.NAME) 806 public static final class JavaForOp extends ExtendedOp 807 implements Op.Loop, Op.Lowerable, JavaStatement { 808 809 public static final class InitBuilder { 810 final Body.Builder ancestorBody; 811 final List<? extends TypeElement> initTypes; 812 813 InitBuilder(Body.Builder ancestorBody, 814 List<? extends TypeElement> initTypes) { 815 this.ancestorBody = ancestorBody; 816 this.initTypes = initTypes.stream().map(VarType::varType).toList(); 817 } 818 819 public JavaForOp.CondBuilder init(Consumer<Block.Builder> c) { 820 Body.Builder init = Body.Builder.of(ancestorBody, 821 FunctionType.functionType(TupleType.tupleType(initTypes))); 822 c.accept(init.entryBlock()); 823 824 return new CondBuilder(ancestorBody, initTypes, init); 825 } 826 } 827 828 public static final class CondBuilder { 829 final Body.Builder ancestorBody; 830 final List<? extends TypeElement> initTypes; 831 final Body.Builder init; 832 833 public CondBuilder(Body.Builder ancestorBody, 834 List<? extends TypeElement> initTypes, 835 Body.Builder init) { 836 this.ancestorBody = ancestorBody; 837 this.initTypes = initTypes; 838 this.init = init; 839 } 840 841 public JavaForOp.UpdateBuilder cond(Consumer<Block.Builder> c) { 842 Body.Builder cond = Body.Builder.of(ancestorBody, 843 FunctionType.functionType(BOOLEAN, initTypes)); 844 c.accept(cond.entryBlock()); 845 846 return new UpdateBuilder(ancestorBody, initTypes, init, cond); 847 } 848 } 849 850 public static final class UpdateBuilder { 851 final Body.Builder ancestorBody; 852 final List<? extends TypeElement> initTypes; 853 final Body.Builder init; 854 final Body.Builder cond; 855 856 public UpdateBuilder(Body.Builder ancestorBody, 857 List<? extends TypeElement> initTypes, 858 Body.Builder init, Body.Builder cond) { 859 this.ancestorBody = ancestorBody; 860 this.initTypes = initTypes; 861 this.init = init; 862 this.cond = cond; 863 } 864 865 public JavaForOp.BodyBuilder cond(Consumer<Block.Builder> c) { 866 Body.Builder update = Body.Builder.of(ancestorBody, 867 FunctionType.functionType(VOID, initTypes)); 868 c.accept(update.entryBlock()); 869 870 return new BodyBuilder(ancestorBody, initTypes, init, cond, update); 871 } 872 873 } 874 875 public static final class BodyBuilder { 876 final Body.Builder ancestorBody; 877 final List<? extends TypeElement> initTypes; 878 final Body.Builder init; 879 final Body.Builder cond; 880 final Body.Builder update; 881 882 public BodyBuilder(Body.Builder ancestorBody, 883 List<? extends TypeElement> initTypes, 884 Body.Builder init, Body.Builder cond, Body.Builder update) { 885 this.ancestorBody = ancestorBody; 886 this.initTypes = initTypes; 887 this.init = init; 888 this.cond = cond; 889 this.update = update; 890 } 891 892 public JavaForOp body(Consumer<Block.Builder> c) { 893 Body.Builder body = Body.Builder.of(ancestorBody, 894 FunctionType.functionType(VOID, initTypes)); 895 c.accept(body.entryBlock()); 896 897 return new JavaForOp(init, cond, update, body); 898 } 899 } 900 901 static final String NAME = "java.for"; 902 903 final Body init; 904 final Body cond; 905 final Body update; 906 final Body body; 907 908 public static JavaForOp create(ExternalizedOp def) { 909 return new JavaForOp(def); 910 } 911 912 public JavaForOp(ExternalizedOp def) { 913 super(def); 914 915 this.init = def.bodyDefinitions().get(0).build(this); 916 this.cond = def.bodyDefinitions().get(1).build(this); 917 this.update = def.bodyDefinitions().get(2).build(this); 918 this.body = def.bodyDefinitions().get(3).build(this); 919 } 920 921 JavaForOp(JavaForOp that, CopyContext cc, OpTransformer ot) { 922 super(that, cc); 923 924 this.init = that.init.transform(cc, ot).build(this); 925 this.cond = that.cond.transform(cc, ot).build(this); 926 this.update = that.update.transform(cc, ot).build(this); 927 this.body = that.body.transform(cc, ot).build(this); 928 } 929 930 @Override 931 public JavaForOp transform(CopyContext cc, OpTransformer ot) { 932 return new JavaForOp(this, cc, ot); 933 } 934 935 JavaForOp(Body.Builder initC, 936 Body.Builder condC, 937 Body.Builder updateC, 938 Body.Builder bodyC) { 939 super(NAME, List.of()); 940 941 this.init = initC.build(this); 942 943 this.cond = condC.build(this); 944 945 this.update = updateC.build(this); 946 if (!update.bodyType().returnType().equals(VOID)) { 947 throw new IllegalArgumentException("Update should return void: " + update.bodyType()); 948 } 949 950 this.body = bodyC.build(this); 951 if (!body.bodyType().returnType().equals(VOID)) { 952 throw new IllegalArgumentException("Body should return void: " + body.bodyType()); 953 } 954 } 955 956 @Override 957 public List<Body> bodies() { 958 return List.of(init, cond, update, body); 959 } 960 961 public Body init() { 962 return init; 963 } 964 965 public Body cond() { 966 return cond; 967 } 968 969 public Body update() { 970 return update; 971 } 972 973 @Override 974 public Body loopBody() { 975 return body; 976 } 977 978 @Override 979 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 980 Block.Builder header = b.block(); 981 Block.Builder body = b.block(); 982 Block.Builder update = b.block(); 983 Block.Builder exit = b.block(); 984 985 List<Value> initValues = new ArrayList<>(); 986 // @@@ Init body has one yield operation yielding 987 // void, a single variable, or a tuple of one or more variables 988 b.transformBody(init, List.of(), opT.andThen((block, op) -> { 989 if (op instanceof CoreOp.TupleOp) { 990 // Drop Tuple if a yielded 991 boolean isResult = op.result().uses().size() == 1 && 992 op.result().uses().stream().allMatch(r -> r.op() instanceof YieldOp); 993 if (!isResult) { 994 block.op(op); 995 } 996 } else if (op instanceof YieldOp yop) { 997 if (yop.yieldValue() == null) { 998 block.op(branch(header.successor())); 999 return block; 1000 } else if (yop.yieldValue() instanceof Result or) { 1001 if (or.op() instanceof CoreOp.TupleOp top) { 1002 initValues.addAll(block.context().getValues(top.operands())); 1003 } else { 1004 initValues.addAll(block.context().getValues(yop.operands())); 1005 } 1006 block.op(branch(header.successor())); 1007 return block; 1008 } 1009 1010 throw new IllegalStateException("Bad yield operation"); 1011 } else { 1012 // @@@ Composition of lowerable ops 1013 block.op(op); 1014 } 1015 return block; 1016 })); 1017 1018 header.transformBody(cond, initValues, opT.andThen((block, op) -> { 1019 if (op instanceof YieldOp yo) { 1020 block.op(conditionalBranch(block.context().getValue(yo.yieldValue()), 1021 body.successor(), exit.successor())); 1022 } else if (op instanceof Lowerable lop) { 1023 // @@@ Composition of lowerable ops 1024 block = lop.lower(block, opT); 1025 } else { 1026 block.op(op); 1027 } 1028 return block; 1029 })); 1030 1031 setBranchTarget(b.context(), this, new BranchTarget(exit, update)); 1032 1033 body.transformBody(this.body, initValues, opT.andThen((block, op) -> { 1034 // @@@ Composition of lowerable ops 1035 if (op instanceof Lowerable lop) { 1036 block = lop.lower(block, opT); 1037 } else { 1038 block.op(op); 1039 } 1040 return block; 1041 })); 1042 1043 update.transformBody(this.update, initValues, opT.andThen((block, op) -> { 1044 if (op instanceof YieldOp) { 1045 block.op(branch(header.successor())); 1046 } else { 1047 // @@@ Composition of lowerable ops 1048 block.op(op); 1049 } 1050 return block; 1051 })); 1052 1053 return exit; 1054 } 1055 1056 @Override 1057 public TypeElement resultType() { 1058 return VOID; 1059 } 1060 } 1061 1062 /** 1063 * The enhanced for operation, that can model a Java language enhanced for statement. 1064 */ 1065 @OpFactory.OpDeclaration(JavaEnhancedForOp.NAME) 1066 public static final class JavaEnhancedForOp extends ExtendedOp 1067 implements Op.Loop, Op.Lowerable, JavaStatement { 1068 1069 public static final class ExpressionBuilder { 1070 final Body.Builder ancestorBody; 1071 final TypeElement iterableType; 1072 final TypeElement elementType; 1073 1074 ExpressionBuilder(Body.Builder ancestorBody, 1075 TypeElement iterableType, TypeElement elementType) { 1076 this.ancestorBody = ancestorBody; 1077 this.iterableType = iterableType; 1078 this.elementType = elementType; 1079 } 1080 1081 public DefinitionBuilder expression(Consumer<Block.Builder> c) { 1082 Body.Builder expression = Body.Builder.of(ancestorBody, 1083 FunctionType.functionType(iterableType)); 1084 c.accept(expression.entryBlock()); 1085 1086 return new DefinitionBuilder(ancestorBody, elementType, expression); 1087 } 1088 } 1089 1090 public static final class DefinitionBuilder { 1091 final Body.Builder ancestorBody; 1092 final TypeElement elementType; 1093 final Body.Builder expression; 1094 1095 DefinitionBuilder(Body.Builder ancestorBody, 1096 TypeElement elementType, Body.Builder expression) { 1097 this.ancestorBody = ancestorBody; 1098 this.elementType = elementType; 1099 this.expression = expression; 1100 } 1101 1102 public BodyBuilder definition(Consumer<Block.Builder> c) { 1103 return definition(elementType, c); 1104 } 1105 1106 public BodyBuilder definition(TypeElement bodyElementType, Consumer<Block.Builder> c) { 1107 Body.Builder definition = Body.Builder.of(ancestorBody, 1108 FunctionType.functionType(bodyElementType, elementType)); 1109 c.accept(definition.entryBlock()); 1110 1111 return new BodyBuilder(ancestorBody, elementType, expression, definition); 1112 } 1113 } 1114 1115 public static final class BodyBuilder { 1116 final Body.Builder ancestorBody; 1117 final TypeElement elementType; 1118 final Body.Builder expression; 1119 final Body.Builder definition; 1120 1121 BodyBuilder(Body.Builder ancestorBody, 1122 TypeElement elementType, Body.Builder expression, Body.Builder definition) { 1123 this.ancestorBody = ancestorBody; 1124 this.elementType = elementType; 1125 this.expression = expression; 1126 this.definition = definition; 1127 } 1128 1129 public JavaEnhancedForOp body(Consumer<Block.Builder> c) { 1130 Body.Builder body = Body.Builder.of(ancestorBody, 1131 FunctionType.functionType(VOID, elementType)); 1132 c.accept(body.entryBlock()); 1133 1134 return new JavaEnhancedForOp(expression, definition, body); 1135 } 1136 } 1137 1138 static final String NAME = "java.enhancedFor"; 1139 1140 final Body expression; 1141 final Body init; 1142 final Body body; 1143 1144 public static JavaEnhancedForOp create(ExternalizedOp def) { 1145 return new JavaEnhancedForOp(def); 1146 } 1147 1148 public JavaEnhancedForOp(ExternalizedOp def) { 1149 super(def); 1150 1151 this.expression = def.bodyDefinitions().get(0).build(this); 1152 this.init = def.bodyDefinitions().get(1).build(this); 1153 this.body = def.bodyDefinitions().get(2).build(this); 1154 } 1155 1156 JavaEnhancedForOp(JavaEnhancedForOp that, CopyContext cc, OpTransformer ot) { 1157 super(that, cc); 1158 1159 this.expression = that.expression.transform(cc, ot).build(this); 1160 this.init = that.init.transform(cc, ot).build(this); 1161 this.body = that.body.transform(cc, ot).build(this); 1162 } 1163 1164 @Override 1165 public JavaEnhancedForOp transform(CopyContext cc, OpTransformer ot) { 1166 return new JavaEnhancedForOp(this, cc, ot); 1167 } 1168 1169 JavaEnhancedForOp(Body.Builder expressionC, Body.Builder initC, Body.Builder bodyC) { 1170 super(NAME, List.of()); 1171 1172 this.expression = expressionC.build(this); 1173 if (expression.bodyType().returnType().equals(VOID)) { 1174 throw new IllegalArgumentException("Expression should return non-void value: " + expression.bodyType()); 1175 } 1176 if (!expression.bodyType().parameterTypes().isEmpty()) { 1177 throw new IllegalArgumentException("Expression should have zero parameters: " + expression.bodyType()); 1178 } 1179 1180 this.init = initC.build(this); 1181 if (init.bodyType().returnType().equals(VOID)) { 1182 throw new IllegalArgumentException("Initialization should return non-void value: " + init.bodyType()); 1183 } 1184 if (init.bodyType().parameterTypes().size() != 1) { 1185 throw new IllegalArgumentException("Initialization should have one parameter: " + init.bodyType()); 1186 } 1187 1188 this.body = bodyC.build(this); 1189 if (!body.bodyType().returnType().equals(VOID)) { 1190 throw new IllegalArgumentException("Body should return void: " + body.bodyType()); 1191 } 1192 if (body.bodyType().parameterTypes().size() != 1) { 1193 throw new IllegalArgumentException("Body should have one parameter: " + body.bodyType()); 1194 } 1195 } 1196 1197 @Override 1198 public List<Body> bodies() { 1199 return List.of(expression, init, body); 1200 } 1201 1202 public Body expression() { 1203 return expression; 1204 } 1205 1206 public Body initialization() { 1207 return init; 1208 } 1209 1210 @Override 1211 public Body loopBody() { 1212 return body; 1213 } 1214 1215 static final MethodRef ITERABLE_ITERATOR = MethodRef.method(Iterable.class, "iterator", Iterator.class); 1216 static final MethodRef ITERATOR_HAS_NEXT = MethodRef.method(Iterator.class, "hasNext", boolean.class); 1217 static final MethodRef ITERATOR_NEXT = MethodRef.method(Iterator.class, "next", Object.class); 1218 1219 @Override 1220 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 1221 JavaType elementType = (JavaType) init.entryBlock().parameters().get(0).type(); 1222 boolean isArray = expression.bodyType().returnType() instanceof ArrayType; 1223 1224 Block.Builder preHeader = b.block(expression.bodyType().returnType()); 1225 Block.Builder header = b.block(isArray ? List.of(INT) : List.of()); 1226 Block.Builder init = b.block(); 1227 Block.Builder body = b.block(); 1228 Block.Builder exit = b.block(); 1229 1230 b.transformBody(expression, List.of(), opT.andThen((block, op) -> { 1231 if (op instanceof YieldOp yop) { 1232 Value loopSource = block.context().getValue(yop.yieldValue()); 1233 block.op(branch(preHeader.successor(loopSource))); 1234 } else { 1235 // @@@ Composition of lowerable ops 1236 block.op(op); 1237 } 1238 return block; 1239 })); 1240 1241 if (isArray) { 1242 Value array = preHeader.parameters().get(0); 1243 Value arrayLength = preHeader.op(arrayLength(array)); 1244 Value i = preHeader.op(constant(INT, 0)); 1245 preHeader.op(branch(header.successor(i))); 1246 1247 i = header.parameters().get(0); 1248 Value p = header.op(lt(i, arrayLength)); 1249 header.op(conditionalBranch(p, init.successor(), exit.successor())); 1250 1251 Value e = init.op(arrayLoadOp(array, i)); 1252 List<Value> initValues = new ArrayList<>(); 1253 // @@@ Init body has one yield operation yielding a single variable 1254 init.transformBody(this.init, List.of(e), (block, op) -> { 1255 if (op instanceof YieldOp yop) { 1256 initValues.addAll(block.context().getValues(yop.operands())); 1257 block.op(branch(body.successor())); 1258 } else { 1259 // @@@ Composition of lowerable ops 1260 block.op(op); 1261 } 1262 return block; 1263 }); 1264 1265 Block.Builder update = b.block(); 1266 setBranchTarget(b.context(), this, new BranchTarget(exit, update)); 1267 1268 body.transformBody(this.body, initValues, opT.andThen((block, op) -> { 1269 // @@@ Composition of lowerable ops 1270 if (op instanceof Lowerable lop) { 1271 block = lop.lower(block, opT); 1272 } else { 1273 block.op(op); 1274 } 1275 return block; 1276 })); 1277 1278 i = update.op(add(i, update.op(constant(INT, 1)))); 1279 update.op(branch(header.successor(i))); 1280 } else { 1281 JavaType iterable = parameterized(type(Iterator.class), elementType); 1282 Value iterator = preHeader.op(CoreOp.invoke(iterable, ITERABLE_ITERATOR, preHeader.parameters().get(0))); 1283 preHeader.op(branch(header.successor())); 1284 1285 Value p = header.op(CoreOp.invoke(ITERATOR_HAS_NEXT, iterator)); 1286 header.op(conditionalBranch(p, init.successor(), exit.successor())); 1287 1288 Value e = init.op(CoreOp.invoke(elementType, ITERATOR_NEXT, iterator)); 1289 List<Value> initValues = new ArrayList<>(); 1290 init.transformBody(this.init, List.of(e), opT.andThen((block, op) -> { 1291 if (op instanceof YieldOp yop) { 1292 initValues.addAll(block.context().getValues(yop.operands())); 1293 block.op(branch(body.successor())); 1294 } else { 1295 // @@@ Composition of lowerable ops 1296 block.op(op); 1297 } 1298 return block; 1299 })); 1300 1301 setBranchTarget(b.context(), this, new BranchTarget(exit, header)); 1302 1303 body.transformBody(this.body, initValues, opT.andThen((block, op) -> { 1304 // @@@ Composition of lowerable ops 1305 if (op instanceof Lowerable lop) { 1306 block = lop.lower(block, opT); 1307 } else { 1308 block.op(op); 1309 } 1310 return block; 1311 })); 1312 } 1313 1314 return exit; 1315 } 1316 1317 @Override 1318 public TypeElement resultType() { 1319 return VOID; 1320 } 1321 } 1322 1323 /** 1324 * The while operation, that can model a Java language while statement. 1325 */ 1326 @OpFactory.OpDeclaration(JavaWhileOp.NAME) 1327 public static final class JavaWhileOp extends ExtendedOp 1328 implements Op.Loop, Op.Lowerable, JavaStatement { 1329 1330 public static class PredicateBuilder { 1331 final Body.Builder ancestorBody; 1332 1333 PredicateBuilder(Body.Builder ancestorBody) { 1334 this.ancestorBody = ancestorBody; 1335 } 1336 1337 public JavaWhileOp.BodyBuilder predicate(Consumer<Block.Builder> c) { 1338 Body.Builder body = Body.Builder.of(ancestorBody, FunctionType.functionType(BOOLEAN)); 1339 c.accept(body.entryBlock()); 1340 1341 return new JavaWhileOp.BodyBuilder(ancestorBody, body); 1342 } 1343 } 1344 1345 public static class BodyBuilder { 1346 final Body.Builder ancestorBody; 1347 private final Body.Builder predicate; 1348 1349 BodyBuilder(Body.Builder ancestorBody, Body.Builder predicate) { 1350 this.ancestorBody = ancestorBody; 1351 this.predicate = predicate; 1352 } 1353 1354 public JavaWhileOp body(Consumer<Block.Builder> c) { 1355 Body.Builder body = Body.Builder.of(ancestorBody, FunctionType.VOID); 1356 c.accept(body.entryBlock()); 1357 1358 return new JavaWhileOp(List.of(predicate, body)); 1359 } 1360 } 1361 1362 private static final String NAME = "java.while"; 1363 1364 private final List<Body> bodies; 1365 1366 public JavaWhileOp(ExternalizedOp def) { 1367 super(def); 1368 1369 // @@@ Validate 1370 this.bodies = def.bodyDefinitions().stream().map(bd -> bd.build(this)).toList(); 1371 } 1372 1373 JavaWhileOp(List<Body.Builder> bodyCs) { 1374 super(NAME, List.of()); 1375 1376 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList(); 1377 } 1378 1379 JavaWhileOp(Body.Builder predicate, Body.Builder body) { 1380 super(NAME, List.of()); 1381 1382 Objects.requireNonNull(body); 1383 1384 this.bodies = Stream.of(predicate, body).filter(Objects::nonNull) 1385 .map(bc -> bc.build(this)).toList(); 1386 1387 // @@@ This will change with pattern bindings 1388 if (!bodies.get(0).bodyType().equals(FunctionType.functionType(BOOLEAN))) { 1389 throw new IllegalArgumentException( 1390 "Predicate body descriptor should be " + FunctionType.functionType(BOOLEAN) + 1391 " but is " + bodies.get(0).bodyType()); 1392 } 1393 if (!bodies.get(1).bodyType().equals(FunctionType.VOID)) { 1394 throw new IllegalArgumentException( 1395 "Body descriptor should be " + FunctionType.functionType(VOID) + 1396 " but is " + bodies.get(1).bodyType()); 1397 } 1398 } 1399 1400 JavaWhileOp(JavaWhileOp that, CopyContext cc, OpTransformer ot) { 1401 super(that, cc); 1402 1403 this.bodies = that.bodies.stream() 1404 .map(b -> b.transform(cc, ot).build(this)).toList(); 1405 } 1406 1407 @Override 1408 public JavaWhileOp transform(CopyContext cc, OpTransformer ot) { 1409 return new JavaWhileOp(this, cc, ot); 1410 } 1411 1412 @Override 1413 public List<Body> bodies() { 1414 return bodies; 1415 } 1416 1417 public Body predicateBody() { 1418 return bodies.get(0); 1419 } 1420 1421 @Override 1422 public Body loopBody() { 1423 return bodies.get(1); 1424 } 1425 1426 @Override 1427 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 1428 Block.Builder header = b.block(); 1429 Block.Builder body = b.block(); 1430 Block.Builder exit = b.block(); 1431 1432 b.op(branch(header.successor())); 1433 1434 header.transformBody(predicateBody(), List.of(), opT.andThen((block, op) -> { 1435 if (op instanceof CoreOp.YieldOp yo) { 1436 block.op(conditionalBranch(block.context().getValue(yo.yieldValue()), 1437 body.successor(), exit.successor())); 1438 } else if (op instanceof Lowerable lop) { 1439 // @@@ Composition of lowerable ops 1440 block = lop.lower(block, opT); 1441 } else { 1442 block.op(op); 1443 } 1444 return block; 1445 })); 1446 1447 setBranchTarget(b.context(), this, new BranchTarget(exit, header)); 1448 1449 body.transformBody(loopBody(), List.of(), opT.andThen((block, op) -> { 1450 // @@@ Composition of lowerable ops 1451 if (op instanceof Lowerable lop) { 1452 block = lop.lower(block, opT); 1453 } else { 1454 block.op(op); 1455 } 1456 return block; 1457 })); 1458 1459 return exit; 1460 } 1461 1462 @Override 1463 public TypeElement resultType() { 1464 return VOID; 1465 } 1466 } 1467 1468 /** 1469 * The do-while operation, that can model a Java language do statement. 1470 */ 1471 // @@@ Unify JavaDoWhileOp and JavaWhileOp with common abstract superclass 1472 @OpFactory.OpDeclaration(JavaDoWhileOp.NAME) 1473 public static final class JavaDoWhileOp extends ExtendedOp 1474 implements Op.Loop, Op.Lowerable, JavaStatement { 1475 1476 public static class PredicateBuilder { 1477 final Body.Builder ancestorBody; 1478 private final Body.Builder body; 1479 1480 PredicateBuilder(Body.Builder ancestorBody, Body.Builder body) { 1481 this.ancestorBody = ancestorBody; 1482 this.body = body; 1483 } 1484 1485 public JavaDoWhileOp predicate(Consumer<Block.Builder> c) { 1486 Body.Builder predicate = Body.Builder.of(ancestorBody, FunctionType.functionType(BOOLEAN)); 1487 c.accept(predicate.entryBlock()); 1488 1489 return new JavaDoWhileOp(List.of(body, predicate)); 1490 } 1491 } 1492 1493 public static class BodyBuilder { 1494 final Body.Builder ancestorBody; 1495 1496 BodyBuilder(Body.Builder ancestorBody) { 1497 this.ancestorBody = ancestorBody; 1498 } 1499 1500 public JavaDoWhileOp.PredicateBuilder body(Consumer<Block.Builder> c) { 1501 Body.Builder body = Body.Builder.of(ancestorBody, FunctionType.VOID); 1502 c.accept(body.entryBlock()); 1503 1504 return new JavaDoWhileOp.PredicateBuilder(ancestorBody, body); 1505 } 1506 } 1507 1508 private static final String NAME = "java.do.while"; 1509 1510 private final List<Body> bodies; 1511 1512 public JavaDoWhileOp(ExternalizedOp def) { 1513 super(def); 1514 1515 // @@@ Validate 1516 this.bodies = def.bodyDefinitions().stream().map(bd -> bd.build(this)).toList(); 1517 } 1518 1519 JavaDoWhileOp(List<Body.Builder> bodyCs) { 1520 super(NAME, List.of()); 1521 1522 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList(); 1523 } 1524 1525 JavaDoWhileOp(Body.Builder body, Body.Builder predicate) { 1526 super(NAME, List.of()); 1527 1528 Objects.requireNonNull(body); 1529 1530 this.bodies = Stream.of(body, predicate).filter(Objects::nonNull) 1531 .map(bc -> bc.build(this)).toList(); 1532 1533 if (!bodies.get(0).bodyType().equals(FunctionType.VOID)) { 1534 throw new IllegalArgumentException( 1535 "Body descriptor should be " + FunctionType.functionType(VOID) + 1536 " but is " + bodies.get(1).bodyType()); 1537 } 1538 if (!bodies.get(1).bodyType().equals(FunctionType.functionType(BOOLEAN))) { 1539 throw new IllegalArgumentException( 1540 "Predicate body descriptor should be " + FunctionType.functionType(BOOLEAN) + 1541 " but is " + bodies.get(0).bodyType()); 1542 } 1543 } 1544 1545 JavaDoWhileOp(JavaDoWhileOp that, CopyContext cc, OpTransformer ot) { 1546 super(that, cc); 1547 1548 this.bodies = that.bodies.stream() 1549 .map(b -> b.transform(cc, ot).build(this)).toList(); 1550 } 1551 1552 @Override 1553 public JavaDoWhileOp transform(CopyContext cc, OpTransformer ot) { 1554 return new JavaDoWhileOp(this, cc, ot); 1555 } 1556 1557 @Override 1558 public List<Body> bodies() { 1559 return bodies; 1560 } 1561 1562 public Body predicateBody() { 1563 return bodies.get(1); 1564 } 1565 1566 @Override 1567 public Body loopBody() { 1568 return bodies.get(0); 1569 } 1570 1571 @Override 1572 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 1573 Block.Builder body = b.block(); 1574 Block.Builder header = b.block(); 1575 Block.Builder exit = b.block(); 1576 1577 b.op(branch(body.successor())); 1578 1579 setBranchTarget(b.context(), this, new BranchTarget(exit, header)); 1580 1581 body.transformBody(loopBody(), List.of(), opT.andThen((block, op) -> { 1582 // @@@ Composition of lowerable ops 1583 if (op instanceof Lowerable lop) { 1584 block = lop.lower(block, opT); 1585 } else { 1586 block.op(op); 1587 } 1588 return block; 1589 })); 1590 1591 header.transformBody(predicateBody(), List.of(), opT.andThen((block, op) -> { 1592 if (op instanceof CoreOp.YieldOp yo) { 1593 block.op(conditionalBranch(block.context().getValue(yo.yieldValue()), 1594 body.successor(), exit.successor())); 1595 } else if (op instanceof Lowerable lop) { 1596 // @@@ Composition of lowerable ops 1597 block = lop.lower(block, opT); 1598 } else { 1599 block.op(op); 1600 } 1601 return block; 1602 })); 1603 1604 return exit; 1605 } 1606 1607 @Override 1608 public TypeElement resultType() { 1609 return VOID; 1610 } 1611 } 1612 1613 /** 1614 * The conditional-and-or operation, that can model Java language condition-or or conditional-and expressions. 1615 */ 1616 public sealed static abstract class JavaConditionalOp extends ExtendedOp 1617 implements Op.Nested, Op.Lowerable, JavaExpression { 1618 final List<Body> bodies; 1619 1620 public JavaConditionalOp(ExternalizedOp def) { 1621 super(def); 1622 1623 if (!def.operands().isEmpty()) { 1624 throw new IllegalStateException("Operation must have no operands"); 1625 } 1626 1627 // @@@ Validate 1628 1629 this.bodies = def.bodyDefinitions().stream().map(bd -> bd.build(this)).toList(); 1630 } 1631 1632 JavaConditionalOp(JavaConditionalOp that, CopyContext cc, OpTransformer ot) { 1633 super(that, cc); 1634 1635 // Copy body 1636 this.bodies = that.bodies.stream().map(b -> b.transform(cc, ot).build(this)).toList(); 1637 } 1638 1639 JavaConditionalOp(String name, List<Body.Builder> bodyCs) { 1640 super(name, List.of()); 1641 1642 if (bodyCs.isEmpty()) { 1643 throw new IllegalArgumentException(); 1644 } 1645 1646 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList(); 1647 for (Body b : bodies) { 1648 if (!b.bodyType().equals(FunctionType.functionType(BOOLEAN))) { 1649 throw new IllegalArgumentException("Body conditional body descriptor: " + b.bodyType()); 1650 } 1651 } 1652 } 1653 1654 @Override 1655 public List<Body> bodies() { 1656 return bodies; 1657 } 1658 1659 static Block.Builder lower(Block.Builder startBlock, OpTransformer opT, JavaConditionalOp cop) { 1660 List<Body> bodies = cop.bodies(); 1661 1662 Block.Builder exit = startBlock.block(); 1663 TypeElement oprType = cop.result().type(); 1664 Block.Parameter arg = exit.parameter(oprType); 1665 startBlock.context().mapValue(cop.result(), arg); 1666 1667 // Transform bodies in reverse order 1668 // This makes available the blocks to be referenced as successors in prior blocks 1669 1670 Block.Builder pred = null; 1671 for (int i = bodies.size() - 1; i >= 0; i--) { 1672 OpTransformer opt; 1673 if (i == bodies.size() - 1) { 1674 opt = (block, op) -> { 1675 if (op instanceof CoreOp.YieldOp yop) { 1676 Value p = block.context().getValue(yop.yieldValue()); 1677 block.op(branch(exit.successor(p))); 1678 } else if (op instanceof Lowerable lop) { 1679 // @@@ Composition of lowerable ops 1680 block = lop.lower(block, opT); 1681 } else { 1682 // Copy 1683 block.apply(op); 1684 } 1685 return block; 1686 }; 1687 } else { 1688 Block.Builder nextPred = pred; 1689 opt = (block, op) -> { 1690 if (op instanceof CoreOp.YieldOp yop) { 1691 Value p = block.context().getValue(yop.yieldValue()); 1692 if (cop instanceof JavaConditionalAndOp) { 1693 block.op(conditionalBranch(p, nextPred.successor(), exit.successor(p))); 1694 } else { 1695 block.op(conditionalBranch(p, exit.successor(p), nextPred.successor())); 1696 } 1697 } else if (op instanceof Lowerable lop) { 1698 // @@@ Composition of lowerable ops 1699 block = lop.lower(block, opT); 1700 } else { 1701 // Copy 1702 block.apply(op); 1703 } 1704 return block; 1705 }; 1706 } 1707 1708 Body fromPred = bodies.get(i); 1709 if (i == 0) { 1710 startBlock.transformBody(fromPred, List.of(), opt); 1711 } else { 1712 pred = startBlock.block(fromPred.bodyType().parameterTypes()); 1713 pred.transformBody(fromPred, pred.parameters(), opT.andThen(opt)); 1714 } 1715 } 1716 1717 return exit; 1718 } 1719 1720 @Override 1721 public TypeElement resultType() { 1722 return BOOLEAN; 1723 } 1724 } 1725 1726 /** 1727 * The conditional-and operation, that can model Java language conditional-and expressions. 1728 */ 1729 @OpFactory.OpDeclaration(JavaConditionalAndOp.NAME) 1730 public static final class JavaConditionalAndOp extends JavaConditionalOp { 1731 1732 public static class Builder { 1733 final Body.Builder ancestorBody; 1734 final List<Body.Builder> bodies; 1735 1736 Builder(Body.Builder ancestorBody, Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) { 1737 this.ancestorBody = ancestorBody; 1738 this.bodies = new ArrayList<>(); 1739 and(lhs); 1740 and(rhs); 1741 } 1742 1743 public Builder and(Consumer<Block.Builder> c) { 1744 Body.Builder body = Body.Builder.of(ancestorBody, FunctionType.functionType(BOOLEAN)); 1745 c.accept(body.entryBlock()); 1746 bodies.add(body); 1747 1748 return this; 1749 } 1750 1751 public JavaConditionalAndOp build() { 1752 return new JavaConditionalAndOp(bodies); 1753 } 1754 } 1755 1756 public static final String NAME = "java.cand"; 1757 1758 public JavaConditionalAndOp(ExternalizedOp def) { 1759 super(def); 1760 } 1761 1762 JavaConditionalAndOp(JavaConditionalAndOp that, CopyContext cc, OpTransformer ot) { 1763 super(that, cc, ot); 1764 } 1765 1766 @Override 1767 public JavaConditionalAndOp transform(CopyContext cc, OpTransformer ot) { 1768 return new JavaConditionalAndOp(this, cc, ot); 1769 } 1770 1771 JavaConditionalAndOp(List<Body.Builder> bodyCs) { 1772 super(NAME, bodyCs); 1773 } 1774 1775 @Override 1776 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 1777 return lower(b, opT, this); 1778 } 1779 } 1780 1781 /** 1782 * The conditional-or operation, that can model Java language conditional-or expressions. 1783 */ 1784 @OpFactory.OpDeclaration(JavaConditionalOrOp.NAME) 1785 public static final class JavaConditionalOrOp extends JavaConditionalOp { 1786 1787 public static class Builder { 1788 final Body.Builder ancestorBody; 1789 final List<Body.Builder> bodies; 1790 1791 Builder(Body.Builder ancestorBody, Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) { 1792 this.ancestorBody = ancestorBody; 1793 this.bodies = new ArrayList<>(); 1794 or(lhs); 1795 or(rhs); 1796 } 1797 1798 public Builder or(Consumer<Block.Builder> c) { 1799 Body.Builder body = Body.Builder.of(ancestorBody, FunctionType.functionType(BOOLEAN)); 1800 c.accept(body.entryBlock()); 1801 bodies.add(body); 1802 1803 return this; 1804 } 1805 1806 public JavaConditionalOrOp build() { 1807 return new JavaConditionalOrOp(bodies); 1808 } 1809 } 1810 1811 public static final String NAME = "java.cor"; 1812 1813 public JavaConditionalOrOp(ExternalizedOp def) { 1814 super(def); 1815 } 1816 1817 JavaConditionalOrOp(JavaConditionalOrOp that, CopyContext cc, OpTransformer ot) { 1818 super(that, cc, ot); 1819 } 1820 1821 @Override 1822 public JavaConditionalOrOp transform(CopyContext cc, OpTransformer ot) { 1823 return new JavaConditionalOrOp(this, cc, ot); 1824 } 1825 1826 JavaConditionalOrOp(List<Body.Builder> bodyCs) { 1827 super(NAME, bodyCs); 1828 } 1829 1830 @Override 1831 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 1832 return lower(b, opT, this); 1833 } 1834 } 1835 1836 /** 1837 * The conditional operation, that can model Java language conditional operator {@code ?} expressions. 1838 */ 1839 @OpFactory.OpDeclaration(JavaConditionalExpressionOp.NAME) 1840 public static final class JavaConditionalExpressionOp extends ExtendedOp 1841 implements Op.Nested, Op.Lowerable, JavaExpression { 1842 1843 public static final String NAME = "java.cexpression"; 1844 1845 final TypeElement resultType; 1846 // {cond, truepart, falsepart} 1847 final List<Body> bodies; 1848 1849 public JavaConditionalExpressionOp(ExternalizedOp def) { 1850 super(def); 1851 1852 if (!def.operands().isEmpty()) { 1853 throw new IllegalStateException("Operation must have no operands"); 1854 } 1855 1856 // @@@ Validate 1857 1858 this.bodies = def.bodyDefinitions().stream().map(bd -> bd.build(this)).toList(); 1859 this.resultType = def.resultType(); 1860 } 1861 1862 JavaConditionalExpressionOp(JavaConditionalExpressionOp that, CopyContext cc, OpTransformer ot) { 1863 super(that, cc); 1864 1865 // Copy body 1866 this.bodies = that.bodies.stream() 1867 .map(b -> b.transform(cc, ot).build(this)).toList(); 1868 this.resultType = that.resultType; 1869 } 1870 1871 @Override 1872 public JavaConditionalExpressionOp transform(CopyContext cc, OpTransformer ot) { 1873 return new JavaConditionalExpressionOp(this, cc, ot); 1874 } 1875 1876 JavaConditionalExpressionOp(TypeElement expressionType, List<Body.Builder> bodyCs) { 1877 super(NAME, List.of()); 1878 1879 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList(); 1880 // @@@ when expressionType is null, we assume truepart and falsepart have the same yieldType 1881 this.resultType = expressionType == null ? bodies.get(1).yieldType() : expressionType; 1882 1883 if (bodies.size() < 3) { 1884 throw new IllegalArgumentException("Incorrect number of bodies: " + bodies.size()); 1885 } 1886 1887 Body cond = bodies.get(0); 1888 if (!cond.bodyType().equals(FunctionType.functionType(BOOLEAN))) { 1889 throw new IllegalArgumentException("Illegal cond body descriptor: " + cond.bodyType()); 1890 } 1891 } 1892 1893 @Override 1894 public List<Body> bodies() { 1895 return bodies; 1896 } 1897 1898 @Override 1899 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 1900 Block.Builder exit = b.block(resultType()); 1901 exit.context().mapValue(result(), exit.parameters().get(0)); 1902 1903 setBranchTarget(b.context(), this, new BranchTarget(exit, null)); 1904 1905 List<Block.Builder> builders = List.of(b.block(), b.block()); 1906 b.transformBody(bodies.get(0), List.of(), opT.andThen((block, op) -> { 1907 if (op instanceof YieldOp yo) { 1908 block.op(conditionalBranch(block.context().getValue(yo.yieldValue()), 1909 builders.get(0).successor(), builders.get(1).successor())); 1910 } else if (op instanceof Lowerable lop) { 1911 // @@@ Composition of lowerable ops 1912 block = lop.lower(block, opT); 1913 } else { 1914 block.op(op); 1915 } 1916 return block; 1917 })); 1918 1919 for (int i = 0; i < 2; i++) { 1920 builders.get(i).transformBody(bodies.get(i + 1), List.of(), opT.andThen((block, op) -> { 1921 if (op instanceof YieldOp yop) { 1922 block.op(branch(exit.successor(block.context().getValue(yop.yieldValue())))); 1923 } else if (op instanceof Lowerable lop) { 1924 // @@@ Composition of lowerable ops 1925 block = lop.lower(block, opT); 1926 } else { 1927 block.op(op); 1928 } 1929 return block; 1930 })); 1931 } 1932 1933 return exit; 1934 } 1935 1936 @Override 1937 public TypeElement resultType() { 1938 return resultType; 1939 } 1940 } 1941 1942 /** 1943 * The try operation, that can model Java language try statements. 1944 */ 1945 @OpFactory.OpDeclaration(JavaTryOp.NAME) 1946 public static final class JavaTryOp extends ExtendedOp 1947 implements Op.Nested, Op.Lowerable, JavaStatement { 1948 1949 public static final class BodyBuilder { 1950 final Body.Builder ancestorBody; 1951 final List<? extends TypeElement> resourceTypes; 1952 final Body.Builder resources; 1953 1954 BodyBuilder(Body.Builder ancestorBody, List<? extends TypeElement> resourceTypes, Body.Builder resources) { 1955 this.ancestorBody = ancestorBody; 1956 this.resourceTypes = resourceTypes; 1957 this.resources = resources; 1958 } 1959 1960 public CatchBuilder body(Consumer<Block.Builder> c) { 1961 Body.Builder body = Body.Builder.of(ancestorBody, 1962 FunctionType.functionType(VOID, resourceTypes)); 1963 c.accept(body.entryBlock()); 1964 1965 return new CatchBuilder(ancestorBody, resources, body); 1966 } 1967 } 1968 1969 public static final class CatchBuilder { 1970 final Body.Builder ancestorBody; 1971 final Body.Builder resources; 1972 final Body.Builder body; 1973 final List<Body.Builder> catchers; 1974 1975 CatchBuilder(Body.Builder ancestorBody, Body.Builder resources, Body.Builder body) { 1976 this.ancestorBody = ancestorBody; 1977 this.resources = resources; 1978 this.body = body; 1979 this.catchers = new ArrayList<>(); 1980 } 1981 1982 // @@@ multi-catch 1983 public CatchBuilder _catch(TypeElement exceptionType, Consumer<Block.Builder> c) { 1984 Body.Builder _catch = Body.Builder.of(ancestorBody, 1985 FunctionType.functionType(VOID, exceptionType)); 1986 c.accept(_catch.entryBlock()); 1987 catchers.add(_catch); 1988 1989 return this; 1990 } 1991 1992 public JavaTryOp _finally(Consumer<Block.Builder> c) { 1993 Body.Builder _finally = Body.Builder.of(ancestorBody, FunctionType.VOID); 1994 c.accept(_finally.entryBlock()); 1995 1996 return new JavaTryOp(resources, body, catchers, _finally); 1997 } 1998 1999 public JavaTryOp noFinalizer() { 2000 return new JavaTryOp(resources, body, catchers, null); 2001 } 2002 } 2003 2004 static final String NAME = "java.try"; 2005 2006 final Body resources; 2007 final Body body; 2008 final List<Body> catchers; 2009 final Body finalizer; 2010 2011 public static JavaTryOp create(ExternalizedOp def) { 2012 return new JavaTryOp(def); 2013 } 2014 2015 public JavaTryOp(ExternalizedOp def) { 2016 super(def); 2017 2018 List<Body> bodies = def.bodyDefinitions().stream().map(b -> b.build(this)).toList(); 2019 Body first = bodies.get(0); 2020 if (first.bodyType().returnType().equals(VOID)) { 2021 this.resources = null; 2022 this.body = first; 2023 } else { 2024 this.resources = first; 2025 this.body = bodies.get(1); 2026 } 2027 2028 Body last = bodies.get(bodies.size() - 1); 2029 if (last.bodyType().parameterTypes().isEmpty()) { 2030 this.finalizer = last; 2031 } else { 2032 this.finalizer = null; 2033 } 2034 this.catchers = bodies.subList( 2035 resources == null ? 1 : 2, 2036 bodies.size() - (finalizer == null ? 0 : 1)); 2037 } 2038 2039 JavaTryOp(JavaTryOp that, CopyContext cc, OpTransformer ot) { 2040 super(that, cc); 2041 2042 if (that.resources != null) { 2043 this.resources = that.resources.transform(cc, ot).build(this); 2044 } else { 2045 this.resources = null; 2046 } 2047 this.body = that.body.transform(cc, ot).build(this); 2048 this.catchers = that.catchers.stream() 2049 .map(b -> b.transform(cc, ot).build(this)) 2050 .toList(); 2051 if (that.finalizer != null) { 2052 this.finalizer = that.finalizer.transform(cc, ot).build(this); 2053 } else { 2054 this.finalizer = null; 2055 } 2056 } 2057 2058 @Override 2059 public JavaTryOp transform(CopyContext cc, OpTransformer ot) { 2060 return new JavaTryOp(this, cc, ot); 2061 } 2062 2063 JavaTryOp(Body.Builder resourcesC, 2064 Body.Builder bodyC, 2065 List<Body.Builder> catchersC, 2066 Body.Builder finalizerC) { 2067 super(NAME, List.of()); 2068 2069 if (resourcesC != null) { 2070 this.resources = resourcesC.build(this); 2071 if (resources.bodyType().returnType().equals(VOID)) { 2072 throw new IllegalArgumentException("Resources should not return void: " + resources.bodyType()); 2073 } 2074 if (!resources.bodyType().parameterTypes().isEmpty()) { 2075 throw new IllegalArgumentException("Resources should have zero parameters: " + resources.bodyType()); 2076 } 2077 } else { 2078 this.resources = null; 2079 } 2080 2081 this.body = bodyC.build(this); 2082 if (!body.bodyType().returnType().equals(VOID)) { 2083 throw new IllegalArgumentException("Try should return void: " + body.bodyType()); 2084 } 2085 2086 this.catchers = catchersC.stream().map(c -> c.build(this)).toList(); 2087 for (Body _catch : catchers) { 2088 if (!_catch.bodyType().returnType().equals(VOID)) { 2089 throw new IllegalArgumentException("Catch should return void: " + _catch.bodyType()); 2090 } 2091 if (_catch.bodyType().parameterTypes().size() != 1) { 2092 throw new IllegalArgumentException("Catch should have zero parameters: " + _catch.bodyType()); 2093 } 2094 } 2095 2096 if (finalizerC != null) { 2097 this.finalizer = finalizerC.build(this); 2098 if (!finalizer.bodyType().returnType().equals(VOID)) { 2099 throw new IllegalArgumentException("Finally should return void: " + finalizer.bodyType()); 2100 } 2101 if (!finalizer.bodyType().parameterTypes().isEmpty()) { 2102 throw new IllegalArgumentException("Finally should have zero parameters: " + finalizer.bodyType()); 2103 } 2104 } else { 2105 this.finalizer = null; 2106 } 2107 } 2108 2109 @Override 2110 public List<Body> bodies() { 2111 ArrayList<Body> bodies = new ArrayList<>(); 2112 if (resources != null) { 2113 bodies.add(resources); 2114 } 2115 bodies.add(body); 2116 bodies.addAll(catchers); 2117 if (finalizer != null) { 2118 bodies.add(finalizer); 2119 } 2120 return bodies; 2121 } 2122 2123 public Body resources() { 2124 return resources; 2125 } 2126 2127 public Body body() { 2128 return body; 2129 } 2130 2131 public List<Body> catchers() { 2132 return catchers; 2133 } 2134 2135 public Body finalizer() { 2136 return finalizer; 2137 } 2138 2139 @Override 2140 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 2141 if (resources != null) { 2142 throw new UnsupportedOperationException("Lowering of try-with-resources is unsupported"); 2143 } 2144 2145 Block.Builder exit = b.block(); 2146 setBranchTarget(b.context(), this, new BranchTarget(exit, null)); 2147 2148 // Simple case with no catch and finally bodies 2149 if (catchers.isEmpty() && finalizer == null) { 2150 b.transformBody(body, List.of(), (block, op) -> { 2151 if (op instanceof YieldOp) { 2152 block.op(branch(exit.successor())); 2153 } else { 2154 // @@@ Composition of lowerable ops 2155 if (op instanceof Lowerable lop) { 2156 block = lop.lower(block, opT); 2157 } else { 2158 block.op(op); 2159 } 2160 } 2161 return block; 2162 }); 2163 return exit; 2164 } 2165 2166 Block.Builder tryRegionEnter = b.block(); 2167 Block.Builder tryRegionExit = b.block(); 2168 2169 // Construct the catcher block builders 2170 List<Block.Builder> catchers = catchers().stream() 2171 .map(catcher -> b.block()) 2172 .toList(); 2173 Block.Builder catcherFinally = null; 2174 if (finalizer != null) { 2175 catcherFinally = b.block(); 2176 catchers = new ArrayList<>(catchers); 2177 catchers.add(catcherFinally); 2178 } 2179 2180 // Enter the try exception region 2181 Result tryExceptionRegion = b.op(exceptionRegionEnter(tryRegionEnter.successor(), catchers.stream() 2182 .map(Block.Builder::successor) 2183 .toList())); 2184 2185 OpTransformer tryExitTransformer; 2186 if (finalizer != null) { 2187 tryExitTransformer = opT.compose((block, op) -> { 2188 if (op instanceof CoreOp.ReturnOp) { 2189 return inlineFinalizer(block, tryExceptionRegion, opT); 2190 } else if (op instanceof ExtendedOp.JavaLabelOp lop && ifExitFromTry(lop)) { 2191 return inlineFinalizer(block, tryExceptionRegion, opT); 2192 } else { 2193 return block; 2194 } 2195 }); 2196 } else { 2197 tryExitTransformer = opT.compose((block, op) -> { 2198 // @@@ break and continue 2199 // when target break/continue is enclosing the try 2200 if (op instanceof CoreOp.ReturnOp) { 2201 Block.Builder tryRegionReturnExit = block.block(); 2202 block.op(exceptionRegionExit(tryExceptionRegion, tryRegionReturnExit.successor())); 2203 return tryRegionReturnExit; 2204 } else { 2205 return block; 2206 } 2207 }); 2208 } 2209 // Inline the try body 2210 AtomicBoolean hasTryRegionExit = new AtomicBoolean(); 2211 tryRegionEnter.transformBody(body, List.of(), tryExitTransformer.andThen((block, op) -> { 2212 if (op instanceof YieldOp) { 2213 hasTryRegionExit.set(true); 2214 block.op(branch(tryRegionExit.successor())); 2215 } else { 2216 // @@@ Composition of lowerable ops 2217 if (op instanceof Lowerable lop) { 2218 block = lop.lower(block, tryExitTransformer); 2219 } else { 2220 block.op(op); 2221 } 2222 } 2223 return block; 2224 })); 2225 2226 Block.Builder finallyEnter = null; 2227 if (finalizer != null) { 2228 finallyEnter = b.block(); 2229 if (hasTryRegionExit.get()) { 2230 // Exit the try exception region 2231 tryRegionExit.op(exceptionRegionExit(tryExceptionRegion, finallyEnter.successor())); 2232 } 2233 } else if (hasTryRegionExit.get()) { 2234 // Exit the try exception region 2235 tryRegionExit.op(exceptionRegionExit(tryExceptionRegion, exit.successor())); 2236 } 2237 2238 // Inline the catch bodies 2239 for (int i = 0; i < this.catchers.size(); i++) { 2240 Block.Builder catcher = catchers.get(i); 2241 Body catcherBody = this.catchers.get(i); 2242 // Create the throwable argument 2243 Block.Parameter t = catcher.parameter(catcherBody.bodyType().parameterTypes().get(0)); 2244 2245 if (finalizer != null) { 2246 Block.Builder catchRegionEnter = b.block(); 2247 Block.Builder catchRegionExit = b.block(); 2248 2249 // Enter the catch exception region 2250 Result catchExceptionRegion = catcher.op( 2251 exceptionRegionEnter(catchRegionEnter.successor(), catcherFinally.successor())); 2252 2253 OpTransformer catchExitTransformer = opT.compose((block, op) -> { 2254 if (op instanceof CoreOp.ReturnOp) { 2255 return inlineFinalizer(block, catchExceptionRegion, opT); 2256 } else if (op instanceof ExtendedOp.JavaLabelOp lop && ifExitFromTry(lop)) { 2257 return inlineFinalizer(block, catchExceptionRegion, opT); 2258 } else { 2259 return block; 2260 } 2261 }); 2262 // Inline the catch body 2263 AtomicBoolean hasCatchRegionExit = new AtomicBoolean(); 2264 catchRegionEnter.transformBody(catcherBody, List.of(t), catchExitTransformer.andThen((block, op) -> { 2265 if (op instanceof YieldOp) { 2266 hasCatchRegionExit.set(true); 2267 block.op(branch(catchRegionExit.successor())); 2268 } else { 2269 // @@@ Composition of lowerable ops 2270 if (op instanceof Lowerable lop) { 2271 block = lop.lower(block, catchExitTransformer); 2272 } else { 2273 block.op(op); 2274 } 2275 } 2276 return block; 2277 })); 2278 2279 // Exit the catch exception region 2280 if (hasCatchRegionExit.get()) { 2281 hasTryRegionExit.set(true); 2282 catchRegionExit.op(exceptionRegionExit(catchExceptionRegion, finallyEnter.successor())); 2283 } 2284 } else { 2285 // Inline the catch body 2286 catcher.transformBody(catcherBody, List.of(t), opT.andThen((block, op) -> { 2287 if (op instanceof YieldOp) { 2288 block.op(branch(exit.successor())); 2289 } else { 2290 // @@@ Composition of lowerable ops 2291 if (op instanceof Lowerable lop) { 2292 block = lop.lower(block, opT); 2293 } else { 2294 block.op(op); 2295 } 2296 } 2297 return block; 2298 })); 2299 } 2300 } 2301 2302 if (finalizer != null && hasTryRegionExit.get()) { 2303 // Inline the finally body 2304 finallyEnter.transformBody(finalizer, List.of(), opT.andThen((block, op) -> { 2305 if (op instanceof YieldOp) { 2306 block.op(branch(exit.successor())); 2307 } else { 2308 // @@@ Composition of lowerable ops 2309 if (op instanceof Lowerable lop) { 2310 block = lop.lower(block, opT); 2311 } else { 2312 block.op(op); 2313 } 2314 } 2315 return block; 2316 })); 2317 } 2318 2319 // Inline the finally body as a catcher of Throwable and adjusting to throw 2320 if (finalizer != null) { 2321 // Create the throwable argument 2322 Block.Parameter t = catcherFinally.parameter(type(Throwable.class)); 2323 2324 catcherFinally.transformBody(finalizer, List.of(), opT.andThen((block, op) -> { 2325 if (op instanceof YieldOp) { 2326 block.op(_throw(t)); 2327 } else { 2328 // @@@ Composition of lowerable ops 2329 if (op instanceof Lowerable lop) { 2330 block = lop.lower(block, opT); 2331 } else { 2332 block.op(op); 2333 } 2334 } 2335 return block; 2336 })); 2337 } 2338 return exit; 2339 } 2340 2341 boolean ifExitFromTry(JavaLabelOp lop) { 2342 Op target = lop.target(); 2343 return target == this || ifAncestorOp(target, this); 2344 } 2345 2346 static boolean ifAncestorOp(Op ancestor, Op op) { 2347 while (op.ancestorBody() != null) { 2348 op = op.ancestorBody().parentOp(); 2349 if (op == ancestor) { 2350 return true; 2351 } 2352 } 2353 return false; 2354 } 2355 2356 Block.Builder inlineFinalizer(Block.Builder block1, Value exceptionRegion, OpTransformer opT) { 2357 Block.Builder finallyEnter = block1.block(); 2358 Block.Builder finallyExit = block1.block(); 2359 2360 block1.op(exceptionRegionExit(exceptionRegion, finallyEnter.successor())); 2361 2362 // Inline the finally body 2363 finallyEnter.transformBody(finalizer, List.of(), opT.andThen((block2, op2) -> { 2364 if (op2 instanceof YieldOp) { 2365 block2.op(branch(finallyExit.successor())); 2366 } else { 2367 // @@@ Composition of lowerable ops 2368 if (op2 instanceof Lowerable lop2) { 2369 block2 = lop2.lower(block2, opT); 2370 } else { 2371 block2.op(op2); 2372 } 2373 } 2374 return block2; 2375 })); 2376 2377 return finallyExit; 2378 } 2379 2380 @Override 2381 public TypeElement resultType() { 2382 return VOID; 2383 } 2384 } 2385 2386 // 2387 // Patterns 2388 2389 static final String Pattern_CLASS_NAME = ExtendedOp_CLASS_NAME + "$" + Pattern.class.getSimpleName(); 2390 2391 // Reified pattern nodes 2392 2393 /** 2394 * Synthetic pattern types 2395 * // @@@ Replace with types extending from TypeElement 2396 */ 2397 public sealed interface Pattern { 2398 2399 /** 2400 * Synthetic binding pattern type. 2401 * 2402 * @param <T> the type of values that are bound 2403 */ 2404 final class Binding<T> implements Pattern { 2405 Binding() { 2406 } 2407 } 2408 2409 /** 2410 * Synthetic record pattern type. 2411 * 2412 * @param <T> the type of records that are bound 2413 */ 2414 final class Record<T> implements Pattern { 2415 Record() { 2416 } 2417 } 2418 2419 // @@@ Pattern types 2420 2421 JavaType PATTERN_BINDING_TYPE = JavaType.type(ClassDesc.of(Pattern_CLASS_NAME + 2422 "$" + Binding.class.getSimpleName())); 2423 JavaType PATTERN_RECORD_TYPE = JavaType.type(ClassDesc.of(Pattern_CLASS_NAME + 2424 "$" + Pattern.Record.class.getSimpleName())); 2425 2426 static JavaType bindingType(TypeElement t) { 2427 return parameterized(PATTERN_BINDING_TYPE, (JavaType) t); 2428 } 2429 2430 static JavaType recordType(TypeElement t) { 2431 return parameterized(PATTERN_RECORD_TYPE, (JavaType) t); 2432 } 2433 2434 static TypeElement targetType(TypeElement t) { 2435 return ((ClassType) t).typeArguments().get(0); 2436 } 2437 } 2438 2439 /** 2440 * Pattern operations. 2441 */ 2442 public static final class PatternOps { 2443 PatternOps() { 2444 } 2445 2446 /** 2447 * The pattern operation. 2448 */ 2449 public sealed static abstract class PatternOp extends ExtendedOp implements Op.Pure { 2450 PatternOp(ExternalizedOp def) { 2451 super(def); 2452 } 2453 2454 PatternOp(PatternOp that, CopyContext cc) { 2455 super(that, cc); 2456 } 2457 2458 PatternOp(String name, List<Value> operands) { 2459 super(name, operands); 2460 } 2461 } 2462 2463 /** 2464 * The binding pattern operation, that can model Java language type patterns. 2465 */ 2466 @OpFactory.OpDeclaration(BindingPatternOp.NAME) 2467 public static final class BindingPatternOp extends PatternOp { 2468 public static final String NAME = "pattern.binding"; 2469 2470 public static final String ATTRIBUTE_BINDING_NAME = NAME + ".binding.name"; 2471 2472 final TypeElement resultType; 2473 final String bindingName; 2474 2475 public static BindingPatternOp create(ExternalizedOp def) { 2476 String name = def.extractAttributeValue(ATTRIBUTE_BINDING_NAME, true, 2477 v -> switch (v) { 2478 case String s -> s; 2479 default -> 2480 throw new UnsupportedOperationException("Unsupported pattern binding name value:" + v); 2481 }); 2482 return new BindingPatternOp(def, name); 2483 } 2484 2485 BindingPatternOp(ExternalizedOp def, String bindingName) { 2486 super(def); 2487 2488 this.bindingName = bindingName; 2489 this.resultType = def.resultType(); 2490 } 2491 2492 BindingPatternOp(BindingPatternOp that, CopyContext cc) { 2493 super(that, cc); 2494 2495 this.bindingName = that.bindingName; 2496 this.resultType = that.resultType; 2497 } 2498 2499 @Override 2500 public BindingPatternOp transform(CopyContext cc, OpTransformer ot) { 2501 return new BindingPatternOp(this, cc); 2502 } 2503 2504 BindingPatternOp(TypeElement targetType, String bindingName) { 2505 super(NAME, List.of()); 2506 2507 this.bindingName = bindingName; 2508 this.resultType = Pattern.bindingType(targetType); 2509 } 2510 2511 @Override 2512 public Map<String, Object> attributes() { 2513 HashMap<String, Object> attrs = new HashMap<>(super.attributes()); 2514 attrs.put("", bindingName); 2515 return attrs; 2516 } 2517 2518 public String bindingName() { 2519 return bindingName; 2520 } 2521 2522 public TypeElement targetType() { 2523 return Pattern.targetType(resultType()); 2524 } 2525 2526 @Override 2527 public TypeElement resultType() { 2528 return resultType; 2529 } 2530 } 2531 2532 /** 2533 * The record pattern operation, that can model Java language record patterns. 2534 */ 2535 @OpFactory.OpDeclaration(RecordPatternOp.NAME) 2536 public static final class RecordPatternOp extends PatternOp { 2537 public static final String NAME = "pattern.record"; 2538 2539 public static final String ATTRIBUTE_RECORD_DESCRIPTOR = NAME + ".descriptor"; 2540 2541 final RecordTypeRef recordDescriptor; 2542 2543 public static RecordPatternOp create(ExternalizedOp def) { 2544 RecordTypeRef recordDescriptor = def.extractAttributeValue(ATTRIBUTE_RECORD_DESCRIPTOR, true, 2545 v -> switch (v) { 2546 case String s -> RecordTypeRef.ofString(s); 2547 case RecordTypeRef rtd -> rtd; 2548 default -> 2549 throw new UnsupportedOperationException("Unsupported record type descriptor value:" + v); 2550 }); 2551 2552 return new RecordPatternOp(def, recordDescriptor); 2553 } 2554 2555 RecordPatternOp(ExternalizedOp def, RecordTypeRef recordDescriptor) { 2556 super(def); 2557 2558 this.recordDescriptor = recordDescriptor; 2559 } 2560 2561 RecordPatternOp(RecordPatternOp that, CopyContext cc) { 2562 super(that, cc); 2563 2564 this.recordDescriptor = that.recordDescriptor; 2565 } 2566 2567 @Override 2568 public RecordPatternOp transform(CopyContext cc, OpTransformer ot) { 2569 return new RecordPatternOp(this, cc); 2570 } 2571 2572 RecordPatternOp(RecordTypeRef recordDescriptor, List<Value> nestedPatterns) { 2573 // The type of each value is a subtype of Pattern 2574 // The number of values corresponds to the number of components of the record 2575 super(NAME, List.copyOf(nestedPatterns)); 2576 2577 this.recordDescriptor = recordDescriptor; 2578 } 2579 2580 @Override 2581 public Map<String, Object> attributes() { 2582 HashMap<String, Object> m = new HashMap<>(super.attributes()); 2583 m.put("", recordDescriptor); 2584 return Collections.unmodifiableMap(m); 2585 } 2586 2587 public RecordTypeRef recordDescriptor() { 2588 return recordDescriptor; 2589 } 2590 2591 public TypeElement targetType() { 2592 return Pattern.targetType(resultType()); 2593 } 2594 2595 @Override 2596 public TypeElement resultType() { 2597 return Pattern.recordType(recordDescriptor.recordType()); 2598 } 2599 } 2600 2601 /** 2602 * The match operation, that can model Java language pattern matching. 2603 */ 2604 @OpFactory.OpDeclaration(MatchOp.NAME) 2605 public static final class MatchOp extends ExtendedOp implements Op.Isolated, Op.Lowerable { 2606 public static final String NAME = "pattern.match"; 2607 2608 final Body pattern; 2609 final Body match; 2610 2611 public MatchOp(ExternalizedOp def) { 2612 super(def); 2613 2614 this.pattern = def.bodyDefinitions().get(0).build(this); 2615 this.match = def.bodyDefinitions().get(1).build(this); 2616 } 2617 2618 MatchOp(MatchOp that, CopyContext cc, OpTransformer ot) { 2619 super(that, cc); 2620 2621 this.pattern = that.pattern.transform(cc, ot).build(this); 2622 this.match = that.match.transform(cc, ot).build(this); 2623 } 2624 2625 @Override 2626 public MatchOp transform(CopyContext cc, OpTransformer ot) { 2627 return new MatchOp(this, cc, ot); 2628 } 2629 2630 MatchOp(Value target, Body.Builder patternC, Body.Builder matchC) { 2631 super(NAME, 2632 List.of(target)); 2633 2634 this.pattern = patternC.build(this); 2635 this.match = matchC.build(this); 2636 } 2637 2638 @Override 2639 public List<Body> bodies() { 2640 return List.of(pattern, match); 2641 } 2642 2643 public Body pattern() { 2644 return pattern; 2645 } 2646 2647 public Body match() { 2648 return match; 2649 } 2650 2651 public Value target() { 2652 return operands().get(0); 2653 } 2654 2655 @Override 2656 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 2657 // No match block 2658 Block.Builder endNoMatchBlock = b.block(); 2659 // Match block 2660 Block.Builder endMatchBlock = b.block(); 2661 // End block 2662 Block.Builder endBlock = b.block(); 2663 Block.Parameter matchResult = endBlock.parameter(resultType()); 2664 // Map match operation result 2665 b.context().mapValue(result(), matchResult); 2666 2667 List<Value> patternValues = new ArrayList<>(); 2668 Op patternYieldOp = pattern.entryBlock().terminatingOp(); 2669 Op.Result rootPatternValue = (Op.Result) patternYieldOp.operands().get(0); 2670 Block.Builder currentBlock = lower(endNoMatchBlock, b, 2671 patternValues, 2672 rootPatternValue.op(), 2673 b.context().getValue(target())); 2674 currentBlock.op(branch(endMatchBlock.successor())); 2675 2676 // No match block 2677 // Pass false 2678 endNoMatchBlock.op(branch(endBlock.successor( 2679 endNoMatchBlock.op(constant(BOOLEAN, false))))); 2680 2681 // Match block 2682 // Lower match body and pass true 2683 endMatchBlock.transformBody(match, patternValues, opT.andThen((block, op) -> { 2684 if (op instanceof YieldOp) { 2685 block.op(branch(endBlock.successor( 2686 block.op(constant(BOOLEAN, true))))); 2687 } else if (op instanceof Lowerable lop) { 2688 // @@@ Composition of lowerable ops 2689 block = lop.lower(block, opT); 2690 } else { 2691 block.op(op); 2692 } 2693 return block; 2694 })); 2695 2696 return endBlock; 2697 } 2698 2699 static Block.Builder lower(Block.Builder endNoMatchBlock, Block.Builder currentBlock, 2700 List<Value> bindings, 2701 Op pattern, Value target) { 2702 if (pattern instanceof ExtendedOp.PatternOps.RecordPatternOp rp) { 2703 return lowerRecordPattern(endNoMatchBlock, currentBlock, bindings, rp, target); 2704 } else if (pattern instanceof ExtendedOp.PatternOps.BindingPatternOp bp) { 2705 return lowerBindingPattern(endNoMatchBlock, currentBlock, bindings, bp, target); 2706 } else { 2707 throw new UnsupportedOperationException("Unknown pattern op: " + pattern); 2708 } 2709 } 2710 2711 static Block.Builder lowerRecordPattern(Block.Builder endNoMatchBlock, Block.Builder currentBlock, 2712 List<Value> bindings, 2713 ExtendedOp.PatternOps.RecordPatternOp rpOp, Value target) { 2714 TypeElement targetType = rpOp.targetType(); 2715 2716 Block.Builder nextBlock = currentBlock.block(); 2717 2718 // Check if instance of target type 2719 Op.Result isInstance = currentBlock.op(CoreOp.instanceOf(targetType, target)); 2720 currentBlock.op(conditionalBranch(isInstance, nextBlock.successor(), endNoMatchBlock.successor())); 2721 2722 currentBlock = nextBlock; 2723 2724 target = currentBlock.op(CoreOp.cast(targetType, target)); 2725 2726 // Access component values of record and match on each as nested target 2727 List<Value> dArgs = rpOp.operands(); 2728 for (int i = 0; i < dArgs.size(); i++) { 2729 Op.Result nestedPattern = (Op.Result) dArgs.get(i); 2730 // @@@ Handle exceptions? 2731 Value nestedTarget = currentBlock.op(CoreOp.invoke(rpOp.recordDescriptor().methodForComponent(i), target)); 2732 2733 currentBlock = lower(endNoMatchBlock, currentBlock, bindings, nestedPattern.op(), nestedTarget); 2734 } 2735 2736 return currentBlock; 2737 } 2738 2739 static Block.Builder lowerBindingPattern(Block.Builder endNoMatchBlock, Block.Builder currentBlock, 2740 List<Value> bindings, 2741 ExtendedOp.PatternOps.BindingPatternOp bpOp, Value target) { 2742 TypeElement targetType = bpOp.targetType(); 2743 2744 Block.Builder nextBlock = currentBlock.block(); 2745 2746 // Check if instance of target type 2747 currentBlock.op(conditionalBranch(currentBlock.op(CoreOp.instanceOf(targetType, target)), 2748 nextBlock.successor(), endNoMatchBlock.successor())); 2749 2750 currentBlock = nextBlock; 2751 2752 target = currentBlock.op(CoreOp.cast(targetType, target)); 2753 bindings.add(target); 2754 2755 return currentBlock; 2756 } 2757 2758 @Override 2759 public TypeElement resultType() { 2760 return BOOLEAN; 2761 } 2762 } 2763 } 2764 2765 2766 /** 2767 * A factory for extended and core operations. 2768 */ 2769 // @@@ Compute lazily 2770 public static final OpFactory FACTORY = CoreOp.FACTORY.andThen(OpFactory.OP_FACTORY.get(ExtendedOp.class)); 2771 2772 2773 /** 2774 * Creates a continue operation. 2775 * 2776 * @return the continue operation 2777 */ 2778 public static JavaContinueOp _continue() { 2779 return _continue(null); 2780 } 2781 2782 /** 2783 * Creates a continue operation. 2784 * 2785 * @param label the value associated with where to continue from 2786 * @return the continue operation 2787 */ 2788 public static JavaContinueOp _continue(Value label) { 2789 return new JavaContinueOp(label); 2790 } 2791 2792 /** 2793 * Creates a break operation. 2794 * 2795 * @return the break operation 2796 */ 2797 public static JavaBreakOp _break() { 2798 return _break(null); 2799 } 2800 2801 /** 2802 * Creates a break operation. 2803 * 2804 * @param label the value associated with where to continue from 2805 * @return the break operation 2806 */ 2807 public static JavaBreakOp _break(Value label) { 2808 return new JavaBreakOp(label); 2809 } 2810 2811 /** 2812 * Creates a yield operation. 2813 * 2814 * @return the yield operation 2815 */ 2816 public static JavaYieldOp java_yield() { 2817 return new JavaYieldOp(); 2818 } 2819 2820 /** 2821 * Creates a yield operation. 2822 * 2823 * @param operand the value to yield 2824 * @return the yield operation 2825 */ 2826 public static JavaYieldOp java_yield(Value operand) { 2827 return new JavaYieldOp(operand); 2828 } 2829 2830 /** 2831 * Creates a block operation. 2832 * 2833 * @param body the body builder of the operation to be built and become its child 2834 * @return the block operation 2835 */ 2836 public static JavaBlockOp block(Body.Builder body) { 2837 return new JavaBlockOp(body); 2838 } 2839 2840 /** 2841 * Creates a labeled operation. 2842 * 2843 * @param body the body builder of the operation to be built and become its child 2844 * @return the block operation 2845 */ 2846 public static JavaLabeledOp labeled(Body.Builder body) { 2847 return new JavaLabeledOp(body); 2848 } 2849 2850 /** 2851 * Creates an if operation builder. 2852 * 2853 * @param ancestorBody the nearest ancestor body builder from which to construct 2854 * body builders for this operation 2855 * @return the if operation builder 2856 */ 2857 public static JavaIfOp.IfBuilder _if(Body.Builder ancestorBody) { 2858 return new JavaIfOp.IfBuilder(ancestorBody); 2859 } 2860 2861 // Pairs of 2862 // predicate ()boolean, body ()void 2863 // And one optional body ()void at the end 2864 2865 /** 2866 * Creates an if operation. 2867 * 2868 * @param bodies the body builders of operation to be built and become its children 2869 * @return the if operation 2870 */ 2871 public static JavaIfOp _if(List<Body.Builder> bodies) { 2872 return new JavaIfOp(bodies); 2873 } 2874 2875 /** 2876 * Creates a switch expression operation. 2877 * <p> 2878 * The result type of the operation will be derived from the yield type of the second body 2879 * 2880 * @param target the switch target value 2881 * @param bodies the body builders of the operation to be built and become its children 2882 * @return the switch expression operation 2883 */ 2884 public static JavaSwitchExpressionOp switchExpression(Value target, List<Body.Builder> bodies) { 2885 return new JavaSwitchExpressionOp(null, target, bodies); 2886 } 2887 2888 /** 2889 * Creates a switch expression operation. 2890 * 2891 * @param resultType the result type of the expression 2892 * @param target the switch target value 2893 * @param bodies the body builders of the operation to be built and become its children 2894 * @return the switch expression operation 2895 */ 2896 public static JavaSwitchExpressionOp switchExpression(TypeElement resultType, Value target, 2897 List<Body.Builder> bodies) { 2898 Objects.requireNonNull(resultType); 2899 return new JavaSwitchExpressionOp(resultType, target, bodies); 2900 } 2901 2902 /** 2903 * Creates a switch fallthrough operation. 2904 * 2905 * @return the switch fallthrough operation 2906 */ 2907 public static JavaSwitchFallthroughOp switchFallthroughOp() { 2908 return new JavaSwitchFallthroughOp(); 2909 } 2910 2911 /** 2912 * Creates a for operation builder. 2913 * 2914 * @param ancestorBody the nearest ancestor body builder from which to construct 2915 * body builders for this operation 2916 * @param initTypes the types of initialized variables 2917 * @return the for operation builder 2918 */ 2919 public static JavaForOp.InitBuilder _for(Body.Builder ancestorBody, TypeElement... initTypes) { 2920 return _for(ancestorBody, List.of(initTypes)); 2921 } 2922 2923 /** 2924 * Creates a for operation builder. 2925 * 2926 * @param ancestorBody the nearest ancestor body builder from which to construct 2927 * body builders for this operation 2928 * @param initTypes the types of initialized variables 2929 * @return the for operation builder 2930 */ 2931 public static JavaForOp.InitBuilder _for(Body.Builder ancestorBody, List<? extends TypeElement> initTypes) { 2932 return new JavaForOp.InitBuilder(ancestorBody, initTypes); 2933 } 2934 2935 2936 /** 2937 * Creates a for operation. 2938 * 2939 * @param init the init body builder of the operation to be built and become its child 2940 * @param cond the cond body builder of the operation to be built and become its child 2941 * @param update the update body builder of the operation to be built and become its child 2942 * @param body the main body builder of the operation to be built and become its child 2943 * @return the for operation 2944 */ 2945 // init ()Tuple<Var<T1>, Var<T2>, ..., Var<TN>>, or init ()void 2946 // cond (Var<T1>, Var<T2>, ..., Var<TN>)boolean 2947 // update (Var<T1>, Var<T2>, ..., Var<TN>)void 2948 // body (Var<T1>, Var<T2>, ..., Var<TN>)void 2949 public static JavaForOp _for(Body.Builder init, 2950 Body.Builder cond, 2951 Body.Builder update, 2952 Body.Builder body) { 2953 return new JavaForOp(init, cond, update, body); 2954 } 2955 2956 /** 2957 * Creates an enhanced for operation builder. 2958 * 2959 * @param ancestorBody the nearest ancestor body builder from which to construct 2960 * body builders for this operation 2961 * @param iterableType the iterable type 2962 * @param elementType the element type 2963 * @return the enhanced for operation builder 2964 */ 2965 public static JavaEnhancedForOp.ExpressionBuilder enhancedFor(Body.Builder ancestorBody, 2966 TypeElement iterableType, TypeElement elementType) { 2967 return new JavaEnhancedForOp.ExpressionBuilder(ancestorBody, iterableType, elementType); 2968 } 2969 2970 // expression ()I<E> 2971 // init (E )Var<T> 2972 // body (Var<T> )void 2973 2974 /** 2975 * Creates an enhanced for operation. 2976 * 2977 * @param expression the expression body builder of the operation to be built and become its child 2978 * @param init the init body builder of the operation to be built and become its child 2979 * @param body the main body builder of the operation to be built and become its child 2980 * @return the enhanced for operation 2981 */ 2982 public static JavaEnhancedForOp enhancedFor(Body.Builder expression, 2983 Body.Builder init, 2984 Body.Builder body) { 2985 return new JavaEnhancedForOp(expression, init, body); 2986 } 2987 2988 /** 2989 * Creates a while operation builder. 2990 * 2991 * @param ancestorBody the nearest ancestor body builder from which to construct 2992 * body builders for this operation 2993 * @return the while operation builder 2994 */ 2995 public static JavaWhileOp.PredicateBuilder _while(Body.Builder ancestorBody) { 2996 return new JavaWhileOp.PredicateBuilder(ancestorBody); 2997 } 2998 2999 /** 3000 * Creates a while operation. 3001 * 3002 * @param predicate the predicate body builder of the operation to be built and become its child 3003 * @param body the main body builder of the operation to be built and become its child 3004 * @return the while operation 3005 */ 3006 // predicate, ()boolean, may be null for predicate returning true 3007 // body, ()void 3008 public static JavaWhileOp _while(Body.Builder predicate, Body.Builder body) { 3009 return new JavaWhileOp(predicate, body); 3010 } 3011 3012 /** 3013 * Creates a do operation builder. 3014 * 3015 * @param ancestorBody the nearest ancestor body builder from which to construct 3016 * body builders for this operation 3017 * @return the do operation builder 3018 */ 3019 public static JavaDoWhileOp.BodyBuilder doWhile(Body.Builder ancestorBody) { 3020 return new JavaDoWhileOp.BodyBuilder(ancestorBody); 3021 } 3022 3023 /** 3024 * Creates a do operation. 3025 * 3026 * @param predicate the predicate body builder of the operation to be built and become its child 3027 * @param body the main body builder of the operation to be built and become its child 3028 * @return the do operation 3029 */ 3030 public static JavaDoWhileOp doWhile(Body.Builder body, Body.Builder predicate) { 3031 return new JavaDoWhileOp(body, predicate); 3032 } 3033 3034 /** 3035 * Creates a conditional-and operation builder. 3036 * 3037 * @param ancestorBody the nearest ancestor body builder from which to construct 3038 * body builders for this operation 3039 * @param lhs a consumer that builds the left-hand side body 3040 * @param rhs a consumer that builds the right-hand side body 3041 * @return the conditional-and operation builder 3042 */ 3043 public static JavaConditionalAndOp.Builder conditionalAnd(Body.Builder ancestorBody, 3044 Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) { 3045 return new JavaConditionalAndOp.Builder(ancestorBody, lhs, rhs); 3046 } 3047 3048 /** 3049 * Creates a conditional-or operation builder. 3050 * 3051 * @param ancestorBody the nearest ancestor body builder from which to construct 3052 * body builders for this operation 3053 * @param lhs a consumer that builds the left-hand side body 3054 * @param rhs a consumer that builds the right-hand side body 3055 * @return the conditional-or operation builder 3056 */ 3057 public static JavaConditionalOrOp.Builder conditionalOr(Body.Builder ancestorBody, 3058 Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) { 3059 return new JavaConditionalOrOp.Builder(ancestorBody, lhs, rhs); 3060 } 3061 3062 /** 3063 * Creates a conditional-and operation 3064 * 3065 * @param bodies the body builders of operation to be built and become its children 3066 * @return the conditional-and operation 3067 */ 3068 // predicates, ()boolean 3069 public static JavaConditionalAndOp conditionalAnd(List<Body.Builder> bodies) { 3070 return new JavaConditionalAndOp(bodies); 3071 } 3072 3073 /** 3074 * Creates a conditional-or operation 3075 * 3076 * @param bodies the body builders of operation to be built and become its children 3077 * @return the conditional-or operation 3078 */ 3079 // predicates, ()boolean 3080 public static JavaConditionalOrOp conditionalOr(List<Body.Builder> bodies) { 3081 return new JavaConditionalOrOp(bodies); 3082 } 3083 3084 /** 3085 * Creates a conditional operation 3086 * 3087 * @param expressionType the result type of the expression 3088 * @param bodies the body builders of operation to be built and become its children 3089 * @return the conditional operation 3090 */ 3091 public static JavaConditionalExpressionOp conditionalExpression(TypeElement expressionType, 3092 List<Body.Builder> bodies) { 3093 Objects.requireNonNull(expressionType); 3094 return new JavaConditionalExpressionOp(expressionType, bodies); 3095 } 3096 3097 /** 3098 * Creates a conditional operation 3099 * <p> 3100 * The result type of the operation will be derived from the yield type of the second body 3101 * 3102 * @param bodies the body builders of operation to be built and become its children 3103 * @return the conditional operation 3104 */ 3105 public static JavaConditionalExpressionOp conditionalExpression(List<Body.Builder> bodies) { 3106 return new JavaConditionalExpressionOp(null, bodies); 3107 } 3108 3109 /** 3110 * Creates try operation builder. 3111 * 3112 * @param ancestorBody the nearest ancestor body builder from which to construct 3113 * body builders for this operation 3114 * @param c a consumer that builds the try body 3115 * @return the try operation builder 3116 */ 3117 public static JavaTryOp.CatchBuilder _try(Body.Builder ancestorBody, Consumer<Block.Builder> c) { 3118 Body.Builder _try = Body.Builder.of(ancestorBody, FunctionType.VOID); 3119 c.accept(_try.entryBlock()); 3120 return new JavaTryOp.CatchBuilder(ancestorBody, null, _try); 3121 } 3122 3123 /** 3124 * Creates try-with-resources operation builder. 3125 * 3126 * @param ancestorBody the nearest ancestor body builder from which to construct 3127 * body builders for this operation 3128 * @param c a consumer that builds the resources body 3129 * @return the try-with-resources operation builder 3130 */ 3131 public static JavaTryOp.BodyBuilder tryWithResources(Body.Builder ancestorBody, 3132 List<? extends TypeElement> resourceTypes, 3133 Consumer<Block.Builder> c) { 3134 resourceTypes = resourceTypes.stream().map(VarType::varType).toList(); 3135 Body.Builder resources = Body.Builder.of(ancestorBody, 3136 FunctionType.functionType(TupleType.tupleType(resourceTypes))); 3137 c.accept(resources.entryBlock()); 3138 return new JavaTryOp.BodyBuilder(ancestorBody, resourceTypes, resources); 3139 } 3140 3141 // resources ()Tuple<Var<R1>, Var<R2>, ..., Var<RN>>, or null 3142 // try (Var<R1>, Var<R2>, ..., Var<RN>)void, or try ()void 3143 // catch (E )void, where E <: Throwable 3144 // finally ()void, or null 3145 3146 /** 3147 * Creates a try or try-with-resources operation. 3148 * 3149 * @param resources the try body builder of the operation to be built and become its child, 3150 * may be null 3151 * @param body the try body builder of the operation to be built and become its child 3152 * @param catchers the catch body builders of the operation to be built and become its children 3153 * @param finalizer the finalizer body builder of the operation to be built and become its child 3154 * @return the try or try-with-resources operation 3155 */ 3156 public static JavaTryOp _try(Body.Builder resources, 3157 Body.Builder body, 3158 List<Body.Builder> catchers, 3159 Body.Builder finalizer) { 3160 return new JavaTryOp(resources, body, catchers, finalizer); 3161 } 3162 3163 // 3164 // Patterns 3165 3166 /** 3167 * Creates a pattern match operation. 3168 * 3169 * @param target the target value 3170 * @param pattern the pattern body builder of the operation to be built and become its child 3171 * @param match the match body builder of the operation to be built and become its child 3172 * @return the pattern match operation 3173 */ 3174 public static PatternOps.MatchOp match(Value target, 3175 Body.Builder pattern, Body.Builder match) { 3176 return new PatternOps.MatchOp(target, pattern, match); 3177 } 3178 3179 /** 3180 * Creates a pattern binding operation. 3181 * 3182 * @param type the type of value to be bound 3183 * @param bindingName the binding name 3184 * @return the pattern binding operation 3185 */ 3186 public static PatternOps.BindingPatternOp bindingPattern(TypeElement type, String bindingName) { 3187 return new PatternOps.BindingPatternOp(type, bindingName); 3188 } 3189 3190 /** 3191 * Creates a record pattern operation. 3192 * 3193 * @param recordDescriptor the record descriptor 3194 * @param nestedPatterns the nested pattern values 3195 * @return the record pattern operation 3196 */ 3197 public static PatternOps.RecordPatternOp recordPattern(RecordTypeRef recordDescriptor, Value... nestedPatterns) { 3198 return recordPattern(recordDescriptor, List.of(nestedPatterns)); 3199 } 3200 3201 /** 3202 * Creates a record pattern operation. 3203 * 3204 * @param recordDescriptor the record descriptor 3205 * @param nestedPatterns the nested pattern values 3206 * @return the record pattern operation 3207 */ 3208 public static PatternOps.RecordPatternOp recordPattern(RecordTypeRef recordDescriptor, List<Value> nestedPatterns) { 3209 return new PatternOps.RecordPatternOp(recordDescriptor, nestedPatterns); 3210 } 3211 3212 }