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 }