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