ThrowExceptionsTest.java revision 8729:0242fce0f717
1/*
2 * Copyright (c) 2011, 2012, 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/* @test
25 * @summary unit tests for method handles which permute their arguments
26 * @run testng test.java.lang.invoke.ThrowExceptionsTest
27 */
28
29package test.java.lang.invoke;
30
31import org.testng.*;
32import org.testng.annotations.*;
33
34import java.util.*;
35import java.lang.reflect.*;
36
37import java.lang.invoke.*;
38import static java.lang.invoke.MethodHandles.*;
39import static java.lang.invoke.MethodType.*;
40
41public class ThrowExceptionsTest {
42    private static final Class<?> CLASS = ThrowExceptionsTest.class;
43    private static final Lookup LOOKUP = lookup();
44
45    public static void main(String argv[]) throws Throwable {
46        new ThrowExceptionsTest().testAll((argv.length == 0 ? null : Arrays.asList(argv).toString()));
47    }
48
49    @Test
50    public void testWMT() throws Throwable {
51        // mostly call testWMTCallee, but sometimes call its void-returning variant
52        MethodHandle mh = testWMTCallee();
53        MethodHandle mh1 = mh.asType(mh.type().changeReturnType(void.class));
54        assert(mh1 != mh);
55        testWMT(mh, mh1, 1000);
56    }
57
58    @Test
59    public void testBoundWMT() throws Throwable {
60        // mostly call exactInvoker.bindTo(testWMTCallee), but sometimes call its void-returning variant
61        MethodHandle callee = testWMTCallee();
62        MethodHandle callee1 = callee.asType(callee.type().changeReturnType(void.class));
63        MethodHandle invoker = exactInvoker(callee.type());
64        MethodHandle mh  = invoker.bindTo(callee);
65        MethodHandle mh1 = invoker.bindTo(callee1);
66        testWMT(mh, mh1, 1000);
67    }
68
69    @Test
70    public void testFoldWMT() throws Throwable {
71        // mostly call exactInvoker.fold(constant(testWMTCallee)), but sometimes call its void-returning variant
72        MethodHandle callee = testWMTCallee();
73        MethodHandle callee1 = callee.asType(callee.type().changeReturnType(void.class));
74        MethodHandle invoker = exactInvoker(callee.type());
75        MethodHandle mh  = foldArguments(invoker, constant(MethodHandle.class, callee));
76        MethodHandle mh1 = foldArguments(invoker, constant(MethodHandle.class, callee1));
77        testWMT(mh, mh1, 1000);
78    }
79
80    @Test
81    public void testFoldCCE() throws Throwable {
82        MethodHandle callee = testWMTCallee();
83        MethodHandle callee1 = callee.asType(callee.type().changeParameterType(1, Number.class)).asType(callee.type());
84        MethodHandle invoker = exactInvoker(callee.type());
85        MethodHandle mh  = foldArguments(invoker, constant(MethodHandle.class, callee));
86        MethodHandle mh1 = foldArguments(invoker, constant(MethodHandle.class, callee1));
87        testWMT(mh, mh1, 1000);
88    }
89
90    @Test
91    public void testStackOverflow() throws Throwable {
92        MethodHandle callee = testWMTCallee();
93        MethodHandle callee1 = makeStackOverflow().asType(callee.type());
94        MethodHandle invoker = exactInvoker(callee.type());
95        MethodHandle mh  = foldArguments(invoker, constant(MethodHandle.class, callee));
96        MethodHandle mh1 = foldArguments(invoker, constant(MethodHandle.class, callee1));
97        for (int i = 0; i < REPEAT; i++) {
98            try {
99                testWMT(mh, mh1, 1000);
100            } catch (StackOverflowError ex) {
101                // OK, try again
102            }
103        }
104    }
105
106    private static MethodHandle makeStackOverflow() {
107        MethodType cellType = methodType(void.class);
108        MethodHandle[] cell = { null };  // recursion point
109        MethodHandle getCell = insertArguments(arrayElementGetter(cell.getClass()), 0, cell, 0);
110        MethodHandle invokeCell = foldArguments(exactInvoker(cellType), getCell);
111        assert(invokeCell.type() == cellType);
112        cell[0] = invokeCell;
113        // make it conformable to any type:
114        invokeCell = dropArguments(invokeCell, 0, Object[].class).asVarargsCollector(Object[].class);
115        return invokeCell;
116    }
117
118    static int testCases;
119
120    private void testAll(String match) throws Throwable {
121        testCases = 0;
122        Lookup lookup = lookup();
123        for (Method m : CLASS.getDeclaredMethods()) {
124            String name = m.getName();
125            if (name.startsWith("test") &&
126                (match == null || match.contains(name.substring("test".length()))) &&
127                m.getParameterTypes().length == 0 &&
128                Modifier.isPublic(m.getModifiers()) &&
129                !Modifier.isStatic(m.getModifiers())) {
130                System.out.println("["+name+"]");
131                int tc = testCases;
132                try {
133                    m.invoke(this);
134                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
135                    System.out.println("*** "+ex);
136                    ex.printStackTrace(System.out);
137                }
138                if (testCases == tc)  testCases++;
139            }
140        }
141        if (testCases == 0)  throw new RuntimeException("no test cases found");
142        System.out.println("ran a total of "+testCases+" test cases");
143    }
144
145    private static MethodHandle findStatic(String name) {
146        return findMethod(name, true);
147    }
148    private static MethodHandle findVirtual(String name) {
149        return findMethod(name, false);
150    }
151    private static MethodHandle findMethod(String name, boolean isStatic) {
152        MethodHandle mh = null;
153        for (Method m : CLASS.getDeclaredMethods()) {
154            if (m.getName().equals(name) &&
155                Modifier.isStatic(m.getModifiers()) == isStatic) {
156                if (mh != null)
157                    throw new RuntimeException("duplicate methods: "+name);
158                try {
159                    mh = LOOKUP.unreflect(m);
160                } catch (ReflectiveOperationException ex) {
161                    throw new RuntimeException(ex);
162                }
163            }
164        }
165        if (mh == null)
166            throw new RuntimeException("no method: "+name);
167        return mh;
168    }
169
170    int testWMTCallee;
171    private int testWMTCallee(String x) {
172        return testWMTCallee++;
173    }
174    private static MethodHandle testWMTCallee() {
175        MethodHandle callee = findVirtual("testWMTCallee");
176        // FIXME: should not have to retype callee
177        callee = callee.asType(callee.type().changeParameterType(0, Object.class));
178        return callee;
179    }
180
181    private Exception testWMT(MethodHandle[] mhs, int reps) throws Throwable {
182        testCases += 1;
183        testWMTCallee = 0;
184        int catches = 0;
185        Exception savedEx = null;
186        for (int i = 0; i < reps; i++) {
187            MethodHandle mh = mhs[i % mhs.length];
188            int n;
189            try {
190                // FIXME: should not have to retype this
191                n = (int) mh.invokeExact((Object)this, "x");
192                assertEquals(n, i - catches);
193                // Using the exact type for this causes endless deopt due to
194                // 'non_cached_result' in SystemDictionary::find_method_handle_invoke.
195                // The problem is that the compiler thread needs to access a cached
196                // invoke method, but invoke methods are not cached if one of the
197                // component types is not on the BCP.
198            } catch (Exception ex) {
199                savedEx = ex;
200                catches++;
201            }
202        }
203        //VERBOSE: System.out.println("reps="+reps+" catches="+catches);
204        return savedEx;
205    }
206
207    private static final int REPEAT = Integer.getInteger(CLASS.getSimpleName()+".REPEAT", 10);
208
209    private Exception testWMT(MethodHandle mh, MethodHandle mh1, int reps) throws Throwable {
210        //VERBOSE: System.out.println("mh="+mh+" mh1="+mh1);
211        MethodHandle[] mhs = new MethodHandle[100];
212        Arrays.fill(mhs, mh);
213        int patch = mhs.length-1;
214        Exception savedEx = null;
215        for (int i = 0; i < REPEAT; i++) {
216            mhs[patch] = mh;
217            testWMT(mhs, 10000);
218            mhs[patch] = mh1;
219            savedEx = testWMT(mhs, reps);
220        }
221        return savedEx;
222    }
223
224    private static void assertEquals(Object x, Object y) {
225        if (x == y || x != null && x.equals(y))  return;
226        throw new RuntimeException(x+" != "+y);
227    }
228}
229