blob: 4524d586844e8511846d85529ebc9b585b0c1c76 [file] [log] [blame]
/*
* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package test.java.lang.invoke.lib;
import jdk.testlibrary.Asserts;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class Helper {
/** Flag for verbose output, true if {@code -Dverbose} specified */
public static final boolean IS_VERBOSE
= System.getProperty("verbose") != null;
/**
* Flag for thorough testing -- all test will be executed,
* true if {@code -Dthorough} specified. */
public static final boolean IS_THOROUGH
= System.getProperty("thorough") != null;
/** Random number generator w/ initial seed equal to {@code -Dseed} */
public static final Random RNG;
static {
String str = System.getProperty("seed");
long seed = str != null ? Long.parseLong(str) : new Random().nextLong();
RNG = new Random(seed);
System.out.printf("-Dseed=%d%n", seed);
}
public static final long TEST_LIMIT;
static {
String str = System.getProperty("testLimit");
TEST_LIMIT = str != null ? Long.parseUnsignedLong(str) : 2000L;
System.out.printf("-DtestLimit=%d%n", TEST_LIMIT);
}
public static final int MAX_ARITY = 254;
public static final String MISSING_ARG = "missingArg";
public static final String MISSING_ARG_2 = "missingArg#2";
private static final int
// first int value
ONE_MILLION = (1000 * 1000),
// scale factor to reach upper 32 bits
TEN_BILLION = (10 * 1000 * 1000 * 1000),
// <<1 makes space for sign bit;
INITIAL_ARG_VAL = ONE_MILLION << 1;
public static final MethodHandle AS_LIST;
static {
try {
AS_LIST = MethodHandles.lookup().findStatic(
Arrays.class, "asList",
MethodType.methodType(List.class, Object[].class));
} catch (NoSuchMethodException | IllegalAccessException ex) {
throw new Error(ex);
}
}
public static boolean isDoubleCost(Class<?> aClass) {
return aClass == double.class || aClass == long.class;
}
private static List<List<Object>> calledLog = new ArrayList<>();
private static long nextArgVal;
public static void assertCalled(String name, Object... args) {
assertCalled(0, name, args);
}
public static void assertCalled(int lag, String name, Object... args) {
Object expected = logEntry(name, args);
Object actual = getCalled(lag);
Asserts.assertEQ(expected, actual, "method call w/ lag = " + lag);
}
public static Object called(String name, Object... args) {
List<Object> entry = logEntry(name, args);
calledLog.add(entry);
return entry;
}
private static List<Object> logEntry(String name, Object... args) {
return Arrays.asList(name, Arrays.asList(args));
}
public static void clear() {
calledLog.clear();
}
public static List<Object> getCalled(int lag) {
int size = calledLog.size();
return size <= lag ? null : calledLog.get(size - lag - 1);
}
public static List<Class<?>> randomClasses(Class<?>[] classes, int size) {
List<Class<?>> result = new ArrayList<>(size);
for (int i = 0; i < size; ++i) {
result.add(classes[RNG.nextInt(classes.length)]);
}
return result;
}
public static List<Class<?>> getParams(List<Class<?>> classes,
boolean isVararg, int argsCount) {
boolean unmodifiable = true;
List<Class<?>> result = classes.subList(0,
Math.min(argsCount, (MAX_ARITY / 2) - 1));
int extra = 0;
if (argsCount >= MAX_ARITY / 2) {
result = new ArrayList<>(result);
unmodifiable = false;
extra = (int) result.stream().filter(Helper::isDoubleCost).count();
int i = result.size();
while (result.size() + extra < argsCount) {
Class<?> aClass = classes.get(i);
if (Helper.isDoubleCost(aClass)) {
++extra;
if (result.size() + extra >= argsCount) {
break;
}
}
result.add(aClass);
}
}
if (isVararg && result.size() > 0) {
if (unmodifiable) {
result = new ArrayList<>(result);
}
int last = result.size() - 1;
Class<?> aClass = result.get(last);
aClass = Array.newInstance(aClass, 2).getClass();
result.set(last, aClass);
}
return result;
}
public static MethodHandle addTrailingArgs(MethodHandle target, int nargs,
List<Class<?>> classes) {
int targetLen = target.type().parameterCount();
int extra = (nargs - targetLen);
if (extra <= 0) {
return target;
}
List<Class<?>> fakeArgs = new ArrayList<>(extra);
for (int i = 0; i < extra; ++i) {
fakeArgs.add(classes.get(i % classes.size()));
}
return MethodHandles.dropArguments(target, targetLen, fakeArgs);
}
public static MethodHandle varargsList(int arity) {
return AS_LIST.asCollector(Object[].class, arity);
}
private static long nextArg(boolean moreBits) {
long val = nextArgVal++;
long sign = -(val & 1); // alternate signs
val >>= 1;
if (moreBits)
// Guarantee some bits in the high word.
// In any case keep the decimal representation simple-looking,
// with lots of zeroes, so as not to make the printed decimal
// strings unnecessarily noisy.
{
val += (val % ONE_MILLION) * TEN_BILLION;
}
return val ^ sign;
}
private static int nextArg() {
// Produce a 32-bit result something like ONE_MILLION+(smallint).
// Example: 1_000_042.
return (int) nextArg(false);
}
private static long nextArg(Class<?> kind) {
if (kind == long.class || kind == Long.class ||
kind == double.class || kind == Double.class)
// produce a 64-bit result something like
// ((TEN_BILLION+1) * (ONE_MILLION+(smallint)))
// Example: 10_000_420_001_000_042.
{
return nextArg(true);
}
return (long) nextArg();
}
private static Object randomArg(Class<?> param) {
Object wrap = castToWrapperOrNull(nextArg(param), param);
if (wrap != null) {
return wrap;
}
if (param.isInterface()) {
for (Class<?> c : param.getClasses()) {
if (param.isAssignableFrom(c) && !c.isInterface()) {
param = c;
break;
}
}
}
if (param.isArray()) {
Class<?> ctype = param.getComponentType();
Object arg = Array.newInstance(ctype, 2);
Array.set(arg, 0, randomArg(ctype));
return arg;
}
if (param.isInterface() && param.isAssignableFrom(List.class)) {
return Arrays.asList("#" + nextArg());
}
if (param.isInterface() || param.isAssignableFrom(String.class)) {
return "#" + nextArg();
}
try {
return param.newInstance();
} catch (InstantiationException | IllegalAccessException ex) {
}
return null; // random class not Object, String, Integer, etc.
}
public static Object[] randomArgs(Class<?>... params) {
Object[] args = new Object[params.length];
for (int i = 0; i < args.length; i++) {
args[i] = randomArg(params[i]);
}
return args;
}
public static Object[] randomArgs(int nargs, Class<?> param) {
Object[] args = new Object[nargs];
for (int i = 0; i < args.length; i++) {
args[i] = randomArg(param);
}
return args;
}
public static Object[] randomArgs(int nargs, Class<?>... params) {
Object[] args = new Object[nargs];
for (int i = 0; i < args.length; i++) {
Class<?> param = params[i % params.length];
args[i] = randomArg(param);
}
return args;
}
public static Object[] randomArgs(List<Class<?>> params) {
return randomArgs(params.toArray(new Class<?>[params.size()]));
}
public static Object castToWrapper(Object value, Class<?> dst) {
Object wrap = null;
if (value instanceof Number) {
wrap = castToWrapperOrNull(((Number) value).longValue(), dst);
}
if (value instanceof Character) {
wrap = castToWrapperOrNull((char) (Character) value, dst);
}
if (wrap != null) {
return wrap;
}
return dst.cast(value);
}
@SuppressWarnings("cast")
// primitive cast to (long) is part of the pattern
private static Object castToWrapperOrNull(long value, Class<?> dst) {
if (dst == int.class || dst == Integer.class) {
return (int) (value);
}
if (dst == long.class || dst == Long.class) {
return (long) (value);
}
if (dst == char.class || dst == Character.class) {
return (char) (value);
}
if (dst == short.class || dst == Short.class) {
return (short) (value);
}
if (dst == float.class || dst == Float.class) {
return (float) (value);
}
if (dst == double.class || dst == Double.class) {
return (double) (value);
}
if (dst == byte.class || dst == Byte.class) {
return (byte) (value);
}
if (dst == boolean.class || dst == Boolean.class) {
return ((value % 29) & 1) == 0;
}
return null;
}
/**
* Routine used to obtain a randomly generated method type.
*
* @param arity Arity of returned method type.
* @return MethodType generated randomly.
*/
public static MethodType randomMethodTypeGenerator(int arity) {
final Class<?>[] CLASSES = {
Object.class,
int.class,
boolean.class,
byte.class,
short.class,
char.class,
long.class,
float.class,
double.class
};
if (arity > MAX_ARITY) {
throw new IllegalArgumentException(
String.format("Arity should not exceed %d!", MAX_ARITY));
}
List<Class<?>> list = randomClasses(CLASSES, arity);
list = getParams(list, false, arity);
int i = RNG.nextInt(CLASSES.length + 1);
Class<?> rtype = i == CLASSES.length ? void.class : CLASSES[i];
return MethodType.methodType(rtype, list);
}
}