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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @modules jdk.incubator.code 27 * @run testng TestExceptionRegionOps 28 */ 29 30 import jdk.incubator.code.dialect.core.CoreType; 31 import jdk.incubator.code.dialect.java.JavaOp; 32 import org.testng.Assert; 33 import org.testng.annotations.Test; 34 35 import jdk.incubator.code.dialect.core.CoreOp; 36 import jdk.incubator.code.dialect.java.MethodRef; 37 import jdk.incubator.code.interpreter.Interpreter; 38 import java.lang.invoke.MethodHandles; 39 import jdk.incubator.code.dialect.java.JavaType; 40 import java.util.ArrayList; 41 import java.util.List; 42 import java.util.function.Consumer; 43 import java.util.function.IntConsumer; 44 45 import static jdk.incubator.code.dialect.core.CoreOp.return_; 46 import static jdk.incubator.code.dialect.java.JavaOp.throw_; 47 import static jdk.incubator.code.dialect.core.CoreOp.branch; 48 import static jdk.incubator.code.dialect.core.CoreOp.constant; 49 import static jdk.incubator.code.dialect.java.JavaOp.exceptionRegionEnter; 50 import static jdk.incubator.code.dialect.java.JavaOp.exceptionRegionExit; 51 import static jdk.incubator.code.dialect.core.CoreOp.func; 52 import static jdk.incubator.code.dialect.java.JavaType.*; 53 import static jdk.incubator.code.dialect.java.JavaType.VOID; 54 55 public class TestExceptionRegionOps { 56 57 public void testF(IntConsumer c) { 58 try { 59 c.accept(0); 60 c.accept(-1); 61 } catch (IllegalStateException e) { 62 c.accept(1); 63 c.accept(-1); 64 } catch (IllegalArgumentException e) { 65 c.accept(2); 66 c.accept(-1); 67 } 68 c.accept(3); 69 c.accept(-1); 70 } 71 72 @Test 73 public void test() { 74 CoreOp.FuncOp f = func("f", CoreType.functionType(VOID, type(IntConsumer.class))) 75 .body(fbody -> { 76 var fblock = fbody.entryBlock(); 77 var catchER1ISE = fblock.block(type(IllegalStateException.class)); 78 var catchER1IAE = fblock.block(type(IllegalArgumentException.class)); 79 var enterER1 = fblock.block(); 80 var end = fblock.block(); 81 82 // 83 var c = fblock.parameters().get(0); 84 fblock.op(exceptionRegionEnter( 85 enterER1.successor(), 86 catchER1IAE.successor(), catchER1ISE.successor())); 87 88 // Start of exception region 89 enterER1.ops(b -> { 90 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 0)))); 91 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 92 // End of exception region 93 b.op(exceptionRegionExit(end.successor(), 94 catchER1ISE.successor(), catchER1IAE.successor())); 95 }); 96 97 // First catch block for exception region 98 catchER1ISE.ops(b -> { 99 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 1)))); 100 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 101 b.op(branch(end.successor())); 102 }); 103 104 // Second catch for exception region 105 catchER1IAE.ops(b -> { 106 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 2)))); 107 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 108 b.op(branch(end.successor())); 109 }); 110 111 // 112 end.ops(b -> { 113 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 3)))); 114 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 115 b.op(CoreOp.return_()); 116 }); 117 }); 118 119 f.writeTo(System.out); 120 121 Consumer<IntConsumer> test = testConsumer( 122 c -> Interpreter.invoke(MethodHandles.lookup(), f, c), 123 this::testF); 124 125 test.accept(i -> {}); 126 test.accept(i -> { 127 if (i == 0) throw new IllegalStateException(); 128 }); 129 test.accept(i -> { 130 if (i == 0) throw new IllegalArgumentException(); 131 }); 132 test.accept(i -> { 133 if (i == 0) throw new NullPointerException(); 134 }); 135 test.accept(i -> { 136 if (i == 0) throw new IllegalStateException(); 137 if (i == 1) throw new RuntimeException(); 138 }); 139 test.accept(i -> { 140 if (i == 0) throw new IllegalArgumentException(); 141 if (i == 2) throw new RuntimeException(); 142 }); 143 test.accept(i -> { 144 if (i == 3) throw new IllegalStateException(); 145 }); 146 } 147 148 149 public void testCatchThrowableF(IntConsumer c) { 150 try { 151 c.accept(0); 152 c.accept(-1); 153 } catch (IllegalStateException e) { 154 c.accept(1); 155 c.accept(-1); 156 } catch (Throwable e) { 157 c.accept(2); 158 c.accept(-1); 159 } 160 c.accept(3); 161 c.accept(-1); 162 } 163 164 @Test 165 public void testCatchThrowable() { 166 CoreOp.FuncOp f = func("f", CoreType.functionType(VOID, type(IntConsumer.class))) 167 .body(fbody -> { 168 var fblock = fbody.entryBlock(); 169 var catchER1ISE = fblock.block(type(IllegalStateException.class)); 170 var catchER1T = fblock.block(type(Throwable.class)); 171 var enterER1 = fblock.block(); 172 var end = fblock.block(); 173 174 // 175 var c = fblock.parameters().get(0); 176 fblock.op(exceptionRegionEnter( 177 enterER1.successor(), 178 catchER1T.successor(), catchER1ISE.successor())); 179 180 // Start of exception region 181 enterER1.ops(b -> { 182 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 0)))); 183 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 184 // End of exception region 185 b.op(exceptionRegionExit(end.successor(), 186 catchER1ISE.successor(), catchER1T.successor())); 187 188 }); 189 190 // First catch block for exception region 191 catchER1ISE.ops(b -> { 192 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 1)))); 193 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 194 b.op(branch(end.successor())); 195 }); 196 197 // Second catch for exception region 198 catchER1T.ops(b -> { 199 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 2)))); 200 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 201 b.op(branch(end.successor())); 202 }); 203 204 // 205 end.ops(b -> { 206 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 3)))); 207 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 208 b.op(CoreOp.return_()); 209 }); 210 }); 211 212 f.writeTo(System.out); 213 214 Consumer<IntConsumer> test = testConsumer( 215 c -> Interpreter.invoke(MethodHandles.lookup(), f, c), 216 this::testCatchThrowableF); 217 218 test.accept(i -> {}); 219 test.accept(i -> { 220 if (i == 0) throw new IllegalStateException(); 221 }); 222 test.accept(i -> { 223 if (i == 0) throw new RuntimeException(); 224 }); 225 test.accept(i -> { 226 if (i == 0) throw new IllegalStateException(); 227 if (i == 1) throw new RuntimeException(); 228 }); 229 test.accept(i -> { 230 if (i == 0) throw new RuntimeException(); 231 if (i == 2) throw new RuntimeException(); 232 }); 233 test.accept(i -> { 234 if (i == 3) throw new IllegalStateException(); 235 }); 236 } 237 238 239 public void testNestedF(IntConsumer c) { 240 try { 241 c.accept(0); 242 c.accept(-1); 243 try { 244 c.accept(1); 245 c.accept(-1); 246 } catch (IllegalStateException e) { 247 c.accept(2); 248 c.accept(-1); 249 } 250 c.accept(3); 251 c.accept(-1); 252 } catch (IllegalArgumentException e) { 253 c.accept(4); 254 c.accept(-1); 255 } 256 c.accept(5); 257 c.accept(-1); 258 } 259 260 @Test 261 public void testNested() { 262 CoreOp.FuncOp f = func("f", CoreType.functionType(VOID, type(IntConsumer.class))) 263 .body(fbody -> { 264 var fblock = fbody.entryBlock(); 265 var catchER1 = fblock.block(type(IllegalArgumentException.class)); 266 var catchER2 = fblock.block(type(IllegalStateException.class)); 267 var enterER1 = fblock.block(); 268 var enterER2 = fblock.block(); 269 var b3 = fblock.block(); 270 var end = fblock.block(); 271 272 // 273 var c = fblock.parameters().get(0); 274 fblock.op(exceptionRegionEnter( 275 enterER1.successor(), 276 catchER1.successor())); 277 278 // Start of first exception region 279 enterER1.ops(b -> { 280 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 0)))); 281 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 282 }); 283 enterER1.op(exceptionRegionEnter( 284 enterER2.successor(), 285 catchER2.successor())); 286 287 // Start of second exception region 288 enterER2.ops(b -> { 289 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 1)))); 290 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 291 // End of second exception region 292 b.op(exceptionRegionExit(b3.successor(), 293 catchER2.successor())); 294 }); 295 296 // Catch block for second exception region 297 catchER2.ops(b -> { 298 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 2)))); 299 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 300 b.op(branch(b3.successor())); 301 }); 302 303 b3.ops(b -> { 304 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 3)))); 305 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 306 // End of first exception region 307 b.op(exceptionRegionExit(end.successor(), 308 catchER1.successor())); 309 }); 310 311 // Catch block for first exception region 312 catchER1.ops(b -> { 313 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 4)))); 314 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 315 b.op(branch(end.successor())); 316 }); 317 318 // 319 end.ops(b -> { 320 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 5)))); 321 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 322 b.op(CoreOp.return_()); 323 }); 324 }); 325 326 f.writeTo(System.out); 327 328 Consumer<IntConsumer> test = testConsumer( 329 c -> Interpreter.invoke(MethodHandles.lookup(), f, c), 330 this::testNestedF); 331 332 test.accept(i -> {}); 333 test.accept(i -> { 334 if (i == 0) throw new IllegalStateException(); 335 }); 336 test.accept(i -> { 337 if (i == 0) throw new IllegalArgumentException(); 338 }); 339 test.accept(i -> { 340 if (i == 1) throw new IllegalStateException(); 341 }); 342 test.accept(i -> { 343 if (i == 1) throw new IllegalArgumentException(); 344 }); 345 test.accept(i -> { 346 if (i == 1) throw new IllegalStateException(); 347 if (i == 2) throw new IllegalArgumentException(); 348 }); 349 test.accept(i -> { 350 if (i == 1) throw new IllegalStateException(); 351 if (i == 2) throw new RuntimeException(); 352 }); 353 test.accept(i -> { 354 if (i == 3) throw new IllegalArgumentException(); 355 }); 356 test.accept(i -> { 357 if (i == 3) throw new RuntimeException(); 358 }); 359 test.accept(i -> { 360 if (i == 3) throw new IllegalArgumentException(); 361 if (i == 4) throw new RuntimeException(); 362 }); 363 test.accept(i -> { 364 if (i == 5) throw new RuntimeException(); 365 }); 366 } 367 368 public void testCatchFinallyF(IntConsumer c) { 369 try { 370 c.accept(0); 371 c.accept(-1); 372 } catch (IllegalStateException e) { 373 c.accept(1); 374 c.accept(-1); 375 } finally { 376 c.accept(2); 377 c.accept(-1); 378 } 379 c.accept(3); 380 c.accept(-1); 381 } 382 383 @Test 384 public void testCatchFinally() { 385 CoreOp.FuncOp f = func("f", CoreType.functionType(VOID, JavaType.type(IntConsumer.class))) 386 .body(fbody -> { 387 var fblock = fbody.entryBlock(); 388 var catchRE = fblock.block(type(IllegalStateException.class)); 389 var catchAll = fblock.block(type(Throwable.class)); 390 var enterER1 = fblock.block(); 391 var exitER1 = fblock.block(); 392 var enterER2 = fblock.block(); 393 var exitER2 = fblock.block(); 394 var end = fblock.block(); 395 396 // 397 var c = fblock.parameters().get(0); 398 fblock.op(exceptionRegionEnter( 399 enterER1.successor(), 400 catchAll.successor(), catchRE.successor())); 401 402 // Start of exception region 403 enterER1.ops(b -> { 404 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 0)))); 405 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 406 // End of exception region 407 b.op(exceptionRegionExit(exitER1.successor(), 408 catchRE.successor(), catchAll.successor())); 409 }); 410 // Inline finally 411 exitER1.ops(b -> { 412 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 2)))); 413 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 414 b.op(branch(end.successor())); 415 }); 416 417 // Catch block for RuntimeException 418 catchRE.op(exceptionRegionEnter( 419 enterER2.successor(), 420 catchAll.successor())); 421 // Start of exception region 422 enterER2.ops(b -> { 423 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 1)))); 424 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 425 // End of exception region 426 b.op(exceptionRegionExit(exitER2.successor(), 427 catchAll.successor())); 428 }); 429 // Inline finally 430 exitER2.ops(b -> { 431 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 2)))); 432 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 433 b.op(branch(end.successor())); 434 }); 435 436 // Catch all block for finally 437 catchAll.ops(b -> { 438 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 2)))); 439 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 440 b.op(throw_(catchAll.parameters().get(0))); 441 }); 442 443 // 444 end.ops(b -> { 445 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, 3)))); 446 b.op(JavaOp.invoke(INT_CONSUMER_ACCEPT_METHOD, c, b.op(constant(INT, -1)))); 447 b.op(CoreOp.return_()); 448 }); 449 }); 450 451 f.writeTo(System.out); 452 453 Consumer<IntConsumer> test = testConsumer( 454 c -> Interpreter.invoke(MethodHandles.lookup(), f, c), 455 this::testCatchFinallyF 456 ); 457 458 test.accept(i -> {}); 459 test.accept(i -> { 460 if (i == 0) throw new IllegalStateException(); 461 }); 462 test.accept(i -> { 463 if (i == 0) throw new RuntimeException(); 464 }); 465 test.accept(i -> { 466 if (i == 2) throw new RuntimeException(); 467 }); 468 test.accept(i -> { 469 if (i == 0) throw new IllegalStateException(); 470 if (i == 1) throw new RuntimeException(); 471 }); 472 test.accept(i -> { 473 if (i == 3) throw new RuntimeException(); 474 }); 475 } 476 477 static final MethodRef INT_CONSUMER_ACCEPT_METHOD = MethodRef.method(type(IntConsumer.class), "accept", 478 VOID, INT); 479 480 static Consumer<IntConsumer> testConsumer(Consumer<IntConsumer> actualR, Consumer<IntConsumer> expectedR) { 481 return c -> { 482 List<Integer> actual = new ArrayList<>(); 483 IntConsumer actualC = actual::add; 484 Throwable actualT = null; 485 try { 486 actualR.accept(actualC.andThen(c)); 487 } catch (Interpreter.InterpreterException e) { 488 throw e; 489 } catch (Throwable t) { 490 actualT = t; 491 } 492 493 List<Integer> expected = new ArrayList<>(); 494 IntConsumer expectedC = expected::add; 495 Throwable expectedT = null; 496 try { 497 expectedR.accept(expectedC.andThen(c)); 498 } catch (Throwable t) { 499 expectedT = t; 500 } 501 502 Assert.assertEquals( 503 actualT != null ? actualT.getClass() : null, 504 expectedT != null ? expectedT.getClass() : null); 505 Assert.assertEquals(actual, expected); 506 }; 507 } 508 }