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 TestTry
 48  */
 49 
 50 public class TestTry {
 51 
 52     @CodeReflection
 53     public static void catching(IntConsumer c) {
 54         try {
 55             c.accept(0);
 56             c.accept(-1);
 57         } catch (IllegalStateException e) {
 58             consume(e);
 59             c.accept(1);
 60             c.accept(-1);
 61         } catch (IllegalArgumentException e) {
 62             consume(e);
 63             c.accept(2);
 64             c.accept(-1);
 65         }
 66         c.accept(3);
 67         c.accept(-1);
 68     }
 69 
 70     @Test
 71     public void testCatching() {
 72         CoreOp.FuncOp f = getFuncOp("catching");
 73 
 74         MethodHandle mh = generate(f);
 75 
 76         Consumer<IntConsumer> test = testConsumer(
 77                 asConsumer(mh),
 78                 TestTry::catching);
 79 
 80         test.accept(i -> {
 81         });
 82         test.accept(i -> {
 83             if (i == 0) throw new IllegalStateException();
 84         });
 85         test.accept(i -> {
 86             if (i == 0) throw new IllegalArgumentException();
 87         });
 88         test.accept(i -> {
 89             if (i == 0) throw new NullPointerException();
 90         });
 91         test.accept(i -> {
 92             if (i == 0) throw new IllegalStateException();
 93             if (i == 1) throw new RuntimeException();
 94         });
 95         test.accept(i -> {
 96             if (i == 0) throw new IllegalArgumentException();
 97             if (i == 2) throw new RuntimeException();
 98         });
 99         test.accept(i -> {
100             if (i == 3) throw new IllegalStateException();
101         });
102     }
103 
104     @CodeReflection
105     public static void catchThrowable(IntConsumer c) {
106         try {
107             c.accept(0);
108             c.accept(-1);
109         } catch (IllegalStateException e) {
110             consume(e);
111             c.accept(1);
112             c.accept(-1);
113         } catch (Throwable e) {
114             consume(e);
115             c.accept(2);
116             c.accept(-1);
117         }
118         c.accept(3);
119         c.accept(-1);
120     }
121 
122     @Test
123     public void testCatchThrowable() {
124         CoreOp.FuncOp f = getFuncOp("catchThrowable");
125 
126         MethodHandle mh = generate(f);
127 
128         Consumer<IntConsumer> test = testConsumer(
129                 asConsumer(mh),
130                 TestTry::catchThrowable);
131 
132         test.accept(i -> {
133         });
134         test.accept(i -> {
135             if (i == 0) throw new IllegalStateException();
136         });
137         test.accept(i -> {
138             if (i == 0) throw new RuntimeException();
139         });
140         test.accept(i -> {
141             if (i == 0) throw new IllegalStateException();
142             if (i == 1) throw new RuntimeException();
143         });
144         test.accept(i -> {
145             if (i == 0) throw new RuntimeException();
146             if (i == 2) throw new RuntimeException();
147         });
148         test.accept(i -> {
149             if (i == 3) throw new IllegalStateException();
150         });
151     }
152 
153 
154     @CodeReflection
155     public static void catchNested(IntConsumer c) {
156         try {
157             c.accept(0);
158             c.accept(-1);
159             try {
160                 c.accept(1);
161                 c.accept(-1);
162             } catch (IllegalStateException e) {
163                 consume(e);
164                 c.accept(2);
165                 c.accept(-1);
166             }
167             c.accept(3);
168             c.accept(-1);
169         } catch (IllegalArgumentException e) {
170             consume(e);
171             c.accept(4);
172             c.accept(-1);
173         }
174         c.accept(5);
175         c.accept(-1);
176     }
177 
178     @Test
179     public void testCatchNested() {
180         CoreOp.FuncOp f = getFuncOp("catchNested");
181 
182         MethodHandle mh = generate(f);
183 
184         Consumer<IntConsumer> test = testConsumer(
185                 asConsumer(mh),
186                 TestTry::catchNested);
187 
188         test.accept(i -> {
189         });
190         test.accept(i -> {
191             if (i == 0) throw new IllegalStateException();
192         });
193         test.accept(i -> {
194             if (i == 0) throw new IllegalArgumentException();
195         });
196         test.accept(i -> {
197             if (i == 1) throw new IllegalStateException();
198         });
199         test.accept(i -> {
200             if (i == 1) throw new IllegalArgumentException();
201         });
202         test.accept(i -> {
203             if (i == 1) throw new IllegalStateException();
204             if (i == 2) throw new IllegalArgumentException();
205         });
206         test.accept(i -> {
207             if (i == 1) throw new IllegalStateException();
208             if (i == 2) throw new RuntimeException();
209         });
210         test.accept(i -> {
211             if (i == 3) throw new IllegalArgumentException();
212         });
213         test.accept(i -> {
214             if (i == 3) throw new RuntimeException();
215         });
216         test.accept(i -> {
217             if (i == 3) throw new IllegalArgumentException();
218             if (i == 4) throw new RuntimeException();
219         });
220         test.accept(i -> {
221             if (i == 5) throw new RuntimeException();
222         });
223     }
224 
225 
226     static void consume(Throwable e) {
227     }
228 
229     static Consumer<IntConsumer> testConsumer(Consumer<IntConsumer> actualR, Consumer<IntConsumer> expectedR) {
230         return c -> {
231             List<Integer> actual = new ArrayList<>();
232             IntConsumer actualC = actual::add;
233             Throwable actualT = null;
234             try {
235                 actualR.accept(actualC.andThen(c));
236             } catch (Interpreter.InterpreterException e) {
237                 throw e;
238             } catch (Throwable t) {
239                 actualT = t;
240                 if (t instanceof AssertionError) {
241                     t.printStackTrace();
242                 }
243             }
244 
245             List<Integer> expected = new ArrayList<>();
246             IntConsumer expectedC = expected::add;
247             Throwable expectedT = null;
248             try {
249                 expectedR.accept(expectedC.andThen(c));
250             } catch (Throwable t) {
251                 expectedT = t;
252             }
253 
254             Assert.assertEquals(
255                     actualT != null ? actualT.getClass() : null,
256                     expectedT != null ? expectedT.getClass() : null);
257             Assert.assertEquals(actual, expected);
258         };
259     }
260 
261     static MethodHandle generate(CoreOp.FuncOp f) {
262         System.out.println(f.toText());
263 
264         CoreOp.FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
265         System.out.println(lf.toText());
266 
267         return BytecodeGenerator.generate(MethodHandles.lookup(), lf);
268     }
269 
270     static <T> Consumer<T> asConsumer(MethodHandle mh) {
271         return c -> {
272             try {
273                 mh.invoke(c);
274             } catch (Throwable e) {
275                 throw erase(e);
276             }
277         };
278     }
279 
280     @SuppressWarnings("unchecked")
281     public static <E extends Throwable> E erase(Throwable e) throws E {
282         return (E) e;
283     }
284 
285     static CoreOp.FuncOp getFuncOp(String name) {
286         Optional<Method> om = Stream.of(TestTry.class.getDeclaredMethods())
287                 .filter(m -> m.getName().equals(name))
288                 .findFirst();
289 
290         Method m = om.get();
291         return Op.ofMethod(m).get();
292     }
293 }