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