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