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