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