1 /* 2 * Copyright (c) 2015, 2016, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* @test 27 * @bug 8139885 8150824 8150825 8194238 8233920 28 * @run testng/othervm -ea -esa -Xverify:all test.java.lang.invoke.TryFinallyTest 29 */ 30 31 package test.java.lang.invoke; 32 33 import java.lang.invoke.MethodHandle; 34 import java.lang.invoke.MethodHandles; 35 import java.lang.invoke.MethodHandles.Lookup; 36 import java.lang.invoke.MethodType; 37 38 import static java.lang.invoke.MethodType.methodType; 39 40 import static org.testng.AssertJUnit.*; 41 42 import org.testng.annotations.*; 43 44 /** 45 * Tests for the tryFinally method handle combinator introduced in JEP 274. 46 */ 47 public class TryFinallyTest { 48 49 static final Lookup LOOKUP = MethodHandles.lookup(); 50 51 @Test testTryFinally()52 public static void testTryFinally() throws Throwable { 53 MethodHandle hello = MethodHandles.tryFinally(TryFinally.MH_greet, TryFinally.MH_exclaim); 54 assertEquals(TryFinally.MT_hello, hello.type()); 55 assertEquals("Hello, world!", hello.invoke("world")); 56 } 57 58 @DataProvider tryFinallyArgs()59 static Object[][] tryFinallyArgs() { 60 return new Object[][] { 61 { boolean.class, true }, 62 { byte.class, (byte) 2 }, 63 { short.class, (short) 2 }, 64 { char.class, (char) 2 }, 65 { int.class, 2 }, 66 { long.class, 2L }, 67 { float.class, 2f }, 68 { double.class, 2D }, 69 { Object.class, new Object() } 70 }; 71 } 72 73 @Test(dataProvider = "tryFinallyArgs") testTryFinally(Class<?> argType, Object arg)74 public static void testTryFinally(Class<?> argType, Object arg) throws Throwable { 75 MethodHandle identity = MethodHandles.identity(argType); 76 MethodHandle tryFinally = MethodHandles.tryFinally( 77 identity, 78 MethodHandles.dropArguments(identity, 0, Throwable.class)); 79 assertEquals(methodType(argType, argType), tryFinally.type()); 80 assertEquals(arg, tryFinally.invoke(arg)); 81 } 82 83 @Test(dataProvider = "tryFinallyArgs", expectedExceptions = TryFinally.T1.class) testTryFinallyException(Class<?> argType, Object arg)84 public static void testTryFinallyException(Class<?> argType, Object arg) throws Throwable { 85 MethodHandle identity = TryFinally.MH_throwingTargetIdentity.asType(methodType(argType, argType)); 86 MethodHandle tryFinally = MethodHandles.tryFinally( 87 identity, 88 MethodHandles.dropArguments(identity, 0, TryFinally.T1.class)); 89 assertEquals(methodType(argType, argType), tryFinally.type()); 90 tryFinally.invoke(arg); // should throw 91 } 92 93 @Test testTryFinallyVoid()94 public static void testTryFinallyVoid() throws Throwable { 95 MethodHandle tfVoid = MethodHandles.tryFinally(TryFinally.MH_print, TryFinally.MH_printMore); 96 assertEquals(TryFinally.MT_printHello, tfVoid.type()); 97 tfVoid.invoke("world"); 98 } 99 100 @Test testTryFinallySublist()101 public static void testTryFinallySublist() throws Throwable { 102 MethodHandle helloMore = MethodHandles.tryFinally(TryFinally.MH_greetMore, TryFinally.MH_exclaimMore); 103 assertEquals(TryFinally.MT_moreHello, helloMore.type()); 104 assertEquals("Hello, world and universe (but world first)!", helloMore.invoke("world", "universe")); 105 } 106 107 @DataProvider omitTrailingArguments()108 static Object[][] omitTrailingArguments() { 109 MethodHandle c = TryFinally.MH_voidCleanup; 110 return new Object[][]{ 111 {c}, 112 {MethodHandles.dropArguments(c, 1, int.class)}, 113 {MethodHandles.dropArguments(c, 1, int.class, long.class)}, 114 {MethodHandles.dropArguments(c, 1, int.class, long.class, Object.class, int.class)}, 115 {MethodHandles.dropArguments(c, 1, int.class, long.class, Object.class, int.class, long.class)} 116 }; 117 } 118 119 @Test(dataProvider = "omitTrailingArguments") testTryFinallyOmitTrailingArguments(MethodHandle cleanup)120 public static void testTryFinallyOmitTrailingArguments(MethodHandle cleanup) throws Throwable { 121 MethodHandle tf = MethodHandles.tryFinally(TryFinally.MH_dummyTarget, cleanup); 122 tf.invoke(1, 2L, "a", 23, 42L, "b"); 123 } 124 125 @DataProvider negativeTestData()126 static Object[][] negativeTestData() { 127 MethodHandle intid = MethodHandles.identity(int.class); 128 MethodHandle intco = MethodHandles.constant(int.class, 0); 129 MethodHandle errTarget = MethodHandles.dropArguments(intco, 0, int.class, double.class, String.class, int.class); 130 MethodHandle errCleanup = MethodHandles.dropArguments(MethodHandles.constant(int.class, 0), 0, Throwable.class, 131 int.class, double.class, Object.class); 132 MethodHandle voidTarget = TryFinally.MH_voidTarget; 133 MethodHandle voidICleanup = MethodHandles.dropArguments(TryFinally.MH_voidCleanup, 1, int.class); 134 return new Object[][]{ 135 {intid, MethodHandles.identity(double.class), 136 "target and return types must match: double != int"}, 137 {intid, MethodHandles.dropArguments(intid, 0, String.class), 138 "cleanup first argument and Throwable must match: (String,int)int != class java.lang.Throwable"}, 139 {intid, MethodHandles.dropArguments(intid, 0, Throwable.class, double.class), 140 "cleanup second argument and target return type must match: (Throwable,double,int)int != int"}, 141 {errTarget, errCleanup, 142 "cleanup parameters after (Throwable,result) and target parameter list prefix must match: " + 143 errCleanup.type() + " != " + errTarget.type()}, 144 {voidTarget, voidICleanup, 145 "cleanup parameters after (Throwable,result) and target parameter list prefix must match: " + 146 voidICleanup.type() + " != " + voidTarget.type()} 147 }; 148 } 149 150 @Test(dataProvider = "negativeTestData") testTryFinallyNegative(MethodHandle target, MethodHandle cleanup, String expectedMessage)151 public static void testTryFinallyNegative(MethodHandle target, MethodHandle cleanup, String expectedMessage) { 152 boolean caught = false; 153 try { 154 MethodHandles.tryFinally(target, cleanup); 155 } catch (IllegalArgumentException iae) { 156 assertEquals(expectedMessage, iae.getMessage()); 157 caught = true; 158 } 159 assertTrue(caught); 160 } 161 162 @Test testTryFinallyThrowableCheck()163 public static void testTryFinallyThrowableCheck() { 164 MethodHandle mh = MethodHandles.tryFinally(TryFinally.MH_throwingTarget, 165 TryFinally.MH_catchingCleanup); 166 try { 167 mh.invoke(); 168 fail("ClassCastException expected"); 169 } catch (Throwable t) { 170 assertTrue("Throwable not assignable to ClassCastException: " + t, 171 ClassCastException.class.isAssignableFrom(t.getClass())); 172 } 173 } 174 175 static class TryFinally { 176 greet(String whom)177 static String greet(String whom) { 178 return "Hello, " + whom; 179 } 180 exclaim(Throwable t, String r, String whom)181 static String exclaim(Throwable t, String r, String whom) { 182 return r + "!"; 183 } 184 print(String what)185 static void print(String what) { 186 System.out.print("Hello, " + what); 187 } 188 printMore(Throwable t, String what)189 static void printMore(Throwable t, String what) { 190 System.out.println("!"); 191 } 192 greetMore(String first, String second)193 static String greetMore(String first, String second) { 194 return "Hello, " + first + " and " + second; 195 } 196 exclaimMore(Throwable t, String r, String first)197 static String exclaimMore(Throwable t, String r, String first) { 198 return r + " (but " + first + " first)!"; 199 } 200 voidTarget()201 static void voidTarget() {} 202 voidCleanup(Throwable t)203 static void voidCleanup(Throwable t) {} 204 205 static class T1 extends Throwable {} 206 207 static class T2 extends Throwable {} 208 throwingTarget()209 static void throwingTarget() throws Throwable { 210 throw new T1(); 211 } 212 throwingTargetIdentity(Object o)213 static Object throwingTargetIdentity(Object o) throws Throwable { 214 throw new T1(); 215 } 216 catchingCleanup(T2 t)217 static void catchingCleanup(T2 t) throws Throwable { 218 } 219 220 static final Class<TryFinally> TRY_FINALLY = TryFinally.class; 221 222 static final MethodType MT_greet = methodType(String.class, String.class); 223 static final MethodType MT_exclaim = methodType(String.class, Throwable.class, String.class, String.class); 224 static final MethodType MT_print = methodType(void.class, String.class); 225 static final MethodType MT_printMore = methodType(void.class, Throwable.class, String.class); 226 static final MethodType MT_greetMore = methodType(String.class, String.class, String.class); 227 static final MethodType MT_exclaimMore = methodType(String.class, Throwable.class, String.class, String.class); 228 static final MethodType MT_voidTarget = methodType(void.class); 229 static final MethodType MT_voidCleanup = methodType(void.class, Throwable.class); 230 static final MethodType MT_throwingTarget = methodType(void.class); 231 static final MethodType MT_throwingTargetIdentity = methodType(Object.class, Object.class); 232 static final MethodType MT_catchingCleanup = methodType(void.class, T2.class); 233 234 static final MethodHandle MH_greet; 235 static final MethodHandle MH_exclaim; 236 static final MethodHandle MH_print; 237 static final MethodHandle MH_printMore; 238 static final MethodHandle MH_greetMore; 239 static final MethodHandle MH_exclaimMore; 240 static final MethodHandle MH_voidTarget; 241 static final MethodHandle MH_voidCleanup; 242 static final MethodHandle MH_throwingTarget; 243 static final MethodHandle MH_throwingTargetIdentity; 244 static final MethodHandle MH_catchingCleanup; 245 246 static final MethodHandle MH_dummyTarget; 247 248 static final MethodType MT_hello = methodType(String.class, String.class); 249 static final MethodType MT_printHello = methodType(void.class, String.class); 250 static final MethodType MT_moreHello = methodType(String.class, String.class, String.class); 251 252 static { 253 try { 254 MH_greet = LOOKUP.findStatic(TRY_FINALLY, "greet", MT_greet); 255 MH_exclaim = LOOKUP.findStatic(TRY_FINALLY, "exclaim", MT_exclaim); 256 MH_print = LOOKUP.findStatic(TRY_FINALLY, "print", MT_print); 257 MH_printMore = LOOKUP.findStatic(TRY_FINALLY, "printMore", MT_printMore); 258 MH_greetMore = LOOKUP.findStatic(TRY_FINALLY, "greetMore", MT_greetMore); 259 MH_exclaimMore = LOOKUP.findStatic(TRY_FINALLY, "exclaimMore", MT_exclaimMore); 260 MH_voidTarget = LOOKUP.findStatic(TRY_FINALLY, "voidTarget", MT_voidTarget); 261 MH_voidCleanup = LOOKUP.findStatic(TRY_FINALLY, "voidCleanup", MT_voidCleanup); 262 MH_throwingTarget = LOOKUP.findStatic(TRY_FINALLY, "throwingTarget", MT_throwingTarget); 263 MH_throwingTargetIdentity = LOOKUP.findStatic(TRY_FINALLY, "throwingTargetIdentity", MT_throwingTargetIdentity); 264 MH_catchingCleanup = LOOKUP.findStatic(TRY_FINALLY, "catchingCleanup", MT_catchingCleanup); 265 MH_dummyTarget = MethodHandles.dropArguments(MH_voidTarget, 0, int.class, long.class, Object.class, 266 int.class, long.class, Object.class); 267 } catch (Exception e) { 268 throw new ExceptionInInitializerError(e); 269 } 270 } 271 272 } 273 274 } 275