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.dialect.java; 27 28 import java.lang.constant.ClassDesc; 29 import jdk.incubator.code.*; 30 import jdk.incubator.code.extern.DialectFactory; 31 import jdk.incubator.code.dialect.core.*; 32 import jdk.incubator.code.extern.ExternalizedOp; 33 import jdk.incubator.code.extern.OpFactory; 34 import jdk.incubator.code.internal.OpDeclaration; 35 36 import java.util.*; 37 import java.util.concurrent.atomic.AtomicBoolean; 38 import java.util.function.BiFunction; 39 import java.util.function.Consumer; 40 import java.util.function.Function; 41 import java.util.function.Predicate; 42 import java.util.stream.Stream; 43 44 import static jdk.incubator.code.Op.Lowerable.*; 45 import static jdk.incubator.code.OpTransformer.*; 46 import static jdk.incubator.code.dialect.core.CoreOp.*; 47 import static jdk.incubator.code.dialect.java.JavaType.*; 48 49 /** 50 * The top-level operation class for Java operations. 51 * <p> 52 * A code model, produced by the Java compiler from Java program source, may consist of core operations and Java 53 * operations. Such a model represents the same Java program and preserves the program meaning as defined by the 54 * Java Language Specification 55 * <p> 56 * Java operations model specific Java language constructs or Java program behaviour. Some Java operations model 57 * structured control flow and nested code. These operations are transformable, commonly referred to as lowering, into 58 * a sequence of other core or Java operations. Those that implement {@link Op.Lowerable} can transform themselves and 59 * will transform associated operations that are not explicitly lowerable. 60 * <p> 61 * A code model, produced by the Java compiler from source, and consisting of core operations and Java operations 62 * can be transformed to one consisting only of non-lowerable operations, where all lowerable operations are lowered. 63 * This transformation preserves programing meaning. The resulting lowered code model also represents the same Java 64 * program. 65 */ 66 public sealed abstract class JavaOp extends Op { 67 68 protected JavaOp(Op that, CopyContext cc) { 69 super(that, cc); 70 } 71 72 protected JavaOp(String name, List<? extends Value> operands) { 73 super(name, operands); 74 } 75 76 /** 77 * An operation that models a Java expression 78 */ 79 public sealed interface JavaExpression permits 80 ArithmeticOperation, 81 ArrayAccessOp.ArrayLoadOp, 82 ArrayAccessOp.ArrayStoreOp, 83 ArrayLengthOp, 84 CastOp, 85 ConvOp, 86 ClosureOp, 87 ConcatOp, 88 ConstantOp, 89 FieldAccessOp.FieldLoadOp, 90 FieldAccessOp.FieldStoreOp, 91 InstanceOfOp, 92 InvokeOp, 93 LambdaOp, 94 NewOp, 95 TestOperation, 96 VarAccessOp.VarLoadOp, 97 VarAccessOp.VarStoreOp, 98 ConditionalExpressionOp, 99 JavaConditionalOp, 100 SwitchExpressionOp { 101 } 102 103 /** 104 * An operation that models a Java statement 105 */ 106 public sealed interface JavaStatement permits 107 ArrayAccessOp.ArrayStoreOp, 108 AssertOp, 109 FieldAccessOp.FieldStoreOp, 110 InvokeOp, 111 NewOp, 112 ReturnOp, 113 ThrowOp, 114 VarAccessOp.VarStoreOp, 115 VarOp, 116 BlockOp, 117 DoWhileOp, 118 EnhancedForOp, 119 ForOp, 120 IfOp, 121 JavaLabelOp, 122 LabeledOp, 123 SynchronizedOp, 124 TryOp, 125 WhileOp, 126 YieldOp, 127 SwitchStatementOp { 128 } 129 130 /** 131 * An operation characteristic indicating the operation's behavior may be emulated using Java reflection. 132 * A descriptor is derived from or declared by the operation that can be resolved at runtime to 133 * an instance of a reflective handle or member. That handle or member can be operated on to 134 * emulate the operation's behavior, specifically as bytecode behavior. 135 */ 136 public sealed interface ReflectiveOp { 137 } 138 139 /** 140 * An operation that performs access. 141 */ 142 public sealed interface AccessOp permits 143 CoreOp.VarAccessOp, 144 FieldAccessOp, 145 ArrayAccessOp { 146 } 147 148 149 150 /** 151 * The lambda operation, that can model a Java lambda expression. 152 */ 153 @OpDeclaration(LambdaOp.NAME) 154 public static final class LambdaOp extends JavaOp 155 implements Invokable, Lowerable, JavaExpression { 156 157 public static class Builder { 158 final Body.Builder ancestorBody; 159 final FunctionType funcType; 160 final TypeElement functionalInterface; 161 162 Builder(Body.Builder ancestorBody, FunctionType funcType, TypeElement functionalInterface) { 163 this.ancestorBody = ancestorBody; 164 this.funcType = funcType; 165 this.functionalInterface = functionalInterface; 166 } 167 168 public LambdaOp body(Consumer<Block.Builder> c) { 169 Body.Builder body = Body.Builder.of(ancestorBody, funcType); 170 c.accept(body.entryBlock()); 171 return new LambdaOp(functionalInterface, body); 172 } 173 } 174 175 static final String NAME = "lambda"; 176 177 final TypeElement functionalInterface; 178 final Body body; 179 180 LambdaOp(ExternalizedOp def) { 181 this(def.resultType(), def.bodyDefinitions().get(0)); 182 } 183 184 LambdaOp(LambdaOp that, CopyContext cc, OpTransformer ot) { 185 super(that, cc); 186 187 this.functionalInterface = that.functionalInterface; 188 this.body = that.body.transform(cc, ot).build(this); 189 } 190 191 @Override 192 public LambdaOp transform(CopyContext cc, OpTransformer ot) { 193 return new LambdaOp(this, cc, ot); 194 } 195 196 LambdaOp(TypeElement functionalInterface, Body.Builder bodyC) { 197 super(NAME, 198 List.of()); 199 200 this.functionalInterface = functionalInterface; 201 this.body = bodyC.build(this); 202 } 203 204 @Override 205 public List<Body> bodies() { 206 return List.of(body); 207 } 208 209 @Override 210 public FunctionType invokableType() { 211 return body.bodyType(); 212 } 213 214 public TypeElement functionalInterface() { 215 return functionalInterface; 216 } 217 218 @Override 219 public Body body() { 220 return body; 221 } 222 223 @Override 224 public List<Value> capturedValues() { 225 return body.capturedValues(); 226 } 227 228 @Override 229 public Block.Builder lower(Block.Builder b, OpTransformer _ignore) { 230 // Isolate body with respect to ancestor transformations 231 b.rebind(b.context(), OpTransformer.LOWERING_TRANSFORMER).op(this); 232 return b; 233 } 234 235 @Override 236 public TypeElement resultType() { 237 return functionalInterface(); 238 } 239 240 /** 241 * Determines if this lambda operation could have originated from a 242 * method reference declared in Java source code. 243 * <p> 244 * Such a lambda operation is one with the following constraints: 245 * <ol> 246 * <li>Zero or one captured value (assuming correspondence to the {@code this} variable). 247 * <li>A body with only one (entry) block that contains only variable declaration 248 * operations, variable load operations, invoke operations to box or unbox 249 * primitive values, a single invoke operation to the method that is 250 * referenced, and a return operation. 251 * <li>if the return operation returns a non-void result then that result is, 252 * or uniquely depends on, the result of the referencing invoke operation. 253 * <li>If the lambda operation captures one value then the first operand corresponds 254 * to captured the value, and subsequent operands of the referencing invocation 255 * operation are, or uniquely depend on, the lambda operation's parameters, in order. 256 * Otherwise, the first and subsequent operands of the referencing invocation 257 * operation are, or uniquely depend on, the lambda operation's parameters, in order. 258 * </ol> 259 * A value, V2, uniquely depends on another value, V1, if the graph of what V2 depends on 260 * contains only nodes with single edges terminating in V1, and the graph of what depends on V1 261 * is bidirectionally equal to the graph of what V2 depends on. 262 * 263 * @return the invocation operation to the method referenced by the lambda 264 * operation, otherwise empty. 265 */ 266 public Optional<InvokeOp> methodReference() { 267 // Single block 268 if (body().blocks().size() > 1) { 269 return Optional.empty(); 270 } 271 272 // Zero or one (this) capture 273 List<Value> cvs = capturedValues(); 274 if (cvs.size() > 1) { 275 return Optional.empty(); 276 } 277 278 Map<Value, Value> valueMapping = new HashMap<>(); 279 InvokeOp methodRefInvokeOp = extractMethodInvoke(valueMapping, body().entryBlock().ops()); 280 if (methodRefInvokeOp == null) { 281 return Optional.empty(); 282 } 283 284 // Lambda's parameters map in encounter order with the invocation's operands 285 List<Value> lambdaParameters = new ArrayList<>(); 286 if (cvs.size() == 1) { 287 lambdaParameters.add(cvs.getFirst()); 288 } 289 lambdaParameters.addAll(parameters()); 290 List<Value> methodRefOperands = methodRefInvokeOp.operands().stream().map(valueMapping::get).toList(); 291 if (!lambdaParameters.equals(methodRefOperands)) { 292 return Optional.empty(); 293 } 294 295 return Optional.of(methodRefInvokeOp); 296 } 297 298 static InvokeOp extractMethodInvoke(Map<Value, Value> valueMapping, List<Op> ops) { 299 InvokeOp methodRefInvokeOp = null; 300 for (Op op : ops) { 301 switch (op) { 302 case VarOp varOp -> { 303 if (isValueUsedWithOp(varOp.result(), o -> o instanceof VarAccessOp.VarStoreOp)) { 304 return null; 305 } 306 } 307 case VarAccessOp.VarLoadOp varLoadOp -> { 308 Value v = varLoadOp.varOp().operands().getFirst(); 309 valueMapping.put(varLoadOp.result(), valueMapping.getOrDefault(v, v)); 310 } 311 case InvokeOp iop when isBoxOrUnboxInvocation(iop) -> { 312 Value v = iop.operands().getFirst(); 313 valueMapping.put(iop.result(), valueMapping.getOrDefault(v, v)); 314 } 315 case InvokeOp iop -> { 316 if (methodRefInvokeOp != null) { 317 return null; 318 } 319 320 for (Value o : iop.operands()) { 321 valueMapping.put(o, valueMapping.getOrDefault(o, o)); 322 } 323 methodRefInvokeOp = iop; 324 } 325 case ReturnOp rop -> { 326 if (methodRefInvokeOp == null) { 327 return null; 328 } 329 Value r = rop.returnValue(); 330 if (!(valueMapping.getOrDefault(r, r) instanceof Result invokeResult)) { 331 return null; 332 } 333 if (invokeResult.op() != methodRefInvokeOp) { 334 return null; 335 } 336 assert methodRefInvokeOp.result().uses().size() == 1; 337 } 338 default -> { 339 return null; 340 } 341 } 342 } 343 344 return methodRefInvokeOp; 345 } 346 347 private static boolean isValueUsedWithOp(Value value, Predicate<Op> opPredicate) { 348 for (Result user : value.uses()) { 349 if (opPredicate.test(user.op())) { 350 return true; 351 } 352 } 353 return false; 354 } 355 356 // @@@ Move to functionality on JavaType(s) 357 static final Set<String> UNBOX_NAMES = Set.of( 358 "byteValue", 359 "shortValue", 360 "charValue", 361 "intValue", 362 "longValue", 363 "floatValue", 364 "doubleValue", 365 "booleanValue"); 366 367 private static boolean isBoxOrUnboxInvocation(InvokeOp iop) { 368 MethodRef mr = iop.invokeDescriptor(); 369 return mr.refType() instanceof ClassType ct && ct.unbox().isPresent() && 370 (UNBOX_NAMES.contains(mr.name()) || mr.name().equals("valueOf")); 371 } 372 } 373 374 /** 375 * The terminating throw operation, that can model the Java language throw statement. 376 */ 377 @OpDeclaration(ThrowOp.NAME) 378 public static final class ThrowOp extends JavaOp 379 implements BodyTerminating, JavaStatement { 380 static final String NAME = "throw"; 381 382 ThrowOp(ExternalizedOp def) { 383 if (def.operands().size() != 1) { 384 throw new IllegalArgumentException("Operation must have one operand " + def.name()); 385 } 386 387 this(def.operands().get(0)); 388 } 389 390 ThrowOp(ThrowOp that, CopyContext cc) { 391 super(that, cc); 392 } 393 394 @Override 395 public ThrowOp transform(CopyContext cc, OpTransformer ot) { 396 return new ThrowOp(this, cc); 397 } 398 399 ThrowOp(Value e) { 400 super(NAME, List.of(e)); 401 } 402 403 public Value argument() { 404 return operands().get(0); 405 } 406 407 @Override 408 public TypeElement resultType() { 409 return VOID; 410 } 411 } 412 413 /** 414 * The assertion operation. Supporting assertions in statement form. 415 */ 416 @OpDeclaration(AssertOp.NAME) 417 public static final class AssertOp extends JavaOp 418 implements Nested, JavaStatement { 419 static final String NAME = "assert"; 420 public final List<Body> bodies; 421 422 AssertOp(ExternalizedOp def) { 423 this(def.bodyDefinitions()); 424 } 425 426 public AssertOp(List<Body.Builder> bodies) { 427 super(NAME, List.of()); 428 429 if (bodies.size() != 1 && bodies.size() != 2) { 430 throw new IllegalArgumentException("Assert must have one or two bodies."); 431 } 432 this.bodies = bodies.stream().map(b -> b.build(this)).toList(); 433 } 434 435 AssertOp(AssertOp that, CopyContext cc, OpTransformer ot) { 436 super(that, cc); 437 this.bodies = that.bodies.stream().map(b -> b.transform(cc, ot).build(this)).toList(); 438 } 439 440 @Override 441 public Op transform(CopyContext cc, OpTransformer ot) { 442 return new AssertOp(this, cc, ot); 443 } 444 445 @Override 446 public TypeElement resultType() { 447 return VOID; 448 } 449 450 @Override 451 public List<Body> bodies() { 452 return this.bodies; 453 } 454 } 455 456 /** 457 * A monitor operation. 458 */ 459 public sealed abstract static class MonitorOp extends JavaOp { 460 MonitorOp(MonitorOp that, CopyContext cc) { 461 super(that, cc); 462 } 463 464 MonitorOp(String name, Value monitor) { 465 super(name, List.of(monitor)); 466 } 467 468 public Value monitorValue() { 469 return operands().getFirst(); 470 } 471 472 @Override 473 public TypeElement resultType() { 474 return VOID; 475 } 476 477 /** 478 * The monitor enter operation. 479 */ 480 @OpDeclaration(MonitorEnterOp.NAME) 481 public static final class MonitorEnterOp extends MonitorOp { 482 static final String NAME = "monitor.enter"; 483 484 MonitorEnterOp(ExternalizedOp def) { 485 if (def.operands().size() != 1) { 486 throw new IllegalArgumentException("Operation must have one operand " + def.name()); 487 } 488 489 this(def.operands().get(0)); 490 } 491 492 MonitorEnterOp(MonitorEnterOp that, CopyContext cc) { 493 super(that, cc); 494 } 495 496 @Override 497 public MonitorEnterOp transform(CopyContext cc, OpTransformer ot) { 498 return new MonitorEnterOp(this, cc); 499 } 500 501 MonitorEnterOp(Value monitor) { 502 super(NAME, monitor); 503 } 504 } 505 506 /** 507 * The monitor exit operation. 508 */ 509 @OpDeclaration(MonitorExitOp.NAME) 510 public static final class MonitorExitOp extends MonitorOp { 511 static final String NAME = "monitor.exit"; 512 513 MonitorExitOp(ExternalizedOp def) { 514 if (def.operands().size() != 1) { 515 throw new IllegalArgumentException("Operation must have one operand " + def.name()); 516 } 517 518 this(def.operands().get(0)); 519 } 520 521 MonitorExitOp(MonitorExitOp that, CopyContext cc) { 522 super(that, cc); 523 } 524 525 @Override 526 public MonitorExitOp transform(CopyContext cc, OpTransformer ot) { 527 return new MonitorExitOp(this, cc); 528 } 529 530 MonitorExitOp(Value monitor) { 531 super(NAME, monitor); 532 } 533 } 534 } 535 536 /** 537 * The invoke operation, that can model Java language method invocation expressions. 538 */ 539 @OpDeclaration(InvokeOp.NAME) 540 public static final class InvokeOp extends JavaOp 541 implements ReflectiveOp, JavaExpression, JavaStatement { 542 543 /** 544 * The kind of invocation. 545 */ 546 public enum InvokeKind { 547 /** 548 * An invocation on a class (static) method. 549 */ 550 STATIC, 551 /** 552 * An invocation on an instance method. 553 */ 554 INSTANCE, 555 /** 556 * A super invocation on an instance method. 557 */ 558 SUPER 559 } 560 561 static final String NAME = "invoke"; 562 public static final String ATTRIBUTE_INVOKE_DESCRIPTOR = NAME + ".descriptor"; 563 public static final String ATTRIBUTE_INVOKE_KIND = NAME + ".kind"; 564 public static final String ATTRIBUTE_INVOKE_VARARGS = NAME + ".varargs"; 565 566 final InvokeKind invokeKind; 567 final boolean isVarArgs; 568 final MethodRef invokeDescriptor; 569 final TypeElement resultType; 570 571 static InvokeOp create(ExternalizedOp def) { 572 // Required attribute 573 MethodRef invokeDescriptor = def.extractAttributeValue(ATTRIBUTE_INVOKE_DESCRIPTOR, 574 true, v -> switch (v) { 575 case MethodRef md -> md; 576 case null, default -> 577 throw new UnsupportedOperationException("Unsupported invoke descriptor value:" + v); 578 }); 579 580 // If not present defaults to false 581 boolean isVarArgs = def.extractAttributeValue(ATTRIBUTE_INVOKE_VARARGS, 582 false, v -> switch (v) { 583 case Boolean b -> b; 584 case null, default -> false; 585 }); 586 587 // If not present and is not varargs defaults to class or instance invocation 588 // based on number of operands and parameters 589 InvokeKind ik = def.extractAttributeValue(ATTRIBUTE_INVOKE_KIND, 590 false, v -> switch (v) { 591 case String s -> InvokeKind.valueOf(s); 592 case InvokeKind k -> k; 593 case null, default -> { 594 if (isVarArgs) { 595 // If varargs then we cannot infer invoke kind 596 throw new UnsupportedOperationException("Unsupported invoke kind value:" + v); 597 } 598 int paramCount = invokeDescriptor.type().parameterTypes().size(); 599 int argCount = def.operands().size(); 600 yield (argCount == paramCount + 1) 601 ? InvokeKind.INSTANCE 602 : InvokeKind.STATIC; 603 } 604 }); 605 606 607 return new InvokeOp(ik, isVarArgs, def.resultType(), invokeDescriptor, def.operands()); 608 } 609 610 InvokeOp(InvokeOp that, CopyContext cc) { 611 super(that, cc); 612 613 this.invokeKind = that.invokeKind; 614 this.isVarArgs = that.isVarArgs; 615 this.invokeDescriptor = that.invokeDescriptor; 616 this.resultType = that.resultType; 617 } 618 619 @Override 620 public InvokeOp transform(CopyContext cc, OpTransformer ot) { 621 return new InvokeOp(this, cc); 622 } 623 624 InvokeOp(InvokeKind invokeKind, boolean isVarArgs, TypeElement resultType, MethodRef invokeDescriptor, List<Value> args) { 625 super(NAME, args); 626 627 validateArgCount(invokeKind, isVarArgs, invokeDescriptor, args); 628 629 this.invokeKind = invokeKind; 630 this.isVarArgs = isVarArgs; 631 this.invokeDescriptor = invokeDescriptor; 632 this.resultType = resultType; 633 } 634 635 static void validateArgCount(InvokeKind invokeKind, boolean isVarArgs, MethodRef invokeDescriptor, List<Value> operands) { 636 int paramCount = invokeDescriptor.type().parameterTypes().size(); 637 int argCount = operands.size() - (invokeKind == InvokeKind.STATIC ? 0 : 1); 638 if ((!isVarArgs && argCount != paramCount) 639 || argCount < paramCount - 1) { 640 throw new IllegalArgumentException(invokeKind + " " + isVarArgs + " " + invokeDescriptor); 641 } 642 } 643 644 @Override 645 public Map<String, Object> externalize() { 646 HashMap<String, Object> m = new HashMap<>(); 647 m.put("", invokeDescriptor); 648 if (isVarArgs) { 649 // If varargs then we need to declare the invoke.kind attribute 650 // Given a method `A::m(A... more)` and an invocation with one 651 // operand, we don't know if that operand corresponds to the 652 // receiver or a method argument 653 m.put(ATTRIBUTE_INVOKE_KIND, invokeKind); 654 m.put(ATTRIBUTE_INVOKE_VARARGS, isVarArgs); 655 } else if (invokeKind == InvokeKind.SUPER) { 656 m.put(ATTRIBUTE_INVOKE_KIND, invokeKind); 657 } 658 return Collections.unmodifiableMap(m); 659 } 660 661 public InvokeKind invokeKind() { 662 return invokeKind; 663 } 664 665 public boolean isVarArgs() { 666 return isVarArgs; 667 } 668 669 public MethodRef invokeDescriptor() { 670 return invokeDescriptor; 671 } 672 673 // @@@ remove? 674 public boolean hasReceiver() { 675 return invokeKind != InvokeKind.STATIC; 676 } 677 678 public List<Value> varArgOperands() { 679 if (!isVarArgs) { 680 return null; 681 } 682 683 int operandCount = operands().size(); 684 int argCount = operandCount - (invokeKind == InvokeKind.STATIC ? 0 : 1); 685 int paramCount = invokeDescriptor.type().parameterTypes().size(); 686 int varArgCount = argCount - (paramCount - 1); 687 return operands().subList(operandCount - varArgCount, operandCount); 688 } 689 690 public List<Value> argOperands() { 691 if (!isVarArgs) { 692 return operands(); 693 } 694 int paramCount = invokeDescriptor().type().parameterTypes().size(); 695 int argOperandsCount = paramCount - (invokeKind() == InvokeKind.STATIC ? 1 : 0); 696 return operands().subList(0, argOperandsCount); 697 } 698 699 @Override 700 public TypeElement resultType() { 701 return resultType; 702 } 703 } 704 705 /** 706 * The conversion operation, that can model Java language cast expressions 707 * for numerical conversion, or such implicit conversion. 708 */ 709 @OpDeclaration(ConvOp.NAME) 710 public static final class ConvOp extends JavaOp 711 implements Pure, JavaExpression { 712 static final String NAME = "conv"; 713 714 final TypeElement resultType; 715 716 ConvOp(ExternalizedOp def) { 717 this(def.resultType(), def.operands().get(0)); 718 } 719 720 ConvOp(ConvOp that, CopyContext cc) { 721 super(that, cc); 722 723 this.resultType = that.resultType; 724 } 725 726 @Override 727 public Op transform(CopyContext cc, OpTransformer ot) { 728 return new ConvOp(this, cc); 729 } 730 731 ConvOp(TypeElement resultType, Value arg) { 732 super(NAME, List.of(arg)); 733 734 this.resultType = resultType; 735 } 736 737 @Override 738 public TypeElement resultType() { 739 return resultType; 740 } 741 } 742 743 /** 744 * The new operation, that can models Java language instance creation expressions. 745 */ 746 @OpDeclaration(NewOp.NAME) 747 public static final class NewOp extends JavaOp 748 implements ReflectiveOp, JavaExpression, JavaStatement { 749 750 static final String NAME = "new"; 751 public static final String ATTRIBUTE_NEW_DESCRIPTOR = NAME + ".descriptor"; 752 public static final String ATTRIBUTE_NEW_VARARGS = NAME + ".varargs"; 753 754 final boolean isVarArgs; 755 final ConstructorRef constructorDescriptor; 756 final TypeElement resultType; 757 758 static NewOp create(ExternalizedOp def) { 759 // Required attribute 760 ConstructorRef constructorDescriptor = def.extractAttributeValue(ATTRIBUTE_NEW_DESCRIPTOR, 761 true, v -> switch (v) { 762 case ConstructorRef cd -> cd; 763 case null, default -> 764 throw new UnsupportedOperationException("Unsupported constructor descriptor value:" + v); 765 }); 766 767 // If not present defaults to false 768 boolean isVarArgs = def.extractAttributeValue(ATTRIBUTE_NEW_VARARGS, 769 false, v -> switch (v) { 770 case Boolean b -> b; 771 case null, default -> false; 772 }); 773 774 return new NewOp(isVarArgs, def.resultType(), constructorDescriptor, def.operands()); 775 } 776 777 NewOp(NewOp that, CopyContext cc) { 778 super(that, cc); 779 780 this.isVarArgs = that.isVarArgs; 781 this.constructorDescriptor = that.constructorDescriptor; 782 this.resultType = that.resultType; 783 } 784 785 @Override 786 public NewOp transform(CopyContext cc, OpTransformer ot) { 787 return new NewOp(this, cc); 788 } 789 790 NewOp(boolean isVarargs, TypeElement resultType, ConstructorRef constructorDescriptor, List<Value> args) { 791 super(NAME, args); 792 793 validateArgCount(isVarargs, constructorDescriptor, args); 794 795 this.isVarArgs = isVarargs; 796 this.constructorDescriptor = constructorDescriptor; 797 this.resultType = resultType; 798 } 799 800 static void validateArgCount(boolean isVarArgs, ConstructorRef constructorDescriptor, List<Value> operands) { 801 int paramCount = constructorDescriptor.type().parameterTypes().size(); 802 int argCount = operands.size(); 803 if ((!isVarArgs && argCount != paramCount) 804 || argCount < paramCount - 1) { 805 throw new IllegalArgumentException(isVarArgs + " " + constructorDescriptor); 806 } 807 } 808 809 @Override 810 public Map<String, Object> externalize() { 811 HashMap<String, Object> m = new HashMap<>(); 812 m.put("", constructorDescriptor); 813 if (isVarArgs) { 814 m.put(ATTRIBUTE_NEW_VARARGS, isVarArgs); 815 } 816 return Collections.unmodifiableMap(m); 817 } 818 819 public boolean isVarargs() { 820 return isVarArgs; 821 } 822 823 public TypeElement type() { 824 return opType().returnType(); 825 } // @@@ duplication, same as resultType() 826 827 public ConstructorRef constructorDescriptor() { 828 return constructorDescriptor; 829 } 830 831 @Override 832 public TypeElement resultType() { 833 return resultType; 834 } 835 } 836 837 /** 838 * A field access operation, that can model Java langauge field access expressions. 839 */ 840 public sealed abstract static class FieldAccessOp extends JavaOp 841 implements AccessOp, ReflectiveOp { 842 public static final String ATTRIBUTE_FIELD_DESCRIPTOR = "field.descriptor"; 843 844 final FieldRef fieldDescriptor; 845 846 FieldAccessOp(FieldAccessOp that, CopyContext cc) { 847 super(that, cc); 848 849 this.fieldDescriptor = that.fieldDescriptor; 850 } 851 852 FieldAccessOp(String name, List<Value> operands, 853 FieldRef fieldDescriptor) { 854 super(name, operands); 855 856 this.fieldDescriptor = fieldDescriptor; 857 } 858 859 @Override 860 public Map<String, Object> externalize() { 861 return Map.of("", fieldDescriptor); 862 } 863 864 public final FieldRef fieldDescriptor() { 865 return fieldDescriptor; 866 } 867 868 /** 869 * The field load operation, that can model Java language field access expressions combined with load access to 870 * field instance variables. 871 */ 872 @OpDeclaration(FieldLoadOp.NAME) 873 public static final class FieldLoadOp extends FieldAccessOp 874 implements Pure, JavaExpression { 875 static final String NAME = "field.load"; 876 877 final TypeElement resultType; 878 879 static FieldLoadOp create(ExternalizedOp def) { 880 if (def.operands().size() > 1) { 881 throw new IllegalArgumentException("Operation must accept zero or one operand"); 882 } 883 884 FieldRef fieldDescriptor = def.extractAttributeValue(ATTRIBUTE_FIELD_DESCRIPTOR, true, 885 v -> switch (v) { 886 case FieldRef fd -> fd; 887 case null, default -> 888 throw new UnsupportedOperationException("Unsupported field descriptor value:" + v); 889 }); 890 if (def.operands().isEmpty()) { 891 return new FieldLoadOp(def.resultType(), fieldDescriptor); 892 } else { 893 return new FieldLoadOp(def.resultType(), fieldDescriptor, def.operands().get(0)); 894 } 895 } 896 897 FieldLoadOp(FieldLoadOp that, CopyContext cc) { 898 super(that, cc); 899 900 resultType = that.resultType(); 901 } 902 903 @Override 904 public FieldLoadOp transform(CopyContext cc, OpTransformer ot) { 905 return new FieldLoadOp(this, cc); 906 } 907 908 // instance 909 FieldLoadOp(TypeElement resultType, FieldRef descriptor, Value receiver) { 910 super(NAME, List.of(receiver), descriptor); 911 912 this.resultType = resultType; 913 } 914 915 // static 916 FieldLoadOp(TypeElement resultType, FieldRef descriptor) { 917 super(NAME, List.of(), descriptor); 918 919 this.resultType = resultType; 920 } 921 922 @Override 923 public TypeElement resultType() { 924 return resultType; 925 } 926 } 927 928 /** 929 * The field store operation, that can model Java language field access expressions combined with store access 930 * to field instance variables. 931 */ 932 @OpDeclaration(FieldStoreOp.NAME) 933 public static final class FieldStoreOp extends FieldAccessOp 934 implements JavaExpression, JavaStatement { 935 static final String NAME = "field.store"; 936 937 static FieldStoreOp create(ExternalizedOp def) { 938 if (def.operands().isEmpty() || def.operands().size() > 2) { 939 throw new IllegalArgumentException("Operation must accept one or two operands"); 940 } 941 942 FieldRef fieldDescriptor = def.extractAttributeValue(ATTRIBUTE_FIELD_DESCRIPTOR, true, 943 v -> switch (v) { 944 case FieldRef fd -> fd; 945 case null, default -> 946 throw new UnsupportedOperationException("Unsupported field descriptor value:" + v); 947 }); 948 if (def.operands().size() == 1) { 949 return new FieldStoreOp(fieldDescriptor, def.operands().get(0)); 950 } else { 951 return new FieldStoreOp(fieldDescriptor, def.operands().get(0), def.operands().get(1)); 952 } 953 } 954 955 FieldStoreOp(FieldStoreOp that, CopyContext cc) { 956 super(that, cc); 957 } 958 959 @Override 960 public FieldStoreOp transform(CopyContext cc, OpTransformer ot) { 961 return new FieldStoreOp(this, cc); 962 } 963 964 // instance 965 FieldStoreOp(FieldRef descriptor, Value receiver, Value v) { 966 super(NAME, List.of(receiver, v), descriptor); 967 } 968 969 // static 970 FieldStoreOp(FieldRef descriptor, Value v) { 971 super(NAME, List.of(v), descriptor); 972 } 973 974 @Override 975 public TypeElement resultType() { 976 return VOID; 977 } 978 } 979 } 980 981 /** 982 * The array length operation, that can model Java language field access expressions to the length field of an 983 * array. 984 */ 985 @OpDeclaration(ArrayLengthOp.NAME) 986 public static final class ArrayLengthOp extends JavaOp 987 implements ReflectiveOp, JavaExpression { 988 static final String NAME = "array.length"; 989 990 ArrayLengthOp(ExternalizedOp def) { 991 this(def.operands().get(0)); 992 } 993 994 ArrayLengthOp(ArrayLengthOp that, CopyContext cc) { 995 super(that, cc); 996 } 997 998 @Override 999 public ArrayLengthOp transform(CopyContext cc, OpTransformer ot) { 1000 return new ArrayLengthOp(this, cc); 1001 } 1002 1003 ArrayLengthOp(Value array) { 1004 super(NAME, List.of(array)); 1005 } 1006 1007 @Override 1008 public TypeElement resultType() { 1009 return INT; 1010 } 1011 } 1012 1013 /** 1014 * The array access operation, that can model Java language array access expressions. 1015 */ 1016 public sealed abstract static class ArrayAccessOp extends JavaOp 1017 implements AccessOp, ReflectiveOp { 1018 1019 ArrayAccessOp(ArrayAccessOp that, CopyContext cc) { 1020 this(that, cc.getValues(that.operands())); 1021 } 1022 1023 ArrayAccessOp(ArrayAccessOp that, List<Value> operands) { 1024 super(that.opName(), operands); 1025 } 1026 1027 ArrayAccessOp(String name, 1028 Value array, Value index, Value v) { 1029 super(name, operands(array, index, v)); 1030 } 1031 1032 static List<Value> operands(Value array, Value index, Value v) { 1033 return v == null 1034 ? List.of(array, index) 1035 : List.of(array, index, v); 1036 } 1037 1038 /** 1039 * The array load operation, that can model Java language array expressions combined with load access to the 1040 * components of an array. 1041 */ 1042 @OpDeclaration(ArrayLoadOp.NAME) 1043 public static final class ArrayLoadOp extends ArrayAccessOp 1044 implements Pure, JavaExpression { 1045 static final String NAME = "array.load"; 1046 final TypeElement componentType; 1047 1048 ArrayLoadOp(ExternalizedOp def) { 1049 if (def.operands().size() != 2) { 1050 throw new IllegalArgumentException("Operation must have two operands"); 1051 } 1052 1053 this(def.operands().get(0), def.operands().get(1), def.resultType()); 1054 } 1055 1056 ArrayLoadOp(ArrayLoadOp that, CopyContext cc) { 1057 super(that, cc); 1058 this.componentType = that.componentType; 1059 } 1060 1061 @Override 1062 public ArrayLoadOp transform(CopyContext cc, OpTransformer ot) { 1063 return new ArrayLoadOp(this, cc); 1064 } 1065 1066 ArrayLoadOp(Value array, Value index) { 1067 // @@@ revisit this when the component type is not explicitly given (see VarOp.resultType as an example) 1068 this(array, index, ((ArrayType)array.type()).componentType()); 1069 } 1070 1071 ArrayLoadOp(Value array, Value index, TypeElement componentType) { 1072 super(NAME, array, index, null); 1073 this.componentType = componentType; 1074 } 1075 1076 @Override 1077 public TypeElement resultType() { 1078 return componentType; 1079 } 1080 } 1081 1082 /** 1083 * The array store operation, that can model Java language array expressions combined with store access to the 1084 * components of an array. 1085 */ 1086 @OpDeclaration(ArrayStoreOp.NAME) 1087 public static final class ArrayStoreOp extends ArrayAccessOp 1088 implements JavaExpression, JavaStatement { 1089 static final String NAME = "array.store"; 1090 1091 ArrayStoreOp(ExternalizedOp def) { 1092 if (def.operands().size() != 3) { 1093 throw new IllegalArgumentException("Operation must have two operands"); 1094 } 1095 1096 this(def.operands().get(0), def.operands().get(1), def.operands().get(2)); 1097 } 1098 1099 ArrayStoreOp(ArrayStoreOp that, CopyContext cc) { 1100 super(that, cc); 1101 } 1102 1103 @Override 1104 public ArrayStoreOp transform(CopyContext cc, OpTransformer ot) { 1105 return new ArrayStoreOp(this, cc); 1106 } 1107 1108 ArrayStoreOp(Value array, Value index, Value v) { 1109 super(NAME, array, index, v); 1110 } 1111 1112 @Override 1113 public TypeElement resultType() { 1114 return VOID; 1115 } 1116 } 1117 } 1118 1119 /** 1120 * The instanceof operation, that can model Java language instanceof expressions when the instanceof keyword is a 1121 * type comparison operator. 1122 */ 1123 @OpDeclaration(InstanceOfOp.NAME) 1124 public static final class InstanceOfOp extends JavaOp 1125 implements Pure, ReflectiveOp, JavaExpression { 1126 static final String NAME = "instanceof"; 1127 public static final String ATTRIBUTE_TYPE_DESCRIPTOR = NAME + ".descriptor"; 1128 1129 final TypeElement typeDescriptor; 1130 1131 static InstanceOfOp create(ExternalizedOp def) { 1132 if (def.operands().size() != 1) { 1133 throw new IllegalArgumentException("Operation must have one operand " + def.name()); 1134 } 1135 1136 TypeElement typeDescriptor = def.extractAttributeValue(ATTRIBUTE_TYPE_DESCRIPTOR, true, 1137 v -> switch (v) { 1138 case JavaType td -> td; 1139 case null, default -> throw new UnsupportedOperationException("Unsupported type descriptor value:" + v); 1140 }); 1141 return new InstanceOfOp(typeDescriptor, def.operands().get(0)); 1142 } 1143 1144 InstanceOfOp(InstanceOfOp that, CopyContext cc) { 1145 super(that, cc); 1146 1147 this.typeDescriptor = that.typeDescriptor; 1148 } 1149 1150 @Override 1151 public InstanceOfOp transform(CopyContext cc, OpTransformer ot) { 1152 return new InstanceOfOp(this, cc); 1153 } 1154 1155 InstanceOfOp(TypeElement t, Value v) { 1156 super(NAME, List.of(v)); 1157 1158 this.typeDescriptor = t; 1159 } 1160 1161 @Override 1162 public Map<String, Object> externalize() { 1163 return Map.of("", typeDescriptor); 1164 } 1165 1166 public TypeElement type() { 1167 return typeDescriptor; 1168 } 1169 1170 @Override 1171 public TypeElement resultType() { 1172 return BOOLEAN; 1173 } 1174 } 1175 1176 /** 1177 * The cast operation, that can model Java language cast expressions for reference types. 1178 */ 1179 @OpDeclaration(CastOp.NAME) 1180 public static final class CastOp extends JavaOp 1181 implements Pure, ReflectiveOp, JavaExpression { 1182 static final String NAME = "cast"; 1183 public static final String ATTRIBUTE_TYPE_DESCRIPTOR = NAME + ".descriptor"; 1184 1185 final TypeElement resultType; 1186 final TypeElement typeDescriptor; 1187 1188 static CastOp create(ExternalizedOp def) { 1189 if (def.operands().size() != 1) { 1190 throw new IllegalArgumentException("Operation must have one operand " + def.name()); 1191 } 1192 1193 TypeElement type = def.extractAttributeValue(ATTRIBUTE_TYPE_DESCRIPTOR, true, 1194 v -> switch (v) { 1195 case JavaType td -> td; 1196 case null, default -> throw new UnsupportedOperationException("Unsupported type descriptor value:" + v); 1197 }); 1198 return new CastOp(def.resultType(), type, def.operands().get(0)); 1199 } 1200 1201 CastOp(CastOp that, CopyContext cc) { 1202 super(that, cc); 1203 1204 this.resultType = that.resultType; 1205 this.typeDescriptor = that.typeDescriptor; 1206 } 1207 1208 @Override 1209 public CastOp transform(CopyContext cc, OpTransformer ot) { 1210 return new CastOp(this, cc); 1211 } 1212 1213 CastOp(TypeElement resultType, TypeElement t, Value v) { 1214 super(NAME, List.of(v)); 1215 1216 this.resultType = resultType; 1217 this.typeDescriptor = t; 1218 } 1219 1220 @Override 1221 public Map<String, Object> externalize() { 1222 return Map.of("", typeDescriptor); 1223 } 1224 1225 public TypeElement type() { 1226 return typeDescriptor; 1227 } 1228 1229 @Override 1230 public TypeElement resultType() { 1231 return resultType; 1232 } 1233 } 1234 1235 /** 1236 * The exception region start operation. 1237 */ 1238 @OpDeclaration(ExceptionRegionEnter.NAME) 1239 public static final class ExceptionRegionEnter extends JavaOp 1240 implements BlockTerminating { 1241 static final String NAME = "exception.region.enter"; 1242 1243 // First successor is the non-exceptional successor whose target indicates 1244 // the first block in the exception region. 1245 // One or more subsequent successors target the exception catching blocks 1246 // each of which have one block argument whose type is an exception type. 1247 final List<Block.Reference> s; 1248 1249 ExceptionRegionEnter(ExternalizedOp def) { 1250 this(def.successors()); 1251 } 1252 1253 ExceptionRegionEnter(ExceptionRegionEnter that, CopyContext cc) { 1254 super(that, cc); 1255 1256 this.s = that.s.stream().map(cc::getSuccessorOrCreate).toList(); 1257 } 1258 1259 @Override 1260 public ExceptionRegionEnter transform(CopyContext cc, OpTransformer ot) { 1261 return new ExceptionRegionEnter(this, cc); 1262 } 1263 1264 ExceptionRegionEnter(List<Block.Reference> s) { 1265 super(NAME, List.of()); 1266 1267 if (s.size() < 2) { 1268 throw new IllegalArgumentException("Operation must have two or more successors" + opName()); 1269 } 1270 1271 this.s = List.copyOf(s); 1272 } 1273 1274 @Override 1275 public List<Block.Reference> successors() { 1276 return s; 1277 } 1278 1279 public Block.Reference start() { 1280 return s.get(0); 1281 } 1282 1283 public List<Block.Reference> catchBlocks() { 1284 return s.subList(1, s.size()); 1285 } 1286 1287 @Override 1288 public TypeElement resultType() { 1289 return VOID; 1290 } 1291 } 1292 1293 /** 1294 * The exception region end operation. 1295 */ 1296 @OpDeclaration(ExceptionRegionExit.NAME) 1297 public static final class ExceptionRegionExit extends JavaOp 1298 implements BlockTerminating { 1299 static final String NAME = "exception.region.exit"; 1300 1301 // First successor is the non-exceptional successor whose target indicates 1302 // the first block following the exception region. 1303 final List<Block.Reference> s; 1304 1305 ExceptionRegionExit(ExternalizedOp def) { 1306 this(def.successors()); 1307 } 1308 1309 ExceptionRegionExit(ExceptionRegionExit that, CopyContext cc) { 1310 super(that, cc); 1311 1312 this.s = that.s.stream().map(cc::getSuccessorOrCreate).toList(); 1313 } 1314 1315 @Override 1316 public ExceptionRegionExit transform(CopyContext cc, OpTransformer ot) { 1317 return new ExceptionRegionExit(this, cc); 1318 } 1319 1320 ExceptionRegionExit(List<Block.Reference> s) { 1321 super(NAME, List.of()); 1322 1323 if (s.size() < 2) { 1324 throw new IllegalArgumentException("Operation must have two or more successors" + opName()); 1325 } 1326 1327 this.s = List.copyOf(s); 1328 } 1329 1330 @Override 1331 public List<Block.Reference> successors() { 1332 return s; 1333 } 1334 1335 public Block.Reference end() { 1336 return s.get(0); 1337 } 1338 1339 public List<Block.Reference> catchBlocks() { 1340 return s.subList(1, s.size()); 1341 } 1342 1343 @Override 1344 public TypeElement resultType() { 1345 return VOID; 1346 } 1347 } 1348 1349 /** 1350 * The String Concatenation Operation 1351 */ 1352 1353 @OpDeclaration(ConcatOp.NAME) 1354 public static final class ConcatOp extends JavaOp 1355 implements Pure, JavaExpression { 1356 static final String NAME = "concat"; 1357 1358 public ConcatOp(ConcatOp that, CopyContext cc) { 1359 super(that, cc); 1360 } 1361 1362 ConcatOp(ExternalizedOp def) { 1363 if (def.operands().size() != 2) { 1364 throw new IllegalArgumentException("Concatenation Operation must have two operands."); 1365 } 1366 1367 this(def.operands().get(0), def.operands().get(1)); 1368 } 1369 1370 public ConcatOp(Value lhs, Value rhs) { 1371 super(ConcatOp.NAME, List.of(lhs, rhs)); 1372 } 1373 1374 @Override 1375 public Op transform(CopyContext cc, OpTransformer ot) { 1376 return new ConcatOp(this, cc); 1377 } 1378 1379 @Override 1380 public TypeElement resultType() { 1381 return J_L_STRING; 1382 } 1383 } 1384 1385 /** 1386 * The arithmetic operation. 1387 */ 1388 public sealed static abstract class ArithmeticOperation extends JavaOp 1389 implements Pure, JavaExpression { 1390 protected ArithmeticOperation(ArithmeticOperation that, CopyContext cc) { 1391 super(that, cc); 1392 } 1393 1394 protected ArithmeticOperation(String name, List<Value> operands) { 1395 super(name, operands); 1396 } 1397 } 1398 1399 /** 1400 * The test operation. 1401 */ 1402 public sealed static abstract class TestOperation extends JavaOp 1403 implements Pure, JavaExpression { 1404 protected TestOperation(TestOperation that, CopyContext cc) { 1405 super(that, cc); 1406 } 1407 1408 protected TestOperation(String name, List<Value> operands) { 1409 super(name, operands); 1410 } 1411 } 1412 1413 /** 1414 * The binary arithmetic operation. 1415 */ 1416 public sealed static abstract class BinaryOp extends ArithmeticOperation { 1417 protected BinaryOp(BinaryOp that, CopyContext cc) { 1418 super(that, cc); 1419 } 1420 1421 protected BinaryOp(String name, Value lhs, Value rhs) { 1422 super(name, List.of(lhs, rhs)); 1423 } 1424 1425 @Override 1426 public TypeElement resultType() { 1427 return operands().get(0).type(); 1428 } 1429 } 1430 1431 /** 1432 * The unary arithmetic operation. 1433 */ 1434 public sealed static abstract class UnaryOp extends ArithmeticOperation { 1435 protected UnaryOp(UnaryOp that, CopyContext cc) { 1436 super(that, cc); 1437 } 1438 1439 protected UnaryOp(String name, Value v) { 1440 super(name, List.of(v)); 1441 } 1442 1443 @Override 1444 public TypeElement resultType() { 1445 return operands().get(0).type(); 1446 } 1447 } 1448 1449 /** 1450 * The binary test operation. 1451 */ 1452 public sealed static abstract class BinaryTestOp extends TestOperation { 1453 protected BinaryTestOp(BinaryTestOp that, CopyContext cc) { 1454 super(that, cc); 1455 } 1456 1457 protected BinaryTestOp(String name, Value lhs, Value rhs) { 1458 super(name, List.of(lhs, rhs)); 1459 } 1460 1461 @Override 1462 public TypeElement resultType() { 1463 return BOOLEAN; 1464 } 1465 } 1466 1467 /** 1468 * The add operation, that can model the Java language binary {@code +} operator for numeric types 1469 */ 1470 @OpDeclaration(AddOp.NAME) 1471 public static final class AddOp extends BinaryOp { 1472 static final String NAME = "add"; 1473 1474 AddOp(ExternalizedOp def) { 1475 this(def.operands().get(0), def.operands().get(1)); 1476 } 1477 1478 AddOp(AddOp that, CopyContext cc) { 1479 super(that, cc); 1480 } 1481 1482 @Override 1483 public AddOp transform(CopyContext cc, OpTransformer ot) { 1484 return new AddOp(this, cc); 1485 } 1486 1487 AddOp(Value lhs, Value rhs) { 1488 super(NAME, lhs, rhs); 1489 } 1490 } 1491 1492 /** 1493 * The sub operation, that can model the Java language binary {@code -} operator for numeric types 1494 */ 1495 @OpDeclaration(SubOp.NAME) 1496 public static final class SubOp extends BinaryOp { 1497 static final String NAME = "sub"; 1498 1499 SubOp(ExternalizedOp def) { 1500 this(def.operands().get(0), def.operands().get(1)); 1501 } 1502 1503 SubOp(SubOp that, CopyContext cc) { 1504 super(that, cc); 1505 } 1506 1507 @Override 1508 public SubOp transform(CopyContext cc, OpTransformer ot) { 1509 return new SubOp(this, cc); 1510 } 1511 1512 SubOp(Value lhs, Value rhs) { 1513 super(NAME, lhs, rhs); 1514 } 1515 } 1516 1517 /** 1518 * The mul operation, that can model the Java language binary {@code *} operator for numeric types 1519 */ 1520 @OpDeclaration(MulOp.NAME) 1521 public static final class MulOp extends BinaryOp { 1522 static final String NAME = "mul"; 1523 1524 MulOp(ExternalizedOp def) { 1525 this(def.operands().get(0), def.operands().get(1)); 1526 } 1527 1528 MulOp(MulOp that, CopyContext cc) { 1529 super(that, cc); 1530 } 1531 1532 @Override 1533 public MulOp transform(CopyContext cc, OpTransformer ot) { 1534 return new MulOp(this, cc); 1535 } 1536 1537 MulOp(Value lhs, Value rhs) { 1538 super(NAME, lhs, rhs); 1539 } 1540 } 1541 1542 /** 1543 * The div operation, that can model the Java language binary {@code /} operator for numeric types 1544 */ 1545 @OpDeclaration(DivOp.NAME) 1546 public static final class DivOp extends BinaryOp { 1547 static final String NAME = "div"; 1548 1549 DivOp(ExternalizedOp def) { 1550 this(def.operands().get(0), def.operands().get(1)); 1551 } 1552 1553 DivOp(DivOp that, CopyContext cc) { 1554 super(that, cc); 1555 } 1556 1557 @Override 1558 public DivOp transform(CopyContext cc, OpTransformer ot) { 1559 return new DivOp(this, cc); 1560 } 1561 1562 DivOp(Value lhs, Value rhs) { 1563 super(NAME, lhs, rhs); 1564 } 1565 } 1566 1567 /** 1568 * The mod operation, that can model the Java language binary {@code %} operator for numeric types 1569 */ 1570 @OpDeclaration(ModOp.NAME) 1571 public static final class ModOp extends BinaryOp { 1572 static final String NAME = "mod"; 1573 1574 ModOp(ExternalizedOp def) { 1575 this(def.operands().get(0), def.operands().get(1)); 1576 } 1577 1578 ModOp(ModOp that, CopyContext cc) { 1579 super(that, cc); 1580 } 1581 1582 @Override 1583 public ModOp transform(CopyContext cc, OpTransformer ot) { 1584 return new ModOp(this, cc); 1585 } 1586 1587 ModOp(Value lhs, Value rhs) { 1588 super(NAME, lhs, rhs); 1589 } 1590 } 1591 1592 /** 1593 * The bitwise/logical or operation, that can model the Java language binary {@code |} operator for integral types 1594 * and booleans 1595 */ 1596 @OpDeclaration(OrOp.NAME) 1597 public static final class OrOp extends BinaryOp { 1598 static final String NAME = "or"; 1599 1600 OrOp(ExternalizedOp def) { 1601 this(def.operands().get(0), def.operands().get(1)); 1602 } 1603 1604 OrOp(OrOp that, CopyContext cc) { 1605 super(that, cc); 1606 } 1607 1608 @Override 1609 public OrOp transform(CopyContext cc, OpTransformer ot) { 1610 return new OrOp(this, cc); 1611 } 1612 1613 OrOp(Value lhs, Value rhs) { 1614 super(NAME, lhs, rhs); 1615 } 1616 } 1617 1618 /** 1619 * The bitwise/logical and operation, that can model the Java language binary {@code &} operator for integral types 1620 * and booleans 1621 */ 1622 @OpDeclaration(AndOp.NAME) 1623 public static final class AndOp extends BinaryOp { 1624 static final String NAME = "and"; 1625 1626 AndOp(ExternalizedOp def) { 1627 this(def.operands().get(0), def.operands().get(1)); 1628 } 1629 1630 AndOp(AndOp that, CopyContext cc) { 1631 super(that, cc); 1632 } 1633 1634 @Override 1635 public AndOp transform(CopyContext cc, OpTransformer ot) { 1636 return new AndOp(this, cc); 1637 } 1638 1639 AndOp(Value lhs, Value rhs) { 1640 super(NAME, lhs, rhs); 1641 } 1642 } 1643 1644 /** 1645 * The xor operation, that can model the Java language binary {@code ^} operator for integral types 1646 * and booleans 1647 */ 1648 @OpDeclaration(XorOp.NAME) 1649 public static final class XorOp extends BinaryOp { 1650 static final String NAME = "xor"; 1651 1652 XorOp(ExternalizedOp def) { 1653 this(def.operands().get(0), def.operands().get(1)); 1654 } 1655 1656 XorOp(XorOp that, CopyContext cc) { 1657 super(that, cc); 1658 } 1659 1660 @Override 1661 public XorOp transform(CopyContext cc, OpTransformer ot) { 1662 return new XorOp(this, cc); 1663 } 1664 1665 XorOp(Value lhs, Value rhs) { 1666 super(NAME, lhs, rhs); 1667 } 1668 } 1669 1670 /** 1671 * The (logical) shift left operation, that can model the Java language binary {@code <<} operator for integral types 1672 */ 1673 @OpDeclaration(LshlOp.NAME) 1674 public static final class LshlOp extends BinaryOp { 1675 static final String NAME = "lshl"; 1676 1677 LshlOp(ExternalizedOp def) { 1678 this(def.operands().get(0), def.operands().get(1)); 1679 } 1680 1681 LshlOp(LshlOp that, CopyContext cc) { 1682 super(that, cc); 1683 } 1684 1685 @Override 1686 public LshlOp transform(CopyContext cc, OpTransformer ot) { 1687 return new LshlOp(this, cc); 1688 } 1689 1690 LshlOp(Value lhs, Value rhs) { 1691 super(NAME, lhs, rhs); 1692 } 1693 } 1694 1695 /** 1696 * The (arithmetic) shift right operation, that can model the Java language binary {@code >>} operator for integral types 1697 */ 1698 @OpDeclaration(AshrOp.NAME) 1699 public static final class AshrOp extends JavaOp.BinaryOp { 1700 static final String NAME = "ashr"; 1701 1702 AshrOp(ExternalizedOp def) { 1703 this(def.operands().get(0), def.operands().get(1)); 1704 } 1705 1706 AshrOp(AshrOp that, CopyContext cc) { 1707 super(that, cc); 1708 } 1709 1710 @Override 1711 public AshrOp transform(CopyContext cc, OpTransformer ot) { 1712 return new AshrOp(this, cc); 1713 } 1714 1715 AshrOp(Value lhs, Value rhs) { 1716 super(NAME, lhs, rhs); 1717 } 1718 } 1719 1720 /** 1721 * The unsigned (logical) shift right operation, that can model the Java language binary {@code >>>} operator for integral types 1722 */ 1723 @OpDeclaration(LshrOp.NAME) 1724 public static final class LshrOp extends JavaOp.BinaryOp { 1725 static final String NAME = "lshr"; 1726 1727 LshrOp(ExternalizedOp def) { 1728 this(def.operands().get(0), def.operands().get(1)); 1729 } 1730 1731 LshrOp(LshrOp that, CopyContext cc) { 1732 super(that, cc); 1733 } 1734 1735 @Override 1736 public LshrOp transform(CopyContext cc, OpTransformer ot) { 1737 return new LshrOp(this, cc); 1738 } 1739 1740 LshrOp(Value lhs, Value rhs) { 1741 super(NAME, lhs, rhs); 1742 } 1743 } 1744 1745 /** 1746 * The neg operation, that can model the Java language unary {@code -} operator for numeric types 1747 */ 1748 @OpDeclaration(NegOp.NAME) 1749 public static final class NegOp extends UnaryOp { 1750 static final String NAME = "neg"; 1751 1752 NegOp(ExternalizedOp def) { 1753 this(def.operands().get(0)); 1754 } 1755 1756 NegOp(NegOp that, CopyContext cc) { 1757 super(that, cc); 1758 } 1759 1760 @Override 1761 public NegOp transform(CopyContext cc, OpTransformer ot) { 1762 return new NegOp(this, cc); 1763 } 1764 1765 NegOp(Value v) { 1766 super(NAME, v); 1767 } 1768 } 1769 1770 /** 1771 * The bitwise complement operation, that can model the Java language unary {@code ~} operator for integral types 1772 */ 1773 @OpDeclaration(ComplOp.NAME) 1774 public static final class ComplOp extends UnaryOp { 1775 static final String NAME = "compl"; 1776 1777 ComplOp(ExternalizedOp def) { 1778 this(def.operands().get(0)); 1779 } 1780 1781 ComplOp(ComplOp that, CopyContext cc) { 1782 super(that, cc); 1783 } 1784 1785 @Override 1786 public ComplOp transform(CopyContext cc, OpTransformer ot) { 1787 return new ComplOp(this, cc); 1788 } 1789 1790 ComplOp(Value v) { 1791 super(NAME, v); 1792 } 1793 } 1794 1795 /** 1796 * The not operation, that can model the Java language unary {@code !} operator for boolean types 1797 */ 1798 @OpDeclaration(NotOp.NAME) 1799 public static final class NotOp extends UnaryOp { 1800 static final String NAME = "not"; 1801 1802 NotOp(ExternalizedOp def) { 1803 this(def.operands().get(0)); 1804 } 1805 1806 NotOp(NotOp that, CopyContext cc) { 1807 super(that, cc); 1808 } 1809 1810 @Override 1811 public NotOp transform(CopyContext cc, OpTransformer ot) { 1812 return new NotOp(this, cc); 1813 } 1814 1815 NotOp(Value v) { 1816 super(NAME, v); 1817 } 1818 } 1819 1820 /** 1821 * The equals operation, that can model the Java language equality {@code ==} operator for numeric, boolean 1822 * and reference types 1823 */ 1824 @OpDeclaration(EqOp.NAME) 1825 public static final class EqOp extends BinaryTestOp { 1826 static final String NAME = "eq"; 1827 1828 EqOp(ExternalizedOp def) { 1829 this(def.operands().get(0), def.operands().get(1)); 1830 } 1831 1832 EqOp(EqOp that, CopyContext cc) { 1833 super(that, cc); 1834 } 1835 1836 @Override 1837 public EqOp transform(CopyContext cc, OpTransformer ot) { 1838 return new EqOp(this, cc); 1839 } 1840 1841 EqOp(Value lhs, Value rhs) { 1842 super(NAME, lhs, rhs); 1843 } 1844 } 1845 1846 /** 1847 * The not equals operation, that can model the Java language equality {@code !=} operator for numeric, boolean 1848 * and reference types 1849 */ 1850 @OpDeclaration(NeqOp.NAME) 1851 public static final class NeqOp extends BinaryTestOp { 1852 static final String NAME = "neq"; 1853 1854 NeqOp(ExternalizedOp def) { 1855 this(def.operands().get(0), def.operands().get(1)); 1856 } 1857 1858 NeqOp(NeqOp that, CopyContext cc) { 1859 super(that, cc); 1860 } 1861 1862 @Override 1863 public NeqOp transform(CopyContext cc, OpTransformer ot) { 1864 return new NeqOp(this, cc); 1865 } 1866 1867 NeqOp(Value lhs, Value rhs) { 1868 super(NAME, lhs, rhs); 1869 } 1870 } 1871 1872 /** 1873 * The greater than operation, that can model the Java language relational {@code >} operator for numeric types 1874 */ 1875 @OpDeclaration(GtOp.NAME) 1876 public static final class GtOp extends BinaryTestOp { 1877 static final String NAME = "gt"; 1878 1879 GtOp(ExternalizedOp def) { 1880 this(def.operands().get(0), def.operands().get(1)); 1881 } 1882 1883 GtOp(GtOp that, CopyContext cc) { 1884 super(that, cc); 1885 } 1886 1887 @Override 1888 public GtOp transform(CopyContext cc, OpTransformer ot) { 1889 return new GtOp(this, cc); 1890 } 1891 1892 GtOp(Value lhs, Value rhs) { 1893 super(NAME, lhs, rhs); 1894 } 1895 } 1896 1897 /** 1898 * The greater than or equal to operation, that can model the Java language relational {@code >=} operator for 1899 * numeric types 1900 */ 1901 @OpDeclaration(GeOp.NAME) 1902 public static final class GeOp extends BinaryTestOp { 1903 static final String NAME = "ge"; 1904 1905 GeOp(ExternalizedOp def) { 1906 this(def.operands().get(0), def.operands().get(1)); 1907 } 1908 1909 GeOp(GeOp that, CopyContext cc) { 1910 super(that, cc); 1911 } 1912 1913 @Override 1914 public GeOp transform(CopyContext cc, OpTransformer ot) { 1915 return new GeOp(this, cc); 1916 } 1917 1918 GeOp(Value lhs, Value rhs) { 1919 super(NAME, lhs, rhs); 1920 } 1921 } 1922 1923 /** 1924 * The less than operation, that can model the Java language relational {@code <} operator for 1925 * numeric types 1926 */ 1927 @OpDeclaration(LtOp.NAME) 1928 public static final class LtOp extends BinaryTestOp { 1929 static final String NAME = "lt"; 1930 1931 LtOp(ExternalizedOp def) { 1932 this(def.operands().get(0), def.operands().get(1)); 1933 } 1934 1935 LtOp(LtOp that, CopyContext cc) { 1936 super(that, cc); 1937 } 1938 1939 @Override 1940 public LtOp transform(CopyContext cc, OpTransformer ot) { 1941 return new LtOp(this, cc); 1942 } 1943 1944 LtOp(Value lhs, Value rhs) { 1945 super(NAME, lhs, rhs); 1946 } 1947 } 1948 1949 /** 1950 * The less than or equal to operation, that can model the Java language relational {@code <=} operator for 1951 * numeric types 1952 */ 1953 @OpDeclaration(LeOp.NAME) 1954 public static final class LeOp extends BinaryTestOp { 1955 static final String NAME = "le"; 1956 1957 LeOp(ExternalizedOp def) { 1958 this(def.operands().get(0), def.operands().get(1)); 1959 } 1960 1961 LeOp(LeOp that, CopyContext cc) { 1962 super(that, cc); 1963 } 1964 1965 @Override 1966 public LeOp transform(CopyContext cc, OpTransformer ot) { 1967 return new LeOp(this, cc); 1968 } 1969 1970 LeOp(Value lhs, Value rhs) { 1971 super(NAME, lhs, rhs); 1972 } 1973 } 1974 1975 /** 1976 * The label operation, that can model Java language statements with label identifiers. 1977 */ 1978 public sealed static abstract class JavaLabelOp extends JavaOp 1979 implements Op.Lowerable, Op.BodyTerminating, JavaStatement { 1980 JavaLabelOp(JavaLabelOp that, CopyContext cc) { 1981 super(that, cc); 1982 } 1983 1984 JavaLabelOp(String name, Value label) { 1985 super(name, checkLabel(label)); 1986 } 1987 1988 static List<Value> checkLabel(Value label) { 1989 return label == null ? List.of() : List.of(label); 1990 } 1991 1992 Op innerMostEnclosingTarget() { 1993 /* 1994 A break statement with no label attempts to transfer control to the 1995 innermost enclosing switch, while, do, or for statement; this enclosing statement, 1996 which is called the break target, then immediately completes normally. 1997 1998 A break statement with label Identifier attempts to transfer control to the 1999 enclosing labeled statement (14.7) that has the same Identifier as its label; 2000 this enclosing statement, which is called the break target, then immediately completes normally. 2001 In this case, the break target need not be a switch, while, do, or for statement. 2002 */ 2003 2004 // No label 2005 // Get innermost enclosing loop operation 2006 Op op = this; 2007 Body b; 2008 do { 2009 b = op.ancestorBody(); 2010 op = b.ancestorOp(); 2011 if (op == null) { 2012 throw new IllegalStateException("No enclosing loop"); 2013 } 2014 } while (!(op instanceof Op.Loop || op instanceof SwitchStatementOp)); 2015 2016 return switch (op) { 2017 case Op.Loop lop -> lop.loopBody() == b ? op : null; 2018 case SwitchStatementOp swStat -> swStat.bodies().contains(b) ? op : null; 2019 default -> throw new IllegalStateException(); 2020 }; 2021 } 2022 2023 boolean isUnlabeled() { 2024 return operands().isEmpty(); 2025 } 2026 2027 Op target() { 2028 // If unlabeled then find the nearest enclosing op 2029 // Otherwise obtain the label target 2030 if (isUnlabeled()) { 2031 return innerMostEnclosingTarget(); 2032 } 2033 2034 Value value = operands().get(0); 2035 if (value instanceof Result r && r.op().ancestorOp() instanceof LabeledOp lop) { 2036 return lop.target(); 2037 } else { 2038 throw new IllegalStateException("Bad label value: " + value + " " + ((Result) value).op()); 2039 } 2040 } 2041 2042 Block.Builder lower(Block.Builder b, Function<BranchTarget, Block.Builder> f) { 2043 Op opt = target(); 2044 BranchTarget t = getBranchTarget(b.context(), opt); 2045 if (t != null) { 2046 b.op(branch(f.apply(t).successor())); 2047 } else { 2048 throw new IllegalStateException("No branch target for operation: " + opt); 2049 } 2050 return b; 2051 } 2052 2053 @Override 2054 public TypeElement resultType() { 2055 return VOID; 2056 } 2057 } 2058 2059 /** 2060 * The break operation, that can model Java language break statements with label identifiers. 2061 */ 2062 @OpDeclaration(BreakOp.NAME) 2063 public static final class BreakOp extends JavaLabelOp { 2064 static final String NAME = "java.break"; 2065 2066 BreakOp(ExternalizedOp def) { 2067 this(def.operands().isEmpty() ? null : def.operands().get(0)); 2068 } 2069 2070 BreakOp(BreakOp that, CopyContext cc) { 2071 super(that, cc); 2072 } 2073 2074 @Override 2075 public BreakOp transform(CopyContext cc, OpTransformer ot) { 2076 return new BreakOp(this, cc); 2077 } 2078 2079 BreakOp(Value label) { 2080 super(NAME, label); 2081 } 2082 2083 @Override 2084 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 2085 return lower(b, BranchTarget::breakBlock); 2086 } 2087 } 2088 2089 /** 2090 * The continue operation, that can model Java language continue statements with label identifiers. 2091 */ 2092 @OpDeclaration(ContinueOp.NAME) 2093 public static final class ContinueOp extends JavaLabelOp { 2094 static final String NAME = "java.continue"; 2095 2096 ContinueOp(ExternalizedOp def) { 2097 this(def.operands().isEmpty() ? null : def.operands().get(0)); 2098 } 2099 2100 ContinueOp(ContinueOp that, CopyContext cc) { 2101 super(that, cc); 2102 } 2103 2104 @Override 2105 public ContinueOp transform(CopyContext cc, OpTransformer ot) { 2106 return new ContinueOp(this, cc); 2107 } 2108 2109 ContinueOp(Value label) { 2110 super(NAME, label); 2111 } 2112 2113 @Override 2114 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 2115 return lower(b, BranchTarget::continueBlock); 2116 } 2117 } 2118 2119 record BranchTarget(Block.Builder breakBlock, Block.Builder continueBlock) { 2120 } 2121 2122 static final String BRANCH_TARGET_MAP_PROPERTY_KEY = "BRANCH_TARGET_MAP"; 2123 2124 static BranchTarget getBranchTarget(CopyContext cc, CodeElement<?, ?> codeElement) { 2125 @SuppressWarnings("unchecked") 2126 Map<CodeElement<?, ?>, BranchTarget> m = (Map<CodeElement<?, ?>, BranchTarget>) cc.getProperty(BRANCH_TARGET_MAP_PROPERTY_KEY); 2127 if (m != null) { 2128 return m.get(codeElement); 2129 } 2130 return null; 2131 } 2132 2133 static void setBranchTarget(CopyContext cc, CodeElement<?, ?> codeElement, BranchTarget t) { 2134 @SuppressWarnings("unchecked") 2135 Map<CodeElement<?, ?>, BranchTarget> x = (Map<CodeElement<?, ?>, BranchTarget>) cc.computePropertyIfAbsent( 2136 BRANCH_TARGET_MAP_PROPERTY_KEY, k -> new HashMap<>()); 2137 x.put(codeElement, t); 2138 } 2139 2140 /** 2141 * The yield operation, that can model Java language yield statements. 2142 */ 2143 @OpDeclaration(YieldOp.NAME) 2144 public static final class YieldOp extends JavaOp 2145 implements Op.BodyTerminating, JavaStatement, Op.Lowerable { 2146 static final String NAME = "java.yield"; 2147 2148 YieldOp(ExternalizedOp def) { 2149 if (def.operands().size() > 1) { 2150 throw new IllegalArgumentException("Operation must have zero or one operand " + def.name()); 2151 } 2152 2153 this(def.operands().isEmpty() ? null : def.operands().get(0)); 2154 } 2155 2156 YieldOp(YieldOp that, CopyContext cc) { 2157 super(that, cc); 2158 } 2159 2160 @Override 2161 public YieldOp transform(CopyContext cc, OpTransformer ot) { 2162 return new YieldOp(this, cc); 2163 } 2164 2165 YieldOp(Value operand) { 2166 super(NAME, operand == null ? List.of() : List.of(operand)); 2167 } 2168 2169 public Value yieldValue() { 2170 if (operands().size() == 1) { 2171 return operands().get(0); 2172 } else { 2173 // @@@ 2174 return null; 2175 } 2176 } 2177 2178 @Override 2179 public TypeElement resultType() { 2180 return VOID; 2181 } 2182 2183 @Override 2184 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 2185 // for now, we will use breakBlock field to indicate java.yield target block 2186 return lower(b, BranchTarget::breakBlock); 2187 } 2188 2189 Block.Builder lower(Block.Builder b, Function<BranchTarget, Block.Builder> f) { 2190 Op opt = target(); 2191 BranchTarget t = getBranchTarget(b.context(), opt); 2192 if (t != null) { 2193 b.op(branch(f.apply(t).successor(b.context().getValue(yieldValue())))); 2194 } else { 2195 throw new IllegalStateException("No branch target for operation: " + opt); 2196 } 2197 return b; 2198 } 2199 2200 Op target() { 2201 return innerMostEnclosingTarget(); 2202 } 2203 2204 Op innerMostEnclosingTarget() { 2205 Op op = this; 2206 Body b; 2207 do { 2208 b = op.ancestorBody(); 2209 op = b.ancestorOp(); 2210 if (op == null) { 2211 throw new IllegalStateException("No enclosing switch"); 2212 } 2213 } while (!(op instanceof SwitchExpressionOp)); 2214 return op; 2215 } 2216 } 2217 2218 /** 2219 * The block operation, that can model Java language blocks. 2220 */ 2221 @OpDeclaration(BlockOp.NAME) 2222 public static final class BlockOp extends JavaOp 2223 implements Op.Nested, Op.Lowerable, JavaStatement { 2224 static final String NAME = "java.block"; 2225 2226 final Body body; 2227 2228 BlockOp(ExternalizedOp def) { 2229 if (!def.operands().isEmpty()) { 2230 throw new IllegalStateException("Operation must have no operands"); 2231 } 2232 2233 this(def.bodyDefinitions().get(0)); 2234 } 2235 2236 BlockOp(BlockOp that, CopyContext cc, OpTransformer ot) { 2237 super(that, cc); 2238 2239 // Copy body 2240 this.body = that.body.transform(cc, ot).build(this); 2241 } 2242 2243 @Override 2244 public BlockOp transform(CopyContext cc, OpTransformer ot) { 2245 return new BlockOp(this, cc, ot); 2246 } 2247 2248 BlockOp(Body.Builder bodyC) { 2249 super(NAME, List.of()); 2250 2251 this.body = bodyC.build(this); 2252 if (!body.bodyType().returnType().equals(VOID)) { 2253 throw new IllegalArgumentException("Body should return void: " + body.bodyType()); 2254 } 2255 if (!body.bodyType().parameterTypes().isEmpty()) { 2256 throw new IllegalArgumentException("Body should have zero parameters: " + body.bodyType()); 2257 } 2258 } 2259 2260 @Override 2261 public List<Body> bodies() { 2262 return List.of(body); 2263 } 2264 2265 public Body body() { 2266 return body; 2267 } 2268 2269 @Override 2270 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 2271 Block.Builder exit = b.block(); 2272 setBranchTarget(b.context(), this, new BranchTarget(exit, null)); 2273 2274 b.body(body, List.of(), andThenLowering(opT, (block, op) -> { 2275 if (op instanceof CoreOp.YieldOp) { 2276 block.op(branch(exit.successor())); 2277 return block; 2278 } else { 2279 return null; 2280 } 2281 })); 2282 2283 return exit; 2284 } 2285 2286 @Override 2287 public TypeElement resultType() { 2288 return VOID; 2289 } 2290 } 2291 2292 /** 2293 * The synchronized operation, that can model Java synchronized statements. 2294 */ 2295 @OpDeclaration(SynchronizedOp.NAME) 2296 public static final class SynchronizedOp extends JavaOp 2297 implements Op.Nested, Op.Lowerable, JavaStatement { 2298 static final String NAME = "java.synchronized"; 2299 2300 final Body expr; 2301 final Body blockBody; 2302 2303 SynchronizedOp(ExternalizedOp def) { 2304 this(def.bodyDefinitions().get(0), def.bodyDefinitions().get(1)); 2305 } 2306 2307 SynchronizedOp(SynchronizedOp that, CopyContext cc, OpTransformer ot) { 2308 super(that, cc); 2309 2310 // Copy bodies 2311 this.expr = that.expr.transform(cc, ot).build(this); 2312 this.blockBody = that.blockBody.transform(cc, ot).build(this); 2313 } 2314 2315 @Override 2316 public SynchronizedOp transform(CopyContext cc, OpTransformer ot) { 2317 return new SynchronizedOp(this, cc, ot); 2318 } 2319 2320 SynchronizedOp(Body.Builder exprC, Body.Builder bodyC) { 2321 super(NAME, List.of()); 2322 2323 this.expr = exprC.build(this); 2324 if (expr.bodyType().returnType().equals(VOID)) { 2325 throw new IllegalArgumentException("Expression body should return non-void value: " + expr.bodyType()); 2326 } 2327 if (!expr.bodyType().parameterTypes().isEmpty()) { 2328 throw new IllegalArgumentException("Expression body should have zero parameters: " + expr.bodyType()); 2329 } 2330 2331 this.blockBody = bodyC.build(this); 2332 if (!blockBody.bodyType().returnType().equals(VOID)) { 2333 throw new IllegalArgumentException("Block body should return void: " + blockBody.bodyType()); 2334 } 2335 if (!blockBody.bodyType().parameterTypes().isEmpty()) { 2336 throw new IllegalArgumentException("Block body should have zero parameters: " + blockBody.bodyType()); 2337 } 2338 } 2339 2340 @Override 2341 public List<Body> bodies() { 2342 return List.of(expr, blockBody); 2343 } 2344 2345 public Body expr() { 2346 return expr; 2347 } 2348 2349 public Body blockBody() { 2350 return blockBody; 2351 } 2352 2353 @Override 2354 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 2355 // Lower the expression body, yielding a monitor target 2356 b = lowerExpr(b, opT); 2357 Value monitorTarget = b.parameters().get(0); 2358 2359 // Monitor enter 2360 b.op(monitorEnter(monitorTarget)); 2361 2362 Block.Builder exit = b.block(); 2363 setBranchTarget(b.context(), this, new BranchTarget(exit, null)); 2364 2365 // Exception region for the body 2366 Block.Builder syncRegionEnter = b.block(); 2367 Block.Builder catcherFinally = b.block(); 2368 b.op(exceptionRegionEnter( 2369 syncRegionEnter.successor(), catcherFinally.successor())); 2370 2371 OpTransformer syncExitTransformer = compose(opT, (block, op) -> { 2372 if (op instanceof CoreOp.ReturnOp || 2373 (op instanceof JavaOp.JavaLabelOp lop && ifExitFromSynchronized(lop))) { 2374 // Monitor exit 2375 block.op(monitorExit(monitorTarget)); 2376 // Exit the exception region 2377 Block.Builder exitRegion = block.block(); 2378 block.op(exceptionRegionExit(exitRegion.successor(), catcherFinally.successor())); 2379 return exitRegion; 2380 } else { 2381 return block; 2382 } 2383 }); 2384 2385 syncRegionEnter.body(blockBody, List.of(), andThenLowering(syncExitTransformer, (block, op) -> { 2386 if (op instanceof CoreOp.YieldOp) { 2387 // Monitor exit 2388 block.op(monitorExit(monitorTarget)); 2389 // Exit the exception region 2390 block.op(exceptionRegionExit(exit.successor(), catcherFinally.successor())); 2391 return block; 2392 } else { 2393 return null; 2394 } 2395 })); 2396 2397 // The catcher, with an exception region back branching to itself 2398 Block.Builder catcherFinallyRegionEnter = b.block(); 2399 catcherFinally.op(exceptionRegionEnter( 2400 catcherFinallyRegionEnter.successor(), catcherFinally.successor())); 2401 2402 // Monitor exit 2403 catcherFinallyRegionEnter.op(monitorExit(monitorTarget)); 2404 Block.Builder catcherFinallyRegionExit = b.block(); 2405 // Exit the exception region 2406 catcherFinallyRegionEnter.op(exceptionRegionExit( 2407 catcherFinallyRegionExit.successor(), catcherFinally.successor())); 2408 // Rethrow outside of region 2409 Block.Parameter t = catcherFinally.parameter(type(Throwable.class)); 2410 catcherFinallyRegionExit.op(throw_(t)); 2411 2412 return exit; 2413 } 2414 2415 Block.Builder lowerExpr(Block.Builder b, OpTransformer opT) { 2416 Block.Builder exprExit = b.block(expr.bodyType().returnType()); 2417 b.body(expr, List.of(), andThenLowering(opT, (block, op) -> { 2418 if (op instanceof CoreOp.YieldOp yop) { 2419 Value monitorTarget = block.context().getValue(yop.yieldValue()); 2420 block.op(branch(exprExit.successor(monitorTarget))); 2421 return block; 2422 } else { 2423 return null; 2424 } 2425 })); 2426 return exprExit; 2427 } 2428 2429 boolean ifExitFromSynchronized(JavaLabelOp lop) { 2430 Op target = lop.target(); 2431 return target == this || target.isAncestorOf(this); 2432 } 2433 2434 @Override 2435 public TypeElement resultType() { 2436 return VOID; 2437 } 2438 } 2439 2440 /** 2441 * The labeled operation, that can model Java language labeled statements. 2442 */ 2443 @OpDeclaration(LabeledOp.NAME) 2444 public static final class LabeledOp extends JavaOp 2445 implements Op.Nested, Op.Lowerable, JavaStatement { 2446 static final String NAME = "java.labeled"; 2447 2448 final Body body; 2449 2450 LabeledOp(ExternalizedOp def) { 2451 if (!def.operands().isEmpty()) { 2452 throw new IllegalStateException("Operation must have no operands"); 2453 } 2454 2455 this(def.bodyDefinitions().get(0)); 2456 } 2457 2458 LabeledOp(LabeledOp that, CopyContext cc, OpTransformer ot) { 2459 super(that, cc); 2460 2461 // Copy body 2462 this.body = that.body.transform(cc, ot).build(this); 2463 } 2464 2465 @Override 2466 public LabeledOp transform(CopyContext cc, OpTransformer ot) { 2467 return new LabeledOp(this, cc, ot); 2468 } 2469 2470 LabeledOp(Body.Builder bodyC) { 2471 super(NAME, List.of()); 2472 2473 this.body = bodyC.build(this); 2474 if (!body.bodyType().returnType().equals(VOID)) { 2475 throw new IllegalArgumentException("Body should return void: " + body.bodyType()); 2476 } 2477 if (!body.bodyType().parameterTypes().isEmpty()) { 2478 throw new IllegalArgumentException("Body should have zero parameters: " + body.bodyType()); 2479 } 2480 } 2481 2482 @Override 2483 public List<Body> bodies() { 2484 return List.of(body); 2485 } 2486 2487 public Op label() { 2488 return body.entryBlock().firstOp(); 2489 } 2490 2491 public Op target() { 2492 return body.entryBlock().nextOp(label()); 2493 } 2494 2495 @Override 2496 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 2497 Block.Builder exit = b.block(); 2498 setBranchTarget(b.context(), this, new BranchTarget(exit, null)); 2499 2500 AtomicBoolean first = new AtomicBoolean(); 2501 b.body(body, List.of(), andThenLowering(opT, (block, op) -> { 2502 // Drop first operation that corresponds to the label 2503 if (!first.get()) { 2504 first.set(true); 2505 return block; 2506 } 2507 2508 if (op instanceof CoreOp.YieldOp) { 2509 block.op(branch(exit.successor())); 2510 return block; 2511 } else { 2512 return null; 2513 } 2514 })); 2515 2516 return exit; 2517 } 2518 2519 @Override 2520 public TypeElement resultType() { 2521 return VOID; 2522 } 2523 } 2524 2525 /** 2526 * The if operation, that can model Java language if, if-then, and if-then-else statements. 2527 */ 2528 @OpDeclaration(IfOp.NAME) 2529 public static final class IfOp extends JavaOp 2530 implements Op.Nested, Op.Lowerable, JavaStatement { 2531 2532 static final FunctionType PREDICATE_TYPE = CoreType.functionType(BOOLEAN); 2533 2534 static final FunctionType ACTION_TYPE = CoreType.FUNCTION_TYPE_VOID; 2535 2536 public static class IfBuilder { 2537 final Body.Builder ancestorBody; 2538 final List<Body.Builder> bodies; 2539 2540 IfBuilder(Body.Builder ancestorBody) { 2541 this.ancestorBody = ancestorBody; 2542 this.bodies = new ArrayList<>(); 2543 } 2544 2545 public ThenBuilder if_(Consumer<Block.Builder> c) { 2546 Body.Builder body = Body.Builder.of(ancestorBody, PREDICATE_TYPE); 2547 c.accept(body.entryBlock()); 2548 bodies.add(body); 2549 2550 return new ThenBuilder(ancestorBody, bodies); 2551 } 2552 } 2553 2554 public static class ThenBuilder { 2555 final Body.Builder ancestorBody; 2556 final List<Body.Builder> bodies; 2557 2558 public ThenBuilder(Body.Builder ancestorBody, List<Body.Builder> bodies) { 2559 this.ancestorBody = ancestorBody; 2560 this.bodies = bodies; 2561 } 2562 2563 public ElseIfBuilder then(Consumer<Block.Builder> c) { 2564 Body.Builder body = Body.Builder.of(ancestorBody, ACTION_TYPE); 2565 c.accept(body.entryBlock()); 2566 bodies.add(body); 2567 2568 return new ElseIfBuilder(ancestorBody, bodies); 2569 } 2570 2571 public ElseIfBuilder then() { 2572 Body.Builder body = Body.Builder.of(ancestorBody, ACTION_TYPE); 2573 body.entryBlock().op(core_yield()); 2574 bodies.add(body); 2575 2576 return new ElseIfBuilder(ancestorBody, bodies); 2577 } 2578 } 2579 2580 public static class ElseIfBuilder { 2581 final Body.Builder ancestorBody; 2582 final List<Body.Builder> bodies; 2583 2584 public ElseIfBuilder(Body.Builder ancestorBody, List<Body.Builder> bodies) { 2585 this.ancestorBody = ancestorBody; 2586 this.bodies = bodies; 2587 } 2588 2589 public ThenBuilder elseif(Consumer<Block.Builder> c) { 2590 Body.Builder body = Body.Builder.of(ancestorBody, PREDICATE_TYPE); 2591 c.accept(body.entryBlock()); 2592 bodies.add(body); 2593 2594 return new ThenBuilder(ancestorBody, bodies); 2595 } 2596 2597 public IfOp else_(Consumer<Block.Builder> c) { 2598 Body.Builder body = Body.Builder.of(ancestorBody, ACTION_TYPE); 2599 c.accept(body.entryBlock()); 2600 bodies.add(body); 2601 2602 return new IfOp(bodies); 2603 } 2604 2605 public IfOp else_() { 2606 Body.Builder body = Body.Builder.of(ancestorBody, ACTION_TYPE); 2607 body.entryBlock().op(core_yield()); 2608 bodies.add(body); 2609 2610 return new IfOp(bodies); 2611 } 2612 } 2613 2614 static final String NAME = "java.if"; 2615 2616 final List<Body> bodies; 2617 2618 IfOp(ExternalizedOp def) { 2619 if (!def.operands().isEmpty()) { 2620 throw new IllegalStateException("Operation must have no operands"); 2621 } 2622 2623 this(def.bodyDefinitions()); 2624 } 2625 2626 IfOp(IfOp that, CopyContext cc, OpTransformer ot) { 2627 super(that, cc); 2628 2629 // Copy body 2630 this.bodies = that.bodies.stream() 2631 .map(b -> b.transform(cc, ot).build(this)).toList(); 2632 } 2633 2634 @Override 2635 public IfOp transform(CopyContext cc, OpTransformer ot) { 2636 return new IfOp(this, cc, ot); 2637 } 2638 2639 IfOp(List<Body.Builder> bodyCs) { 2640 super(NAME, List.of()); 2641 2642 // Normalize by adding an empty else action 2643 // @@@ Is this needed? 2644 if (bodyCs.size() % 2 == 0) { 2645 bodyCs = new ArrayList<>(bodyCs); 2646 Body.Builder end = Body.Builder.of(bodyCs.get(0).ancestorBody(), 2647 CoreType.FUNCTION_TYPE_VOID); 2648 end.entryBlock().op(core_yield()); 2649 bodyCs.add(end); 2650 } 2651 2652 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList(); 2653 2654 if (bodies.size() < 2) { 2655 throw new IllegalArgumentException("Incorrect number of bodies: " + bodies.size()); 2656 } 2657 for (int i = 0; i < bodies.size(); i += 2) { 2658 Body action; 2659 if (i == bodies.size() - 1) { 2660 action = bodies.get(i); 2661 } else { 2662 action = bodies.get(i + 1); 2663 Body fromPred = bodies.get(i); 2664 if (!fromPred.bodyType().equals(CoreType.functionType(BOOLEAN))) { 2665 throw new IllegalArgumentException("Illegal predicate body descriptor: " + fromPred.bodyType()); 2666 } 2667 } 2668 if (!action.bodyType().equals(CoreType.FUNCTION_TYPE_VOID)) { 2669 throw new IllegalArgumentException("Illegal action body descriptor: " + action.bodyType()); 2670 } 2671 } 2672 } 2673 2674 @Override 2675 public List<Body> bodies() { 2676 return bodies; 2677 } 2678 2679 @Override 2680 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 2681 Block.Builder exit = b.block(); 2682 setBranchTarget(b.context(), this, new BranchTarget(exit, null)); 2683 2684 // Create predicate and action blocks 2685 List<Block.Builder> builders = new ArrayList<>(); 2686 for (int i = 0; i < bodies.size(); i += 2) { 2687 if (i == bodies.size() - 1) { 2688 builders.add(b.block()); 2689 } else { 2690 builders.add(i == 0 ? b : b.block()); 2691 builders.add(b.block()); 2692 } 2693 } 2694 2695 for (int i = 0; i < bodies.size(); i += 2) { 2696 Body actionBody; 2697 Block.Builder action; 2698 if (i == bodies.size() - 1) { 2699 actionBody = bodies.get(i); 2700 action = builders.get(i); 2701 } else { 2702 Body predBody = bodies.get(i); 2703 actionBody = bodies.get(i + 1); 2704 2705 Block.Builder pred = builders.get(i); 2706 action = builders.get(i + 1); 2707 Block.Builder next = builders.get(i + 2); 2708 2709 pred.body(predBody, List.of(), andThenLowering(opT, (block, op) -> { 2710 if (op instanceof CoreOp.YieldOp yo) { 2711 block.op(conditionalBranch(block.context().getValue(yo.yieldValue()), 2712 action.successor(), next.successor())); 2713 return block; 2714 } else { 2715 return null; 2716 } 2717 })); 2718 } 2719 2720 action.body(actionBody, List.of(), andThenLowering(opT, (block, op) -> { 2721 if (op instanceof CoreOp.YieldOp) { 2722 block.op(branch(exit.successor())); 2723 return block; 2724 } else { 2725 return null; 2726 } 2727 })); 2728 } 2729 2730 return exit; 2731 } 2732 2733 @Override 2734 public TypeElement resultType() { 2735 return VOID; 2736 } 2737 } 2738 2739 public abstract static sealed class JavaSwitchOp extends JavaOp implements Op.Nested, Op.Lowerable 2740 permits SwitchStatementOp, SwitchExpressionOp { 2741 2742 final List<Body> bodies; 2743 2744 JavaSwitchOp(JavaSwitchOp that, CopyContext cc, OpTransformer ot) { 2745 super(that, cc); 2746 2747 // Copy body 2748 this.bodies = that.bodies.stream() 2749 .map(b -> b.transform(cc, ot).build(this)).toList(); 2750 } 2751 2752 JavaSwitchOp(String name, Value target, List<Body.Builder> bodyCs) { 2753 super(name, List.of(target)); 2754 2755 // Each case is modelled as a contiguous pair of bodies 2756 // The first body models the case labels, and the second models the case statements 2757 // The labels body has a parameter whose type is target operand's type and returns a boolean value 2758 // The statements body has no parameters and returns void 2759 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList(); 2760 } 2761 2762 @Override 2763 public List<Body> bodies() { 2764 return bodies; 2765 } 2766 2767 @Override 2768 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 2769 Value selectorExpression = b.context().getValue(operands().get(0)); 2770 2771 // @@@ we can add this during model generation 2772 // if no case null, add one that throws NPE 2773 if (!(selectorExpression.type() instanceof PrimitiveType) && !haveNullCase()) { 2774 Block.Builder throwBlock = b.block(); 2775 throwBlock.op(throw_( 2776 throwBlock.op(new_(ConstructorRef.constructor(NullPointerException.class))) 2777 )); 2778 2779 Block.Builder continueBlock = b.block(); 2780 2781 Result p = b.op(invoke(MethodRef.method(Objects.class, "equals", boolean.class, Object.class, Object.class), 2782 selectorExpression, b.op(constant(J_L_OBJECT, null)))); 2783 b.op(conditionalBranch(p, throwBlock.successor(), continueBlock.successor())); 2784 2785 b = continueBlock; 2786 } 2787 2788 List<Block.Builder> blocks = new ArrayList<>(); 2789 for (int i = 0; i < bodies().size(); i++) { 2790 Block.Builder bb = b.block(); 2791 if (i == 0) { 2792 bb = b; 2793 } 2794 blocks.add(bb); 2795 } 2796 2797 Block.Builder exit; 2798 if (bodies().isEmpty()) { 2799 exit = b; 2800 } else { 2801 exit = b.block(resultType()); 2802 if (this instanceof SwitchExpressionOp) { 2803 exit.context().mapValue(result(), exit.parameters().get(0)); 2804 } 2805 } 2806 2807 setBranchTarget(b.context(), this, new BranchTarget(exit, null)); 2808 // map statement body to nextExprBlock 2809 // this mapping will be used for lowering SwitchFallThroughOp 2810 for (int i = 1; i < bodies().size() - 2; i+=2) { 2811 setBranchTarget(b.context(), bodies().get(i), new BranchTarget(null, blocks.get(i + 2))); 2812 } 2813 2814 for (int i = 0; i < bodies().size(); i++) { 2815 boolean isLabelBody = i % 2 == 0; 2816 Block.Builder curr = blocks.get(i); 2817 if (isLabelBody) { 2818 Block.Builder statement = blocks.get(i + 1); 2819 boolean isLastLabel = i == blocks.size() - 2; 2820 Block.Builder nextLabel = isLastLabel ? null : blocks.get(i + 2); 2821 curr.body(bodies().get(i), List.of(selectorExpression), andThenLowering(opT, 2822 (block, op) -> switch (op) { 2823 case CoreOp.YieldOp _ when isLastLabel && this instanceof SwitchExpressionOp -> { 2824 block.op(branch(statement.successor())); 2825 yield block; 2826 } 2827 case CoreOp.YieldOp yop -> { 2828 block.op(conditionalBranch( 2829 block.context().getValue(yop.yieldValue()), 2830 statement.successor(), 2831 isLastLabel ? exit.successor() : nextLabel.successor())); 2832 yield block; 2833 } 2834 default -> null; 2835 })); 2836 } else { // statement body 2837 curr.body(bodies().get(i), blocks.get(i).parameters(), andThenLowering(opT, 2838 (block, op) -> switch (op) { 2839 case CoreOp.YieldOp _ when this instanceof SwitchStatementOp -> { 2840 block.op(branch(exit.successor())); 2841 yield block; 2842 } 2843 case CoreOp.YieldOp yop when this instanceof SwitchExpressionOp -> { 2844 block.op(branch(exit.successor(block.context().getValue(yop.yieldValue())))); 2845 yield block; 2846 } 2847 default -> null; 2848 })); 2849 } 2850 } 2851 2852 return exit; 2853 } 2854 2855 boolean haveNullCase() { 2856 /* 2857 case null is modeled like this: 2858 (%4 : T)boolean -> { 2859 %5 : java.lang.Object = constant @null; 2860 %6 : boolean = invoke %4 %5 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean"; 2861 yield %6; 2862 } 2863 * */ 2864 for (int i = 0; i < bodies().size() - 2; i+=2) { 2865 Body labelBody = bodies().get(i); 2866 if (labelBody.blocks().size() != 1) { 2867 continue; // we skip, for now 2868 } 2869 Op terminatingOp = bodies().get(i).entryBlock().terminatingOp(); 2870 //@@@ when op pattern matching is ready, we can use it 2871 if (terminatingOp instanceof CoreOp.YieldOp yieldOp && 2872 yieldOp.yieldValue() instanceof Op.Result opr && 2873 opr.op() instanceof InvokeOp invokeOp && 2874 invokeOp.invokeDescriptor().equals(MethodRef.method(Objects.class, "equals", boolean.class, Object.class, Object.class)) && 2875 invokeOp.operands().stream().anyMatch(o -> o instanceof Op.Result r && r.op() instanceof ConstantOp cop && cop.value() == null)) { 2876 return true; 2877 } 2878 } 2879 return false; 2880 } 2881 } 2882 2883 /** 2884 * The switch expression operation, that can model Java language switch expressions. 2885 */ 2886 @OpDeclaration(SwitchExpressionOp.NAME) 2887 public static final class SwitchExpressionOp extends JavaSwitchOp 2888 implements JavaExpression { 2889 static final String NAME = "java.switch.expression"; 2890 2891 final TypeElement resultType; 2892 2893 SwitchExpressionOp(ExternalizedOp def) { 2894 this(def.resultType(), def.operands().get(0), def.bodyDefinitions()); 2895 } 2896 2897 SwitchExpressionOp(SwitchExpressionOp that, CopyContext cc, OpTransformer ot) { 2898 super(that, cc, ot); 2899 2900 this.resultType = that.resultType; 2901 } 2902 2903 @Override 2904 public SwitchExpressionOp transform(CopyContext cc, OpTransformer ot) { 2905 return new SwitchExpressionOp(this, cc, ot); 2906 } 2907 2908 SwitchExpressionOp(TypeElement resultType, Value target, List<Body.Builder> bodyCs) { 2909 super(NAME, target, bodyCs); 2910 2911 this.resultType = resultType == null ? bodies.get(1).yieldType() : resultType; 2912 } 2913 2914 @Override 2915 public TypeElement resultType() { 2916 return resultType; 2917 } 2918 } 2919 2920 /** 2921 * The switch statement operation, that can model Java language switch statement. 2922 */ 2923 @OpDeclaration(SwitchStatementOp.NAME) 2924 public static final class SwitchStatementOp extends JavaSwitchOp 2925 implements JavaStatement { 2926 static final String NAME = "java.switch.statement"; 2927 2928 SwitchStatementOp(ExternalizedOp def) { 2929 this(def.operands().get(0), def.bodyDefinitions()); 2930 } 2931 2932 SwitchStatementOp(SwitchStatementOp that, CopyContext cc, OpTransformer ot) { 2933 super(that, cc, ot); 2934 } 2935 2936 @Override 2937 public SwitchStatementOp transform(CopyContext cc, OpTransformer ot) { 2938 return new SwitchStatementOp(this, cc, ot); 2939 } 2940 2941 SwitchStatementOp(Value target, List<Body.Builder> bodyCs) { 2942 super(NAME, target, bodyCs); 2943 } 2944 2945 @Override 2946 public TypeElement resultType() { 2947 return VOID; 2948 } 2949 } 2950 2951 /** 2952 * The switch fall-through operation, that can model fall-through to the next statement in the switch block after 2953 * the last statement of the current switch label. 2954 */ 2955 @OpDeclaration(SwitchFallthroughOp.NAME) 2956 public static final class SwitchFallthroughOp extends JavaOp 2957 implements Op.BodyTerminating, Op.Lowerable { 2958 static final String NAME = "java.switch.fallthrough"; 2959 2960 SwitchFallthroughOp(ExternalizedOp def) { 2961 this(); 2962 } 2963 2964 SwitchFallthroughOp(SwitchFallthroughOp that, CopyContext cc) { 2965 super(that, cc); 2966 } 2967 2968 @Override 2969 public SwitchFallthroughOp transform(CopyContext cc, OpTransformer ot) { 2970 return new SwitchFallthroughOp(this, cc); 2971 } 2972 2973 SwitchFallthroughOp() { 2974 super(NAME, List.of()); 2975 } 2976 2977 @Override 2978 public TypeElement resultType() { 2979 return VOID; 2980 } 2981 2982 @Override 2983 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 2984 return lower(b, BranchTarget::continueBlock); 2985 } 2986 2987 Block.Builder lower(Block.Builder b, Function<BranchTarget, Block.Builder> f) { 2988 BranchTarget t = getBranchTarget(b.context(), ancestorBody()); 2989 if (t != null) { 2990 b.op(branch(f.apply(t).successor())); 2991 } else { 2992 throw new IllegalStateException("No branch target for operation: " + this); 2993 } 2994 return b; 2995 } 2996 } 2997 2998 /** 2999 * The for operation, that can model a Java language for statement. 3000 */ 3001 @OpDeclaration(ForOp.NAME) 3002 public static final class ForOp extends JavaOp 3003 implements Op.Loop, Op.Lowerable, JavaStatement { 3004 3005 public static final class InitBuilder { 3006 final Body.Builder ancestorBody; 3007 final List<? extends TypeElement> initTypes; 3008 3009 InitBuilder(Body.Builder ancestorBody, 3010 List<? extends TypeElement> initTypes) { 3011 this.ancestorBody = ancestorBody; 3012 this.initTypes = initTypes.stream().map(CoreType::varType).toList(); 3013 } 3014 3015 public ForOp.CondBuilder init(Consumer<Block.Builder> c) { 3016 Body.Builder init = Body.Builder.of(ancestorBody, 3017 CoreType.functionType(CoreType.tupleType(initTypes))); 3018 c.accept(init.entryBlock()); 3019 3020 return new CondBuilder(ancestorBody, initTypes, init); 3021 } 3022 } 3023 3024 public static final class CondBuilder { 3025 final Body.Builder ancestorBody; 3026 final List<? extends TypeElement> initTypes; 3027 final Body.Builder init; 3028 3029 public CondBuilder(Body.Builder ancestorBody, 3030 List<? extends TypeElement> initTypes, 3031 Body.Builder init) { 3032 this.ancestorBody = ancestorBody; 3033 this.initTypes = initTypes; 3034 this.init = init; 3035 } 3036 3037 public ForOp.UpdateBuilder cond(Consumer<Block.Builder> c) { 3038 Body.Builder cond = Body.Builder.of(ancestorBody, 3039 CoreType.functionType(BOOLEAN, initTypes)); 3040 c.accept(cond.entryBlock()); 3041 3042 return new UpdateBuilder(ancestorBody, initTypes, init, cond); 3043 } 3044 } 3045 3046 public static final class UpdateBuilder { 3047 final Body.Builder ancestorBody; 3048 final List<? extends TypeElement> initTypes; 3049 final Body.Builder init; 3050 final Body.Builder cond; 3051 3052 public UpdateBuilder(Body.Builder ancestorBody, 3053 List<? extends TypeElement> initTypes, 3054 Body.Builder init, Body.Builder cond) { 3055 this.ancestorBody = ancestorBody; 3056 this.initTypes = initTypes; 3057 this.init = init; 3058 this.cond = cond; 3059 } 3060 3061 public ForOp.BodyBuilder cond(Consumer<Block.Builder> c) { 3062 Body.Builder update = Body.Builder.of(ancestorBody, 3063 CoreType.functionType(VOID, initTypes)); 3064 c.accept(update.entryBlock()); 3065 3066 return new BodyBuilder(ancestorBody, initTypes, init, cond, update); 3067 } 3068 3069 } 3070 3071 public static final class BodyBuilder { 3072 final Body.Builder ancestorBody; 3073 final List<? extends TypeElement> initTypes; 3074 final Body.Builder init; 3075 final Body.Builder cond; 3076 final Body.Builder update; 3077 3078 public BodyBuilder(Body.Builder ancestorBody, 3079 List<? extends TypeElement> initTypes, 3080 Body.Builder init, Body.Builder cond, Body.Builder update) { 3081 this.ancestorBody = ancestorBody; 3082 this.initTypes = initTypes; 3083 this.init = init; 3084 this.cond = cond; 3085 this.update = update; 3086 } 3087 3088 public ForOp body(Consumer<Block.Builder> c) { 3089 Body.Builder body = Body.Builder.of(ancestorBody, 3090 CoreType.functionType(VOID, initTypes)); 3091 c.accept(body.entryBlock()); 3092 3093 return new ForOp(init, cond, update, body); 3094 } 3095 } 3096 3097 static final String NAME = "java.for"; 3098 3099 final Body init; 3100 final Body cond; 3101 final Body update; 3102 final Body body; 3103 3104 static ForOp create(ExternalizedOp def) { 3105 return new ForOp(def); 3106 } 3107 3108 ForOp(ExternalizedOp def) { 3109 this(def.bodyDefinitions().get(0), 3110 def.bodyDefinitions().get(1), 3111 def.bodyDefinitions().get(2), 3112 def.bodyDefinitions().get(3)); 3113 } 3114 3115 ForOp(ForOp that, CopyContext cc, OpTransformer ot) { 3116 super(that, cc); 3117 3118 this.init = that.init.transform(cc, ot).build(this); 3119 this.cond = that.cond.transform(cc, ot).build(this); 3120 this.update = that.update.transform(cc, ot).build(this); 3121 this.body = that.body.transform(cc, ot).build(this); 3122 } 3123 3124 @Override 3125 public ForOp transform(CopyContext cc, OpTransformer ot) { 3126 return new ForOp(this, cc, ot); 3127 } 3128 3129 ForOp(Body.Builder initC, 3130 Body.Builder condC, 3131 Body.Builder updateC, 3132 Body.Builder bodyC) { 3133 super(NAME, List.of()); 3134 3135 this.init = initC.build(this); 3136 3137 this.cond = condC.build(this); 3138 3139 this.update = updateC.build(this); 3140 if (!update.bodyType().returnType().equals(VOID)) { 3141 throw new IllegalArgumentException("Update should return void: " + update.bodyType()); 3142 } 3143 3144 this.body = bodyC.build(this); 3145 if (!body.bodyType().returnType().equals(VOID)) { 3146 throw new IllegalArgumentException("Body should return void: " + body.bodyType()); 3147 } 3148 } 3149 3150 @Override 3151 public List<Body> bodies() { 3152 return List.of(init, cond, update, body); 3153 } 3154 3155 public Body init() { 3156 return init; 3157 } 3158 3159 public Body cond() { 3160 return cond; 3161 } 3162 3163 public Body update() { 3164 return update; 3165 } 3166 3167 @Override 3168 public Body loopBody() { 3169 return body; 3170 } 3171 3172 @Override 3173 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 3174 Block.Builder header = b.block(); 3175 Block.Builder body = b.block(); 3176 Block.Builder update = b.block(); 3177 Block.Builder exit = b.block(); 3178 3179 List<Value> initValues = new ArrayList<>(); 3180 // @@@ Init body has one yield operation yielding 3181 // void, a single variable, or a tuple of one or more variables 3182 b.body(init, List.of(), andThenLowering(opT, (block, op) -> switch (op) { 3183 case TupleOp _ -> { 3184 // Drop Tuple if a yielded 3185 boolean isResult = op.result().uses().size() == 1 && 3186 op.result().uses().stream().allMatch(r -> r.op() instanceof CoreOp.YieldOp); 3187 if (!isResult) { 3188 block.op(op); 3189 } 3190 yield block; 3191 } 3192 case CoreOp.YieldOp yop -> { 3193 if (yop.yieldValue() == null) { 3194 block.op(branch(header.successor())); 3195 yield block; 3196 } else if (yop.yieldValue() instanceof Result or) { 3197 if (or.op() instanceof TupleOp top) { 3198 initValues.addAll(block.context().getValues(top.operands())); 3199 } else { 3200 initValues.addAll(block.context().getValues(yop.operands())); 3201 } 3202 block.op(branch(header.successor())); 3203 yield block; 3204 } 3205 3206 throw new IllegalStateException("Bad yield operation"); 3207 } 3208 default -> null; 3209 })); 3210 3211 header.body(cond, initValues, andThenLowering(opT, (block, op) -> { 3212 if (op instanceof CoreOp.YieldOp yo) { 3213 block.op(conditionalBranch(block.context().getValue(yo.yieldValue()), 3214 body.successor(), exit.successor())); 3215 return block; 3216 } else { 3217 return null; 3218 } 3219 })); 3220 3221 setBranchTarget(b.context(), this, new BranchTarget(exit, update)); 3222 3223 body.body(this.body, initValues, andThenLowering(opT, (_, _) -> null)); 3224 3225 update.body(this.update, initValues, andThenLowering(opT, (block, op) -> { 3226 if (op instanceof CoreOp.YieldOp) { 3227 block.op(branch(header.successor())); 3228 return block; 3229 } else { 3230 return null; 3231 } 3232 })); 3233 3234 return exit; 3235 } 3236 3237 @Override 3238 public TypeElement resultType() { 3239 return VOID; 3240 } 3241 } 3242 3243 /** 3244 * The enhanced for operation, that can model a Java language enhanced for statement. 3245 */ 3246 @OpDeclaration(EnhancedForOp.NAME) 3247 public static final class EnhancedForOp extends JavaOp 3248 implements Op.Loop, Op.Lowerable, JavaStatement { 3249 3250 public static final class ExpressionBuilder { 3251 final Body.Builder ancestorBody; 3252 final TypeElement iterableType; 3253 final TypeElement elementType; 3254 3255 ExpressionBuilder(Body.Builder ancestorBody, 3256 TypeElement iterableType, TypeElement elementType) { 3257 this.ancestorBody = ancestorBody; 3258 this.iterableType = iterableType; 3259 this.elementType = elementType; 3260 } 3261 3262 public DefinitionBuilder expression(Consumer<Block.Builder> c) { 3263 Body.Builder expression = Body.Builder.of(ancestorBody, 3264 CoreType.functionType(iterableType)); 3265 c.accept(expression.entryBlock()); 3266 3267 return new DefinitionBuilder(ancestorBody, elementType, expression); 3268 } 3269 } 3270 3271 public static final class DefinitionBuilder { 3272 final Body.Builder ancestorBody; 3273 final TypeElement elementType; 3274 final Body.Builder expression; 3275 3276 DefinitionBuilder(Body.Builder ancestorBody, 3277 TypeElement elementType, Body.Builder expression) { 3278 this.ancestorBody = ancestorBody; 3279 this.elementType = elementType; 3280 this.expression = expression; 3281 } 3282 3283 public BodyBuilder definition(Consumer<Block.Builder> c) { 3284 return definition(elementType, c); 3285 } 3286 3287 public BodyBuilder definition(TypeElement bodyElementType, Consumer<Block.Builder> c) { 3288 Body.Builder definition = Body.Builder.of(ancestorBody, 3289 CoreType.functionType(bodyElementType, elementType)); 3290 c.accept(definition.entryBlock()); 3291 3292 return new BodyBuilder(ancestorBody, elementType, expression, definition); 3293 } 3294 } 3295 3296 public static final class BodyBuilder { 3297 final Body.Builder ancestorBody; 3298 final TypeElement elementType; 3299 final Body.Builder expression; 3300 final Body.Builder definition; 3301 3302 BodyBuilder(Body.Builder ancestorBody, 3303 TypeElement elementType, Body.Builder expression, Body.Builder definition) { 3304 this.ancestorBody = ancestorBody; 3305 this.elementType = elementType; 3306 this.expression = expression; 3307 this.definition = definition; 3308 } 3309 3310 public EnhancedForOp body(Consumer<Block.Builder> c) { 3311 Body.Builder body = Body.Builder.of(ancestorBody, 3312 CoreType.functionType(VOID, elementType)); 3313 c.accept(body.entryBlock()); 3314 3315 return new EnhancedForOp(expression, definition, body); 3316 } 3317 } 3318 3319 static final String NAME = "java.enhancedFor"; 3320 3321 final Body expression; 3322 final Body init; 3323 final Body body; 3324 3325 static EnhancedForOp create(ExternalizedOp def) { 3326 return new EnhancedForOp(def); 3327 } 3328 3329 EnhancedForOp(ExternalizedOp def) { 3330 this(def.bodyDefinitions().get(0), 3331 def.bodyDefinitions().get(1), 3332 def.bodyDefinitions().get(2)); 3333 } 3334 3335 EnhancedForOp(EnhancedForOp that, CopyContext cc, OpTransformer ot) { 3336 super(that, cc); 3337 3338 this.expression = that.expression.transform(cc, ot).build(this); 3339 this.init = that.init.transform(cc, ot).build(this); 3340 this.body = that.body.transform(cc, ot).build(this); 3341 } 3342 3343 @Override 3344 public EnhancedForOp transform(CopyContext cc, OpTransformer ot) { 3345 return new EnhancedForOp(this, cc, ot); 3346 } 3347 3348 EnhancedForOp(Body.Builder expressionC, Body.Builder initC, Body.Builder bodyC) { 3349 super(NAME, List.of()); 3350 3351 this.expression = expressionC.build(this); 3352 if (expression.bodyType().returnType().equals(VOID)) { 3353 throw new IllegalArgumentException("Expression should return non-void value: " + expression.bodyType()); 3354 } 3355 if (!expression.bodyType().parameterTypes().isEmpty()) { 3356 throw new IllegalArgumentException("Expression should have zero parameters: " + expression.bodyType()); 3357 } 3358 3359 this.init = initC.build(this); 3360 if (init.bodyType().returnType().equals(VOID)) { 3361 throw new IllegalArgumentException("Initialization should return non-void value: " + init.bodyType()); 3362 } 3363 if (init.bodyType().parameterTypes().size() != 1) { 3364 throw new IllegalArgumentException("Initialization should have one parameter: " + init.bodyType()); 3365 } 3366 3367 this.body = bodyC.build(this); 3368 if (!body.bodyType().returnType().equals(VOID)) { 3369 throw new IllegalArgumentException("Body should return void: " + body.bodyType()); 3370 } 3371 if (body.bodyType().parameterTypes().size() != 1) { 3372 throw new IllegalArgumentException("Body should have one parameter: " + body.bodyType()); 3373 } 3374 } 3375 3376 @Override 3377 public List<Body> bodies() { 3378 return List.of(expression, init, body); 3379 } 3380 3381 public Body expression() { 3382 return expression; 3383 } 3384 3385 public Body initialization() { 3386 return init; 3387 } 3388 3389 @Override 3390 public Body loopBody() { 3391 return body; 3392 } 3393 3394 static final MethodRef ITERABLE_ITERATOR = MethodRef.method(Iterable.class, "iterator", Iterator.class); 3395 static final MethodRef ITERATOR_HAS_NEXT = MethodRef.method(Iterator.class, "hasNext", boolean.class); 3396 static final MethodRef ITERATOR_NEXT = MethodRef.method(Iterator.class, "next", Object.class); 3397 3398 @Override 3399 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 3400 JavaType elementType = (JavaType) init.entryBlock().parameters().get(0).type(); 3401 boolean isArray = expression.bodyType().returnType() instanceof ArrayType; 3402 3403 Block.Builder preHeader = b.block(expression.bodyType().returnType()); 3404 Block.Builder header = b.block(isArray ? List.of(INT) : List.of()); 3405 Block.Builder init = b.block(); 3406 Block.Builder body = b.block(); 3407 Block.Builder exit = b.block(); 3408 3409 b.body(expression, List.of(), andThenLowering(opT, (block, op) -> { 3410 if (op instanceof CoreOp.YieldOp yop) { 3411 Value loopSource = block.context().getValue(yop.yieldValue()); 3412 block.op(branch(preHeader.successor(loopSource))); 3413 return block; 3414 } else { 3415 return null; 3416 } 3417 })); 3418 3419 if (isArray) { 3420 Value array = preHeader.parameters().get(0); 3421 Value arrayLength = preHeader.op(arrayLength(array)); 3422 Value i = preHeader.op(constant(INT, 0)); 3423 preHeader.op(branch(header.successor(i))); 3424 3425 i = header.parameters().get(0); 3426 Value p = header.op(lt(i, arrayLength)); 3427 header.op(conditionalBranch(p, init.successor(), exit.successor())); 3428 3429 Value e = init.op(arrayLoadOp(array, i)); 3430 List<Value> initValues = new ArrayList<>(); 3431 init.body(this.init, List.of(e), andThenLowering(opT, (block, op) -> { 3432 if (op instanceof CoreOp.YieldOp yop) { 3433 initValues.addAll(block.context().getValues(yop.operands())); 3434 block.op(branch(body.successor())); 3435 return block; 3436 } else { 3437 return null; 3438 } 3439 })); 3440 3441 Block.Builder update = b.block(); 3442 setBranchTarget(b.context(), this, new BranchTarget(exit, update)); 3443 3444 body.body(this.body, initValues, andThenLowering(opT, (_, _) -> null)); 3445 3446 i = update.op(add(i, update.op(constant(INT, 1)))); 3447 update.op(branch(header.successor(i))); 3448 } else { 3449 JavaType iterable = parameterized(type(Iterator.class), elementType); 3450 Value iterator = preHeader.op(invoke(iterable, ITERABLE_ITERATOR, preHeader.parameters().get(0))); 3451 preHeader.op(branch(header.successor())); 3452 3453 Value p = header.op(invoke(ITERATOR_HAS_NEXT, iterator)); 3454 header.op(conditionalBranch(p, init.successor(), exit.successor())); 3455 3456 Value e = init.op(invoke(elementType, ITERATOR_NEXT, iterator)); 3457 List<Value> initValues = new ArrayList<>(); 3458 init.body(this.init, List.of(e), andThenLowering(opT, (block, op) -> { 3459 if (op instanceof CoreOp.YieldOp yop) { 3460 initValues.addAll(block.context().getValues(yop.operands())); 3461 block.op(branch(body.successor())); 3462 return block; 3463 } else { 3464 return null; 3465 } 3466 })); 3467 3468 setBranchTarget(b.context(), this, new BranchTarget(exit, header)); 3469 3470 body.body(this.body, initValues, andThenLowering(opT, (_, _) -> null)); 3471 } 3472 3473 return exit; 3474 } 3475 3476 @Override 3477 public TypeElement resultType() { 3478 return VOID; 3479 } 3480 } 3481 3482 /** 3483 * The while operation, that can model a Java language while statement. 3484 */ 3485 @OpDeclaration(WhileOp.NAME) 3486 public static final class WhileOp extends JavaOp 3487 implements Op.Loop, Op.Lowerable, JavaStatement { 3488 3489 public static class PredicateBuilder { 3490 final Body.Builder ancestorBody; 3491 3492 PredicateBuilder(Body.Builder ancestorBody) { 3493 this.ancestorBody = ancestorBody; 3494 } 3495 3496 public WhileOp.BodyBuilder predicate(Consumer<Block.Builder> c) { 3497 Body.Builder body = Body.Builder.of(ancestorBody, CoreType.functionType(BOOLEAN)); 3498 c.accept(body.entryBlock()); 3499 3500 return new WhileOp.BodyBuilder(ancestorBody, body); 3501 } 3502 } 3503 3504 public static class BodyBuilder { 3505 final Body.Builder ancestorBody; 3506 private final Body.Builder predicate; 3507 3508 BodyBuilder(Body.Builder ancestorBody, Body.Builder predicate) { 3509 this.ancestorBody = ancestorBody; 3510 this.predicate = predicate; 3511 } 3512 3513 public WhileOp body(Consumer<Block.Builder> c) { 3514 Body.Builder body = Body.Builder.of(ancestorBody, CoreType.FUNCTION_TYPE_VOID); 3515 c.accept(body.entryBlock()); 3516 3517 return new WhileOp(List.of(predicate, body)); 3518 } 3519 } 3520 3521 private static final String NAME = "java.while"; 3522 3523 private final List<Body> bodies; 3524 3525 WhileOp(ExternalizedOp def) { 3526 this(def.bodyDefinitions()); 3527 } 3528 3529 WhileOp(List<Body.Builder> bodyCs) { 3530 super(NAME, List.of()); 3531 3532 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList(); 3533 } 3534 3535 WhileOp(Body.Builder predicate, Body.Builder body) { 3536 super(NAME, List.of()); 3537 3538 Objects.requireNonNull(body); 3539 3540 this.bodies = Stream.of(predicate, body).filter(Objects::nonNull) 3541 .map(bc -> bc.build(this)).toList(); 3542 3543 // @@@ This will change with pattern bindings 3544 if (!bodies.get(0).bodyType().equals(CoreType.functionType(BOOLEAN))) { 3545 throw new IllegalArgumentException( 3546 "Predicate body descriptor should be " + CoreType.functionType(BOOLEAN) + 3547 " but is " + bodies.get(0).bodyType()); 3548 } 3549 if (!bodies.get(1).bodyType().equals(CoreType.FUNCTION_TYPE_VOID)) { 3550 throw new IllegalArgumentException( 3551 "Body descriptor should be " + CoreType.functionType(VOID) + 3552 " but is " + bodies.get(1).bodyType()); 3553 } 3554 } 3555 3556 WhileOp(WhileOp that, CopyContext cc, OpTransformer ot) { 3557 super(that, cc); 3558 3559 this.bodies = that.bodies.stream() 3560 .map(b -> b.transform(cc, ot).build(this)).toList(); 3561 } 3562 3563 @Override 3564 public WhileOp transform(CopyContext cc, OpTransformer ot) { 3565 return new WhileOp(this, cc, ot); 3566 } 3567 3568 @Override 3569 public List<Body> bodies() { 3570 return bodies; 3571 } 3572 3573 public Body predicateBody() { 3574 return bodies.get(0); 3575 } 3576 3577 @Override 3578 public Body loopBody() { 3579 return bodies.get(1); 3580 } 3581 3582 @Override 3583 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 3584 Block.Builder header = b.block(); 3585 Block.Builder body = b.block(); 3586 Block.Builder exit = b.block(); 3587 3588 b.op(branch(header.successor())); 3589 3590 header.body(predicateBody(), List.of(), andThenLowering(opT, (block, op) -> { 3591 if (op instanceof CoreOp.YieldOp yo) { 3592 block.op(conditionalBranch(block.context().getValue(yo.yieldValue()), 3593 body.successor(), exit.successor())); 3594 return block; 3595 } else { 3596 return null; 3597 } 3598 })); 3599 3600 setBranchTarget(b.context(), this, new BranchTarget(exit, header)); 3601 3602 body.body(loopBody(), List.of(), andThenLowering(opT, (_, _) -> null)); 3603 3604 return exit; 3605 } 3606 3607 @Override 3608 public TypeElement resultType() { 3609 return VOID; 3610 } 3611 } 3612 3613 /** 3614 * The do-while operation, that can model a Java language do statement. 3615 */ 3616 // @@@ Unify JavaDoWhileOp and JavaWhileOp with common abstract superclass 3617 @OpDeclaration(DoWhileOp.NAME) 3618 public static final class DoWhileOp extends JavaOp 3619 implements Op.Loop, Op.Lowerable, JavaStatement { 3620 3621 public static class PredicateBuilder { 3622 final Body.Builder ancestorBody; 3623 private final Body.Builder body; 3624 3625 PredicateBuilder(Body.Builder ancestorBody, Body.Builder body) { 3626 this.ancestorBody = ancestorBody; 3627 this.body = body; 3628 } 3629 3630 public DoWhileOp predicate(Consumer<Block.Builder> c) { 3631 Body.Builder predicate = Body.Builder.of(ancestorBody, CoreType.functionType(BOOLEAN)); 3632 c.accept(predicate.entryBlock()); 3633 3634 return new DoWhileOp(List.of(body, predicate)); 3635 } 3636 } 3637 3638 public static class BodyBuilder { 3639 final Body.Builder ancestorBody; 3640 3641 BodyBuilder(Body.Builder ancestorBody) { 3642 this.ancestorBody = ancestorBody; 3643 } 3644 3645 public DoWhileOp.PredicateBuilder body(Consumer<Block.Builder> c) { 3646 Body.Builder body = Body.Builder.of(ancestorBody, CoreType.FUNCTION_TYPE_VOID); 3647 c.accept(body.entryBlock()); 3648 3649 return new DoWhileOp.PredicateBuilder(ancestorBody, body); 3650 } 3651 } 3652 3653 private static final String NAME = "java.do.while"; 3654 3655 private final List<Body> bodies; 3656 3657 DoWhileOp(ExternalizedOp def) { 3658 this(def.bodyDefinitions()); 3659 } 3660 3661 DoWhileOp(List<Body.Builder> bodyCs) { 3662 super(NAME, List.of()); 3663 3664 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList(); 3665 } 3666 3667 DoWhileOp(Body.Builder body, Body.Builder predicate) { 3668 super(NAME, List.of()); 3669 3670 Objects.requireNonNull(body); 3671 3672 this.bodies = Stream.of(body, predicate).filter(Objects::nonNull) 3673 .map(bc -> bc.build(this)).toList(); 3674 3675 if (!bodies.get(0).bodyType().equals(CoreType.FUNCTION_TYPE_VOID)) { 3676 throw new IllegalArgumentException( 3677 "Body descriptor should be " + CoreType.functionType(VOID) + 3678 " but is " + bodies.get(1).bodyType()); 3679 } 3680 if (!bodies.get(1).bodyType().equals(CoreType.functionType(BOOLEAN))) { 3681 throw new IllegalArgumentException( 3682 "Predicate body descriptor should be " + CoreType.functionType(BOOLEAN) + 3683 " but is " + bodies.get(0).bodyType()); 3684 } 3685 } 3686 3687 DoWhileOp(DoWhileOp that, CopyContext cc, OpTransformer ot) { 3688 super(that, cc); 3689 3690 this.bodies = that.bodies.stream() 3691 .map(b -> b.transform(cc, ot).build(this)).toList(); 3692 } 3693 3694 @Override 3695 public DoWhileOp transform(CopyContext cc, OpTransformer ot) { 3696 return new DoWhileOp(this, cc, ot); 3697 } 3698 3699 @Override 3700 public List<Body> bodies() { 3701 return bodies; 3702 } 3703 3704 public Body predicateBody() { 3705 return bodies.get(1); 3706 } 3707 3708 @Override 3709 public Body loopBody() { 3710 return bodies.get(0); 3711 } 3712 3713 @Override 3714 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 3715 Block.Builder body = b.block(); 3716 Block.Builder header = b.block(); 3717 Block.Builder exit = b.block(); 3718 3719 b.op(branch(body.successor())); 3720 3721 setBranchTarget(b.context(), this, new BranchTarget(exit, header)); 3722 3723 body.body(loopBody(), List.of(), andThenLowering(opT, (_, _) -> null)); 3724 3725 header.body(predicateBody(), List.of(), andThenLowering(opT, (block, op) -> { 3726 if (op instanceof CoreOp.YieldOp yo) { 3727 block.op(conditionalBranch(block.context().getValue(yo.yieldValue()), 3728 body.successor(), exit.successor())); 3729 return block; 3730 } else { 3731 return null; 3732 } 3733 })); 3734 3735 return exit; 3736 } 3737 3738 @Override 3739 public TypeElement resultType() { 3740 return VOID; 3741 } 3742 } 3743 3744 /** 3745 * The conditional-and-or operation, that can model Java language condition-or or conditional-and expressions. 3746 */ 3747 public sealed static abstract class JavaConditionalOp extends JavaOp 3748 implements Op.Nested, Op.Lowerable, JavaExpression { 3749 final List<Body> bodies; 3750 3751 JavaConditionalOp(JavaConditionalOp that, CopyContext cc, OpTransformer ot) { 3752 super(that, cc); 3753 3754 // Copy body 3755 this.bodies = that.bodies.stream().map(b -> b.transform(cc, ot).build(this)).toList(); 3756 } 3757 3758 JavaConditionalOp(String name, List<Body.Builder> bodyCs) { 3759 super(name, List.of()); 3760 3761 if (bodyCs.isEmpty()) { 3762 throw new IllegalArgumentException(); 3763 } 3764 3765 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList(); 3766 for (Body b : bodies) { 3767 if (!b.bodyType().equals(CoreType.functionType(BOOLEAN))) { 3768 throw new IllegalArgumentException("Body conditional body descriptor: " + b.bodyType()); 3769 } 3770 } 3771 } 3772 3773 @Override 3774 public List<Body> bodies() { 3775 return bodies; 3776 } 3777 3778 static Block.Builder lower(Block.Builder startBlock, OpTransformer opT, JavaConditionalOp cop) { 3779 List<Body> bodies = cop.bodies(); 3780 3781 Block.Builder exit = startBlock.block(); 3782 TypeElement oprType = cop.result().type(); 3783 Block.Parameter arg = exit.parameter(oprType); 3784 startBlock.context().mapValue(cop.result(), arg); 3785 3786 // Transform bodies in reverse order 3787 // This makes available the blocks to be referenced as successors in prior blocks 3788 3789 Block.Builder pred = null; 3790 for (int i = bodies.size() - 1; i >= 0; i--) { 3791 BiFunction<Block.Builder, Op, Block.Builder> opt; 3792 if (i == bodies.size() - 1) { 3793 opt = lowering(opT, (block, op) -> { 3794 if (op instanceof CoreOp.YieldOp yop) { 3795 Value p = block.context().getValue(yop.yieldValue()); 3796 block.op(branch(exit.successor(p))); 3797 return block; 3798 } else { 3799 return null; 3800 } 3801 }); 3802 } else { 3803 Block.Builder nextPred = pred; 3804 opt = lowering(opT, (block, op) -> { 3805 if (op instanceof CoreOp.YieldOp yop) { 3806 Value p = block.context().getValue(yop.yieldValue()); 3807 if (cop instanceof ConditionalAndOp) { 3808 block.op(conditionalBranch(p, nextPred.successor(), exit.successor(p))); 3809 } else { 3810 block.op(conditionalBranch(p, exit.successor(p), nextPred.successor())); 3811 } 3812 return block; 3813 } else { 3814 return null; 3815 } 3816 }); 3817 } 3818 3819 Body fromPred = bodies.get(i); 3820 if (i == 0) { 3821 startBlock.body(fromPred, List.of(), opt::apply); 3822 } else { 3823 pred = startBlock.block(fromPred.bodyType().parameterTypes()); 3824 pred.body(fromPred, pred.parameters(), andThen(opT, opt)); 3825 } 3826 } 3827 3828 return exit; 3829 } 3830 3831 @Override 3832 public TypeElement resultType() { 3833 return BOOLEAN; 3834 } 3835 } 3836 3837 /** 3838 * The conditional-and operation, that can model Java language conditional-and expressions. 3839 */ 3840 @OpDeclaration(ConditionalAndOp.NAME) 3841 public static final class ConditionalAndOp extends JavaConditionalOp { 3842 3843 public static class Builder { 3844 final Body.Builder ancestorBody; 3845 final List<Body.Builder> bodies; 3846 3847 Builder(Body.Builder ancestorBody, Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) { 3848 this.ancestorBody = ancestorBody; 3849 this.bodies = new ArrayList<>(); 3850 and(lhs); 3851 and(rhs); 3852 } 3853 3854 public Builder and(Consumer<Block.Builder> c) { 3855 Body.Builder body = Body.Builder.of(ancestorBody, CoreType.functionType(BOOLEAN)); 3856 c.accept(body.entryBlock()); 3857 bodies.add(body); 3858 3859 return this; 3860 } 3861 3862 public ConditionalAndOp build() { 3863 return new ConditionalAndOp(bodies); 3864 } 3865 } 3866 3867 static final String NAME = "java.cand"; 3868 3869 ConditionalAndOp(ExternalizedOp def) { 3870 this(def.bodyDefinitions()); 3871 } 3872 3873 ConditionalAndOp(ConditionalAndOp that, CopyContext cc, OpTransformer ot) { 3874 super(that, cc, ot); 3875 } 3876 3877 @Override 3878 public ConditionalAndOp transform(CopyContext cc, OpTransformer ot) { 3879 return new ConditionalAndOp(this, cc, ot); 3880 } 3881 3882 ConditionalAndOp(List<Body.Builder> bodyCs) { 3883 super(NAME, bodyCs); 3884 } 3885 3886 @Override 3887 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 3888 return lower(b, opT, this); 3889 } 3890 } 3891 3892 /** 3893 * The conditional-or operation, that can model Java language conditional-or expressions. 3894 */ 3895 @OpDeclaration(ConditionalOrOp.NAME) 3896 public static final class ConditionalOrOp extends JavaConditionalOp { 3897 3898 public static class Builder { 3899 final Body.Builder ancestorBody; 3900 final List<Body.Builder> bodies; 3901 3902 Builder(Body.Builder ancestorBody, Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) { 3903 this.ancestorBody = ancestorBody; 3904 this.bodies = new ArrayList<>(); 3905 or(lhs); 3906 or(rhs); 3907 } 3908 3909 public Builder or(Consumer<Block.Builder> c) { 3910 Body.Builder body = Body.Builder.of(ancestorBody, CoreType.functionType(BOOLEAN)); 3911 c.accept(body.entryBlock()); 3912 bodies.add(body); 3913 3914 return this; 3915 } 3916 3917 public ConditionalOrOp build() { 3918 return new ConditionalOrOp(bodies); 3919 } 3920 } 3921 3922 static final String NAME = "java.cor"; 3923 3924 ConditionalOrOp(ExternalizedOp def) { 3925 this(def.bodyDefinitions()); 3926 } 3927 3928 ConditionalOrOp(ConditionalOrOp that, CopyContext cc, OpTransformer ot) { 3929 super(that, cc, ot); 3930 } 3931 3932 @Override 3933 public ConditionalOrOp transform(CopyContext cc, OpTransformer ot) { 3934 return new ConditionalOrOp(this, cc, ot); 3935 } 3936 3937 ConditionalOrOp(List<Body.Builder> bodyCs) { 3938 super(NAME, bodyCs); 3939 } 3940 3941 @Override 3942 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 3943 return lower(b, opT, this); 3944 } 3945 } 3946 3947 /** 3948 * The conditional operation, that can model Java language conditional operator {@code ?} expressions. 3949 */ 3950 @OpDeclaration(ConditionalExpressionOp.NAME) 3951 public static final class ConditionalExpressionOp extends JavaOp 3952 implements Op.Nested, Op.Lowerable, JavaExpression { 3953 3954 static final String NAME = "java.cexpression"; 3955 3956 final TypeElement resultType; 3957 // {cond, truepart, falsepart} 3958 final List<Body> bodies; 3959 3960 ConditionalExpressionOp(ExternalizedOp def) { 3961 if (!def.operands().isEmpty()) { 3962 throw new IllegalStateException("Operation must have no operands"); 3963 } 3964 3965 this(def.resultType(), def.bodyDefinitions()); 3966 } 3967 3968 ConditionalExpressionOp(ConditionalExpressionOp that, CopyContext cc, OpTransformer ot) { 3969 super(that, cc); 3970 3971 // Copy body 3972 this.bodies = that.bodies.stream() 3973 .map(b -> b.transform(cc, ot).build(this)).toList(); 3974 this.resultType = that.resultType; 3975 } 3976 3977 @Override 3978 public ConditionalExpressionOp transform(CopyContext cc, OpTransformer ot) { 3979 return new ConditionalExpressionOp(this, cc, ot); 3980 } 3981 3982 ConditionalExpressionOp(TypeElement expressionType, List<Body.Builder> bodyCs) { 3983 super(NAME, List.of()); 3984 3985 this.bodies = bodyCs.stream().map(bc -> bc.build(this)).toList(); 3986 // @@@ when expressionType is null, we assume truepart and falsepart have the same yieldType 3987 this.resultType = expressionType == null ? bodies.get(1).yieldType() : expressionType; 3988 3989 if (bodies.size() < 3) { 3990 throw new IllegalArgumentException("Incorrect number of bodies: " + bodies.size()); 3991 } 3992 3993 Body cond = bodies.get(0); 3994 if (!cond.bodyType().equals(CoreType.functionType(BOOLEAN))) { 3995 throw new IllegalArgumentException("Illegal cond body descriptor: " + cond.bodyType()); 3996 } 3997 } 3998 3999 @Override 4000 public List<Body> bodies() { 4001 return bodies; 4002 } 4003 4004 @Override 4005 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 4006 Block.Builder exit = b.block(resultType()); 4007 exit.context().mapValue(result(), exit.parameters().get(0)); 4008 4009 setBranchTarget(b.context(), this, new BranchTarget(exit, null)); 4010 4011 List<Block.Builder> builders = List.of(b.block(), b.block()); 4012 b.body(bodies.get(0), List.of(), andThenLowering(opT, (block, op) -> { 4013 if (op instanceof CoreOp.YieldOp yo) { 4014 block.op(conditionalBranch(block.context().getValue(yo.yieldValue()), 4015 builders.get(0).successor(), builders.get(1).successor())); 4016 return block; 4017 } else { 4018 return null; 4019 } 4020 })); 4021 4022 for (int i = 0; i < 2; i++) { 4023 builders.get(i).body(bodies.get(i + 1), List.of(), andThenLowering(opT, (block, op) -> { 4024 if (op instanceof CoreOp.YieldOp yop) { 4025 block.op(branch(exit.successor(block.context().getValue(yop.yieldValue())))); 4026 return block; 4027 } else { 4028 return null; 4029 } 4030 })); 4031 } 4032 4033 return exit; 4034 } 4035 4036 @Override 4037 public TypeElement resultType() { 4038 return resultType; 4039 } 4040 } 4041 4042 /** 4043 * The try operation, that can model Java language try statements. 4044 */ 4045 @OpDeclaration(TryOp.NAME) 4046 public static final class TryOp extends JavaOp 4047 implements Op.Nested, Op.Lowerable, JavaStatement { 4048 4049 public static final class BodyBuilder { 4050 final Body.Builder ancestorBody; 4051 final List<? extends TypeElement> resourceTypes; 4052 final Body.Builder resources; 4053 4054 BodyBuilder(Body.Builder ancestorBody, List<? extends TypeElement> resourceTypes, Body.Builder resources) { 4055 this.ancestorBody = ancestorBody; 4056 this.resourceTypes = resourceTypes; 4057 this.resources = resources; 4058 } 4059 4060 public CatchBuilder body(Consumer<Block.Builder> c) { 4061 Body.Builder body = Body.Builder.of(ancestorBody, 4062 CoreType.functionType(VOID, resourceTypes)); 4063 c.accept(body.entryBlock()); 4064 4065 return new CatchBuilder(ancestorBody, resources, body); 4066 } 4067 } 4068 4069 public static final class CatchBuilder { 4070 final Body.Builder ancestorBody; 4071 final Body.Builder resources; 4072 final Body.Builder body; 4073 final List<Body.Builder> catchers; 4074 4075 CatchBuilder(Body.Builder ancestorBody, Body.Builder resources, Body.Builder body) { 4076 this.ancestorBody = ancestorBody; 4077 this.resources = resources; 4078 this.body = body; 4079 this.catchers = new ArrayList<>(); 4080 } 4081 4082 // @@@ multi-catch 4083 public CatchBuilder catch_(TypeElement exceptionType, Consumer<Block.Builder> c) { 4084 Body.Builder _catch = Body.Builder.of(ancestorBody, 4085 CoreType.functionType(VOID, exceptionType)); 4086 c.accept(_catch.entryBlock()); 4087 catchers.add(_catch); 4088 4089 return this; 4090 } 4091 4092 public TryOp finally_(Consumer<Block.Builder> c) { 4093 Body.Builder _finally = Body.Builder.of(ancestorBody, CoreType.FUNCTION_TYPE_VOID); 4094 c.accept(_finally.entryBlock()); 4095 4096 return new TryOp(resources, body, catchers, _finally); 4097 } 4098 4099 public TryOp noFinalizer() { 4100 return new TryOp(resources, body, catchers, null); 4101 } 4102 } 4103 4104 static final String NAME = "java.try"; 4105 4106 final Body resources; 4107 final Body body; 4108 final List<Body> catchers; 4109 final Body finalizer; 4110 4111 static TryOp create(ExternalizedOp def) { 4112 return new TryOp(def); 4113 } 4114 4115 TryOp(ExternalizedOp def) { 4116 List<Body.Builder> bodies = def.bodyDefinitions(); 4117 Body.Builder first = bodies.getFirst(); 4118 Body.Builder resources; 4119 Body.Builder body; 4120 if (first.bodyType().returnType().equals(VOID)) { 4121 resources = null; 4122 body = first; 4123 } else { 4124 resources = first; 4125 body = bodies.get(1); 4126 } 4127 4128 Body.Builder last = bodies.getLast(); 4129 Body.Builder finalizer; 4130 if (last.bodyType().parameterTypes().isEmpty()) { 4131 finalizer = last; 4132 } else { 4133 finalizer = null; 4134 } 4135 List<Body.Builder> catchers = bodies.subList( 4136 resources == null ? 1 : 2, 4137 bodies.size() - (finalizer == null ? 0 : 1)); 4138 4139 this(resources, body, catchers, finalizer); 4140 } 4141 4142 TryOp(TryOp that, CopyContext cc, OpTransformer ot) { 4143 super(that, cc); 4144 4145 if (that.resources != null) { 4146 this.resources = that.resources.transform(cc, ot).build(this); 4147 } else { 4148 this.resources = null; 4149 } 4150 this.body = that.body.transform(cc, ot).build(this); 4151 this.catchers = that.catchers.stream() 4152 .map(b -> b.transform(cc, ot).build(this)) 4153 .toList(); 4154 if (that.finalizer != null) { 4155 this.finalizer = that.finalizer.transform(cc, ot).build(this); 4156 } else { 4157 this.finalizer = null; 4158 } 4159 } 4160 4161 @Override 4162 public TryOp transform(CopyContext cc, OpTransformer ot) { 4163 return new TryOp(this, cc, ot); 4164 } 4165 4166 TryOp(Body.Builder resourcesC, 4167 Body.Builder bodyC, 4168 List<Body.Builder> catchersC, 4169 Body.Builder finalizerC) { 4170 super(NAME, List.of()); 4171 4172 if (resourcesC != null) { 4173 this.resources = resourcesC.build(this); 4174 if (resources.bodyType().returnType().equals(VOID)) { 4175 throw new IllegalArgumentException("Resources should not return void: " + resources.bodyType()); 4176 } 4177 if (!resources.bodyType().parameterTypes().isEmpty()) { 4178 throw new IllegalArgumentException("Resources should have zero parameters: " + resources.bodyType()); 4179 } 4180 } else { 4181 this.resources = null; 4182 } 4183 4184 this.body = bodyC.build(this); 4185 if (!body.bodyType().returnType().equals(VOID)) { 4186 throw new IllegalArgumentException("Try should return void: " + body.bodyType()); 4187 } 4188 4189 this.catchers = catchersC.stream().map(c -> c.build(this)).toList(); 4190 for (Body _catch : catchers) { 4191 if (!_catch.bodyType().returnType().equals(VOID)) { 4192 throw new IllegalArgumentException("Catch should return void: " + _catch.bodyType()); 4193 } 4194 if (_catch.bodyType().parameterTypes().size() != 1) { 4195 throw new IllegalArgumentException("Catch should have zero parameters: " + _catch.bodyType()); 4196 } 4197 } 4198 4199 if (finalizerC != null) { 4200 this.finalizer = finalizerC.build(this); 4201 if (!finalizer.bodyType().returnType().equals(VOID)) { 4202 throw new IllegalArgumentException("Finally should return void: " + finalizer.bodyType()); 4203 } 4204 if (!finalizer.bodyType().parameterTypes().isEmpty()) { 4205 throw new IllegalArgumentException("Finally should have zero parameters: " + finalizer.bodyType()); 4206 } 4207 } else { 4208 this.finalizer = null; 4209 } 4210 } 4211 4212 @Override 4213 public List<Body> bodies() { 4214 ArrayList<Body> bodies = new ArrayList<>(); 4215 if (resources != null) { 4216 bodies.add(resources); 4217 } 4218 bodies.add(body); 4219 bodies.addAll(catchers); 4220 if (finalizer != null) { 4221 bodies.add(finalizer); 4222 } 4223 return bodies; 4224 } 4225 4226 public Body resources() { 4227 return resources; 4228 } 4229 4230 public Body body() { 4231 return body; 4232 } 4233 4234 public List<Body> catchers() { 4235 return catchers; 4236 } 4237 4238 public Body finalizer() { 4239 return finalizer; 4240 } 4241 4242 @Override 4243 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 4244 if (resources != null) { 4245 throw new UnsupportedOperationException("Lowering of try-with-resources is unsupported"); 4246 } 4247 4248 Block.Builder exit = b.block(); 4249 setBranchTarget(b.context(), this, new BranchTarget(exit, null)); 4250 4251 // Simple case with no catch and finally bodies 4252 if (catchers.isEmpty() && finalizer == null) { 4253 b.body(body, List.of(), andThenLowering(opT, (block, op) -> { 4254 if (op instanceof CoreOp.YieldOp) { 4255 block.op(branch(exit.successor())); 4256 return block; 4257 } else { 4258 return null; 4259 } 4260 })); 4261 return exit; 4262 } 4263 4264 Block.Builder tryRegionEnter = b.block(); 4265 Block.Builder tryRegionExit = b.block(); 4266 4267 // Construct the catcher block builders 4268 List<Block.Builder> catchers = catchers().stream() 4269 .map(catcher -> b.block()) 4270 .toList(); 4271 Block.Builder catcherFinally; 4272 if (finalizer == null) { 4273 catcherFinally = null; 4274 } else { 4275 catcherFinally = b.block(); 4276 catchers = new ArrayList<>(catchers); 4277 catchers.add(catcherFinally); 4278 } 4279 4280 // Enter the try exception region 4281 List<Block.Reference> exitHandlers = catchers.stream() 4282 .map(Block.Builder::successor) 4283 .toList(); 4284 b.op(exceptionRegionEnter(tryRegionEnter.successor(), exitHandlers.reversed())); 4285 4286 OpTransformer tryExitTransformer; 4287 if (finalizer != null) { 4288 tryExitTransformer = compose(opT, (block, op) -> { 4289 if (op instanceof CoreOp.ReturnOp || 4290 (op instanceof JavaOp.JavaLabelOp lop && ifExitFromTry(lop))) { 4291 return inlineFinalizer(block, exitHandlers, opT); 4292 } else { 4293 return block; 4294 } 4295 }); 4296 } else { 4297 tryExitTransformer = compose(opT, (block, op) -> { 4298 if (op instanceof CoreOp.ReturnOp || 4299 (op instanceof JavaOp.JavaLabelOp lop && ifExitFromTry(lop))) { 4300 Block.Builder tryRegionReturnExit = block.block(); 4301 block.op(exceptionRegionExit(tryRegionReturnExit.successor(), exitHandlers)); 4302 return tryRegionReturnExit; 4303 } else { 4304 return block; 4305 } 4306 }); 4307 } 4308 // Inline the try body 4309 AtomicBoolean hasTryRegionExit = new AtomicBoolean(); 4310 tryRegionEnter.body(body, List.of(), andThenLowering(tryExitTransformer, (block, op) -> { 4311 if (op instanceof CoreOp.YieldOp) { 4312 hasTryRegionExit.set(true); 4313 block.op(branch(tryRegionExit.successor())); 4314 return block; 4315 } else { 4316 return null; 4317 } 4318 })); 4319 4320 Block.Builder finallyEnter = null; 4321 if (finalizer != null) { 4322 finallyEnter = b.block(); 4323 if (hasTryRegionExit.get()) { 4324 // Exit the try exception region 4325 tryRegionExit.op(exceptionRegionExit(finallyEnter.successor(), exitHandlers)); 4326 } 4327 } else if (hasTryRegionExit.get()) { 4328 // Exit the try exception region 4329 tryRegionExit.op(exceptionRegionExit(exit.successor(), exitHandlers)); 4330 } 4331 4332 // Inline the catch bodies 4333 for (int i = 0; i < this.catchers.size(); i++) { 4334 Block.Builder catcher = catchers.get(i); 4335 Body catcherBody = this.catchers.get(i); 4336 // Create the throwable argument 4337 Block.Parameter t = catcher.parameter(catcherBody.bodyType().parameterTypes().get(0)); 4338 4339 if (finalizer != null) { 4340 Block.Builder catchRegionEnter = b.block(); 4341 Block.Builder catchRegionExit = b.block(); 4342 4343 // Enter the catch exception region 4344 Result catchExceptionRegion = catcher.op( 4345 exceptionRegionEnter(catchRegionEnter.successor(), catcherFinally.successor())); 4346 4347 OpTransformer catchExitTransformer = compose(opT, (block, op) -> { 4348 if (op instanceof CoreOp.ReturnOp) { 4349 return inlineFinalizer(block, List.of(catcherFinally.successor()), opT); 4350 } else if (op instanceof JavaOp.JavaLabelOp lop && ifExitFromTry(lop)) { 4351 return inlineFinalizer(block, List.of(catcherFinally.successor()), opT); 4352 } else { 4353 return block; 4354 } 4355 }); 4356 // Inline the catch body 4357 AtomicBoolean hasCatchRegionExit = new AtomicBoolean(); 4358 catchRegionEnter.body(catcherBody, List.of(t), andThenLowering(catchExitTransformer, (block, op) -> { 4359 if (op instanceof CoreOp.YieldOp) { 4360 hasCatchRegionExit.set(true); 4361 block.op(branch(catchRegionExit.successor())); 4362 return block; 4363 } else { 4364 return null; 4365 } 4366 })); 4367 4368 // Exit the catch exception region 4369 if (hasCatchRegionExit.get()) { 4370 hasTryRegionExit.set(true); 4371 catchRegionExit.op(exceptionRegionExit(finallyEnter.successor(), catcherFinally.successor())); 4372 } 4373 } else { 4374 // Inline the catch body 4375 catcher.body(catcherBody, List.of(t), andThenLowering(opT, (block, op) -> { 4376 if (op instanceof CoreOp.YieldOp) { 4377 block.op(branch(exit.successor())); 4378 return block; 4379 } else { 4380 return null; 4381 } 4382 })); 4383 } 4384 } 4385 4386 if (finalizer != null && hasTryRegionExit.get()) { 4387 // Inline the finally body 4388 finallyEnter.body(finalizer, List.of(), andThenLowering(opT, (block, op) -> { 4389 if (op instanceof CoreOp.YieldOp) { 4390 block.op(branch(exit.successor())); 4391 return block; 4392 } else { 4393 return null; 4394 } 4395 })); 4396 } 4397 4398 // Inline the finally body as a catcher of Throwable and adjusting to throw 4399 if (finalizer != null) { 4400 // Create the throwable argument 4401 Block.Parameter t = catcherFinally.parameter(type(Throwable.class)); 4402 4403 catcherFinally.body(finalizer, List.of(), andThenLowering(opT, (block, op) -> { 4404 if (op instanceof CoreOp.YieldOp) { 4405 block.op(throw_(t)); 4406 return block; 4407 } else { 4408 return null; 4409 } 4410 })); 4411 } 4412 return exit; 4413 } 4414 4415 boolean ifExitFromTry(JavaLabelOp lop) { 4416 Op target = lop.target(); 4417 return target == this || target.isAncestorOf(this); 4418 } 4419 4420 Block.Builder inlineFinalizer(Block.Builder block1, List<Block.Reference> tryHandlers, OpTransformer opT) { 4421 Block.Builder finallyEnter = block1.block(); 4422 Block.Builder finallyExit = block1.block(); 4423 4424 block1.op(exceptionRegionExit(finallyEnter.successor(), tryHandlers)); 4425 4426 // Inline the finally body 4427 finallyEnter.body(finalizer, List.of(), andThenLowering(opT, (block2, op2) -> { 4428 if (op2 instanceof CoreOp.YieldOp) { 4429 block2.op(branch(finallyExit.successor())); 4430 return block2; 4431 } else { 4432 return null; 4433 } 4434 })); 4435 4436 return finallyExit; 4437 } 4438 4439 @Override 4440 public TypeElement resultType() { 4441 return VOID; 4442 } 4443 } 4444 4445 // 4446 // Patterns 4447 4448 // Reified pattern nodes 4449 4450 /** 4451 * Synthetic pattern types 4452 * // @@@ Replace with types extending from TypeElement 4453 */ 4454 public sealed interface Pattern { 4455 4456 /** 4457 * Synthetic type pattern type. 4458 * 4459 * @param <T> the type of values that are bound 4460 */ 4461 final class Type<T> implements Pattern { 4462 Type() { 4463 } 4464 } 4465 4466 /** 4467 * Synthetic record pattern type. 4468 * 4469 * @param <T> the type of records that are bound 4470 */ 4471 final class Record<T> implements Pattern { 4472 Record() { 4473 } 4474 } 4475 4476 final class MatchAll implements Pattern { 4477 MatchAll() { 4478 } 4479 } 4480 4481 // @@@ Pattern types 4482 4483 JavaType PATTERN_BINDING_TYPE = JavaType.type(Type.class); 4484 4485 JavaType PATTERN_RECORD_TYPE = JavaType.type(Record.class); 4486 4487 JavaType PATTERN_MATCH_ALL_TYPE = JavaType.type(MatchAll.class); 4488 4489 static JavaType bindingType(TypeElement t) { 4490 return parameterized(PATTERN_BINDING_TYPE, (JavaType) t); 4491 } 4492 4493 static JavaType recordType(TypeElement t) { 4494 return parameterized(PATTERN_RECORD_TYPE, (JavaType) t); 4495 } 4496 4497 static JavaType matchAllType() { 4498 return PATTERN_MATCH_ALL_TYPE; 4499 } 4500 4501 static TypeElement targetType(TypeElement t) { 4502 return ((ClassType) t).typeArguments().get(0); 4503 } 4504 } 4505 4506 /** 4507 * Pattern operations. 4508 */ 4509 public static final class PatternOps { 4510 PatternOps() { 4511 } 4512 4513 /** 4514 * The pattern operation. 4515 */ 4516 public sealed static abstract class PatternOp extends JavaOp implements Op.Pure { 4517 PatternOp(PatternOp that, CopyContext cc) { 4518 super(that, cc); 4519 } 4520 4521 PatternOp(String name, List<Value> operands) { 4522 super(name, operands); 4523 } 4524 } 4525 4526 /** 4527 * The binding pattern operation, that can model Java language type patterns. 4528 */ 4529 @OpDeclaration(TypePatternOp.NAME) 4530 public static final class TypePatternOp extends PatternOp { 4531 static final String NAME = "pattern.type"; 4532 4533 public static final String ATTRIBUTE_BINDING_NAME = NAME + ".binding.name"; 4534 4535 final TypeElement resultType; 4536 final String bindingName; 4537 4538 TypePatternOp(ExternalizedOp def) { 4539 super(NAME, List.of()); 4540 4541 this.bindingName = def.extractAttributeValue(ATTRIBUTE_BINDING_NAME, true, 4542 v -> switch (v) { 4543 case String s -> s; 4544 case null -> null; 4545 default -> throw new UnsupportedOperationException("Unsupported pattern binding name value:" + v); 4546 }); 4547 // @@@ Cannot use canonical constructor because it wraps the given type 4548 this.resultType = def.resultType(); 4549 } 4550 4551 TypePatternOp(TypePatternOp that, CopyContext cc) { 4552 super(that, cc); 4553 4554 this.bindingName = that.bindingName; 4555 this.resultType = that.resultType; 4556 } 4557 4558 @Override 4559 public TypePatternOp transform(CopyContext cc, OpTransformer ot) { 4560 return new TypePatternOp(this, cc); 4561 } 4562 4563 TypePatternOp(TypeElement targetType, String bindingName) { 4564 super(NAME, List.of()); 4565 4566 this.bindingName = bindingName; 4567 this.resultType = Pattern.bindingType(targetType); 4568 } 4569 4570 @Override 4571 public Map<String, Object> externalize() { 4572 return bindingName == null ? Map.of() : Map.of("", bindingName); 4573 } 4574 4575 public String bindingName() { 4576 return bindingName; 4577 } 4578 4579 public TypeElement targetType() { 4580 return Pattern.targetType(resultType()); 4581 } 4582 4583 @Override 4584 public TypeElement resultType() { 4585 return resultType; 4586 } 4587 } 4588 4589 /** 4590 * The record pattern operation, that can model Java language record patterns. 4591 */ 4592 @OpDeclaration(RecordPatternOp.NAME) 4593 public static final class RecordPatternOp extends PatternOp { 4594 static final String NAME = "pattern.record"; 4595 4596 public static final String ATTRIBUTE_RECORD_DESCRIPTOR = NAME + ".descriptor"; 4597 4598 final RecordTypeRef recordDescriptor; 4599 4600 static RecordPatternOp create(ExternalizedOp def) { 4601 RecordTypeRef recordDescriptor = def.extractAttributeValue(ATTRIBUTE_RECORD_DESCRIPTOR, true, 4602 v -> switch (v) { 4603 case RecordTypeRef rtd -> rtd; 4604 case null, default -> 4605 throw new UnsupportedOperationException("Unsupported record type descriptor value:" + v); 4606 }); 4607 4608 return new RecordPatternOp(recordDescriptor, def.operands()); 4609 } 4610 4611 RecordPatternOp(RecordPatternOp that, CopyContext cc) { 4612 super(that, cc); 4613 4614 this.recordDescriptor = that.recordDescriptor; 4615 } 4616 4617 @Override 4618 public RecordPatternOp transform(CopyContext cc, OpTransformer ot) { 4619 return new RecordPatternOp(this, cc); 4620 } 4621 4622 RecordPatternOp(RecordTypeRef recordDescriptor, List<Value> nestedPatterns) { 4623 // The type of each value is a subtype of Pattern 4624 // The number of values corresponds to the number of components of the record 4625 super(NAME, List.copyOf(nestedPatterns)); 4626 4627 this.recordDescriptor = recordDescriptor; 4628 } 4629 4630 @Override 4631 public Map<String, Object> externalize() { 4632 return Map.of("", recordDescriptor()); 4633 } 4634 4635 public RecordTypeRef recordDescriptor() { 4636 return recordDescriptor; 4637 } 4638 4639 public TypeElement targetType() { 4640 return Pattern.targetType(resultType()); 4641 } 4642 4643 @Override 4644 public TypeElement resultType() { 4645 return Pattern.recordType(recordDescriptor.recordType()); 4646 } 4647 } 4648 4649 @OpDeclaration(MatchAllPatternOp.NAME) 4650 public static final class MatchAllPatternOp extends PatternOp { 4651 4652 // @@@ we may need to add info about the type of the record component 4653 // this info can be used when lowering 4654 4655 static final String NAME = "pattern.match.all"; 4656 4657 MatchAllPatternOp(ExternalizedOp def) { 4658 this(); 4659 } 4660 4661 MatchAllPatternOp(MatchAllPatternOp that, CopyContext cc) { 4662 super(that, cc); 4663 } 4664 4665 MatchAllPatternOp() { 4666 super(NAME, List.of()); 4667 } 4668 4669 @Override 4670 public Op transform(CopyContext cc, OpTransformer ot) { 4671 return new MatchAllPatternOp(this, cc); 4672 } 4673 4674 @Override 4675 public TypeElement resultType() { 4676 return Pattern.matchAllType(); 4677 } 4678 } 4679 4680 /** 4681 * The match operation, that can model Java language pattern matching. 4682 */ 4683 @OpDeclaration(MatchOp.NAME) 4684 public static final class MatchOp extends JavaOp implements Op.Isolated, Op.Lowerable { 4685 static final String NAME = "pattern.match"; 4686 4687 final Body pattern; 4688 final Body match; 4689 4690 MatchOp(ExternalizedOp def) { 4691 this(def.operands().get(0), 4692 def.bodyDefinitions().get(0), def.bodyDefinitions().get(1)); 4693 } 4694 4695 MatchOp(MatchOp that, CopyContext cc, OpTransformer ot) { 4696 super(that, cc); 4697 4698 this.pattern = that.pattern.transform(cc, ot).build(this); 4699 this.match = that.match.transform(cc, ot).build(this); 4700 } 4701 4702 @Override 4703 public MatchOp transform(CopyContext cc, OpTransformer ot) { 4704 return new MatchOp(this, cc, ot); 4705 } 4706 4707 MatchOp(Value target, Body.Builder patternC, Body.Builder matchC) { 4708 super(NAME, 4709 List.of(target)); 4710 4711 this.pattern = patternC.build(this); 4712 this.match = matchC.build(this); 4713 } 4714 4715 @Override 4716 public List<Body> bodies() { 4717 return List.of(pattern, match); 4718 } 4719 4720 public Body pattern() { 4721 return pattern; 4722 } 4723 4724 public Body match() { 4725 return match; 4726 } 4727 4728 public Value target() { 4729 return operands().get(0); 4730 } 4731 4732 @Override 4733 public Block.Builder lower(Block.Builder b, OpTransformer opT) { 4734 // No match block 4735 Block.Builder endNoMatchBlock = b.block(); 4736 // Match block 4737 Block.Builder endMatchBlock = b.block(); 4738 // End block 4739 Block.Builder endBlock = b.block(); 4740 Block.Parameter matchResult = endBlock.parameter(resultType()); 4741 // Map match operation result 4742 b.context().mapValue(result(), matchResult); 4743 4744 List<Value> patternValues = new ArrayList<>(); 4745 Op patternYieldOp = pattern.entryBlock().terminatingOp(); 4746 Op.Result rootPatternValue = (Op.Result) patternYieldOp.operands().get(0); 4747 Block.Builder currentBlock = lower(endNoMatchBlock, b, 4748 patternValues, 4749 rootPatternValue.op(), 4750 b.context().getValue(target())); 4751 currentBlock.op(branch(endMatchBlock.successor())); 4752 4753 // No match block 4754 // Pass false 4755 endNoMatchBlock.op(branch(endBlock.successor( 4756 endNoMatchBlock.op(constant(BOOLEAN, false))))); 4757 4758 // Match block 4759 // Lower match body and pass true 4760 endMatchBlock.body(match, patternValues, andThenLowering(opT, (block, op) -> { 4761 if (op instanceof CoreOp.YieldOp) { 4762 block.op(branch(endBlock.successor( 4763 block.op(constant(BOOLEAN, true))))); 4764 return block; 4765 } else { 4766 return null; 4767 } 4768 })); 4769 4770 return endBlock; 4771 } 4772 4773 static Block.Builder lower(Block.Builder endNoMatchBlock, Block.Builder currentBlock, 4774 List<Value> bindings, 4775 Op pattern, Value target) { 4776 return switch (pattern) { 4777 case RecordPatternOp rp -> lowerRecordPattern(endNoMatchBlock, currentBlock, bindings, rp, target); 4778 case TypePatternOp tp -> lowerTypePattern(endNoMatchBlock, currentBlock, bindings, tp, target); 4779 case MatchAllPatternOp map -> lowerMatchAllPattern(currentBlock); 4780 case null, default -> throw new UnsupportedOperationException("Unknown pattern op: " + pattern); 4781 }; 4782 } 4783 4784 static Block.Builder lowerRecordPattern(Block.Builder endNoMatchBlock, Block.Builder currentBlock, 4785 List<Value> bindings, 4786 JavaOp.PatternOps.RecordPatternOp rpOp, Value target) { 4787 TypeElement targetType = rpOp.targetType(); 4788 4789 Block.Builder nextBlock = currentBlock.block(); 4790 4791 // Check if instance of target type 4792 Op.Result isInstance = currentBlock.op(instanceOf(targetType, target)); 4793 currentBlock.op(conditionalBranch(isInstance, nextBlock.successor(), endNoMatchBlock.successor())); 4794 4795 currentBlock = nextBlock; 4796 4797 target = currentBlock.op(cast(targetType, target)); 4798 4799 // Access component values of record and match on each as nested target 4800 List<Value> dArgs = rpOp.operands(); 4801 for (int i = 0; i < dArgs.size(); i++) { 4802 Op.Result nestedPattern = (Op.Result) dArgs.get(i); 4803 // @@@ Handle exceptions? 4804 Value nestedTarget = currentBlock.op(invoke(rpOp.recordDescriptor().methodForComponent(i), target)); 4805 4806 currentBlock = lower(endNoMatchBlock, currentBlock, bindings, nestedPattern.op(), nestedTarget); 4807 } 4808 4809 return currentBlock; 4810 } 4811 4812 static Block.Builder lowerTypePattern(Block.Builder endNoMatchBlock, Block.Builder currentBlock, 4813 List<Value> bindings, 4814 TypePatternOp tpOp, Value target) { 4815 TypeElement targetType = tpOp.targetType(); 4816 4817 // Check if instance of target type 4818 Op p; // op that perform type check 4819 Op c; // op that perform conversion 4820 TypeElement s = target.type(); 4821 TypeElement t = targetType; 4822 if (t instanceof PrimitiveType pt) { 4823 if (s instanceof ClassType cs) { 4824 // unboxing conversions 4825 ClassType box; 4826 if (cs.unbox().isEmpty()) { // s not a boxed type 4827 // e.g. Number -> int, narrowing + unboxing 4828 box = pt.box().orElseThrow(); 4829 p = instanceOf(box, target); 4830 } else { 4831 // e.g. Float -> float, unboxing 4832 // e.g. Integer -> long, unboxing + widening 4833 box = cs; 4834 p = null; 4835 } 4836 c = invoke(MethodRef.method(box, t + "Value", t), target); 4837 } else { 4838 // primitive to primitive conversion 4839 PrimitiveType ps = ((PrimitiveType) s); 4840 if (isNarrowingPrimitiveConv(ps, pt) || isWideningPrimitiveConvWithCheck(ps, pt) 4841 || isWideningAndNarrowingPrimitiveConv(ps, pt)) { 4842 // e.g. int -> byte, narrowing 4843 // e,g. int -> float, widening with check 4844 // e.g. byte -> char, widening and narrowing 4845 MethodRef mref = convMethodRef(s, t); 4846 p = invoke(mref, target); 4847 } else { 4848 p = null; 4849 } 4850 c = conv(targetType, target); 4851 } 4852 } else if (s instanceof PrimitiveType ps) { 4853 // boxing conversions 4854 // e.g. int -> Number, boxing + widening 4855 // e.g. byte -> Byte, boxing 4856 p = null; 4857 ClassType box = ps.box().orElseThrow(); 4858 c = invoke(MethodRef.method(box, "valueOf", box, ps), target); 4859 } else if (!s.equals(t)) { 4860 // reference to reference, but not identity 4861 // e.g. Number -> Double, narrowing 4862 // e.g. Short -> Object, widening 4863 p = instanceOf(targetType, target); 4864 c = cast(targetType, target); 4865 } else { 4866 // identity reference 4867 // e.g. Character -> Character 4868 p = null; 4869 c = null; 4870 } 4871 4872 if (c != null) { 4873 if (p != null) { 4874 // p != null, we need to perform type check at runtime 4875 Block.Builder nextBlock = currentBlock.block(); 4876 currentBlock.op(conditionalBranch(currentBlock.op(p), nextBlock.successor(), endNoMatchBlock.successor())); 4877 currentBlock = nextBlock; 4878 } 4879 target = currentBlock.op(c); 4880 } 4881 4882 bindings.add(target); 4883 4884 return currentBlock; 4885 } 4886 4887 private static boolean isWideningAndNarrowingPrimitiveConv(PrimitiveType s, PrimitiveType t) { 4888 return BYTE.equals(s) && CHAR.equals(t); 4889 } 4890 4891 private static boolean isWideningPrimitiveConvWithCheck(PrimitiveType s, PrimitiveType t) { 4892 return (INT.equals(s) && FLOAT.equals(t)) 4893 || (LONG.equals(s) && FLOAT.equals(t)) 4894 || (LONG.equals(s) && DOUBLE.equals(t)); 4895 } 4896 4897 // s -> t is narrowing if order(t) <= order(s) 4898 private final static Map<PrimitiveType, Integer> narrowingOrder = Map.of( 4899 BYTE, 1, 4900 SHORT, 2, 4901 CHAR, 2, 4902 INT, 3, 4903 LONG, 4, 4904 FLOAT, 5, 4905 DOUBLE, 6 4906 ); 4907 private static boolean isNarrowingPrimitiveConv(PrimitiveType s, PrimitiveType t) { 4908 return narrowingOrder.get(t) <= narrowingOrder.get(s); 4909 } 4910 4911 private static MethodRef convMethodRef(TypeElement s, TypeElement t) { 4912 if (BYTE.equals(s) || SHORT.equals(s) || CHAR.equals(s)) { 4913 s = INT; 4914 } 4915 String sn = capitalize(s.toString()); 4916 String tn = capitalize(t.toString()); 4917 String mn = "is%sTo%sExact".formatted(sn, tn); 4918 JavaType exactConversionSupport = JavaType.type(ClassDesc.of("java.lang.runtime.ExactConversionsSupport")); 4919 return MethodRef.method(exactConversionSupport, mn, BOOLEAN, s); 4920 } 4921 4922 private static String capitalize(String s) { 4923 return s.substring(0, 1).toUpperCase() + s.substring(1); 4924 } 4925 4926 static Block.Builder lowerMatchAllPattern(Block.Builder currentBlock) { 4927 return currentBlock; 4928 } 4929 4930 @Override 4931 public TypeElement resultType() { 4932 return BOOLEAN; 4933 } 4934 } 4935 } 4936 4937 static Op createOp(ExternalizedOp def) { 4938 Op op = switch (def.name()) { 4939 case "add" -> new AddOp(def); 4940 case "and" -> new AndOp(def); 4941 case "array.length" -> new ArrayLengthOp(def); 4942 case "array.load" -> new ArrayAccessOp.ArrayLoadOp(def); 4943 case "array.store" -> new ArrayAccessOp.ArrayStoreOp(def); 4944 case "ashr" -> new AshrOp(def); 4945 case "assert" -> new AssertOp(def); 4946 case "cast" -> CastOp.create(def); 4947 case "compl" -> new ComplOp(def); 4948 case "concat" -> new ConcatOp(def); 4949 case "conv" -> new ConvOp(def); 4950 case "div" -> new DivOp(def); 4951 case "eq" -> new EqOp(def); 4952 case "exception.region.enter" -> new ExceptionRegionEnter(def); 4953 case "exception.region.exit" -> new ExceptionRegionExit(def); 4954 case "field.load" -> FieldAccessOp.FieldLoadOp.create(def); 4955 case "field.store" -> FieldAccessOp.FieldStoreOp.create(def); 4956 case "ge" -> new GeOp(def); 4957 case "gt" -> new GtOp(def); 4958 case "instanceof" -> InstanceOfOp.create(def); 4959 case "invoke" -> InvokeOp.create(def); 4960 case "java.block" -> new BlockOp(def); 4961 case "java.break" -> new BreakOp(def); 4962 case "java.cand" -> new ConditionalAndOp(def); 4963 case "java.cexpression" -> new ConditionalExpressionOp(def); 4964 case "java.continue" -> new ContinueOp(def); 4965 case "java.cor" -> new ConditionalOrOp(def); 4966 case "java.do.while" -> new DoWhileOp(def); 4967 case "java.enhancedFor" -> EnhancedForOp.create(def); 4968 case "java.for" -> ForOp.create(def); 4969 case "java.if" -> new IfOp(def); 4970 case "java.labeled" -> new LabeledOp(def); 4971 case "java.switch.expression" -> new SwitchExpressionOp(def); 4972 case "java.switch.fallthrough" -> new SwitchFallthroughOp(def); 4973 case "java.switch.statement" -> new SwitchStatementOp(def); 4974 case "java.synchronized" -> new SynchronizedOp(def); 4975 case "java.try" -> TryOp.create(def); 4976 case "java.while" -> new WhileOp(def); 4977 case "java.yield" -> new YieldOp(def); 4978 case "lambda" -> new LambdaOp(def); 4979 case "le" -> new LeOp(def); 4980 case "lshl" -> new LshlOp(def); 4981 case "lshr" -> new LshrOp(def); 4982 case "lt" -> new LtOp(def); 4983 case "mod" -> new ModOp(def); 4984 case "monitor.enter" -> new MonitorOp.MonitorEnterOp(def); 4985 case "monitor.exit" -> new MonitorOp.MonitorExitOp(def); 4986 case "mul" -> new MulOp(def); 4987 case "neg" -> new NegOp(def); 4988 case "neq" -> new NeqOp(def); 4989 case "new" -> NewOp.create(def); 4990 case "not" -> new NotOp(def); 4991 case "or" -> new OrOp(def); 4992 case "pattern.match" -> new PatternOps.MatchOp(def); 4993 case "pattern.match.all" -> new PatternOps.MatchAllPatternOp(def); 4994 case "pattern.record" -> PatternOps.RecordPatternOp.create(def); 4995 case "pattern.type" -> new PatternOps.TypePatternOp(def); 4996 case "sub" -> new SubOp(def); 4997 case "throw" -> new ThrowOp(def); 4998 case "xor" -> new XorOp(def); 4999 default -> null; 5000 }; 5001 if (op != null) { 5002 op.setLocation(def.location()); 5003 } 5004 return op; 5005 } 5006 5007 /** 5008 * An operation factory for core operations composed with Java operations. 5009 */ 5010 public static final OpFactory JAVA_OP_FACTORY = CoreOp.CORE_OP_FACTORY.andThen(JavaOp::createOp); 5011 5012 /** 5013 * A Java dialect factory, for constructing core and Java operations and constructing 5014 * core type and Java type elements, where the core type elements can refer to Java 5015 * type elements. 5016 */ 5017 public static final DialectFactory JAVA_DIALECT_FACTORY = new DialectFactory( 5018 JAVA_OP_FACTORY, 5019 JAVA_TYPE_FACTORY); 5020 5021 /** 5022 * Creates a lambda operation. 5023 * 5024 * @param ancestorBody the ancestor of the body of the lambda operation 5025 * @param funcType the lambda operation's function type 5026 * @param functionalInterface the lambda operation's functional interface type 5027 * @return the lambda operation 5028 */ 5029 public static LambdaOp.Builder lambda(Body.Builder ancestorBody, 5030 FunctionType funcType, TypeElement functionalInterface) { 5031 return new LambdaOp.Builder(ancestorBody, funcType, functionalInterface); 5032 } 5033 5034 /** 5035 * Creates a lambda operation. 5036 * 5037 * @param functionalInterface the lambda operation's functional interface type 5038 * @param body the body of the lambda operation 5039 * @return the lambda operation 5040 */ 5041 public static LambdaOp lambda(TypeElement functionalInterface, Body.Builder body) { 5042 return new LambdaOp(functionalInterface, body); 5043 } 5044 5045 /** 5046 * Creates an exception region enter operation 5047 * 5048 * @param start the exception region block 5049 * @param catchers the blocks handling exceptions thrown by the region block 5050 * @return the exception region enter operation 5051 */ 5052 public static ExceptionRegionEnter exceptionRegionEnter(Block.Reference start, Block.Reference... catchers) { 5053 return exceptionRegionEnter(start, List.of(catchers)); 5054 } 5055 5056 /** 5057 * Creates an exception region enter operation 5058 * 5059 * @param start the exception region block 5060 * @param catchers the blocks handling exceptions thrown by the region block 5061 * @return the exception region enter operation 5062 */ 5063 public static ExceptionRegionEnter exceptionRegionEnter(Block.Reference start, List<Block.Reference> catchers) { 5064 List<Block.Reference> s = new ArrayList<>(); 5065 s.add(start); 5066 s.addAll(catchers); 5067 return new ExceptionRegionEnter(s); 5068 } 5069 5070 /** 5071 * Creates an exception region exit operation 5072 * 5073 * @param end the block to which control is transferred after the exception region is exited 5074 * @param catchers the blocks handling exceptions thrown by the region block 5075 * @return the exception region exit operation 5076 */ 5077 public static ExceptionRegionExit exceptionRegionExit(Block.Reference end, Block.Reference... catchers) { 5078 return exceptionRegionExit(end, List.of(catchers)); 5079 } 5080 5081 /** 5082 * Creates an exception region exit operation 5083 * 5084 * @param end the block to which control is transferred after the exception region is exited 5085 * @param catchers the blocks handling exceptions thrown by the region block 5086 * @return the exception region exit operation 5087 */ 5088 public static ExceptionRegionExit exceptionRegionExit(Block.Reference end, List<Block.Reference> catchers) { 5089 List<Block.Reference> s = new ArrayList<>(); 5090 s.add(end); 5091 s.addAll(catchers); 5092 return new ExceptionRegionExit(s); 5093 } 5094 5095 /** 5096 * Creates a throw operation. 5097 * 5098 * @param exceptionValue the thrown value 5099 * @return the throw operation 5100 */ 5101 public static ThrowOp throw_(Value exceptionValue) { 5102 return new ThrowOp(exceptionValue); 5103 } 5104 5105 /** 5106 * Creates an assert operation. 5107 * 5108 * @param bodies the nested bodies 5109 * @return the assert operation 5110 */ 5111 public static AssertOp assert_(List<Body.Builder> bodies) { 5112 return new AssertOp(bodies); 5113 } 5114 5115 public static MonitorOp.MonitorEnterOp monitorEnter(Value monitor) { 5116 return new MonitorOp.MonitorEnterOp(monitor); 5117 } 5118 5119 public static MonitorOp.MonitorExitOp monitorExit(Value monitor) { 5120 return new MonitorOp.MonitorExitOp(monitor); 5121 } 5122 5123 /** 5124 * Creates an invoke operation modeling an invocation to an 5125 * instance or static (class) method with no variable arguments. 5126 * <p> 5127 * The invoke kind of the invoke operation is determined by 5128 * comparing the argument count with the invoke descriptor's 5129 * parameter count. If they are equal then the invoke kind is 5130 * {@link InvokeOp.InvokeKind#STATIC static}. If the parameter count 5131 * plus one is equal to the argument count then the invoke kind 5132 * is {@link InvokeOp.InvokeKind#STATIC instance}. 5133 * <p> 5134 * The invoke return type is the invoke descriptors return type. 5135 * 5136 * @param invokeDescriptor the invoke descriptor 5137 * @param args the invoke parameters 5138 * @return the invoke operation 5139 */ 5140 public static InvokeOp invoke(MethodRef invokeDescriptor, Value... args) { 5141 return invoke(invokeDescriptor, List.of(args)); 5142 } 5143 5144 /** 5145 * Creates an invoke operation modeling an invocation to an 5146 * instance or static (class) method with no variable arguments. 5147 * <p> 5148 * The invoke kind of the invoke operation is determined by 5149 * comparing the argument count with the invoke descriptor's 5150 * parameter count. If they are equal then the invoke kind is 5151 * {@link InvokeOp.InvokeKind#STATIC static}. If the parameter count 5152 * plus one is equal to the argument count then the invoke kind 5153 * is {@link InvokeOp.InvokeKind#STATIC instance}. 5154 * <p> 5155 * The invoke return type is the invoke descriptors return type. 5156 * 5157 * @param invokeDescriptor the invoke descriptor 5158 * @param args the invoke arguments 5159 * @return the invoke operation 5160 */ 5161 public static InvokeOp invoke(MethodRef invokeDescriptor, List<Value> args) { 5162 return invoke(invokeDescriptor.type().returnType(), invokeDescriptor, args); 5163 } 5164 5165 /** 5166 * Creates an invoke operation modeling an invocation to an 5167 * instance or static (class) method with no variable arguments. 5168 * <p> 5169 * The invoke kind of the invoke operation is determined by 5170 * comparing the argument count with the invoke descriptor's 5171 * parameter count. If they are equal then the invoke kind is 5172 * {@link InvokeOp.InvokeKind#STATIC static}. If the parameter count 5173 * plus one is equal to the argument count then the invoke kind 5174 * is {@link InvokeOp.InvokeKind#STATIC instance}. 5175 * 5176 * @param returnType the invoke return type 5177 * @param invokeDescriptor the invoke descriptor 5178 * @param args the invoke arguments 5179 * @return the invoke operation 5180 */ 5181 public static InvokeOp invoke(TypeElement returnType, MethodRef invokeDescriptor, Value... args) { 5182 return invoke(returnType, invokeDescriptor, List.of(args)); 5183 } 5184 5185 /** 5186 * Creates an invoke operation modeling an invocation to an 5187 * instance or static (class) method with no variable arguments. 5188 * <p> 5189 * The invoke kind of the invoke operation is determined by 5190 * comparing the argument count with the invoke descriptor's 5191 * parameter count. If they are equal then the invoke kind is 5192 * {@link InvokeOp.InvokeKind#STATIC static}. If the parameter count 5193 * plus one is equal to the argument count then the invoke kind 5194 * is {@link InvokeOp.InvokeKind#STATIC instance}. 5195 * 5196 * @param returnType the invoke return type 5197 * @param invokeDescriptor the invoke descriptor 5198 * @param args the invoke arguments 5199 * @return the invoke super operation 5200 */ 5201 public static InvokeOp invoke(TypeElement returnType, MethodRef invokeDescriptor, List<Value> args) { 5202 int paramCount = invokeDescriptor.type().parameterTypes().size(); 5203 int argCount = args.size(); 5204 InvokeOp.InvokeKind ik = (argCount == paramCount + 1) 5205 ? InvokeOp.InvokeKind.INSTANCE 5206 : InvokeOp.InvokeKind.STATIC; 5207 return new InvokeOp(ik, false, returnType, invokeDescriptor, args); 5208 } 5209 5210 /** 5211 * Creates an invoke operation modelling an invocation to a method. 5212 * 5213 * @param invokeKind the invoke kind 5214 * @param isVarArgs true if an invocation to a variable argument method 5215 * @param returnType the return type 5216 * @param invokeDescriptor the invoke descriptor 5217 * @param args the invoke arguments 5218 * @return the invoke operation 5219 * @throws IllegalArgumentException if there is a mismatch between the argument count 5220 * and the invoke descriptors parameter count. 5221 */ 5222 public static InvokeOp invoke(InvokeOp.InvokeKind invokeKind, boolean isVarArgs, 5223 TypeElement returnType, MethodRef invokeDescriptor, List<Value> args) { 5224 return new InvokeOp(invokeKind, isVarArgs, returnType, invokeDescriptor, args); 5225 } 5226 5227 /** 5228 * Creates a conversion operation. 5229 * 5230 * @param to the conversion target type 5231 * @param from the value to be converted 5232 * @return the conversion operation 5233 */ 5234 public static ConvOp conv(TypeElement to, Value from) { 5235 return new ConvOp(to, from); 5236 } 5237 5238 /** 5239 * Creates an instance creation operation. 5240 * 5241 * @param constructorDescriptor the constructor descriptor 5242 * @param args the constructor arguments 5243 * @return the instance creation operation 5244 */ 5245 public static NewOp new_(ConstructorRef constructorDescriptor, Value... args) { 5246 return new_(constructorDescriptor, List.of(args)); 5247 } 5248 5249 /** 5250 * Creates an instance creation operation. 5251 * 5252 * @param constructorDescriptor the constructor descriptor 5253 * @param args the constructor arguments 5254 * @return the instance creation operation 5255 */ 5256 public static NewOp new_(ConstructorRef constructorDescriptor, List<Value> args) { 5257 return new NewOp(false, constructorDescriptor.refType(), constructorDescriptor, args); 5258 } 5259 5260 /** 5261 * Creates an instance creation operation. 5262 * 5263 * @param returnType the instance type 5264 * @param constructorDescriptor the constructor descriptor 5265 * @param args the constructor arguments 5266 * @return the instance creation operation 5267 */ 5268 public static NewOp new_(TypeElement returnType, ConstructorRef constructorDescriptor, 5269 Value... args) { 5270 return new_(returnType, constructorDescriptor, List.of(args)); 5271 } 5272 5273 /** 5274 * Creates an instance creation operation. 5275 * 5276 * @param returnType the instance type 5277 * @param constructorDescriptor the constructor descriptor 5278 * @param args the constructor arguments 5279 * @return the instance creation operation 5280 */ 5281 public static NewOp new_(TypeElement returnType, ConstructorRef constructorDescriptor, 5282 List<Value> args) { 5283 return new NewOp(false, returnType, constructorDescriptor, args); 5284 } 5285 5286 /** 5287 * Creates an instance creation operation. 5288 * 5289 * @param returnType the instance type 5290 * @param constructorDescriptor the constructor descriptor 5291 * @param args the constructor arguments 5292 * @return the instance creation operation 5293 */ 5294 public static NewOp new_(boolean isVarargs, TypeElement returnType, ConstructorRef constructorDescriptor, 5295 List<Value> args) { 5296 return new NewOp(isVarargs, returnType, constructorDescriptor, args); 5297 } 5298 5299 /** 5300 * Creates an array creation operation. 5301 * 5302 * @param arrayType the array type 5303 * @param length the array size 5304 * @return the array creation operation 5305 */ 5306 public static NewOp newArray(TypeElement arrayType, Value length) { 5307 ConstructorRef constructorDescriptor = ConstructorRef.constructor(arrayType, INT); 5308 return new_(constructorDescriptor, length); 5309 } 5310 5311 /** 5312 * Creates a field load operation to a non-static field. 5313 * 5314 * @param descriptor the field descriptor 5315 * @param receiver the receiver value 5316 * @return the field load operation 5317 */ 5318 public static FieldAccessOp.FieldLoadOp fieldLoad(FieldRef descriptor, Value receiver) { 5319 return new FieldAccessOp.FieldLoadOp(descriptor.type(), descriptor, receiver); 5320 } 5321 5322 /** 5323 * Creates a field load operation to a non-static field. 5324 * 5325 * @param resultType the result type of the operation 5326 * @param descriptor the field descriptor 5327 * @param receiver the receiver value 5328 * @return the field load operation 5329 */ 5330 public static FieldAccessOp.FieldLoadOp fieldLoad(TypeElement resultType, FieldRef descriptor, Value receiver) { 5331 return new FieldAccessOp.FieldLoadOp(resultType, descriptor, receiver); 5332 } 5333 5334 /** 5335 * Creates a field load operation to a static field. 5336 * 5337 * @param descriptor the field descriptor 5338 * @return the field load operation 5339 */ 5340 public static FieldAccessOp.FieldLoadOp fieldLoad(FieldRef descriptor) { 5341 return new FieldAccessOp.FieldLoadOp(descriptor.type(), descriptor); 5342 } 5343 5344 /** 5345 * Creates a field load operation to a static field. 5346 * 5347 * @param resultType the result type of the operation 5348 * @param descriptor the field descriptor 5349 * @return the field load operation 5350 */ 5351 public static FieldAccessOp.FieldLoadOp fieldLoad(TypeElement resultType, FieldRef descriptor) { 5352 return new FieldAccessOp.FieldLoadOp(resultType, descriptor); 5353 } 5354 5355 /** 5356 * Creates a field store operation to a non-static field. 5357 * 5358 * @param descriptor the field descriptor 5359 * @param receiver the receiver value 5360 * @param v the value to store 5361 * @return the field store operation 5362 */ 5363 public static FieldAccessOp.FieldStoreOp fieldStore(FieldRef descriptor, Value receiver, Value v) { 5364 return new FieldAccessOp.FieldStoreOp(descriptor, receiver, v); 5365 } 5366 5367 /** 5368 * Creates a field load operation to a static field. 5369 * 5370 * @param descriptor the field descriptor 5371 * @param v the value to store 5372 * @return the field store operation 5373 */ 5374 public static FieldAccessOp.FieldStoreOp fieldStore(FieldRef descriptor, Value v) { 5375 return new FieldAccessOp.FieldStoreOp(descriptor, v); 5376 } 5377 5378 /** 5379 * Creates an array length operation. 5380 * 5381 * @param array the array value 5382 * @return the array length operation 5383 */ 5384 public static ArrayLengthOp arrayLength(Value array) { 5385 return new ArrayLengthOp(array); 5386 } 5387 5388 /** 5389 * Creates an array load operation. 5390 * 5391 * @param array the array value 5392 * @param index the index value 5393 * @return the array load operation 5394 */ 5395 public static ArrayAccessOp.ArrayLoadOp arrayLoadOp(Value array, Value index) { 5396 return new ArrayAccessOp.ArrayLoadOp(array, index); 5397 } 5398 5399 /** 5400 * Creates an array load operation. 5401 * 5402 * @param array the array value 5403 * @param index the index value 5404 * @param componentType type of the array component 5405 * @return the array load operation 5406 */ 5407 public static ArrayAccessOp.ArrayLoadOp arrayLoadOp(Value array, Value index, TypeElement componentType) { 5408 return new ArrayAccessOp.ArrayLoadOp(array, index, componentType); 5409 } 5410 5411 /** 5412 * Creates an array store operation. 5413 * 5414 * @param array the array value 5415 * @param index the index value 5416 * @param v the value to store 5417 * @return the array store operation 5418 */ 5419 public static ArrayAccessOp.ArrayStoreOp arrayStoreOp(Value array, Value index, Value v) { 5420 return new ArrayAccessOp.ArrayStoreOp(array, index, v); 5421 } 5422 5423 /** 5424 * Creates an instanceof operation. 5425 * 5426 * @param t the type to test against 5427 * @param v the value to test 5428 * @return the instanceof operation 5429 */ 5430 public static InstanceOfOp instanceOf(TypeElement t, Value v) { 5431 return new InstanceOfOp(t, v); 5432 } 5433 5434 /** 5435 * Creates a cast operation. 5436 * 5437 * @param resultType the result type of the operation 5438 * @param v the value to cast 5439 * @return the cast operation 5440 */ 5441 public static CastOp cast(TypeElement resultType, Value v) { 5442 return new CastOp(resultType, resultType, v); 5443 } 5444 5445 /** 5446 * Creates a cast operation. 5447 * 5448 * @param resultType the result type of the operation 5449 * @param t the type to cast to 5450 * @param v the value to cast 5451 * @return the cast operation 5452 */ 5453 public static CastOp cast(TypeElement resultType, JavaType t, Value v) { 5454 return new CastOp(resultType, t, v); 5455 } 5456 5457 /** 5458 * Creates an add operation. 5459 * 5460 * @param lhs the first operand 5461 * @param rhs the second operand 5462 * @return the add operation 5463 */ 5464 public static BinaryOp add(Value lhs, Value rhs) { 5465 return new AddOp(lhs, rhs); 5466 } 5467 5468 /** 5469 * Creates a sub operation. 5470 * 5471 * @param lhs the first operand 5472 * @param rhs the second operand 5473 * @return the sub operation 5474 */ 5475 public static BinaryOp sub(Value lhs, Value rhs) { 5476 return new SubOp(lhs, rhs); 5477 } 5478 5479 /** 5480 * Creates a mul operation. 5481 * 5482 * @param lhs the first operand 5483 * @param rhs the second operand 5484 * @return the mul operation 5485 */ 5486 public static BinaryOp mul(Value lhs, Value rhs) { 5487 return new MulOp(lhs, rhs); 5488 } 5489 5490 /** 5491 * Creates a div operation. 5492 * 5493 * @param lhs the first operand 5494 * @param rhs the second operand 5495 * @return the div operation 5496 */ 5497 public static BinaryOp div(Value lhs, Value rhs) { 5498 return new DivOp(lhs, rhs); 5499 } 5500 5501 /** 5502 * Creates a mod operation. 5503 * 5504 * @param lhs the first operand 5505 * @param rhs the second operand 5506 * @return the mod operation 5507 */ 5508 public static BinaryOp mod(Value lhs, Value rhs) { 5509 return new ModOp(lhs, rhs); 5510 } 5511 5512 /** 5513 * Creates a bitwise/logical or operation. 5514 * 5515 * @param lhs the first operand 5516 * @param rhs the second operand 5517 * @return the or operation 5518 */ 5519 public static BinaryOp or(Value lhs, Value rhs) { 5520 return new OrOp(lhs, rhs); 5521 } 5522 5523 /** 5524 * Creates a bitwise/logical and operation. 5525 * 5526 * @param lhs the first operand 5527 * @param rhs the second operand 5528 * @return the and operation 5529 */ 5530 public static BinaryOp and(Value lhs, Value rhs) { 5531 return new AndOp(lhs, rhs); 5532 } 5533 5534 /** 5535 * Creates a bitwise/logical xor operation. 5536 * 5537 * @param lhs the first operand 5538 * @param rhs the second operand 5539 * @return the xor operation 5540 */ 5541 public static BinaryOp xor(Value lhs, Value rhs) { 5542 return new XorOp(lhs, rhs); 5543 } 5544 5545 /** 5546 * Creates a left shift operation. 5547 * 5548 * @param lhs the first operand 5549 * @param rhs the second operand 5550 * @return the xor operation 5551 */ 5552 public static BinaryOp lshl(Value lhs, Value rhs) { 5553 return new LshlOp(lhs, rhs); 5554 } 5555 5556 /** 5557 * Creates a right shift operation. 5558 * 5559 * @param lhs the first operand 5560 * @param rhs the second operand 5561 * @return the xor operation 5562 */ 5563 public static BinaryOp ashr(Value lhs, Value rhs) { 5564 return new AshrOp(lhs, rhs); 5565 } 5566 5567 /** 5568 * Creates an unsigned right shift operation. 5569 * 5570 * @param lhs the first operand 5571 * @param rhs the second operand 5572 * @return the xor operation 5573 */ 5574 public static BinaryOp lshr(Value lhs, Value rhs) { 5575 return new LshrOp(lhs, rhs); 5576 } 5577 5578 /** 5579 * Creates a neg operation. 5580 * 5581 * @param v the operand 5582 * @return the neg operation 5583 */ 5584 public static UnaryOp neg(Value v) { 5585 return new NegOp(v); 5586 } 5587 5588 /** 5589 * Creates a bitwise complement operation. 5590 * 5591 * @param v the operand 5592 * @return the bitwise complement operation 5593 */ 5594 public static UnaryOp compl(Value v) { 5595 return new ComplOp(v); 5596 } 5597 5598 /** 5599 * Creates a not operation. 5600 * 5601 * @param v the operand 5602 * @return the not operation 5603 */ 5604 public static UnaryOp not(Value v) { 5605 return new NotOp(v); 5606 } 5607 5608 /** 5609 * Creates an equals comparison operation. 5610 * 5611 * @param lhs the first operand 5612 * @param rhs the second operand 5613 * @return the equals comparison operation 5614 */ 5615 public static BinaryTestOp eq(Value lhs, Value rhs) { 5616 return new EqOp(lhs, rhs); 5617 } 5618 5619 /** 5620 * Creates a not equals comparison operation. 5621 * 5622 * @param lhs the first operand 5623 * @param rhs the second operand 5624 * @return the not equals comparison operation 5625 */ 5626 public static BinaryTestOp neq(Value lhs, Value rhs) { 5627 return new NeqOp(lhs, rhs); 5628 } 5629 5630 /** 5631 * Creates a greater than comparison operation. 5632 * 5633 * @param lhs the first operand 5634 * @param rhs the second operand 5635 * @return the greater than comparison operation 5636 */ 5637 public static BinaryTestOp gt(Value lhs, Value rhs) { 5638 return new GtOp(lhs, rhs); 5639 } 5640 5641 /** 5642 * Creates a greater than or equals to comparison operation. 5643 * 5644 * @param lhs the first operand 5645 * @param rhs the second operand 5646 * @return the greater than or equals to comparison operation 5647 */ 5648 public static BinaryTestOp ge(Value lhs, Value rhs) { 5649 return new GeOp(lhs, rhs); 5650 } 5651 5652 /** 5653 * Creates a less than comparison operation. 5654 * 5655 * @param lhs the first operand 5656 * @param rhs the second operand 5657 * @return the less than comparison operation 5658 */ 5659 public static BinaryTestOp lt(Value lhs, Value rhs) { 5660 return new LtOp(lhs, rhs); 5661 } 5662 5663 /** 5664 * Creates a less than or equals to comparison operation. 5665 * 5666 * @param lhs the first operand 5667 * @param rhs the second operand 5668 * @return the less than or equals to comparison operation 5669 */ 5670 public static BinaryTestOp le(Value lhs, Value rhs) { 5671 return new LeOp(lhs, rhs); 5672 } 5673 5674 /** 5675 * Creates a string concatenation operation. 5676 * 5677 * @param lhs the first operand 5678 * @param rhs the second operand 5679 * @return the string concatenation operation 5680 */ 5681 public static ConcatOp concat(Value lhs, Value rhs) { 5682 return new ConcatOp(lhs, rhs); 5683 } 5684 5685 /** 5686 * Creates a continue operation. 5687 * 5688 * @return the continue operation 5689 */ 5690 public static ContinueOp continue_() { 5691 return continue_(null); 5692 } 5693 5694 /** 5695 * Creates a continue operation. 5696 * 5697 * @param label the value associated with where to continue from 5698 * @return the continue operation 5699 */ 5700 public static ContinueOp continue_(Value label) { 5701 return new ContinueOp(label); 5702 } 5703 5704 /** 5705 * Creates a break operation. 5706 * 5707 * @return the break operation 5708 */ 5709 public static BreakOp break_() { 5710 return break_(null); 5711 } 5712 5713 /** 5714 * Creates a break operation. 5715 * 5716 * @param label the value associated with where to continue from 5717 * @return the break operation 5718 */ 5719 public static BreakOp break_(Value label) { 5720 return new BreakOp(label); 5721 } 5722 5723 /** 5724 * Creates a yield operation. 5725 * 5726 * @return the yield operation 5727 */ 5728 public static YieldOp java_yield() { 5729 return java_yield(null); 5730 } 5731 5732 /** 5733 * Creates a yield operation. 5734 * 5735 * @param operand the value to yield 5736 * @return the yield operation 5737 */ 5738 public static YieldOp java_yield(Value operand) { 5739 return new YieldOp(operand); 5740 } 5741 5742 /** 5743 * Creates a block operation. 5744 * 5745 * @param body the body builder of the operation to be built and become its child 5746 * @return the block operation 5747 */ 5748 public static BlockOp block(Body.Builder body) { 5749 return new BlockOp(body); 5750 } 5751 5752 /** 5753 * Creates a synchronized operation. 5754 * 5755 * @param expr the expression body builder of the operation to be built and become its child 5756 * @param blockBody the block body builder of the operation to be built and become its child 5757 * @return the synchronized operation 5758 */ 5759 public static SynchronizedOp synchronized_(Body.Builder expr, Body.Builder blockBody) { 5760 return new SynchronizedOp(expr, blockBody); 5761 } 5762 5763 /** 5764 * Creates a labeled operation. 5765 * 5766 * @param body the body builder of the operation to be built and become its child 5767 * @return the block operation 5768 */ 5769 public static LabeledOp labeled(Body.Builder body) { 5770 return new LabeledOp(body); 5771 } 5772 5773 /** 5774 * Creates an if operation builder. 5775 * 5776 * @param ancestorBody the nearest ancestor body builder from which to construct 5777 * body builders for this operation 5778 * @return the if operation builder 5779 */ 5780 public static IfOp.IfBuilder if_(Body.Builder ancestorBody) { 5781 return new IfOp.IfBuilder(ancestorBody); 5782 } 5783 5784 // Pairs of 5785 // predicate ()boolean, body ()void 5786 // And one optional body ()void at the end 5787 5788 /** 5789 * Creates an if operation. 5790 * 5791 * @param bodies the body builders of operation to be built and become its children 5792 * @return the if operation 5793 */ 5794 public static IfOp if_(List<Body.Builder> bodies) { 5795 return new IfOp(bodies); 5796 } 5797 5798 /** 5799 * Creates a switch expression operation. 5800 * <p> 5801 * The result type of the operation will be derived from the yield type of the second body 5802 * 5803 * @param target the switch target value 5804 * @param bodies the body builders of the operation to be built and become its children 5805 * @return the switch expression operation 5806 */ 5807 public static SwitchExpressionOp switchExpression(Value target, List<Body.Builder> bodies) { 5808 return new SwitchExpressionOp(null, target, bodies); 5809 } 5810 5811 /** 5812 * Creates a switch expression operation. 5813 * 5814 * @param resultType the result type of the expression 5815 * @param target the switch target value 5816 * @param bodies the body builders of the operation to be built and become its children 5817 * @return the switch expression operation 5818 */ 5819 public static SwitchExpressionOp switchExpression(TypeElement resultType, Value target, 5820 List<Body.Builder> bodies) { 5821 Objects.requireNonNull(resultType); 5822 return new SwitchExpressionOp(resultType, target, bodies); 5823 } 5824 5825 /** 5826 * Creates a switch statement operation. 5827 * @param target the switch target value 5828 * @param bodies the body builders of the operation to be built and become its children 5829 * @return the switch statement operation 5830 */ 5831 public static SwitchStatementOp switchStatement(Value target, List<Body.Builder> bodies) { 5832 return new SwitchStatementOp(target, bodies); 5833 } 5834 5835 /** 5836 * Creates a switch fallthrough operation. 5837 * 5838 * @return the switch fallthrough operation 5839 */ 5840 public static SwitchFallthroughOp switchFallthroughOp() { 5841 return new SwitchFallthroughOp(); 5842 } 5843 5844 /** 5845 * Creates a for operation builder. 5846 * 5847 * @param ancestorBody the nearest ancestor body builder from which to construct 5848 * body builders for this operation 5849 * @param initTypes the types of initialized variables 5850 * @return the for operation builder 5851 */ 5852 public static ForOp.InitBuilder for_(Body.Builder ancestorBody, TypeElement... initTypes) { 5853 return for_(ancestorBody, List.of(initTypes)); 5854 } 5855 5856 /** 5857 * Creates a for operation builder. 5858 * 5859 * @param ancestorBody the nearest ancestor body builder from which to construct 5860 * body builders for this operation 5861 * @param initTypes the types of initialized variables 5862 * @return the for operation builder 5863 */ 5864 public static ForOp.InitBuilder for_(Body.Builder ancestorBody, List<? extends TypeElement> initTypes) { 5865 return new ForOp.InitBuilder(ancestorBody, initTypes); 5866 } 5867 5868 5869 /** 5870 * Creates a for operation. 5871 * 5872 * @param init the init body builder of the operation to be built and become its child 5873 * @param cond the cond body builder of the operation to be built and become its child 5874 * @param update the update body builder of the operation to be built and become its child 5875 * @param body the main body builder of the operation to be built and become its child 5876 * @return the for operation 5877 */ 5878 // init ()Tuple<Var<T1>, Var<T2>, ..., Var<TN>>, or init ()void 5879 // cond (Var<T1>, Var<T2>, ..., Var<TN>)boolean 5880 // update (Var<T1>, Var<T2>, ..., Var<TN>)void 5881 // body (Var<T1>, Var<T2>, ..., Var<TN>)void 5882 public static ForOp for_(Body.Builder init, 5883 Body.Builder cond, 5884 Body.Builder update, 5885 Body.Builder body) { 5886 return new ForOp(init, cond, update, body); 5887 } 5888 5889 /** 5890 * Creates an enhanced for operation builder. 5891 * 5892 * @param ancestorBody the nearest ancestor body builder from which to construct 5893 * body builders for this operation 5894 * @param iterableType the iterable type 5895 * @param elementType the element type 5896 * @return the enhanced for operation builder 5897 */ 5898 public static EnhancedForOp.ExpressionBuilder enhancedFor(Body.Builder ancestorBody, 5899 TypeElement iterableType, TypeElement elementType) { 5900 return new EnhancedForOp.ExpressionBuilder(ancestorBody, iterableType, elementType); 5901 } 5902 5903 // expression ()I<E> 5904 // init (E )Var<T> 5905 // body (Var<T> )void 5906 5907 /** 5908 * Creates an enhanced for operation. 5909 * 5910 * @param expression the expression body builder of the operation to be built and become its child 5911 * @param init the init body builder of the operation to be built and become its child 5912 * @param body the main body builder of the operation to be built and become its child 5913 * @return the enhanced for operation 5914 */ 5915 public static EnhancedForOp enhancedFor(Body.Builder expression, 5916 Body.Builder init, 5917 Body.Builder body) { 5918 return new EnhancedForOp(expression, init, body); 5919 } 5920 5921 /** 5922 * Creates a while operation builder. 5923 * 5924 * @param ancestorBody the nearest ancestor body builder from which to construct 5925 * body builders for this operation 5926 * @return the while operation builder 5927 */ 5928 public static WhileOp.PredicateBuilder while_(Body.Builder ancestorBody) { 5929 return new WhileOp.PredicateBuilder(ancestorBody); 5930 } 5931 5932 /** 5933 * Creates a while operation. 5934 * 5935 * @param predicate the predicate body builder of the operation to be built and become its child 5936 * @param body the main body builder of the operation to be built and become its child 5937 * @return the while operation 5938 */ 5939 // predicate, ()boolean, may be null for predicate returning true 5940 // body, ()void 5941 public static WhileOp while_(Body.Builder predicate, Body.Builder body) { 5942 return new WhileOp(predicate, body); 5943 } 5944 5945 /** 5946 * Creates a do operation builder. 5947 * 5948 * @param ancestorBody the nearest ancestor body builder from which to construct 5949 * body builders for this operation 5950 * @return the do operation builder 5951 */ 5952 public static DoWhileOp.BodyBuilder doWhile(Body.Builder ancestorBody) { 5953 return new DoWhileOp.BodyBuilder(ancestorBody); 5954 } 5955 5956 /** 5957 * Creates a do operation. 5958 * 5959 * @param predicate the predicate body builder of the operation to be built and become its child 5960 * @param body the main body builder of the operation to be built and become its child 5961 * @return the do operation 5962 */ 5963 public static DoWhileOp doWhile(Body.Builder body, Body.Builder predicate) { 5964 return new DoWhileOp(body, predicate); 5965 } 5966 5967 /** 5968 * Creates a conditional-and operation builder. 5969 * 5970 * @param ancestorBody the nearest ancestor body builder from which to construct 5971 * body builders for this operation 5972 * @param lhs a consumer that builds the left-hand side body 5973 * @param rhs a consumer that builds the right-hand side body 5974 * @return the conditional-and operation builder 5975 */ 5976 public static ConditionalAndOp.Builder conditionalAnd(Body.Builder ancestorBody, 5977 Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) { 5978 return new ConditionalAndOp.Builder(ancestorBody, lhs, rhs); 5979 } 5980 5981 /** 5982 * Creates a conditional-or operation builder. 5983 * 5984 * @param ancestorBody the nearest ancestor body builder from which to construct 5985 * body builders for this operation 5986 * @param lhs a consumer that builds the left-hand side body 5987 * @param rhs a consumer that builds the right-hand side body 5988 * @return the conditional-or operation builder 5989 */ 5990 public static ConditionalOrOp.Builder conditionalOr(Body.Builder ancestorBody, 5991 Consumer<Block.Builder> lhs, Consumer<Block.Builder> rhs) { 5992 return new ConditionalOrOp.Builder(ancestorBody, lhs, rhs); 5993 } 5994 5995 /** 5996 * Creates a conditional-and operation 5997 * 5998 * @param bodies the body builders of operation to be built and become its children 5999 * @return the conditional-and operation 6000 */ 6001 // predicates, ()boolean 6002 public static ConditionalAndOp conditionalAnd(List<Body.Builder> bodies) { 6003 return new ConditionalAndOp(bodies); 6004 } 6005 6006 /** 6007 * Creates a conditional-or operation 6008 * 6009 * @param bodies the body builders of operation to be built and become its children 6010 * @return the conditional-or operation 6011 */ 6012 // predicates, ()boolean 6013 public static ConditionalOrOp conditionalOr(List<Body.Builder> bodies) { 6014 return new ConditionalOrOp(bodies); 6015 } 6016 6017 /** 6018 * Creates a conditional operation 6019 * 6020 * @param expressionType the result type of the expression 6021 * @param bodies the body builders of operation to be built and become its children 6022 * @return the conditional operation 6023 */ 6024 public static ConditionalExpressionOp conditionalExpression(TypeElement expressionType, 6025 List<Body.Builder> bodies) { 6026 Objects.requireNonNull(expressionType); 6027 return new ConditionalExpressionOp(expressionType, bodies); 6028 } 6029 6030 /** 6031 * Creates a conditional operation 6032 * <p> 6033 * The result type of the operation will be derived from the yield type of the second body 6034 * 6035 * @param bodies the body builders of operation to be built and become its children 6036 * @return the conditional operation 6037 */ 6038 public static ConditionalExpressionOp conditionalExpression(List<Body.Builder> bodies) { 6039 return new ConditionalExpressionOp(null, bodies); 6040 } 6041 6042 /** 6043 * Creates try operation builder. 6044 * 6045 * @param ancestorBody the nearest ancestor body builder from which to construct 6046 * body builders for this operation 6047 * @param c a consumer that builds the try body 6048 * @return the try operation builder 6049 */ 6050 public static TryOp.CatchBuilder try_(Body.Builder ancestorBody, Consumer<Block.Builder> c) { 6051 Body.Builder _try = Body.Builder.of(ancestorBody, CoreType.FUNCTION_TYPE_VOID); 6052 c.accept(_try.entryBlock()); 6053 return new TryOp.CatchBuilder(ancestorBody, null, _try); 6054 } 6055 6056 /** 6057 * Creates try-with-resources operation builder. 6058 * 6059 * @param ancestorBody the nearest ancestor body builder from which to construct 6060 * body builders for this operation 6061 * @param c a consumer that builds the resources body 6062 * @return the try-with-resources operation builder 6063 */ 6064 public static TryOp.BodyBuilder tryWithResources(Body.Builder ancestorBody, 6065 List<? extends TypeElement> resourceTypes, 6066 Consumer<Block.Builder> c) { 6067 resourceTypes = resourceTypes.stream().map(CoreType::varType).toList(); 6068 Body.Builder resources = Body.Builder.of(ancestorBody, 6069 CoreType.functionType(CoreType.tupleType(resourceTypes))); 6070 c.accept(resources.entryBlock()); 6071 return new TryOp.BodyBuilder(ancestorBody, resourceTypes, resources); 6072 } 6073 6074 // resources ()Tuple<Var<R1>, Var<R2>, ..., Var<RN>>, or null 6075 // try (Var<R1>, Var<R2>, ..., Var<RN>)void, or try ()void 6076 // catch (E )void, where E <: Throwable 6077 // finally ()void, or null 6078 6079 /** 6080 * Creates a try or try-with-resources operation. 6081 * 6082 * @param resources the try body builder of the operation to be built and become its child, 6083 * may be null 6084 * @param body the try body builder of the operation to be built and become its child 6085 * @param catchers the catch body builders of the operation to be built and become its children 6086 * @param finalizer the finalizer body builder of the operation to be built and become its child 6087 * @return the try or try-with-resources operation 6088 */ 6089 public static TryOp try_(Body.Builder resources, 6090 Body.Builder body, 6091 List<Body.Builder> catchers, 6092 Body.Builder finalizer) { 6093 return new TryOp(resources, body, catchers, finalizer); 6094 } 6095 6096 // 6097 // Patterns 6098 6099 /** 6100 * Creates a pattern match operation. 6101 * 6102 * @param target the target value 6103 * @param pattern the pattern body builder of the operation to be built and become its child 6104 * @param match the match body builder of the operation to be built and become its child 6105 * @return the pattern match operation 6106 */ 6107 public static PatternOps.MatchOp match(Value target, 6108 Body.Builder pattern, Body.Builder match) { 6109 return new PatternOps.MatchOp(target, pattern, match); 6110 } 6111 6112 /** 6113 * Creates a pattern binding operation. 6114 * 6115 * @param type the type of value to be bound 6116 * @param bindingName the binding name 6117 * @return the pattern binding operation 6118 */ 6119 public static PatternOps.TypePatternOp typePattern(TypeElement type, String bindingName) { 6120 return new PatternOps.TypePatternOp(type, bindingName); 6121 } 6122 6123 /** 6124 * Creates a record pattern operation. 6125 * 6126 * @param recordDescriptor the record descriptor 6127 * @param nestedPatterns the nested pattern values 6128 * @return the record pattern operation 6129 */ 6130 public static PatternOps.RecordPatternOp recordPattern(RecordTypeRef recordDescriptor, Value... nestedPatterns) { 6131 return recordPattern(recordDescriptor, List.of(nestedPatterns)); 6132 } 6133 6134 /** 6135 * Creates a record pattern operation. 6136 * 6137 * @param recordDescriptor the record descriptor 6138 * @param nestedPatterns the nested pattern values 6139 * @return the record pattern operation 6140 */ 6141 public static PatternOps.RecordPatternOp recordPattern(RecordTypeRef recordDescriptor, List<Value> nestedPatterns) { 6142 return new PatternOps.RecordPatternOp(recordDescriptor, nestedPatterns); 6143 } 6144 6145 public static PatternOps.MatchAllPatternOp matchAllPattern() { 6146 return new PatternOps.MatchAllPatternOp(); 6147 } 6148 }