PrivateInvokeTest.java revision 8729:0242fce0f717
1/* 2 * Copyright (c) 2009, 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 white-box testing of method handle sub-primitives 26 * @run junit test.java.lang.invoke.PrivateInvokeTest 27 */ 28 29package test.java.lang.invoke; 30 31import java.lang.invoke.*; 32import static java.lang.invoke.MethodHandles.*; 33import static java.lang.invoke.MethodType.*; 34import java.lang.reflect.*; 35import java.util.ArrayList; 36import java.util.Arrays; 37import java.util.logging.Level; 38import java.util.logging.Logger; 39import org.junit.*; 40import static org.junit.Assert.*; 41 42public class PrivateInvokeTest { 43 // Utility functions 44 private static final Lookup LOOKUP = lookup(); 45 private static final Class<?> THIS_CLASS = PrivateInvokeTest.class; 46 private static final int 47 REF_NONE = 0, // null value 48 REF_getField = 1, 49 REF_getStatic = 2, 50 REF_putField = 3, 51 REF_putStatic = 4, 52 REF_invokeVirtual = 5, 53 REF_invokeStatic = 6, 54 REF_invokeSpecial = 7, 55 REF_newInvokeSpecial = 8, 56 REF_invokeInterface = 9, 57 REF_LIMIT = 10, 58 REF_MH_invokeBasic = REF_NONE;; 59 private static final String[] REF_KIND_NAMES = { 60 "MH::invokeBasic", 61 "REF_getField", "REF_getStatic", "REF_putField", "REF_putStatic", 62 "REF_invokeVirtual", "REF_invokeStatic", "REF_invokeSpecial", 63 "REF_newInvokeSpecial", "REF_invokeInterface" 64 }; 65 private int verbose; 66 //{ verbose = 99; } // for debugging 67 { 68 String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".verbose"); 69 if (vstr == null) 70 vstr = System.getProperty(THIS_CLASS.getName()+".verbose"); 71 if (vstr == null) 72 vstr = System.getProperty("test.verbose"); 73 if (vstr != null) verbose = Integer.parseInt(vstr); 74 } 75 private static int referenceKind(Method m) { 76 if (Modifier.isStatic(m.getModifiers())) 77 return REF_invokeStatic; 78 else if (m.getDeclaringClass().isInterface()) 79 return REF_invokeInterface; 80 else if (Modifier.isFinal(m.getModifiers()) || 81 Modifier.isFinal(m.getDeclaringClass().getModifiers())) 82 return REF_invokeSpecial; 83 else 84 return REF_invokeVirtual; 85 } 86 private static MethodType basicType(MethodType mtype) { 87 MethodType btype = mtype.erase(); 88 if (btype.hasPrimitives()) { 89 for (int i = -1; i < mtype.parameterCount(); i++) { 90 Class<?> type = (i < 0 ? mtype.returnType() : mtype.parameterType(i)); 91 if (type == boolean.class || 92 type == byte.class || 93 type == char.class || 94 type == short.class) { 95 type = int.class; 96 if (i < 0) 97 btype = btype.changeReturnType(type); 98 else 99 btype = btype.changeParameterType(i, type); 100 } 101 } 102 } 103 return btype; 104 } 105 private static Method getMethod(Class<?> defc, String name, Class<?>... ptypes) { 106 try { 107 return defc.getDeclaredMethod(name, ptypes); 108 } catch (NoSuchMethodException ex) { 109 } 110 try { 111 return defc.getMethod(name, ptypes); 112 } catch (NoSuchMethodException ex) { 113 throw new IllegalArgumentException(ex); 114 } 115 } 116 private static MethodHandle unreflect(Method m) { 117 try { 118 MethodHandle mh = LOOKUP.unreflect(m); 119 if (Modifier.isTransient(m.getModifiers())) 120 mh = mh.asFixedArity(); // remove varargs wrapper 121 return mh; 122 } catch (IllegalAccessException ex) { 123 throw new IllegalArgumentException(ex); 124 } 125 } 126 private static final Lookup DIRECT_INVOKER_LOOKUP; 127 private static final Class<?> MEMBER_NAME_CLASS; 128 private static final MethodHandle MH_INTERNAL_MEMBER_NAME; 129 private static final MethodHandle MH_DEBUG_STRING; 130 static { 131 try { 132 // This is white box testing. Use reflection to grab private implementation bits. 133 String magicName = "IMPL_LOOKUP"; 134 Field magicLookup = MethodHandles.Lookup.class.getDeclaredField(magicName); 135 // This unit test will fail if a security manager is installed. 136 magicLookup.setAccessible(true); 137 // Forbidden fruit... 138 DIRECT_INVOKER_LOOKUP = (Lookup) magicLookup.get(null); 139 MEMBER_NAME_CLASS = Class.forName("java.lang.invoke.MemberName", false, MethodHandle.class.getClassLoader()); 140 MH_INTERNAL_MEMBER_NAME = DIRECT_INVOKER_LOOKUP 141 .findVirtual(MethodHandle.class, "internalMemberName", methodType(MEMBER_NAME_CLASS)) 142 .asType(methodType(Object.class, MethodHandle.class)); 143 MH_DEBUG_STRING = DIRECT_INVOKER_LOOKUP 144 .findVirtual(MethodHandle.class, "debugString", methodType(String.class)); 145 } catch (ReflectiveOperationException ex) { 146 throw new Error(ex); 147 } 148 } 149 private Object internalMemberName(MethodHandle mh) { 150 try { 151 return MH_INTERNAL_MEMBER_NAME.invokeExact(mh); 152 } catch (Throwable ex) { 153 throw new Error(ex); 154 } 155 } 156 private String debugString(MethodHandle mh) { 157 try { 158 return (String) MH_DEBUG_STRING.invokeExact(mh); 159 } catch (Throwable ex) { 160 throw new Error(ex); 161 } 162 } 163 private static MethodHandle directInvoker(int refKind, MethodType mtype) { 164 return directInvoker(REF_KIND_NAMES[refKind], mtype); 165 } 166 private static MethodHandle directInvoker(String name, MethodType mtype) { 167 boolean isStatic; 168 mtype = mtype.erase(); 169 if (name.startsWith("MH::")) { 170 isStatic = false; 171 name = strip("MH::", name); 172 } else if (name.startsWith("REF_")) { 173 isStatic = true; 174 name = strip("REF_", name); 175 if (name.startsWith("invoke")) 176 name = "linkTo"+strip("invoke", name); 177 mtype = mtype.appendParameterTypes(MEMBER_NAME_CLASS); 178 } else { 179 throw new AssertionError("name="+name); 180 } 181 //System.out.println("directInvoker = "+name+mtype); 182 try { 183 if (isStatic) 184 return DIRECT_INVOKER_LOOKUP 185 .findStatic(MethodHandle.class, name, mtype); 186 else 187 return DIRECT_INVOKER_LOOKUP 188 .findVirtual(MethodHandle.class, name, mtype); 189 } catch (ReflectiveOperationException ex) { 190 throw new IllegalArgumentException(ex); 191 } 192 } 193 private Object invokeWithArguments(Method m, Object... args) { 194 Object recv = null; 195 if (!Modifier.isStatic(m.getModifiers())) { 196 recv = args[0]; 197 args = pop(1, args); 198 } 199 try { 200 return m.invoke(recv, args); 201 } catch (IllegalAccessException|IllegalArgumentException|InvocationTargetException ex) { 202 throw new IllegalArgumentException(ex); 203 } 204 } 205 private Object invokeWithArguments(MethodHandle mh, Object... args) { 206 try { 207 return mh.invokeWithArguments(args); 208 } catch (Throwable ex) { 209 throw new IllegalArgumentException(ex); 210 } 211 } 212 private int counter; 213 private Object makeArgument(Class<?> type) { 214 final String cname = type.getSimpleName(); 215 final int n = ++counter; 216 final int nn = (n << 10) + 13; 217 if (type.isAssignableFrom(String.class)) { 218 return "<"+cname+"#"+nn+">"; 219 } 220 if (type == THIS_CLASS) return this.withCounter(nn); 221 if (type == Integer.class || type == int.class) return nn; 222 if (type == Character.class || type == char.class) return (char)(n % 100+' '); 223 if (type == Byte.class || type == byte.class) return (byte)-(n % 100); 224 if (type == Long.class || type == long.class) return (long)nn; 225 throw new IllegalArgumentException("don't know how to make argument of type: "+type); 226 } 227 private Object[] makeArguments(Class<?>... ptypes) { 228 Object[] args = new Object[ptypes.length]; 229 for (int i = 0; i < args.length; i++) 230 args[i] = makeArgument(ptypes[i]); 231 return args; 232 } 233 private Object[] makeArguments(MethodType mtype) { 234 return makeArguments(mtype.parameterArray()); 235 } 236 private Object[] pop(int n, Object[] args) { 237 if (n >= 0) 238 return Arrays.copyOfRange(args, n, args.length); 239 else 240 return Arrays.copyOfRange(args, 0, args.length+n); 241 } 242 private Object[] pushAtFront(Object arg1, Object[] args) { 243 Object[] res = new Object[1+args.length]; 244 res[0] = arg1; 245 System.arraycopy(args, 0, res, 1, args.length); 246 return res; 247 } 248 private Object[] pushAtBack(Object[] args, Object argN) { 249 Object[] res = new Object[1+args.length]; 250 System.arraycopy(args, 0, res, 0, args.length); 251 res[args.length] = argN; 252 return res; 253 } 254 private static String strip(String prefix, String s) { 255 assert(s.startsWith(prefix)); 256 return s.substring(prefix.length()); 257 } 258 259 private final int[] refKindTestCounts = new int[REF_KIND_NAMES.length]; 260 @After 261 public void printCounts() { 262 ArrayList<String> zeroes = new ArrayList<>(); 263 for (int i = 0; i < refKindTestCounts.length; i++) { 264 final int count = refKindTestCounts[i]; 265 final String name = REF_KIND_NAMES[i]; 266 if (count == 0) { 267 if (name != null) zeroes.add(name); 268 continue; 269 } 270 if (verbose >= 0) 271 System.out.println("test count for "+name+" : "+count); 272 else if (name != null) 273 zeroes.add(name); 274 } 275 if (verbose >= 0) 276 System.out.println("test counts zero for "+zeroes); 277 } 278 279 // Test subjects 280 public static String makeString(Object x) { return "makeString("+x+")"; } 281 public static String dupString(String x) { return "("+x+"+"+x+")"; } 282 public static String intString(int x) { return "intString("+x+")"; } 283 public static String byteString(byte x) { return "byteString("+x+")"; } 284 public static String longString(String x, long y, String z) { return "longString("+x+y+z+")"; } 285 286 public final String toString() { 287 return "<"+getClass().getSimpleName()+"#"+counter+">"; 288 } 289 public final String hello() { return "hello from "+this; } 290 private PrivateInvokeTest withCounter(int counter) { 291 PrivateInvokeTest res = new PrivateInvokeTest(); 292 res.counter = counter; 293 return res; 294 } 295 296 public static void main(String... av) throws Throwable { 297 new PrivateInvokeTest().run(); 298 } 299 public void run() throws Throwable { 300 testFirst(); 301 testInvokeDirect(); 302 } 303 304 @Test 305 public void testFirst() throws Throwable { 306 if (true) return; // nothing here 307 try { 308 System.out.println("start of testFirst"); 309 } finally { 310 System.out.println("end of testFirst"); 311 } 312 } 313 314 @Test 315 public void testInvokeDirect() { 316 testInvokeDirect(getMethod(THIS_CLASS, "hello")); 317 testInvokeDirect(getMethod(Object.class, "toString")); 318 testInvokeDirect(getMethod(Comparable.class, "compareTo", Object.class)); 319 testInvokeDirect(getMethod(THIS_CLASS, "makeString", Object.class)); 320 testInvokeDirect(getMethod(THIS_CLASS, "dupString", String.class)); 321 testInvokeDirect(getMethod(THIS_CLASS, "intString", int.class)); 322 testInvokeDirect(getMethod(THIS_CLASS, "byteString", byte.class)); 323 testInvokeDirect(getMethod(THIS_CLASS, "longString", String.class, long.class, String.class)); 324 } 325 326 void testInvokeDirect(Method m) { 327 final int refKind = referenceKind(m); 328 testInvokeDirect(m, refKind); 329 testInvokeDirect(m, REF_MH_invokeBasic); 330 } 331 void testInvokeDirect(Method m, int refKind) { 332 if (verbose >= 1) 333 System.out.println("testInvoke m="+m+" : "+REF_KIND_NAMES[refKind]); 334 final MethodHandle mh = unreflect(m); 335 Object[] args = makeArguments(mh.type()); 336 Object res1 = invokeWithArguments(m, args); 337 // res1 comes from java.lang.reflect.Method::invoke 338 if (verbose >= 1) 339 System.out.println("m"+Arrays.asList(args)+" => "+res1); 340 // res2 comes from java.lang.invoke.MethodHandle::invoke 341 Object res2 = invokeWithArguments(mh, args); 342 assertEquals(res1, res2); 343 MethodType mtype = mh.type(); 344 testInvokeVia("DMH invoker", refKind, directInvoker(refKind, mtype), mh, res1, args); 345 MethodType etype = mtype.erase(); 346 if (etype != mtype) { 347 // Try a detuned invoker. 348 testInvokeVia("erased DMH invoker", refKind, directInvoker(refKind, etype), mh, res1, args); 349 } 350 MethodType btype = basicType(mtype); 351 if (btype != mtype && btype != etype) { 352 // Try a detuned invoker. 353 testInvokeVia("basic DMH invoker", refKind, directInvoker(refKind, btype), mh, res1, args); 354 } 355 if (false) { 356 // this can crash the JVM 357 testInvokeVia("generic DMH invoker", refKind, directInvoker(refKind, mtype.generic()), mh, res1, args); 358 } 359 refKindTestCounts[refKind] += 1; 360 } 361 362 void testInvokeVia(String kind, int refKind, MethodHandle invoker, MethodHandle mh, Object res1, Object... args) { 363 Object[] args1; 364 if (refKind == REF_MH_invokeBasic) 365 args1 = pushAtFront(mh, args); 366 else 367 args1 = pushAtBack(args, internalMemberName(mh)); 368 if (verbose >= 2) { 369 System.out.println(kind+" invoker="+invoker+" mh="+debugString(mh)+" args="+Arrays.asList(args1)); 370 } 371 Object res3 = invokeWithArguments(invoker, args1); 372 assertEquals(res1, res3); 373 } 374} 375