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