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 }