1 /*
   2  * Copyright (c) 2018, 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.MethodHandles
  26  * @library /lib/testlibrary /java/lang/invoke/common
  27  * @compile MethodHandlesTest.java MethodHandlesSpreadArgumentsTest.java remote/RemoteExample.java
  28  * @run junit/othervm/timeout=2500 -XX:+IgnoreUnrecognizedVMOptions
  29  *                                 -XX:-VerifyDependencies
  30  *                                 -esa
  31  *                                 test.java.lang.invoke.MethodHandlesSpreadArgumentsTest
  32  */
  33 
  34 package test.java.lang.invoke;
  35 
  36 import org.junit.*;
  37 import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;
  38 
  39 import java.lang.invoke.MethodHandle;
  40 import java.lang.invoke.MethodType;
  41 import java.util.ArrayList;
  42 import java.util.Arrays;
  43 import java.util.List;
  44 
  45 import static org.junit.Assert.*;
  46 
  47 public class MethodHandlesSpreadArgumentsTest extends MethodHandlesTest {
  48 
  49     @Test // SLOW
  50     public void testSpreadArguments() throws Throwable {
  51         CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments0);
  52         CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments1);
  53     }
  54 
  55     public void testSpreadArguments0() throws Throwable {
  56         if (CAN_SKIP_WORKING)  return;
  57         startTest("spreadArguments");
  58         for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) {
  59             if (verbosity >= 3)
  60                 System.out.println("spreadArguments "+argType);
  61             Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass();
  62             for (int nargs = 0; nargs < 50; nargs++) {
  63                 if (CAN_TEST_LIGHTLY && nargs > 11)  break;
  64                 for (int pos = 0; pos <= nargs; pos++) {
  65                     if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2)  continue;
  66                     if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
  67                         continue;
  68                     testSpreadArguments(argType, arrayType, pos, nargs);
  69                 }
  70             }
  71         }
  72     }
  73 
  74     public void testSpreadArguments(Class<?> argType, Class<?> arrayType, int pos, int nargs) throws Throwable {
  75         countTest();
  76         MethodHandle target2 = varargsArray(arrayType, nargs);
  77         MethodHandle target = target2.asType(target2.type().generic());
  78         if (verbosity >= 3)
  79             System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]");
  80         Object[] args = randomArgs(target2.type().parameterArray());
  81         // make sure the target does what we think it does:
  82         checkTarget(argType, pos, nargs, target, args);
  83         List<Class<?>> newParams = new ArrayList<>(target2.type().parameterList());
  84         {   // modify newParams in place
  85             List<Class<?>> spreadParams = newParams.subList(pos, nargs);
  86             spreadParams.clear(); spreadParams.add(arrayType);
  87         }
  88         MethodType newType = MethodType.methodType(arrayType, newParams);
  89         MethodHandle result = target2.asSpreader(arrayType, nargs-pos);
  90         assert(result.type() == newType) : Arrays.asList(result, newType);
  91         result = result.asType(newType.generic());
  92         Object returnValue;
  93         if (pos == 0) {
  94             Object args2 = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
  95             returnValue = result.invokeExact(args2);
  96         } else {
  97             Object[] args1 = Arrays.copyOfRange(args, 0, pos+1);
  98             args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
  99             returnValue = result.invokeWithArguments(args1);
 100         }
 101         checkReturnValue(argType, args, result, returnValue);
 102     }
 103 
 104     public void testSpreadArguments1() throws Throwable {
 105         if (CAN_SKIP_WORKING)  return;
 106         startTest("spreadArguments/pos");
 107         for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) {
 108             if (verbosity >= 3)
 109                 System.out.println("spreadArguments "+argType);
 110             Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass();
 111             for (int nargs = 0; nargs < 50; nargs++) {
 112                 if (CAN_TEST_LIGHTLY && nargs > 11)  break;
 113                 for (int pos = 0; pos <= nargs; pos++) {
 114                     if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2)  continue;
 115                     if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
 116                         continue;
 117                     for (int spr = 1; spr < nargs - pos; ++spr) {
 118                         if (spr > 4 && spr != 7 && spr != 11 && spr != 20 && spr < nargs - pos - 4) continue;
 119                         testSpreadArguments(argType, arrayType, pos, spr, nargs);
 120                     }
 121                 }
 122             }
 123         }
 124     }
 125 
 126     public void testSpreadArguments(Class<?> argType, Class<?> arrayType,
 127                                     int pos, int spread, int nargs) throws Throwable {
 128         countTest();
 129         MethodHandle target2 = varargsArray(arrayType, nargs);
 130         MethodHandle target = target2.asType(target2.type().generic());
 131         if (verbosity >= 3)
 132             System.out.println("spread into " + target2 + " [" + pos + ".." + (pos + spread) + "[");
 133         Object[] args = randomArgs(target2.type().parameterArray());
 134         // make sure the target does what we think it does:
 135         checkTarget(argType, pos, nargs, target, args);
 136         List<Class<?>> newParams = new ArrayList<>(target2.type().parameterList());
 137         {   // modify newParams in place
 138             List<Class<?>> spreadParams = newParams.subList(pos, pos + spread);
 139             spreadParams.clear();
 140             spreadParams.add(arrayType);
 141         }
 142         MethodType newType = MethodType.methodType(arrayType, newParams);
 143         MethodHandle result = target2.asSpreader(pos, arrayType, spread);
 144         assert (result.type() == newType) : Arrays.asList(result, newType);
 145         result = result.asType(newType.generic());
 146         // args1 has nargs-spread entries, plus one for the to-be-spread array
 147         int args1Length = nargs - (spread - 1);
 148         Object[] args1 = new Object[args1Length];
 149         System.arraycopy(args, 0, args1, 0, pos);
 150         args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, pos + spread));
 151         System.arraycopy(args, pos + spread, args1, pos + 1, nargs - spread - pos);
 152         Object returnValue = result.invokeWithArguments(args1);
 153         checkReturnValue(argType, args, result, returnValue);
 154     }
 155 
 156     private static void checkTarget(Class<?> argType, int pos, int nargs,
 157                                     MethodHandle target, Object[] args) throws Throwable {
 158         if (pos == 0 && nargs < 5 && !argType.isPrimitive()) {
 159             Object[] check = (Object[]) target.invokeWithArguments(args);
 160             assertArrayEquals(args, check);
 161             switch (nargs) {
 162                 case 0:
 163                     check = (Object[]) (Object) target.invokeExact();
 164                     assertArrayEquals(args, check);
 165                     break;
 166                 case 1:
 167                     check = (Object[]) (Object) target.invokeExact(args[0]);
 168                     assertArrayEquals(args, check);
 169                     break;
 170                 case 2:
 171                     check = (Object[]) (Object) target.invokeExact(args[0], args[1]);
 172                     assertArrayEquals(args, check);
 173                     break;
 174             }
 175         }
 176     }
 177 
 178     private static void checkReturnValue(Class<?> argType, Object[] args, MethodHandle result, Object returnValue) {
 179         String argstr = Arrays.toString(args);
 180         if (!argType.isPrimitive()) {
 181             Object[] rv = (Object[]) returnValue;
 182             String rvs = Arrays.toString(rv);
 183             if (!Arrays.equals(args, rv)) {
 184                 System.out.println("method:   "+result);
 185                 System.out.println("expected: "+argstr);
 186                 System.out.println("returned: "+rvs);
 187                 assertArrayEquals(args, rv);
 188             }
 189         } else if (argType == int.class) {
 190             String rvs = Arrays.toString((int[]) returnValue);
 191             if (!argstr.equals(rvs)) {
 192                 System.out.println("method:   "+result);
 193                 System.out.println("expected: "+argstr);
 194                 System.out.println("returned: "+rvs);
 195                 assertEquals(argstr, rvs);
 196             }
 197         } else if (argType == long.class) {
 198             String rvs = Arrays.toString((long[]) returnValue);
 199             if (!argstr.equals(rvs)) {
 200                 System.out.println("method:   "+result);
 201                 System.out.println("expected: "+argstr);
 202                 System.out.println("returned: "+rvs);
 203                 assertEquals(argstr, rvs);
 204             }
 205         } else {
 206             // cannot test...
 207         }
 208     }
 209 
 210 }