InvokeGenericTest.java revision 4251:f09930d526ba
1/* 2 * Copyright (c) 2009, 2011, 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 java.lang.invoke.MethodHandle.invoke 28 * @compile -target 7 InvokeGenericTest.java 29 * @run junit/othervm test.java.lang.invoke.InvokeGenericTest 30 */ 31 32package test.java.lang.invoke; 33 34import java.lang.invoke.*; 35import static java.lang.invoke.MethodHandles.*; 36import static java.lang.invoke.MethodType.*; 37import java.lang.reflect.*; 38import java.util.*; 39import org.junit.*; 40import static org.junit.Assert.*; 41import static org.junit.Assume.*; 42 43 44/** 45 * 46 * @author jrose 47 */ 48public class InvokeGenericTest { 49 // How much output? 50 static int verbosity = 0; 51 static { 52 String vstr = System.getProperty("test.java.lang.invoke.InvokeGenericTest.verbosity"); 53 if (vstr != null) verbosity = Integer.parseInt(vstr); 54 } 55 56// public static void main(String... av) throws Throwable { 57// new InvokeGenericTest().testFirst(); 58// } 59 60 @Test 61 public void testFirst() throws Throwable { 62 verbosity += 9; try { 63 // left blank for debugging 64 } finally { printCounts(); verbosity -= 9; } 65 } 66 67 public InvokeGenericTest() { 68 } 69 70 @Before 71 public void checkImplementedPlatform() { 72 boolean platformOK = false; 73 Properties properties = System.getProperties(); 74 String vers = properties.getProperty("java.vm.version"); 75 String name = properties.getProperty("java.vm.name"); 76 String arch = properties.getProperty("os.arch"); 77 if ((arch.equals("amd64") || arch.equals("i386") || arch.equals("x86") || 78 arch.equals("sparc") || arch.equals("sparcv9")) && 79 (name.contains("Client") || name.contains("Server")) 80 ) { 81 platformOK = true; 82 } else { 83 System.err.println("Skipping tests for unsupported platform: "+Arrays.asList(vers, name, arch)); 84 } 85 assumeTrue(platformOK); 86 } 87 88 String testName; 89 static int allPosTests, allNegTests; 90 int posTests, negTests; 91 @After 92 public void printCounts() { 93 if (verbosity >= 2 && (posTests | negTests) != 0) { 94 System.out.println(); 95 if (posTests != 0) System.out.println("=== "+testName+": "+posTests+" positive test cases run"); 96 if (negTests != 0) System.out.println("=== "+testName+": "+negTests+" negative test cases run"); 97 allPosTests += posTests; 98 allNegTests += negTests; 99 posTests = negTests = 0; 100 } 101 } 102 void countTest(boolean positive) { 103 if (positive) ++posTests; 104 else ++negTests; 105 } 106 void countTest() { countTest(true); } 107 void startTest(String name) { 108 if (testName != null) printCounts(); 109 if (verbosity >= 1) 110 System.out.println("["+name+"]"); 111 posTests = negTests = 0; 112 testName = name; 113 } 114 115 @BeforeClass 116 public static void setUpClass() throws Exception { 117 calledLog.clear(); 118 calledLog.add(null); 119 nextArgVal = INITIAL_ARG_VAL; 120 } 121 122 @AfterClass 123 public static void tearDownClass() throws Exception { 124 int posTests = allPosTests, negTests = allNegTests; 125 if (verbosity >= 2 && (posTests | negTests) != 0) { 126 System.out.println(); 127 if (posTests != 0) System.out.println("=== "+posTests+" total positive test cases"); 128 if (negTests != 0) System.out.println("=== "+negTests+" total negative test cases"); 129 } 130 } 131 132 static List<Object> calledLog = new ArrayList<Object>(); 133 static Object logEntry(String name, Object... args) { 134 return Arrays.asList(name, Arrays.asList(args)); 135 } 136 static Object called(String name, Object... args) { 137 Object entry = logEntry(name, args); 138 calledLog.add(entry); 139 return entry; 140 } 141 static void assertCalled(String name, Object... args) { 142 Object expected = logEntry(name, args); 143 Object actual = calledLog.get(calledLog.size() - 1); 144 if (expected.equals(actual) && verbosity < 9) return; 145 System.out.println("assertCalled "+name+":"); 146 System.out.println("expected: "+expected); 147 System.out.println("actual: "+actual); 148 System.out.println("ex. types: "+getClasses(expected)); 149 System.out.println("act. types: "+getClasses(actual)); 150 assertEquals("previous method call", expected, actual); 151 } 152 static void printCalled(MethodHandle target, String name, Object... args) { 153 if (verbosity >= 3) 154 System.out.println("calling MH="+target+" to "+name+Arrays.toString(args)); 155 } 156 157 static Object castToWrapper(Object value, Class<?> dst) { 158 Object wrap = null; 159 if (value instanceof Number) 160 wrap = castToWrapperOrNull(((Number)value).longValue(), dst); 161 if (value instanceof Character) 162 wrap = castToWrapperOrNull((char)(Character)value, dst); 163 if (wrap != null) return wrap; 164 return dst.cast(value); 165 } 166 167 static Object castToWrapperOrNull(long value, Class<?> dst) { 168 if (dst == int.class || dst == Integer.class) 169 return (int)(value); 170 if (dst == long.class || dst == Long.class) 171 return (long)(value); 172 if (dst == char.class || dst == Character.class) 173 return (char)(value); 174 if (dst == short.class || dst == Short.class) 175 return (short)(value); 176 if (dst == float.class || dst == Float.class) 177 return (float)(value); 178 if (dst == double.class || dst == Double.class) 179 return (double)(value); 180 if (dst == byte.class || dst == Byte.class) 181 return (byte)(value); 182 if (dst == boolean.class || dst == boolean.class) 183 return ((value % 29) & 1) == 0; 184 return null; 185 } 186 187 static final int ONE_MILLION = (1000*1000), // first int value 188 TEN_BILLION = (10*1000*1000*1000), // scale factor to reach upper 32 bits 189 INITIAL_ARG_VAL = ONE_MILLION << 1; // <<1 makes space for sign bit; 190 static long nextArgVal; 191 static long nextArg(boolean moreBits) { 192 long val = nextArgVal++; 193 long sign = -(val & 1); // alternate signs 194 val >>= 1; 195 if (moreBits) 196 // Guarantee some bits in the high word. 197 // In any case keep the decimal representation simple-looking, 198 // with lots of zeroes, so as not to make the printed decimal 199 // strings unnecessarily noisy. 200 val += (val % ONE_MILLION) * TEN_BILLION; 201 return val ^ sign; 202 } 203 static int nextArg() { 204 // Produce a 32-bit result something like ONE_MILLION+(smallint). 205 // Example: 1_000_042. 206 return (int) nextArg(false); 207 } 208 static long nextArg(Class<?> kind) { 209 if (kind == long.class || kind == Long.class || 210 kind == double.class || kind == Double.class) 211 // produce a 64-bit result something like 212 // ((TEN_BILLION+1) * (ONE_MILLION+(smallint))) 213 // Example: 10_000_420_001_000_042. 214 return nextArg(true); 215 return (long) nextArg(); 216 } 217 218 static Object randomArg(Class<?> param) { 219 Object wrap = castToWrapperOrNull(nextArg(param), param); 220 if (wrap != null) { 221 return wrap; 222 } 223// import sun.invoke.util.Wrapper; 224// Wrapper wrap = Wrapper.forBasicType(dst); 225// if (wrap == Wrapper.OBJECT && Wrapper.isWrapperType(dst)) 226// wrap = Wrapper.forWrapperType(dst); 227// if (wrap != Wrapper.OBJECT) 228// return wrap.wrap(nextArg++); 229 if (param.isInterface()) { 230 for (Class<?> c : param.getClasses()) { 231 if (param.isAssignableFrom(c) && !c.isInterface()) 232 { param = c; break; } 233 } 234 } 235 if (param.isInterface() || param.isAssignableFrom(String.class)) 236 return "#"+nextArg(); 237 else 238 try { 239 return param.newInstance(); 240 } catch (InstantiationException ex) { 241 } catch (IllegalAccessException ex) { 242 } 243 return null; // random class not Object, String, Integer, etc. 244 } 245 static Object[] randomArgs(Class<?>... params) { 246 Object[] args = new Object[params.length]; 247 for (int i = 0; i < args.length; i++) 248 args[i] = randomArg(params[i]); 249 return args; 250 } 251 static Object[] randomArgs(int nargs, Class<?> param) { 252 Object[] args = new Object[nargs]; 253 for (int i = 0; i < args.length; i++) 254 args[i] = randomArg(param); 255 return args; 256 } 257 258 static final Object ANON_OBJ = new Object(); 259 static Object zeroArg(Class<?> param) { 260 Object x = castToWrapperOrNull(0L, param); 261 if (x != null) return x; 262 if (param.isInterface() || param.isAssignableFrom(String.class)) return "\"\""; 263 if (param == Object.class) return ANON_OBJ; 264 if (param.getComponentType() != null) return Array.newInstance(param.getComponentType(), 0); 265 return null; 266 } 267 static Object[] zeroArgs(Class<?>... params) { 268 Object[] args = new Object[params.length]; 269 for (int i = 0; i < args.length; i++) 270 args[i] = zeroArg(params[i]); 271 return args; 272 } 273 static Object[] zeroArgs(List<Class<?>> params) { 274 return zeroArgs(params.toArray(new Class<?>[0])); 275 } 276 277 static <T, E extends T> T[] array(Class<T[]> atype, E... a) { 278 return Arrays.copyOf(a, a.length, atype); 279 } 280 static <T> T[] cat(T[] a, T... b) { 281 int alen = a.length, blen = b.length; 282 if (blen == 0) return a; 283 T[] c = Arrays.copyOf(a, alen + blen); 284 System.arraycopy(b, 0, c, alen, blen); 285 return c; 286 } 287 static Integer[] boxAll(int... vx) { 288 Integer[] res = new Integer[vx.length]; 289 for (int i = 0; i < res.length; i++) { 290 res[i] = vx[i]; 291 } 292 return res; 293 } 294 static Object getClasses(Object x) { 295 if (x == null) return x; 296 if (x instanceof String) return x; // keep the name 297 if (x instanceof List) { 298 // recursively report classes of the list elements 299 Object[] xa = ((List)x).toArray(); 300 for (int i = 0; i < xa.length; i++) 301 xa[i] = getClasses(xa[i]); 302 return Arrays.asList(xa); 303 } 304 return x.getClass().getSimpleName(); 305 } 306 307 static MethodHandle changeArgTypes(MethodHandle target, Class<?> argType) { 308 return changeArgTypes(target, 0, 999, argType); 309 } 310 static MethodHandle changeArgTypes(MethodHandle target, 311 int beg, int end, Class<?> argType) { 312 MethodType targetType = target.type(); 313 end = Math.min(end, targetType.parameterCount()); 314 ArrayList<Class<?>> argTypes = new ArrayList<Class<?>>(targetType.parameterList()); 315 Collections.fill(argTypes.subList(beg, end), argType); 316 MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes); 317 return target.asType(ttype2); 318 } 319 320 // This lookup is good for all members in and under InvokeGenericTest. 321 static final Lookup LOOKUP = MethodHandles.lookup(); 322 323 Map<List<Class<?>>, MethodHandle> CALLABLES = new HashMap<List<Class<?>>, MethodHandle>(); 324 MethodHandle callable(List<Class<?>> params) { 325 MethodHandle mh = CALLABLES.get(params); 326 if (mh == null) { 327 mh = collector_MH.asType(methodType(Object.class, params)); 328 CALLABLES.put(params, mh); 329 } 330 return mh; 331 } 332 MethodHandle callable(Class<?>... params) { 333 return callable(Arrays.asList(params)); 334 } 335 private static Object collector(Object... args) { 336 return Arrays.asList(args); 337 } 338 private static final MethodHandle collector_MH; 339 static { 340 try { 341 collector_MH 342 = LOOKUP.findStatic(LOOKUP.lookupClass(), 343 "collector", 344 methodType(Object.class, Object[].class)); 345 } catch (ReflectiveOperationException ex) { 346 throw new RuntimeException(ex); 347 } 348 } 349 350 @Test 351 public void testSimple() throws Throwable { 352 startTest("testSimple"); 353 countTest(); 354 String[] args = { "one", "two" }; 355 MethodHandle mh = callable(Object.class, String.class); 356 Object res; List resl; 357 res = resl = (List) mh.invoke((String)args[0], (Object)args[1]); 358 //System.out.println(res); 359 assertEquals(Arrays.asList(args), res); 360 } 361 362 @Test 363 public void testSimplePrims() throws Throwable { 364 startTest("testSimplePrims"); 365 countTest(); 366 int[] args = { 1, 2 }; 367 MethodHandle mh = callable(Object.class, Object.class); 368 Object res; List resl; 369 res = resl = (List) mh.invoke(args[0], args[1]); 370 //System.out.println(res); 371 assertEquals(Arrays.toString(args), res.toString()); 372 } 373 374 @Test 375 public void testAlternateName() throws Throwable { 376 startTest("testAlternateName"); 377 countTest(); 378 String[] args = { "one", "two" }; 379 MethodHandle mh = callable(Object.class, String.class); 380 Object res; List resl; 381 res = resl = (List) mh.invoke((String)args[0], (Object)args[1]); 382 //System.out.println(res); 383 assertEquals(Arrays.asList(args), res); 384 } 385 386 @Test 387 public void testWrongArgumentCount() throws Throwable { 388 startTest("testWrongArgumentCount"); 389 for (int i = 0; i <= 10; i++) { 390 testWrongArgumentCount(Collections.<Class<?>>nCopies(i, Integer.class)); 391 if (i <= 4) { 392 testWrongArgumentCount(Collections.<Class<?>>nCopies(i, int.class)); 393 testWrongArgumentCount(Collections.<Class<?>>nCopies(i, long.class)); 394 } 395 } 396 } 397 public void testWrongArgumentCount(List<Class<?>> params) throws Throwable { 398 int max = params.size(); 399 for (int i = 0; i < max; i++) { 400 List<Class<?>> params2 = params.subList(0, i); 401 for (int k = 0; k <= 2; k++) { 402 if (k == 1) params = methodType(Object.class, params).generic().parameterList(); 403 if (k == 2) params2 = methodType(Object.class, params2).generic().parameterList(); 404 testWrongArgumentCount(params, params2); 405 testWrongArgumentCount(params2, params); 406 } 407 } 408 } 409 public void testWrongArgumentCount(List<Class<?>> expect, List<Class<?>> observe) throws Throwable { 410 countTest(false); 411 if (expect.equals(observe)) 412 assert(false); 413 MethodHandle target = callable(expect); 414 Object[] args = zeroArgs(observe); 415 Object junk; 416 try { 417 switch (args.length) { 418 case 0: 419 junk = target.invoke(); break; 420 case 1: 421 junk = target.invoke(args[0]); break; 422 case 2: 423 junk = target.invoke(args[0], args[1]); break; 424 case 3: 425 junk = target.invoke(args[0], args[1], args[2]); break; 426 case 4: 427 junk = target.invoke(args[0], args[1], args[2], args[3]); break; 428 default: 429 junk = target.invokeWithArguments(args); break; 430 } 431 } catch (WrongMethodTypeException ex) { 432 return; 433 } catch (Exception ex) { 434 throw new RuntimeException("wrong exception calling "+target+" on "+Arrays.asList(args), ex); 435 } 436 throw new RuntimeException("bad success calling "+target+" on "+Arrays.asList(args)); 437 } 438 439 /** Make a list of all combinations of the given types, with the given arities. 440 * A void return type is possible iff the first type is void.class. 441 */ 442 static List<MethodType> allMethodTypes(int minargc, int maxargc, Class<?>... types) { 443 ArrayList<MethodType> result = new ArrayList<MethodType>(); 444 if (types.length > 0) { 445 ArrayList<MethodType> argcTypes = new ArrayList<MethodType>(); 446 // build arity-zero types first 447 for (Class<?> rtype : types) { 448 argcTypes.add(MethodType.methodType(rtype)); 449 } 450 if (types[0] == void.class) 451 // void is not an argument type 452 types = Arrays.copyOfRange(types, 1, types.length); 453 for (int argc = 0; argc <= maxargc; argc++) { 454 if (argc >= minargc) 455 result.addAll(argcTypes); 456 if (argc >= maxargc) 457 break; 458 ArrayList<MethodType> prevTypes = argcTypes; 459 argcTypes = new ArrayList<MethodType>(); 460 for (MethodType prevType : prevTypes) { 461 for (Class<?> ptype : types) { 462 argcTypes.add(prevType.insertParameterTypes(argc, ptype)); 463 } 464 } 465 } 466 } 467 return Collections.unmodifiableList(result); 468 } 469 static List<MethodType> allMethodTypes(int argc, Class<?>... types) { 470 return allMethodTypes(argc, argc, types); 471 } 472 473 MethodHandle toString_MH; 474 475 @Test 476 public void testReferenceConversions() throws Throwable { 477 startTest("testReferenceConversions"); 478 toString_MH = LOOKUP. 479 findVirtual(Object.class, "toString", MethodType.methodType(String.class)); 480 Object[] args = { "one", "two" }; 481 for (MethodType type : allMethodTypes(2, Object.class, String.class, CharSequence.class)) { 482 testReferenceConversions(type, args); 483 } 484 } 485 public void testReferenceConversions(MethodType type, Object... args) throws Throwable { 486 countTest(); 487 int nargs = args.length; 488 List<Object> argList = Arrays.asList(args); 489 String expectString = argList.toString(); 490 if (verbosity > 3) System.out.println("target type: "+type+expectString); 491 MethodHandle mh = callable(type.parameterList()); 492 mh = MethodHandles.filterReturnValue(mh, toString_MH); 493 mh = mh.asType(type); 494 Object res = null; 495 if (nargs == 2) { 496 res = mh.invoke((Object)args[0], (Object)args[1]); 497 assertEquals(expectString, res); 498 res = mh.invoke((String)args[0], (Object)args[1]); 499 assertEquals(expectString, res); 500 res = mh.invoke((Object)args[0], (String)args[1]); 501 assertEquals(expectString, res); 502 res = mh.invoke((String)args[0], (String)args[1]); 503 assertEquals(expectString, res); 504 res = mh.invoke((String)args[0], (CharSequence)args[1]); 505 assertEquals(expectString, res); 506 res = mh.invoke((CharSequence)args[0], (Object)args[1]); 507 assertEquals(expectString, res); 508 res = (String) mh.invoke((Object)args[0], (Object)args[1]); 509 assertEquals(expectString, res); 510 res = (String) mh.invoke((String)args[0], (Object)args[1]); 511 assertEquals(expectString, res); 512 res = (CharSequence) mh.invoke((String)args[0], (Object)args[1]); 513 assertEquals(expectString, res); 514 } else { 515 assert(false); // write this code 516 } 517 //System.out.println(res); 518 } 519 520 521 @Test 522 public void testBoxConversions() throws Throwable { 523 startTest("testBoxConversions"); 524 countTest(); 525 Object[] args = { 1, 2 }; 526 MethodHandle mh = callable(Object.class, int.class); 527 Object res; List resl; int resi; 528 res = resl = (List) mh.invoke((int)args[0], (Object)args[1]); 529 //System.out.println(res); 530 assertEquals(Arrays.asList(args), res); 531 mh = MethodHandles.identity(int.class); 532 mh = MethodHandles.dropArguments(mh, 1, int.class); 533 res = resi = (int) mh.invoke((Object) args[0], (Object) args[1]); 534 assertEquals(args[0], res); 535 res = resi = (int) mh.invoke((int) args[0], (Object) args[1]); 536 assertEquals(args[0], res); 537 } 538 539} 540