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 }