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