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
28 * @bug 8150824
29 * @bug 8150825
30 * @run testng/othervm -ea -esa test.java.lang.invoke.TryFinallyTest
31 */
32
33package test.java.lang.invoke;
34
35import java.lang.invoke.MethodHandle;
36import java.lang.invoke.MethodHandles;
37import java.lang.invoke.MethodHandles.Lookup;
38import java.lang.invoke.MethodType;
39
40import static java.lang.invoke.MethodType.methodType;
41
42import static org.testng.AssertJUnit.*;
43
44import org.testng.annotations.*;
45
46/**
47 * Tests for the tryFinally method handle combinator introduced in JEP 274.
48 */
49public class TryFinallyTest {
50
51    static final Lookup LOOKUP = MethodHandles.lookup();
52
53    @Test
54    public static void testTryFinally() throws Throwable {
55        MethodHandle hello = MethodHandles.tryFinally(TryFinally.MH_greet, TryFinally.MH_exclaim);
56        assertEquals(TryFinally.MT_hello, hello.type());
57        assertEquals("Hello, world!", hello.invoke("world"));
58    }
59
60    @Test
61    public static void testTryFinallyVoid() throws Throwable {
62        MethodHandle tfVoid = MethodHandles.tryFinally(TryFinally.MH_print, TryFinally.MH_printMore);
63        assertEquals(TryFinally.MT_printHello, tfVoid.type());
64        tfVoid.invoke("world");
65    }
66
67    @Test
68    public static void testTryFinallySublist() throws Throwable {
69        MethodHandle helloMore = MethodHandles.tryFinally(TryFinally.MH_greetMore, TryFinally.MH_exclaimMore);
70        assertEquals(TryFinally.MT_moreHello, helloMore.type());
71        assertEquals("Hello, world and universe (but world first)!", helloMore.invoke("world", "universe"));
72    }
73
74    @DataProvider
75    static Object[][] omitTrailingArguments() {
76        MethodHandle c = TryFinally.MH_voidCleanup;
77        return new Object[][]{
78                {c},
79                {MethodHandles.dropArguments(c, 1, int.class)},
80                {MethodHandles.dropArguments(c, 1, int.class, long.class)},
81                {MethodHandles.dropArguments(c, 1, int.class, long.class, Object.class, int.class)},
82                {MethodHandles.dropArguments(c, 1, int.class, long.class, Object.class, int.class, long.class)}
83        };
84    }
85
86    @Test(dataProvider = "omitTrailingArguments")
87    public static void testTryFinallyOmitTrailingArguments(MethodHandle cleanup) throws Throwable {
88        MethodHandle tf = MethodHandles.tryFinally(TryFinally.MH_dummyTarget, cleanup);
89        tf.invoke(1, 2L, "a", 23, 42L, "b");
90    }
91
92    @DataProvider
93    static Object[][] negativeTestData() {
94        MethodHandle intid = MethodHandles.identity(int.class);
95        MethodHandle intco = MethodHandles.constant(int.class, 0);
96        MethodHandle errTarget = MethodHandles.dropArguments(intco, 0, int.class, double.class, String.class, int.class);
97        MethodHandle errCleanup = MethodHandles.dropArguments(MethodHandles.constant(int.class, 0), 0, Throwable.class,
98                int.class, double.class, Object.class);
99        MethodHandle voidTarget = TryFinally.MH_voidTarget;
100        MethodHandle voidICleanup = MethodHandles.dropArguments(TryFinally.MH_voidCleanup, 1, int.class);
101        return new Object[][]{
102                {intid, MethodHandles.identity(double.class),
103                        "target and return types must match: double != int"},
104                {intid, MethodHandles.dropArguments(intid, 0, String.class),
105                        "cleanup first argument and Throwable must match: (String,int)int != class java.lang.Throwable"},
106                {intid, MethodHandles.dropArguments(intid, 0, Throwable.class, double.class),
107                        "cleanup second argument and target return type must match: (Throwable,double,int)int != int"},
108                {errTarget, errCleanup,
109                        "cleanup parameters after (Throwable,result) and target parameter list prefix must match: " +
110                                errCleanup.type() + " != " + errTarget.type()},
111                {voidTarget, voidICleanup,
112                        "cleanup parameters after (Throwable,result) and target parameter list prefix must match: " +
113                                voidICleanup.type() + " != " + voidTarget.type()}
114        };
115    }
116
117    @Test(dataProvider = "negativeTestData")
118    public static void testTryFinallyNegative(MethodHandle target, MethodHandle cleanup, String expectedMessage) {
119        boolean caught = false;
120        try {
121            MethodHandles.tryFinally(target, cleanup);
122        } catch (IllegalArgumentException iae) {
123            assertEquals(expectedMessage, iae.getMessage());
124            caught = true;
125        }
126        assertTrue(caught);
127    }
128
129    static class TryFinally {
130
131        static String greet(String whom) {
132            return "Hello, " + whom;
133        }
134
135        static String exclaim(Throwable t, String r, String whom) {
136            return r + "!";
137        }
138
139        static void print(String what) {
140            System.out.print("Hello, " + what);
141        }
142
143        static void printMore(Throwable t, String what) {
144            System.out.println("!");
145        }
146
147        static String greetMore(String first, String second) {
148            return "Hello, " + first + " and " + second;
149        }
150
151        static String exclaimMore(Throwable t, String r, String first) {
152            return r + " (but " + first + " first)!";
153        }
154
155        static void voidTarget() {}
156
157        static void voidCleanup(Throwable t) {}
158
159        static final Class<TryFinally> TRY_FINALLY = TryFinally.class;
160
161        static final MethodType MT_greet = methodType(String.class, String.class);
162        static final MethodType MT_exclaim = methodType(String.class, Throwable.class, String.class, String.class);
163        static final MethodType MT_print = methodType(void.class, String.class);
164        static final MethodType MT_printMore = methodType(void.class, Throwable.class, String.class);
165        static final MethodType MT_greetMore = methodType(String.class, String.class, String.class);
166        static final MethodType MT_exclaimMore = methodType(String.class, Throwable.class, String.class, String.class);
167        static final MethodType MT_voidTarget = methodType(void.class);
168        static final MethodType MT_voidCleanup = methodType(void.class, Throwable.class);
169
170        static final MethodHandle MH_greet;
171        static final MethodHandle MH_exclaim;
172        static final MethodHandle MH_print;
173        static final MethodHandle MH_printMore;
174        static final MethodHandle MH_greetMore;
175        static final MethodHandle MH_exclaimMore;
176        static final MethodHandle MH_voidTarget;
177        static final MethodHandle MH_voidCleanup;
178
179        static final MethodHandle MH_dummyTarget;
180
181        static final MethodType MT_hello = methodType(String.class, String.class);
182        static final MethodType MT_printHello = methodType(void.class, String.class);
183        static final MethodType MT_moreHello = methodType(String.class, String.class, String.class);
184
185        static {
186            try {
187                MH_greet = LOOKUP.findStatic(TRY_FINALLY, "greet", MT_greet);
188                MH_exclaim = LOOKUP.findStatic(TRY_FINALLY, "exclaim", MT_exclaim);
189                MH_print = LOOKUP.findStatic(TRY_FINALLY, "print", MT_print);
190                MH_printMore = LOOKUP.findStatic(TRY_FINALLY, "printMore", MT_printMore);
191                MH_greetMore = LOOKUP.findStatic(TRY_FINALLY, "greetMore", MT_greetMore);
192                MH_exclaimMore = LOOKUP.findStatic(TRY_FINALLY, "exclaimMore", MT_exclaimMore);
193                MH_voidTarget = LOOKUP.findStatic(TRY_FINALLY, "voidTarget", MT_voidTarget);
194                MH_voidCleanup = LOOKUP.findStatic(TRY_FINALLY, "voidCleanup", MT_voidCleanup);
195                MH_dummyTarget = MethodHandles.dropArguments(MH_voidTarget, 0, int.class, long.class, Object.class,
196                        int.class, long.class, Object.class);
197            } catch (Exception e) {
198                throw new ExceptionInInitializerError(e);
199            }
200        }
201
202    }
203
204}
205