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.
   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
  59     public void testFirst() throws Throwable {
  60         verbosity += 9; try {
  61             // left blank for debugging
  62         } finally { printCounts(); verbosity -= 9; }
  63     }
  64 
  65     public InvokeGenericTest() {
  66     }
  67 
  68     String testName;
  69     static int allPosTests, allNegTests;
  70     int posTests, negTests;
  71     @AfterMethod
  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     }
  82     void countTest(boolean positive) {
  83         if (positive) ++posTests;
  84         else          ++negTests;
  85     }
  86     void countTest() { countTest(true); }
  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
  96     public static void setUpClass() throws Exception {
  97         calledLog.clear();
  98         calledLog.add(null);
  99         nextArgVal = INITIAL_ARG_VAL;
 100     }
 101 
 102     @AfterClass
 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<>();
 113     static Object logEntry(String name, Object... args) {
 114         return Arrays.asList(name, Arrays.asList(args));
 115     }
 116     static Object called(String name, Object... args) {
 117         Object entry = logEntry(name, args);
 118         calledLog.add(entry);
 119         return entry;
 120     }
 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     }
 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 
 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 
 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;
 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     }
 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     }
 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 
 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     }
 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     }
 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();
 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     }
 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     }
 252     static Object[] zeroArgs(List<Class<?>> params) {
 253         return zeroArgs(params.toArray(new Class<?>[0]));
 254     }
 255 
 256     @SafeVarargs @SuppressWarnings("varargs")
 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")
 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     }
 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     }
 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 
 288     static MethodHandle changeArgTypes(MethodHandle target, Class<?> argType) {
 289         return changeArgTypes(target, 0, 999, argType);
 290     }
 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<>();
 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     }
 313     MethodHandle callable(Class<?>... params) {
 314         return callable(Arrays.asList(params));
 315     }
 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
 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
 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
 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
 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     }
 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     }
 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      */
 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     }
 450     static List<MethodType> allMethodTypes(int argc, Class<?>... types) {
 451         return allMethodTypes(argc, argc, types);
 452     }
 453 
 454     MethodHandle toString_MH;
 455 
 456     @Test
 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     }
 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
 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 }