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