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