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 TestTryFinally
 28  * @run main Unreflect TestTryFinally
 29  * @run junit TestTryFinally
 30  */
 31 
 32 import jdk.incubator.code.Reflect;
 33 import jdk.incubator.code.CodeTransformer;
 34 import jdk.incubator.code.Op;
 35 import jdk.incubator.code.dialect.core.CoreOp;
 36 import jdk.incubator.code.interpreter.Interpreter;
 37 import org.junit.jupiter.api.Assertions;
 38 import org.junit.jupiter.api.Test;
 39 
 40 import java.lang.invoke.MethodHandles;
 41 import java.lang.reflect.Method;
 42 import java.util.ArrayList;
 43 import java.util.List;
 44 import java.util.Optional;
 45 import java.util.function.Consumer;
 46 import java.util.function.IntConsumer;
 47 import java.util.stream.Stream;
 48 
 49 public class TestTryFinally {
 50 
 51     @Reflect
 52     public static void tryCatchFinally(IntConsumer c) {
 53         try {
 54             c.accept(0);
 55             c.accept(-1);
 56         } catch (IllegalStateException e) {
 57             c.accept(1);
 58             c.accept(-1);
 59         } finally {
 60             c.accept(2);
 61             c.accept(-1);
 62         }
 63         c.accept(3);
 64         c.accept(-1);
 65     }
 66 
 67     @Test
 68     public void testCatchFinally() {
 69         CoreOp.FuncOp f = getFuncOp("tryCatchFinally");
 70 
 71         System.out.println(f.toText());
 72 
 73         CoreOp.FuncOp lf = f.transform(CodeTransformer.LOWERING_TRANSFORMER);
 74 
 75         System.out.println(lf.toText());
 76 
 77         Consumer<IntConsumer> test = testConsumer(
 78                 c -> Interpreter.invoke(MethodHandles.lookup(), lf, c),
 79                 TestTryFinally::tryCatchFinally
 80         );
 81 
 82         test(test);
 83     }
 84 
 85 
 86     @Reflect
 87     public static void tryReturn(IntConsumer c) {
 88         try {
 89             c.accept(0);
 90             c.accept(-1);
 91             return;
 92         } catch (IllegalStateException e) {
 93             c.accept(1);
 94             c.accept(-1);
 95         } finally {
 96             c.accept(2);
 97             c.accept(-1);
 98         }
 99         c.accept(3);
100         c.accept(-1);
101     }
102 
103     @Test
104     public void testTryReturn() {
105         CoreOp.FuncOp f = getFuncOp("tryReturn");
106 
107         System.out.println(f.toText());
108 
109         CoreOp.FuncOp lf = f.transform(CodeTransformer.LOWERING_TRANSFORMER);
110 
111         System.out.println(lf.toText());
112 
113         Consumer<IntConsumer> test = testConsumer(
114                 c -> Interpreter.invoke(MethodHandles.lookup(), lf, c),
115                 TestTryFinally::tryReturn
116                 );
117 
118         test(test);
119     }
120 
121 
122     @Reflect
123     public static void catchThrow(IntConsumer c) {
124         try {
125             c.accept(0);
126             c.accept(-1);
127         } catch (IllegalStateException e) {
128             c.accept(1);
129             c.accept(-1);
130             throw e;
131         } finally {
132             c.accept(2);
133             c.accept(-1);
134         }
135         c.accept(3);
136         c.accept(-1);
137     }
138 
139     @Test
140     public void testCatchThrow() {
141         CoreOp.FuncOp f = getFuncOp("catchThrow");
142 
143         System.out.println(f.toText());
144 
145         CoreOp.FuncOp lf = f.transform(CodeTransformer.LOWERING_TRANSFORMER);
146 
147         System.out.println(lf.toText());
148 
149         Consumer<IntConsumer> test = testConsumer(
150                 c -> Interpreter.invoke(MethodHandles.lookup(), lf, c),
151                 TestTryFinally::catchThrow
152         );
153 
154         test(test);
155     }
156 
157 
158     @Reflect
159     public static void finallyReturn(IntConsumer c) {
160         try {
161             c.accept(0);
162             c.accept(-1);
163         } catch (IllegalStateException e) {
164             c.accept(1);
165             c.accept(-1);
166         } finally {
167             c.accept(2);
168             c.accept(-1);
169             return;
170         }
171     }
172 
173     @Test
174     public void testFinallyReturn() {
175         CoreOp.FuncOp f = getFuncOp("finallyReturn");
176 
177         System.out.println(f.toText());
178 
179         CoreOp.FuncOp lf = f.transform(CodeTransformer.LOWERING_TRANSFORMER);
180 
181         System.out.println(lf.toText());
182 
183         Consumer<IntConsumer> test = testConsumer(
184                 c -> Interpreter.invoke(MethodHandles.lookup(), lf, c),
185                 TestTryFinally::finallyReturn
186         );
187 
188         test(test);
189     }
190 
191 
192     static void test(Consumer<IntConsumer> test) {
193         test.accept(i -> {});
194         test.accept(i -> {
195             if (i == 0) throw new IllegalStateException();
196         });
197         test.accept(i -> {
198             if (i == 0) throw new RuntimeException();
199         });
200         test.accept(i -> {
201             if (i == 2) throw new RuntimeException();
202         });
203         test.accept(i -> {
204             if (i == 0) throw new IllegalStateException();
205             if (i == 1) throw new RuntimeException();
206         });
207         test.accept(i -> {
208             if (i == 3) throw new RuntimeException();
209         });
210     }
211 
212     static CoreOp.FuncOp getFuncOp(String name) {
213         Optional<Method> om = Stream.of(TestTryFinally.class.getDeclaredMethods())
214                 .filter(m -> m.getName().equals(name))
215                 .findFirst();
216 
217         Method m = om.get();
218         return Op.ofMethod(m).get();
219     }
220 
221     static Consumer<IntConsumer> testConsumer(Consumer<IntConsumer> actualR, Consumer<IntConsumer> expectedR) {
222         return c -> {
223             List<Integer> actual = new ArrayList<>();
224             IntConsumer actualC = actual::add;
225             Throwable actualT = null;
226             try {
227                 actualR.accept(actualC.andThen(c));
228             } catch (Interpreter.InterpreterException e) {
229                 throw e;
230             } catch (Throwable t) {
231                 actualT = t;
232                 if (t instanceof AssertionError) {
233                     t.printStackTrace();
234                 }
235             }
236 
237             List<Integer> expected = new ArrayList<>();
238             IntConsumer expectedC = expected::add;
239             Throwable expectedT = null;
240             try {
241                 expectedR.accept(expectedC.andThen(c));
242             } catch (Throwable t) {
243                 expectedT = t;
244             }
245 
246             Assertions.assertEquals(
247                     expectedT != null ? expectedT.getClass() : null, actualT != null ? actualT.getClass() : null
248             );
249             Assertions.assertEquals(expected, actual);
250         };
251     }
252 }