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 }