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