1/*
2 * Copyright (c) 2014, 2017, 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
24import sun.invoke.util.Wrapper;
25import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;
26
27import java.lang.invoke.MethodHandle;
28import java.lang.invoke.MethodHandleHelper;
29import java.lang.invoke.MethodType;
30import java.util.Arrays;
31import java.util.Collections;
32
33/* @test
34 * @summary unit tests for varargs array methods: MethodHandleInfo.varargsArray(int),
35 *          MethodHandleInfo.varargsArray(Class,int) & MethodHandleInfo.varargsList(int)
36 * @modules java.base/sun.invoke.util
37 * @library /lib/testlibrary /java/lang/invoke/common
38 * @compile/module=java.base java/lang/invoke/MethodHandleHelper.java
39 * @run main/bootclasspath VarargsArrayTest
40 * @run main/bootclasspath/othervm -DVarargsArrayTest.MAX_ARITY=255 -DVarargsArrayTest.START_ARITY=250
41 *                         VarargsArrayTest
42 */
43
44/* This might take a while and burn lots of metadata:
45 * @run main/bootclasspath -DVarargsArrayTest.MAX_ARITY=255 -DVarargsArrayTest.EXHAUSTIVE=true VarargsArrayTest
46 */
47public class VarargsArrayTest {
48    private static final Class<?> CLASS = VarargsArrayTest.class;
49    private static final int MAX_ARITY = Integer.getInteger(
50            CLASS.getSimpleName()+".MAX_ARITY", 40);
51    private static final int START_ARITY = Integer.getInteger(
52            CLASS.getSimpleName()+".START_ARITY", 0);
53    private static final boolean EXHAUSTIVE = Boolean.getBoolean(
54            CLASS.getSimpleName()+".EXHAUSTIVE");
55
56    public static void main(String[] args) throws Throwable {
57        CodeCacheOverflowProcessor.runMHTest(VarargsArrayTest::test);
58    }
59
60    public static void test() throws Throwable {
61        testVarargsArray();
62        testVarargsReferenceArray();
63        testVarargsPrimitiveArray();
64    }
65
66    public static void testVarargsArray() throws Throwable {
67        final int MIN = START_ARITY;
68        final int MAX = MAX_ARITY-2;  // 253+1 would cause parameter overflow with 'this' added
69        for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 17, MAX)) {
70            MethodHandle target = MethodHandleHelper.varargsArray(nargs);
71            Object[] args = new Object[nargs];
72            for (int i = 0; i < nargs; i++)
73                args[i] = "#"+i;
74            Object res = target.invokeWithArguments(args);
75            assertArrayEquals(args, (Object[])res);
76        }
77    }
78
79    public static void testVarargsReferenceArray() throws Throwable {
80        testTypedVarargsArray(Object[].class);
81        testTypedVarargsArray(String[].class);
82        testTypedVarargsArray(Number[].class);
83    }
84
85    public static void testVarargsPrimitiveArray() throws Throwable {
86        testTypedVarargsArray(int[].class);
87        testTypedVarargsArray(long[].class);
88        testTypedVarargsArray(byte[].class);
89        testTypedVarargsArray(boolean[].class);
90        testTypedVarargsArray(short[].class);
91        testTypedVarargsArray(char[].class);
92        testTypedVarargsArray(float[].class);
93        testTypedVarargsArray(double[].class);
94    }
95
96    private static int nextArgCount(int nargs, int density, int MAX) {
97        if (EXHAUSTIVE)  return nargs + 1;
98        if (nargs >= MAX)  return Integer.MAX_VALUE;
99        int BOT = 20, TOP = MAX-5;
100        if (density < 10) { BOT = 10; MAX = TOP-2; }
101        if (nargs <= BOT || nargs >= TOP) {
102            ++nargs;
103        } else {
104            int bump = Math.max(1, 100 / density);
105            nargs += bump;
106            if (nargs > TOP)  nargs = TOP;
107        }
108        return nargs;
109    }
110
111    private static void testTypedVarargsArray(Class<?> arrayType) throws Throwable {
112        Class<?> elemType = arrayType.getComponentType();
113        int MIN = START_ARITY;
114        int MAX = MAX_ARITY-2;  // 253+1 would cause parameter overflow with 'this' added
115        int density = 3;
116        if (elemType == int.class || elemType == long.class)  density = 7;
117        if (elemType == long.class || elemType == double.class) { MAX /= 2; MIN /= 2; }
118        for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, density, MAX)) {
119            Object[] args = makeTestArray(elemType, nargs);
120            MethodHandle varargsArray = MethodHandleHelper.varargsArray(arrayType, nargs);
121            MethodType vaType = varargsArray.type();
122            assertEquals(arrayType, vaType.returnType());
123            if (nargs != 0) {
124                assertEquals(elemType, vaType.parameterType(0));
125                assertEquals(elemType, vaType.parameterType(vaType.parameterCount()-1));
126            }
127            assertEquals(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)),
128                         vaType);
129            Object res = varargsArray.invokeWithArguments(args);
130            assertEquals(res.getClass(), arrayType);
131            String resString = toArrayString(res);
132            assertEquals(Arrays.toString(args), resString);
133
134            MethodHandle spreader = varargsArray.asSpreader(arrayType, nargs);
135            MethodType stype = spreader.type();
136            assert(stype == MethodType.methodType(arrayType, arrayType));
137            if (nargs <= 5) {
138                // invoke target as a spreader also:
139                @SuppressWarnings("cast")
140                Object res2 = spreader.invokeWithArguments((Object)res);
141                String res2String = toArrayString(res2);
142                assertEquals(Arrays.toString(args), res2String);
143                // invoke the spreader on a generic Object[] array; check for error
144                try {
145                    Object res3 = spreader.invokeWithArguments((Object)args);
146                    String res3String = toArrayString(res3);
147                    assertTrue(arrayType.getName(), arrayType.isAssignableFrom(Object[].class));
148                    assertEquals(Arrays.toString(args), res3String);
149                } catch (ClassCastException ex) {
150                    assertFalse(arrayType.getName(), arrayType.isAssignableFrom(Object[].class));
151                }
152            }
153            if (nargs == 0) {
154                // invoke spreader on null arglist
155                Object res3 = spreader.invokeWithArguments((Object)null);
156                String res3String = toArrayString(res3);
157                assertEquals(Arrays.toString(args), res3String);
158            }
159        }
160    }
161
162    private static Object[] makeTestArray(Class<?> elemType, int len) {
163        Wrapper elem = null;
164        if (elemType.isPrimitive())
165            elem = Wrapper.forPrimitiveType(elemType);
166        else if (Wrapper.isWrapperType(elemType))
167            elem = Wrapper.forWrapperType(elemType);
168        Object[] args = new Object[len];
169        for (int i = 0; i < len; i++) {
170            Object arg = i * 100;
171            if (elem == null) {
172                if (elemType == String.class)
173                    arg = "#"+arg;
174                arg = elemType.cast(arg);  // just to make sure
175            } else {
176                switch (elem) {
177                    case BOOLEAN: arg = (i % 3 == 0);           break;
178                    case CHAR:    arg = 'a' + i;                break;
179                    case LONG:    arg = (long)i * 1000_000_000; break;
180                    case FLOAT:   arg = (float)i / 100;         break;
181                    case DOUBLE:  arg = (double)i / 1000_000;   break;
182                }
183                arg = elem.cast(arg, elemType);
184            }
185            args[i] = arg;
186        }
187        return args;
188    }
189
190    private static String toArrayString(Object a) {
191        if (a == null)  return "null";
192        Class<?> elemType = a.getClass().getComponentType();
193        if (elemType == null)  return a.toString();
194        if (elemType.isPrimitive()) {
195            switch (Wrapper.forPrimitiveType(elemType)) {
196                case INT:      return Arrays.toString((int[])a);
197                case BYTE:     return Arrays.toString((byte[])a);
198                case BOOLEAN:  return Arrays.toString((boolean[])a);
199                case SHORT:    return Arrays.toString((short[])a);
200                case CHAR:     return Arrays.toString((char[])a);
201                case FLOAT:    return Arrays.toString((float[])a);
202                case LONG:     return Arrays.toString((long[])a);
203                case DOUBLE:   return Arrays.toString((double[])a);
204            }
205        }
206        return Arrays.toString((Object[])a);
207    }
208
209    public static void assertArrayEquals(Object[] arr1, Object[] arr2) {
210        if (arr1 == null && arr2 == null)  return;
211        if (arr1 != null && arr2 != null && arr1.length == arr2.length) {
212            for (int i = 0; i < arr1.length; i++) {
213                assertEquals(arr1[i], arr2[i]);
214            }
215            return;
216        }
217        throw new AssertionError(Arrays.deepToString(arr1)
218                + " != " + Arrays.deepToString(arr2));
219    }
220
221    public static void assertEquals(Object o1, Object o2) {
222        if (o1 == null && o2 == null)    return;
223        if (o1 != null && o1.equals(o2)) return;
224        throw new AssertionError(o1 + " != " + o2);
225    }
226
227    public static void assertTrue(String msg, boolean b) {
228        if (!b) {
229            throw new AssertionError(msg);
230        }
231    }
232
233    public static void assertFalse(String msg, boolean b) {
234        assertTrue(msg, !b);
235    }
236}
237