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