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