/* * Copyright (c) 2011, 2012, 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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); } }