jdk/test/java/dyn/InvokeGenericTest.java
changeset 8823 7cd28219a1e4
parent 8717 f75a1efb1412
parent 8822 8145ab9f5f86
child 8824 0762fa26f813
child 9033 a88f5656f05d
equal deleted inserted replaced
8717:f75a1efb1412 8823:7cd28219a1e4
     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 }