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