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