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 
24 package test.java.lang.invoke.lib;
25 
26 // Android-removed: drop dependency on jdk.testlibrary.Asserts.
27 // import jdk.testlibrary.Asserts;
28 
29 import java.lang.invoke.MethodHandle;
30 import java.lang.invoke.MethodHandles;
31 import java.lang.invoke.MethodType;
32 import java.lang.reflect.Array;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.List;
36 import java.util.Random;
37 
38 public class Helper {
39     /** Flag for verbose output, true if {@code -Dverbose} specified */
40     public static final boolean IS_VERBOSE
41             = System.getProperty("verbose") != null;
42     /**
43      * Flag for thorough testing -- all test will be executed,
44      * true if {@code -Dthorough} specified. */
45     public static final boolean IS_THOROUGH
46             = System.getProperty("thorough") != null;
47     /** Random number generator w/ initial seed equal to {@code -Dseed} */
48     public static final Random RNG;
49 
50     static {
51         String str = System.getProperty("seed");
52         long seed = str != null ? Long.parseLong(str) : new Random().nextLong();
53         RNG = new Random(seed);
54         System.out.printf("-Dseed=%d%n", seed);
55     }
56 
57     public static final long TEST_LIMIT;
58     static {
59         String str = System.getProperty("testLimit");
60         TEST_LIMIT = str != null ? Long.parseUnsignedLong(str) : 2000L;
61         System.out.printf("-DtestLimit=%d%n", TEST_LIMIT);
62     }
63 
64     public static final int MAX_ARITY = 254;
65     public static final String MISSING_ARG = "missingArg";
66     public static final String MISSING_ARG_2 = "missingArg#2";
67 
68     private static final int
69             // first int value
70             ONE_MILLION = (1000 * 1000),
71             // scale factor to reach upper 32 bits
72             TEN_BILLION = (10 * 1000 * 1000 * 1000),
73             // <<1 makes space for sign bit;
74             INITIAL_ARG_VAL = ONE_MILLION << 1;
75 
76     public static final MethodHandle AS_LIST;
77 
78     static {
79         try {
80             AS_LIST = MethodHandles.lookup().findStatic(
81                     Arrays.class, "asList",
82                     MethodType.methodType(List.class, Object[].class));
83         } catch (NoSuchMethodException | IllegalAccessException ex) {
84             throw new Error(ex);
85         }
86     }
87 
isDoubleCost(Class<?> aClass)88     public static boolean isDoubleCost(Class<?> aClass) {
89         return aClass == double.class || aClass == long.class;
90     }
91 
92     private static List<List<Object>> calledLog = new ArrayList<>();
93     private static long nextArgVal;
94 
assertCalled(String name, Object... args)95     public static void assertCalled(String name, Object... args) {
96         assertCalled(0, name, args);
97     }
98 
assertCalled(int lag, String name, Object... args)99     public static void assertCalled(int lag, String name, Object... args) {
100         Object expected = logEntry(name, args);
101         Object actual = getCalled(lag);
102         // Android-changed: check and throw in-situ.
103         // Asserts.assertEQ(expected, actual, "method call w/ lag = " + lag);
104         if (expected != actual) {
105             throw new AssertionError("Expected " + expected + " actual " + actual +
106                     "method call w/ lag = " + lag);
107         }
108     }
109 
called(String name, Object... args)110     public static Object called(String name, Object... args) {
111         List<Object> entry = logEntry(name, args);
112         calledLog.add(entry);
113         return entry;
114     }
115 
logEntry(String name, Object... args)116     private static List<Object> logEntry(String name, Object... args) {
117         return Arrays.asList(name, Arrays.asList(args));
118     }
119 
clear()120     public static void clear() {
121         calledLog.clear();
122     }
123 
getCalled(int lag)124     public static List<Object> getCalled(int lag) {
125         int size = calledLog.size();
126         return size <= lag ? null : calledLog.get(size - lag - 1);
127     }
128 
randomClasses(Class<?>[] classes, int size)129     public static List<Class<?>> randomClasses(Class<?>[] classes, int size) {
130         List<Class<?>> result = new ArrayList<>(size);
131         for (int i = 0; i < size; ++i) {
132             result.add(classes[RNG.nextInt(classes.length)]);
133         }
134         return result;
135     }
136 
getParams(List<Class<?>> classes, boolean isVararg, int argsCount)137     public static List<Class<?>> getParams(List<Class<?>> classes,
138             boolean isVararg, int argsCount) {
139         boolean unmodifiable = true;
140         List<Class<?>> result = classes.subList(0,
141                 Math.min(argsCount, (MAX_ARITY / 2) - 1));
142         int extra = 0;
143         if (argsCount >= MAX_ARITY / 2) {
144             result = new ArrayList<>(result);
145             unmodifiable = false;
146             extra = (int) result.stream().filter(Helper::isDoubleCost).count();
147             int i = result.size();
148             while (result.size() + extra < argsCount) {
149                 Class<?> aClass = classes.get(i);
150                 if (Helper.isDoubleCost(aClass)) {
151                     ++extra;
152                     if (result.size() + extra >= argsCount) {
153                         break;
154                     }
155                 }
156                 result.add(aClass);
157             }
158         }
159         if (isVararg && result.size() > 0) {
160             if (unmodifiable) {
161                 result = new ArrayList<>(result);
162             }
163             int last = result.size() - 1;
164             Class<?> aClass = result.get(last);
165             aClass = Array.newInstance(aClass, 2).getClass();
166             result.set(last, aClass);
167         }
168         return result;
169     }
170 
addTrailingArgs(MethodHandle target, int nargs, List<Class<?>> classes)171     public static MethodHandle addTrailingArgs(MethodHandle target, int nargs,
172             List<Class<?>> classes) {
173         int targetLen = target.type().parameterCount();
174         int extra = (nargs - targetLen);
175         if (extra <= 0) {
176             return target;
177         }
178         List<Class<?>> fakeArgs = new ArrayList<>(extra);
179         for (int i = 0; i < extra; ++i) {
180             fakeArgs.add(classes.get(i % classes.size()));
181         }
182         return MethodHandles.dropArguments(target, targetLen, fakeArgs);
183     }
184 
varargsList(int arity)185     public static MethodHandle varargsList(int arity) {
186         return AS_LIST.asCollector(Object[].class, arity);
187     }
188 
nextArg(boolean moreBits)189     private static long nextArg(boolean moreBits) {
190         long val = nextArgVal++;
191         long sign = -(val & 1); // alternate signs
192         val >>= 1;
193         if (moreBits)
194         // Guarantee some bits in the high word.
195         // In any case keep the decimal representation simple-looking,
196         // with lots of zeroes, so as not to make the printed decimal
197         // strings unnecessarily noisy.
198         {
199             val += (val % ONE_MILLION) * TEN_BILLION;
200         }
201         return val ^ sign;
202     }
203 
nextArg()204     private static int nextArg() {
205         // Produce a 32-bit result something like ONE_MILLION+(smallint).
206         // Example: 1_000_042.
207         return (int) nextArg(false);
208     }
209 
nextArg(Class<?> kind)210     private static long nextArg(Class<?> kind) {
211         if (kind == long.class || kind == Long.class ||
212                 kind == double.class || kind == Double.class)
213         // produce a 64-bit result something like
214         // ((TEN_BILLION+1) * (ONE_MILLION+(smallint)))
215         // Example: 10_000_420_001_000_042.
216         {
217             return nextArg(true);
218         }
219         return (long) nextArg();
220     }
221 
randomArg(Class<?> param)222     private static Object randomArg(Class<?> param) {
223         Object wrap = castToWrapperOrNull(nextArg(param), param);
224         if (wrap != null) {
225             return wrap;
226         }
227 
228         if (param.isInterface()) {
229             for (Class<?> c : param.getClasses()) {
230                 if (param.isAssignableFrom(c) && !c.isInterface()) {
231                     param = c;
232                     break;
233                 }
234             }
235         }
236         if (param.isArray()) {
237             Class<?> ctype = param.getComponentType();
238             Object arg = Array.newInstance(ctype, 2);
239             Array.set(arg, 0, randomArg(ctype));
240             return arg;
241         }
242         if (param.isInterface() && param.isAssignableFrom(List.class)) {
243             return Arrays.asList("#" + nextArg());
244         }
245         if (param.isInterface() || param.isAssignableFrom(String.class)) {
246             return "#" + nextArg();
247         }
248 
249         try {
250             return param.newInstance();
251         } catch (InstantiationException | IllegalAccessException ex) {
252         }
253         return null;  // random class not Object, String, Integer, etc.
254     }
255 
randomArgs(Class<?>.... params)256     public static Object[] randomArgs(Class<?>... params) {
257         Object[] args = new Object[params.length];
258         for (int i = 0; i < args.length; i++) {
259             args[i] = randomArg(params[i]);
260         }
261         return args;
262     }
263 
randomArgs(int nargs, Class<?> param)264     public static Object[] randomArgs(int nargs, Class<?> param) {
265         Object[] args = new Object[nargs];
266         for (int i = 0; i < args.length; i++) {
267             args[i] = randomArg(param);
268         }
269         return args;
270     }
271 
randomArgs(int nargs, Class<?>... params)272     public static Object[] randomArgs(int nargs, Class<?>... params) {
273         Object[] args = new Object[nargs];
274         for (int i = 0; i < args.length; i++) {
275             Class<?> param = params[i % params.length];
276             args[i] = randomArg(param);
277         }
278         return args;
279     }
280 
randomArgs(List<Class<?>> params)281     public static Object[] randomArgs(List<Class<?>> params) {
282         return randomArgs(params.toArray(new Class<?>[params.size()]));
283     }
284 
castToWrapper(Object value, Class<?> dst)285     public static Object castToWrapper(Object value, Class<?> dst) {
286         Object wrap = null;
287         if (value instanceof Number) {
288             wrap = castToWrapperOrNull(((Number) value).longValue(), dst);
289         }
290         if (value instanceof Character) {
291             wrap = castToWrapperOrNull((char) (Character) value, dst);
292         }
293         if (wrap != null) {
294             return wrap;
295         }
296         return dst.cast(value);
297     }
298 
299     @SuppressWarnings("cast")
300     // primitive cast to (long) is part of the pattern
castToWrapperOrNull(long value, Class<?> dst)301     private static Object castToWrapperOrNull(long value, Class<?> dst) {
302         if (dst == int.class || dst == Integer.class) {
303             return (int) (value);
304         }
305         if (dst == long.class || dst == Long.class) {
306             return (long) (value);
307         }
308         if (dst == char.class || dst == Character.class) {
309             return (char) (value);
310         }
311         if (dst == short.class || dst == Short.class) {
312             return (short) (value);
313         }
314         if (dst == float.class || dst == Float.class) {
315             return (float) (value);
316         }
317         if (dst == double.class || dst == Double.class) {
318             return (double) (value);
319         }
320         if (dst == byte.class || dst == Byte.class) {
321             return (byte) (value);
322         }
323         if (dst == boolean.class || dst == Boolean.class) {
324             return ((value % 29) & 1) == 0;
325         }
326         return null;
327     }
328 
329     /**
330      * Routine used to obtain a randomly generated method type.
331      *
332      * @param arity Arity of returned method type.
333      * @return MethodType generated randomly.
334      */
randomMethodTypeGenerator(int arity)335     public static MethodType randomMethodTypeGenerator(int arity) {
336         final Class<?>[] CLASSES = {
337             Object.class,
338             int.class,
339             boolean.class,
340             byte.class,
341             short.class,
342             char.class,
343             long.class,
344             float.class,
345             double.class
346         };
347         if (arity > MAX_ARITY) {
348             throw new IllegalArgumentException(
349                     String.format("Arity should not exceed %d!", MAX_ARITY));
350         }
351         List<Class<?>> list = randomClasses(CLASSES, arity);
352         list = getParams(list, false, arity);
353         int i = RNG.nextInt(CLASSES.length + 1);
354         Class<?> rtype = i == CLASSES.length ? void.class : CLASSES[i];
355         return MethodType.methodType(rtype, list);
356     }
357 }
358