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