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