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