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