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