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