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