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