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.op.CoreOp; 29 import jdk.incubator.code.Op; 30 import jdk.incubator.code.bytecode.BytecodeGenerator; 31 import jdk.incubator.code.interpreter.Interpreter; 32 import java.lang.invoke.MethodHandle; 33 import java.lang.invoke.MethodHandles; 34 import java.lang.reflect.Method; 35 import jdk.incubator.code.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 * @modules jdk.incubator.code 46 * @run testng 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 Assert.assertEquals( 213 actualT != null ? actualT.getClass() : null, 214 expectedT != null ? expectedT.getClass() : null); 215 Assert.assertEquals(actual, expected); 216 }; 217 } 218 219 static MethodHandle generate(CoreOp.FuncOp f) { 220 f.writeTo(System.out); 221 222 CoreOp.FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER); 223 lf.writeTo(System.out); 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 }