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