| /* |
| * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* @test |
| * @summary unit tests for method handles which permute their arguments |
| * @run testng test.java.lang.invoke.ThrowExceptionsTest |
| */ |
| |
| package test.java.lang.invoke; |
| |
| import org.testng.*; |
| import org.testng.annotations.*; |
| |
| import java.util.*; |
| import java.lang.reflect.*; |
| |
| import java.lang.invoke.*; |
| import static java.lang.invoke.MethodHandles.*; |
| import static java.lang.invoke.MethodType.*; |
| |
| public class ThrowExceptionsTest { |
| private static final Class<?> CLASS = ThrowExceptionsTest.class; |
| private static final Lookup LOOKUP = lookup(); |
| |
| public static void main(String argv[]) throws Throwable { |
| new ThrowExceptionsTest().testAll((argv.length == 0 ? null : Arrays.asList(argv).toString())); |
| } |
| |
| @Test |
| public void testWMT() throws Throwable { |
| // mostly call testWMTCallee, but sometimes call its void-returning variant |
| MethodHandle mh = testWMTCallee(); |
| MethodHandle mh1 = mh.asType(mh.type().changeReturnType(void.class)); |
| assert(mh1 != mh); |
| testWMT(mh, mh1, 1000); |
| } |
| |
| @Test |
| public void testBoundWMT() throws Throwable { |
| // mostly call exactInvoker.bindTo(testWMTCallee), but sometimes call its void-returning variant |
| MethodHandle callee = testWMTCallee(); |
| MethodHandle callee1 = callee.asType(callee.type().changeReturnType(void.class)); |
| MethodHandle invoker = exactInvoker(callee.type()); |
| MethodHandle mh = invoker.bindTo(callee); |
| MethodHandle mh1 = invoker.bindTo(callee1); |
| testWMT(mh, mh1, 1000); |
| } |
| |
| @Test |
| public void testFoldWMT() throws Throwable { |
| // mostly call exactInvoker.fold(constant(testWMTCallee)), but sometimes call its void-returning variant |
| MethodHandle callee = testWMTCallee(); |
| MethodHandle callee1 = callee.asType(callee.type().changeReturnType(void.class)); |
| MethodHandle invoker = exactInvoker(callee.type()); |
| MethodHandle mh = foldArguments(invoker, constant(MethodHandle.class, callee)); |
| MethodHandle mh1 = foldArguments(invoker, constant(MethodHandle.class, callee1)); |
| testWMT(mh, mh1, 1000); |
| } |
| |
| @Test |
| public void testFoldCCE() throws Throwable { |
| MethodHandle callee = testWMTCallee(); |
| MethodHandle callee1 = callee.asType(callee.type().changeParameterType(1, Number.class)).asType(callee.type()); |
| MethodHandle invoker = exactInvoker(callee.type()); |
| MethodHandle mh = foldArguments(invoker, constant(MethodHandle.class, callee)); |
| MethodHandle mh1 = foldArguments(invoker, constant(MethodHandle.class, callee1)); |
| testWMT(mh, mh1, 1000); |
| } |
| |
| @Test |
| public void testStackOverflow() throws Throwable { |
| MethodHandle callee = testWMTCallee(); |
| MethodHandle callee1 = makeStackOverflow().asType(callee.type()); |
| MethodHandle invoker = exactInvoker(callee.type()); |
| MethodHandle mh = foldArguments(invoker, constant(MethodHandle.class, callee)); |
| MethodHandle mh1 = foldArguments(invoker, constant(MethodHandle.class, callee1)); |
| for (int i = 0; i < REPEAT; i++) { |
| try { |
| testWMT(mh, mh1, 1000); |
| } catch (StackOverflowError ex) { |
| // OK, try again |
| } |
| } |
| } |
| |
| private static MethodHandle makeStackOverflow() { |
| MethodType cellType = methodType(void.class); |
| MethodHandle[] cell = { null }; // recursion point |
| MethodHandle getCell = insertArguments(arrayElementGetter(cell.getClass()), 0, cell, 0); |
| MethodHandle invokeCell = foldArguments(exactInvoker(cellType), getCell); |
| assert(invokeCell.type() == cellType); |
| cell[0] = invokeCell; |
| // make it conformable to any type: |
| invokeCell = dropArguments(invokeCell, 0, Object[].class).asVarargsCollector(Object[].class); |
| return invokeCell; |
| } |
| |
| static int testCases; |
| |
| private void testAll(String match) throws Throwable { |
| testCases = 0; |
| Lookup lookup = lookup(); |
| for (Method m : CLASS.getDeclaredMethods()) { |
| String name = m.getName(); |
| if (name.startsWith("test") && |
| (match == null || match.contains(name.substring("test".length()))) && |
| m.getParameterTypes().length == 0 && |
| Modifier.isPublic(m.getModifiers()) && |
| !Modifier.isStatic(m.getModifiers())) { |
| System.out.println("["+name+"]"); |
| int tc = testCases; |
| try { |
| m.invoke(this); |
| } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { |
| System.out.println("*** "+ex); |
| ex.printStackTrace(System.out); |
| } |
| if (testCases == tc) testCases++; |
| } |
| } |
| if (testCases == 0) throw new RuntimeException("no test cases found"); |
| System.out.println("ran a total of "+testCases+" test cases"); |
| } |
| |
| private static MethodHandle findStatic(String name) { |
| return findMethod(name, true); |
| } |
| private static MethodHandle findVirtual(String name) { |
| return findMethod(name, false); |
| } |
| private static MethodHandle findMethod(String name, boolean isStatic) { |
| MethodHandle mh = null; |
| for (Method m : CLASS.getDeclaredMethods()) { |
| if (m.getName().equals(name) && |
| Modifier.isStatic(m.getModifiers()) == isStatic) { |
| if (mh != null) |
| throw new RuntimeException("duplicate methods: "+name); |
| try { |
| mh = LOOKUP.unreflect(m); |
| } catch (ReflectiveOperationException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| } |
| if (mh == null) |
| throw new RuntimeException("no method: "+name); |
| return mh; |
| } |
| |
| int testWMTCallee; |
| private int testWMTCallee(String x) { |
| return testWMTCallee++; |
| } |
| private static MethodHandle testWMTCallee() { |
| MethodHandle callee = findVirtual("testWMTCallee"); |
| // FIXME: should not have to retype callee |
| callee = callee.asType(callee.type().changeParameterType(0, Object.class)); |
| return callee; |
| } |
| |
| private Exception testWMT(MethodHandle[] mhs, int reps) throws Throwable { |
| testCases += 1; |
| testWMTCallee = 0; |
| int catches = 0; |
| Exception savedEx = null; |
| for (int i = 0; i < reps; i++) { |
| MethodHandle mh = mhs[i % mhs.length]; |
| int n; |
| try { |
| // FIXME: should not have to retype this |
| n = (int) mh.invokeExact((Object)this, "x"); |
| assertEquals(n, i - catches); |
| // Using the exact type for this causes endless deopt due to |
| // 'non_cached_result' in SystemDictionary::find_method_handle_invoke. |
| // The problem is that the compiler thread needs to access a cached |
| // invoke method, but invoke methods are not cached if one of the |
| // component types is not on the BCP. |
| } catch (Exception ex) { |
| savedEx = ex; |
| catches++; |
| } |
| } |
| //VERBOSE: System.out.println("reps="+reps+" catches="+catches); |
| return savedEx; |
| } |
| |
| private static final int REPEAT = Integer.getInteger(CLASS.getSimpleName()+".REPEAT", 10); |
| |
| private Exception testWMT(MethodHandle mh, MethodHandle mh1, int reps) throws Throwable { |
| //VERBOSE: System.out.println("mh="+mh+" mh1="+mh1); |
| MethodHandle[] mhs = new MethodHandle[100]; |
| Arrays.fill(mhs, mh); |
| int patch = mhs.length-1; |
| Exception savedEx = null; |
| for (int i = 0; i < REPEAT; i++) { |
| mhs[patch] = mh; |
| testWMT(mhs, 10000); |
| mhs[patch] = mh1; |
| savedEx = testWMT(mhs, reps); |
| } |
| return savedEx; |
| } |
| |
| private static void assertEquals(Object x, Object y) { |
| if (x == y || x != null && x.equals(y)) return; |
| throw new RuntimeException(x+" != "+y); |
| } |
| } |