PrivateInvokeTest.java revision 16177:89ef4b822745
120253Sjoerg/* 220302Sjoerg * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved. 320302Sjoerg * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 420253Sjoerg * 520253Sjoerg * This code is free software; you can redistribute it and/or modify it 620253Sjoerg * under the terms of the GNU General Public License version 2 only, as 720253Sjoerg * published by the Free Software Foundation. 820253Sjoerg * 920302Sjoerg * This code is distributed in the hope that it will be useful, but WITHOUT 1020253Sjoerg * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1120253Sjoerg * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1220253Sjoerg * version 2 for more details (a copy is included in the LICENSE file that 1320253Sjoerg * accompanied this code). 1420302Sjoerg * 1520253Sjoerg * You should have received a copy of the GNU General Public License version 1620253Sjoerg * 2 along with this work; if not, write to the Free Software Foundation, 1720302Sjoerg * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1820253Sjoerg * 1920253Sjoerg * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2020253Sjoerg * or visit www.oracle.com if you need additional information or have any 2120253Sjoerg * questions. 2220253Sjoerg */ 2320253Sjoerg 2420253Sjoerg/* @test 2544229Sdavidn * @summary white-box testing of method handle sub-primitives 2620253Sjoerg * @modules java.base/java.lang.invoke:open 2720253Sjoerg * @run junit test.java.lang.invoke.PrivateInvokeTest 2830259Scharnier */ 2930259Scharnier 3050479Speterpackage test.java.lang.invoke; 3130259Scharnier 3230259Scharnierimport java.lang.invoke.*; 3330259Scharnierimport static java.lang.invoke.MethodHandles.*; 3430259Scharnierimport static java.lang.invoke.MethodType.*; 3520253Sjoergimport java.lang.reflect.*; 3620253Sjoergimport java.util.ArrayList; 3720253Sjoergimport java.util.Arrays; 3830259Scharnierimport org.junit.*; 3920253Sjoergimport static org.junit.Assert.*; 4020555Sdavidn 4120555Sdavidnpublic class PrivateInvokeTest { 4220555Sdavidn // Utility functions 4330259Scharnier private static final Lookup LOOKUP = lookup(); 4464918Sgreen private static final Class<?> THIS_CLASS = PrivateInvokeTest.class; 4520253Sjoerg private static final int 4620253Sjoerg REF_NONE = 0, // null value 4720253Sjoerg REF_getField = 1, 4823318Sache REF_getStatic = 2, 4922394Sdavidn REF_putField = 3, 5052512Sdavidn REF_putStatic = 4, 5124214Sache REF_invokeVirtual = 5, 5244386Sdavidn REF_invokeStatic = 6, 5320253Sjoerg REF_invokeSpecial = 7, 5420253Sjoerg REF_newInvokeSpecial = 8, 5520253Sjoerg REF_invokeInterface = 9, 5620253Sjoerg REF_LIMIT = 10, 5720253Sjoerg REF_MH_invokeBasic = REF_NONE;; 5820253Sjoerg private static final String[] REF_KIND_NAMES = { 5920253Sjoerg "MH::invokeBasic", 6020253Sjoerg "REF_getField", "REF_getStatic", "REF_putField", "REF_putStatic", 6120253Sjoerg "REF_invokeVirtual", "REF_invokeStatic", "REF_invokeSpecial", 6285145Sache "REF_newInvokeSpecial", "REF_invokeInterface" 6320253Sjoerg }; 6420253Sjoerg private int verbose; 6520253Sjoerg //{ verbose = 99; } // for debugging 6620253Sjoerg { 6720253Sjoerg String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".verbose"); 6820253Sjoerg if (vstr == null) 6920253Sjoerg vstr = System.getProperty(THIS_CLASS.getName()+".verbose"); 7020253Sjoerg if (vstr == null) 7120253Sjoerg vstr = System.getProperty("test.verbose"); 7220253Sjoerg if (vstr != null) verbose = Integer.parseInt(vstr); 7320253Sjoerg } 7420253Sjoerg private static int referenceKind(Method m) { 7520253Sjoerg if (Modifier.isStatic(m.getModifiers())) 7620253Sjoerg return REF_invokeStatic; 7720253Sjoerg else if (m.getDeclaringClass().isInterface()) 7820253Sjoerg return REF_invokeInterface; 7920253Sjoerg else if (Modifier.isFinal(m.getModifiers()) || 8020253Sjoerg Modifier.isFinal(m.getDeclaringClass().getModifiers())) 81124382Siedowse return REF_invokeSpecial; 8220253Sjoerg else 8320253Sjoerg return REF_invokeVirtual; 8420253Sjoerg } 8520253Sjoerg private static MethodType basicType(MethodType mtype) { 8620253Sjoerg MethodType btype = mtype.erase(); 8720253Sjoerg if (btype.hasPrimitives()) { 8820253Sjoerg for (int i = -1; i < mtype.parameterCount(); i++) { 8920253Sjoerg Class<?> type = (i < 0 ? mtype.returnType() : mtype.parameterType(i)); 9020253Sjoerg if (type == boolean.class || 9120253Sjoerg type == byte.class || 9220253Sjoerg type == char.class || 9320253Sjoerg type == short.class) { 9420253Sjoerg type = int.class; 9520253Sjoerg if (i < 0) 9620253Sjoerg btype = btype.changeReturnType(type); 9720253Sjoerg else 9820253Sjoerg btype = btype.changeParameterType(i, type); 9952527Sdavidn } 10020253Sjoerg } 10152512Sdavidn } 10220253Sjoerg return btype; 10320253Sjoerg } 10420253Sjoerg private static Method getMethod(Class<?> defc, String name, Class<?>... ptypes) { 10520253Sjoerg try { 10620253Sjoerg return defc.getDeclaredMethod(name, ptypes); 10720253Sjoerg } catch (NoSuchMethodException ex) { 10820747Sdavidn } 10982868Sdd try { 110167919Sle return defc.getMethod(name, ptypes); 111167919Sle } catch (NoSuchMethodException ex) { 11220253Sjoerg throw new IllegalArgumentException(ex); 11320253Sjoerg } 11420253Sjoerg } 11520253Sjoerg private static MethodHandle unreflect(Method m) { 11620253Sjoerg try { 11720253Sjoerg MethodHandle mh = LOOKUP.unreflect(m); 11820253Sjoerg if (Modifier.isTransient(m.getModifiers())) 11920253Sjoerg mh = mh.asFixedArity(); // remove varargs wrapper 12020253Sjoerg return mh; 12120253Sjoerg } catch (IllegalAccessException ex) { 12256000Sdavidn throw new IllegalArgumentException(ex); 12320253Sjoerg } 12420253Sjoerg } 12556000Sdavidn private static final Lookup DIRECT_INVOKER_LOOKUP; 12656000Sdavidn private static final Class<?> MEMBER_NAME_CLASS; 12756000Sdavidn private static final MethodHandle MH_INTERNAL_MEMBER_NAME; 12820253Sjoerg private static final MethodHandle MH_DEBUG_STRING; 12920253Sjoerg static { 13052512Sdavidn try { 13120253Sjoerg // This is white box testing. Use reflection to grab private implementation bits. 13220267Sjoerg String magicName = "IMPL_LOOKUP"; 13320267Sjoerg Field magicLookup = MethodHandles.Lookup.class.getDeclaredField(magicName); 13420267Sjoerg // This unit test will fail if a security manager is installed. 13520267Sjoerg magicLookup.setAccessible(true); 13620267Sjoerg // Forbidden fruit... 13720267Sjoerg DIRECT_INVOKER_LOOKUP = (Lookup) magicLookup.get(null); 13820267Sjoerg MEMBER_NAME_CLASS = Class.forName("java.lang.invoke.MemberName", false, MethodHandle.class.getClassLoader()); 13920267Sjoerg MH_INTERNAL_MEMBER_NAME = DIRECT_INVOKER_LOOKUP 14020267Sjoerg .findVirtual(MethodHandle.class, "internalMemberName", methodType(MEMBER_NAME_CLASS)) 14120267Sjoerg .asType(methodType(Object.class, MethodHandle.class)); 14220267Sjoerg MH_DEBUG_STRING = DIRECT_INVOKER_LOOKUP 14320267Sjoerg .findVirtual(MethodHandle.class, "debugString", methodType(String.class)); 14420267Sjoerg } catch (ReflectiveOperationException ex) { 14520267Sjoerg throw new Error(ex); 14620253Sjoerg } 14720253Sjoerg } 14820253Sjoerg private Object internalMemberName(MethodHandle mh) { 14920253Sjoerg try { 15020267Sjoerg return MH_INTERNAL_MEMBER_NAME.invokeExact(mh); 15120253Sjoerg } catch (Throwable ex) { 15221052Sdavidn throw new Error(ex); 153167919Sle } 154167919Sle } 155167919Sle private String debugString(MethodHandle mh) { 156167919Sle try { 157167919Sle return (String) MH_DEBUG_STRING.invokeExact(mh); 158219408Sjkim } catch (Throwable ex) { 159167919Sle throw new Error(ex); 160168044Sle } 161167919Sle } 16221052Sdavidn private static MethodHandle directInvoker(int refKind, MethodType mtype) { 16321052Sdavidn return directInvoker(REF_KIND_NAMES[refKind], mtype); 16421052Sdavidn } 16521052Sdavidn private static MethodHandle directInvoker(String name, MethodType mtype) { 166224535Sdelphij boolean isStatic; 16721052Sdavidn mtype = mtype.erase(); 16821052Sdavidn if (name.startsWith("MH::")) { 16921052Sdavidn isStatic = false; 17021052Sdavidn name = strip("MH::", name); 17121052Sdavidn } else if (name.startsWith("REF_")) { 17221052Sdavidn isStatic = true; 17330259Scharnier name = strip("REF_", name); 17421052Sdavidn if (name.startsWith("invoke")) 17521052Sdavidn name = "linkTo"+strip("invoke", name); 17621052Sdavidn mtype = mtype.appendParameterTypes(MEMBER_NAME_CLASS); 17721052Sdavidn } else { 17821242Sdavidn throw new AssertionError("name="+name); 17921242Sdavidn } 18021242Sdavidn //System.out.println("directInvoker = "+name+mtype); 18121242Sdavidn try { 18221242Sdavidn if (isStatic) 18321242Sdavidn return DIRECT_INVOKER_LOOKUP 18421242Sdavidn .findStatic(MethodHandle.class, name, mtype); 18521242Sdavidn else 18621242Sdavidn return DIRECT_INVOKER_LOOKUP 187219408Sjkim .findVirtual(MethodHandle.class, name, mtype); 18821242Sdavidn } catch (ReflectiveOperationException ex) { 189148584Spjd throw new IllegalArgumentException(ex); 190148584Spjd } 191148584Spjd } 192148584Spjd private Object invokeWithArguments(Method m, Object... args) { 193148584Spjd Object recv = null; 19421242Sdavidn if (!Modifier.isStatic(m.getModifiers())) { 19521242Sdavidn recv = args[0]; 19621242Sdavidn args = pop(1, args); 197130633Srobert } 198130633Srobert try { 19921242Sdavidn return m.invoke(recv, args); 20021242Sdavidn } catch (IllegalAccessException|IllegalArgumentException|InvocationTargetException ex) { 20121242Sdavidn throw new IllegalArgumentException(ex); 20221242Sdavidn } 203219408Sjkim } 20421242Sdavidn private Object invokeWithArguments(MethodHandle mh, Object... args) { 20521242Sdavidn try { 20621242Sdavidn return mh.invokeWithArguments(args); 20730259Scharnier } catch (Throwable ex) { 20821242Sdavidn throw new IllegalArgumentException(ex); 20921242Sdavidn } 21021052Sdavidn } 21121242Sdavidn private int counter; 212219408Sjkim private Object makeArgument(Class<?> type) { 21330259Scharnier final String cname = type.getSimpleName(); 21421052Sdavidn final int n = ++counter; 21521052Sdavidn final int nn = (n << 10) + 13; 21621052Sdavidn if (type.isAssignableFrom(String.class)) { 21721052Sdavidn return "<"+cname+"#"+nn+">"; 21830259Scharnier } 21921052Sdavidn if (type == THIS_CLASS) return this.withCounter(nn); 22021052Sdavidn if (type == Integer.class || type == int.class) return nn; 22120253Sjoerg if (type == Character.class || type == char.class) return (char)(n % 100+' '); 22220253Sjoerg if (type == Byte.class || type == byte.class) return (byte)-(n % 100); 22320253Sjoerg if (type == Long.class || type == long.class) return (long)nn; 22421330Sdavidn throw new IllegalArgumentException("don't know how to make argument of type: "+type); 22521330Sdavidn } 22621330Sdavidn private Object[] makeArguments(Class<?>... ptypes) { 22720253Sjoerg Object[] args = new Object[ptypes.length]; 22820253Sjoerg for (int i = 0; i < args.length; i++) 22920253Sjoerg args[i] = makeArgument(ptypes[i]); 23020253Sjoerg return args; 23163596Sdavidn } 23263596Sdavidn private Object[] makeArguments(MethodType mtype) { 23363596Sdavidn return makeArguments(mtype.parameterArray()); 23463596Sdavidn } 23563596Sdavidn private Object[] pop(int n, Object[] args) { 23663596Sdavidn if (n >= 0) 23763596Sdavidn return Arrays.copyOfRange(args, n, args.length); 23863596Sdavidn else 23920253Sjoerg return Arrays.copyOfRange(args, 0, args.length+n); 24020253Sjoerg } 24120253Sjoerg private Object[] pushAtFront(Object arg1, Object[] args) { 24220679Sdavidn Object[] res = new Object[1+args.length]; 24320253Sjoerg res[0] = arg1; 24420253Sjoerg System.arraycopy(args, 0, res, 1, args.length); 24552527Sdavidn return res; 24620253Sjoerg } 24720747Sdavidn private Object[] pushAtBack(Object[] args, Object argN) { 24844229Sdavidn Object[] res = new Object[1+args.length]; 24961957Sache System.arraycopy(args, 0, res, 0, args.length); 25030259Scharnier res[args.length] = argN; 25120253Sjoerg return res; 25220747Sdavidn } 25320747Sdavidn private static String strip(String prefix, String s) { 25420253Sjoerg assert(s.startsWith(prefix)); 25520747Sdavidn return s.substring(prefix.length()); 25620253Sjoerg } 25720253Sjoerg 25852527Sdavidn private final int[] refKindTestCounts = new int[REF_KIND_NAMES.length]; 25920253Sjoerg @After 26026088Sdavidn public void printCounts() { 26130259Scharnier ArrayList<String> zeroes = new ArrayList<>(); 26220253Sjoerg for (int i = 0; i < refKindTestCounts.length; i++) { 26352527Sdavidn final int count = refKindTestCounts[i]; 26420253Sjoerg final String name = REF_KIND_NAMES[i]; 26520253Sjoerg if (count == 0) { 26620253Sjoerg if (name != null) zeroes.add(name); 26763600Sdavidn continue; 26863600Sdavidn } 26920253Sjoerg if (verbose >= 0) 27020253Sjoerg System.out.println("test count for "+name+" : "+count); 27130259Scharnier else if (name != null) 27220253Sjoerg zeroes.add(name); 27320253Sjoerg } 27420253Sjoerg if (verbose >= 0) 27520253Sjoerg System.out.println("test counts zero for "+zeroes); 27620253Sjoerg } 27720253Sjoerg 27820253Sjoerg // Test subjects 27920253Sjoerg public static String makeString(Object x) { return "makeString("+x+")"; } 28020253Sjoerg public static String dupString(String x) { return "("+x+"+"+x+")"; } 28120253Sjoerg public static String intString(int x) { return "intString("+x+")"; } 28220253Sjoerg public static String byteString(byte x) { return "byteString("+x+")"; } 28320253Sjoerg public static String longString(String x, long y, String z) { return "longString("+x+y+z+")"; } 28420253Sjoerg 28520253Sjoerg public final String toString() { 28620253Sjoerg return "<"+getClass().getSimpleName()+"#"+counter+">"; 28720267Sjoerg } 28830259Scharnier public final String hello() { return "hello from "+this; } 28920267Sjoerg private PrivateInvokeTest withCounter(int counter) { 29020253Sjoerg PrivateInvokeTest res = new PrivateInvokeTest(); 29152527Sdavidn res.counter = counter; 29220253Sjoerg return res; 29320267Sjoerg } 29444386Sdavidn 29520253Sjoerg public static void main(String... av) throws Throwable { 29644229Sdavidn new PrivateInvokeTest().run(); 29744229Sdavidn } 29844386Sdavidn public void run() throws Throwable { 29944229Sdavidn testFirst(); 30020267Sjoerg testInvokeDirect(); 30120253Sjoerg } 30252527Sdavidn 30320253Sjoerg @Test 30444229Sdavidn public void testFirst() throws Throwable { 30520253Sjoerg if (true) return; // nothing here 30620253Sjoerg try { 30720253Sjoerg System.out.println("start of testFirst"); 30820253Sjoerg } finally { 30930259Scharnier System.out.println("end of testFirst"); 31020253Sjoerg } 31120253Sjoerg } 31220253Sjoerg 31320253Sjoerg @Test 31420253Sjoerg public void testInvokeDirect() { 31520253Sjoerg testInvokeDirect(getMethod(THIS_CLASS, "hello")); 31643780Sdes testInvokeDirect(getMethod(Object.class, "toString")); 31743780Sdes testInvokeDirect(getMethod(Comparable.class, "compareTo", Object.class)); 31843780Sdes testInvokeDirect(getMethod(THIS_CLASS, "makeString", Object.class)); 31920253Sjoerg testInvokeDirect(getMethod(THIS_CLASS, "dupString", String.class)); 32020253Sjoerg testInvokeDirect(getMethod(THIS_CLASS, "intString", int.class)); 32120253Sjoerg testInvokeDirect(getMethod(THIS_CLASS, "byteString", byte.class)); 32220253Sjoerg testInvokeDirect(getMethod(THIS_CLASS, "longString", String.class, long.class, String.class)); 32352527Sdavidn } 32420253Sjoerg 32520253Sjoerg void testInvokeDirect(Method m) { 32620253Sjoerg final int refKind = referenceKind(m); 32752512Sdavidn testInvokeDirect(m, refKind); 32852512Sdavidn testInvokeDirect(m, REF_MH_invokeBasic); 32952527Sdavidn } 33020253Sjoerg void testInvokeDirect(Method m, int refKind) { 33144229Sdavidn if (verbose >= 1) 33220253Sjoerg System.out.println("testInvoke m="+m+" : "+REF_KIND_NAMES[refKind]); 33320253Sjoerg final MethodHandle mh = unreflect(m); 33420253Sjoerg Object[] args = makeArguments(mh.type()); 33520253Sjoerg Object res1 = invokeWithArguments(m, args); 33620253Sjoerg // res1 comes from java.lang.reflect.Method::invoke 33744386Sdavidn if (verbose >= 1) 33844386Sdavidn System.out.println("m"+Arrays.asList(args)+" => "+res1); 33944386Sdavidn // res2 comes from java.lang.invoke.MethodHandle::invoke 34020253Sjoerg Object res2 = invokeWithArguments(mh, args); 34120253Sjoerg assertEquals(res1, res2); 34230259Scharnier MethodType mtype = mh.type(); 34330259Scharnier testInvokeVia("DMH invoker", refKind, directInvoker(refKind, mtype), mh, res1, args); 34420253Sjoerg MethodType etype = mtype.erase(); 34552527Sdavidn if (etype != mtype) { 34620253Sjoerg // Try a detuned invoker. 34720253Sjoerg testInvokeVia("erased DMH invoker", refKind, directInvoker(refKind, etype), mh, res1, args); 34820253Sjoerg } 34920253Sjoerg MethodType btype = basicType(mtype); 35052512Sdavidn if (btype != mtype && btype != etype) { 35152512Sdavidn // Try a detuned invoker. 35252512Sdavidn testInvokeVia("basic DMH invoker", refKind, directInvoker(refKind, btype), mh, res1, args); 35352512Sdavidn } 35452512Sdavidn if (false) { 35552512Sdavidn // this can crash the JVM 35652512Sdavidn testInvokeVia("generic DMH invoker", refKind, directInvoker(refKind, mtype.generic()), mh, res1, args); 35752512Sdavidn } 35852512Sdavidn refKindTestCounts[refKind] += 1; 35952512Sdavidn } 36052512Sdavidn 36152512Sdavidn void testInvokeVia(String kind, int refKind, MethodHandle invoker, MethodHandle mh, Object res1, Object... args) { 36252512Sdavidn Object[] args1; 36352512Sdavidn if (refKind == REF_MH_invokeBasic) 36452512Sdavidn args1 = pushAtFront(mh, args); 36552512Sdavidn else 36652512Sdavidn args1 = pushAtBack(args, internalMemberName(mh)); 36752512Sdavidn if (verbose >= 2) { 36852527Sdavidn System.out.println(kind+" invoker="+invoker+" mh="+debugString(mh)+" args="+Arrays.asList(args1)); 36952512Sdavidn } 37052512Sdavidn Object res3 = invokeWithArguments(invoker, args1); 37152512Sdavidn assertEquals(res1, res3); 37252512Sdavidn } 37352527Sdavidn} 37452527Sdavidn