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 }