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