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