8038186: [TESTBUG] improvements of test j.l.i.MethodHandles
Reviewed-by: iveresov, twisti, vlivanov
This commit is contained in:
parent
6db88c8747
commit
4fdcaf04d8
542
jdk/test/java/lang/invoke/MethodHandles/CatchExceptionTest.java
Normal file
542
jdk/test/java/lang/invoke/MethodHandles/CatchExceptionTest.java
Normal file
@ -0,0 +1,542 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.MethodHandles;
|
||||
|
||||
import com.oracle.testlibrary.jsr292.Helper;
|
||||
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.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/* @test
|
||||
* @library /lib/testlibrary/jsr292 /lib/testlibrary/
|
||||
* @compile CatchExceptionTest.java
|
||||
* @run main/othervm -esa test.java.lang.invoke.MethodHandles.CatchExceptionTest
|
||||
*/
|
||||
public class CatchExceptionTest {
|
||||
private static final List<Class<?>> ARGS_CLASSES;
|
||||
protected static final int MAX_ARITY = Helper.MAX_ARITY - 1;
|
||||
static {
|
||||
Class<?> classes[] = {
|
||||
Object.class,
|
||||
long.class,
|
||||
int.class,
|
||||
byte.class,
|
||||
Integer[].class,
|
||||
double[].class,
|
||||
String.class,
|
||||
};
|
||||
List<Class<?>> list = new ArrayList<>(MAX_ARITY);
|
||||
for (int i = 0; i < MAX_ARITY; ++i) {
|
||||
list.add(classes[Helper.RNG.nextInt(classes.length)]);
|
||||
}
|
||||
ARGS_CLASSES = Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
private final TestCase testCase;
|
||||
private final int nargs;
|
||||
private final int argsCount;
|
||||
private final MethodHandle catcher;
|
||||
private int dropped;
|
||||
private MethodHandle thrower;
|
||||
|
||||
|
||||
public CatchExceptionTest(TestCase testCase, final boolean isVararg, final int argsCount,
|
||||
final int catchDrops) {
|
||||
this.testCase = testCase;
|
||||
this.dropped = catchDrops;
|
||||
if (Helper.IS_VERBOSE) {
|
||||
System.out.printf("CatchException::CatchException(%s, isVararg=%b " +
|
||||
"argsCount=%d catchDrops=%d)%n",
|
||||
testCase, isVararg, argsCount, catchDrops
|
||||
);
|
||||
}
|
||||
MethodHandle thrower = testCase.thrower;
|
||||
int throwerLen = thrower.type().parameterCount();
|
||||
List<Class<?>> classes;
|
||||
int extra = Math.max(0, argsCount - throwerLen);
|
||||
classes = getThrowerParams(isVararg, extra);
|
||||
this.argsCount = throwerLen + classes.size();
|
||||
thrower = Helper.addTrailingArgs(thrower, this.argsCount, classes);
|
||||
if (isVararg && argsCount > throwerLen) {
|
||||
MethodType mt = thrower.type();
|
||||
Class<?> lastParam = mt.parameterType(mt.parameterCount() - 1);
|
||||
thrower = thrower.asVarargsCollector(lastParam);
|
||||
}
|
||||
this.thrower = thrower;
|
||||
this.dropped = Math.min(this.argsCount, catchDrops);
|
||||
catcher = testCase.getCatcher(getCatcherParams());
|
||||
nargs = Math.max(2, this.argsCount);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
for (CatchExceptionTest test : TestFactory.MANDATORY_TEST_CASES) {
|
||||
test.runTest();
|
||||
}
|
||||
TestFactory factory = new TestFactory();
|
||||
CatchExceptionTest test;
|
||||
while ((test = factory.nextTest()) != null ) {
|
||||
test.runTest();
|
||||
}
|
||||
}
|
||||
|
||||
private List<Class<?>> getThrowerParams(boolean isVararg, int argsCount) {
|
||||
boolean unmodifiable = true;
|
||||
List<Class<?>> classes;
|
||||
classes = ARGS_CLASSES.subList(0,
|
||||
Math.min(argsCount, (MAX_ARITY / 2) - 1));
|
||||
int extra = 0;
|
||||
if (argsCount >= MAX_ARITY / 2) {
|
||||
classes = new ArrayList<>(classes);
|
||||
unmodifiable = false;
|
||||
extra = (int) classes.stream().filter(Helper::isDoubleCost).count();
|
||||
int i = classes.size();
|
||||
while (classes.size() + extra < argsCount) {
|
||||
Class<?> aClass = ARGS_CLASSES.get(i);
|
||||
if (Helper.isDoubleCost(aClass)) {
|
||||
++extra;
|
||||
if (classes.size() + extra >= argsCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
classes.add(aClass);
|
||||
}
|
||||
}
|
||||
if (isVararg && classes.size() > 0) {
|
||||
if (unmodifiable) {
|
||||
classes = new ArrayList<>(classes);
|
||||
}
|
||||
int last = classes.size() - 1;
|
||||
Class<?> aClass = classes.get(classes.size() - 1);
|
||||
aClass = Array.newInstance(aClass, 2).getClass();
|
||||
classes.set(last, aClass);
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
|
||||
private List<Class<?>> getCatcherParams() {
|
||||
int catchArgc = 1 + this.argsCount - dropped;
|
||||
List<Class<?>> result = new ArrayList<>(
|
||||
thrower.type().parameterList().subList(0, catchArgc - 1));
|
||||
// prepend throwable
|
||||
result.add(0, testCase.throwableClass);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void runTest() {
|
||||
Helper.clear();
|
||||
|
||||
Object[] args = Helper.randomArgs(
|
||||
argsCount, thrower.type().parameterArray());
|
||||
Object arg0 = Helper.MISSING_ARG;
|
||||
Object arg1 = testCase.thrown;
|
||||
if (argsCount > 0) {
|
||||
arg0 = args[0];
|
||||
}
|
||||
if (argsCount > 1) {
|
||||
args[1] = arg1;
|
||||
}
|
||||
Asserts.assertEQ(nargs, thrower.type().parameterCount());
|
||||
if (argsCount < nargs) {
|
||||
Object[] appendArgs = {arg0, arg1};
|
||||
appendArgs = Arrays.copyOfRange(appendArgs, argsCount, nargs);
|
||||
thrower = MethodHandles.insertArguments(
|
||||
thrower, argsCount, appendArgs);
|
||||
}
|
||||
Asserts.assertEQ(argsCount, thrower.type().parameterCount());
|
||||
|
||||
MethodHandle target = MethodHandles.catchException(
|
||||
testCase.filter(thrower), testCase.throwableClass,
|
||||
testCase.filter(catcher));
|
||||
|
||||
Asserts.assertEQ(thrower.type(), target.type());
|
||||
Asserts.assertEQ(argsCount, target.type().parameterCount());
|
||||
|
||||
Object returned;
|
||||
try {
|
||||
returned = target.invokeWithArguments(args);
|
||||
} catch (Throwable ex) {
|
||||
testCase.assertCatch(ex);
|
||||
returned = ex;
|
||||
}
|
||||
|
||||
testCase.assertReturn(returned, arg0, arg1, dropped, args);
|
||||
}
|
||||
}
|
||||
|
||||
class TestFactory {
|
||||
public static final List<CatchExceptionTest> MANDATORY_TEST_CASES = new ArrayList<>();
|
||||
|
||||
private static final int MIN_TESTED_ARITY = 10;
|
||||
|
||||
static {
|
||||
for (int[] args : new int[][]{
|
||||
{0, 0},
|
||||
{MIN_TESTED_ARITY, 0},
|
||||
{MIN_TESTED_ARITY, MIN_TESTED_ARITY},
|
||||
{CatchExceptionTest.MAX_ARITY, 0},
|
||||
{CatchExceptionTest.MAX_ARITY, CatchExceptionTest.MAX_ARITY},
|
||||
}) {
|
||||
MANDATORY_TEST_CASES.addAll(createTests(args[0], args[1]));
|
||||
}
|
||||
}
|
||||
|
||||
private int count;
|
||||
private int args;
|
||||
private int dropArgs;
|
||||
private int currentMaxDrops;
|
||||
private int maxArgs;
|
||||
private int maxDrops;
|
||||
private int constructor;
|
||||
private int constructorSize;
|
||||
private boolean isVararg;
|
||||
|
||||
public TestFactory() {
|
||||
if (Helper.IS_THOROUGH) {
|
||||
maxArgs = maxDrops = CatchExceptionTest.MAX_ARITY;
|
||||
} else {
|
||||
maxArgs = MIN_TESTED_ARITY
|
||||
+ Helper.RNG.nextInt(CatchExceptionTest.MAX_ARITY
|
||||
- MIN_TESTED_ARITY)
|
||||
+ 1;
|
||||
maxDrops = MIN_TESTED_ARITY
|
||||
+ Helper.RNG.nextInt(maxArgs - MIN_TESTED_ARITY)
|
||||
+ 1;
|
||||
args = 1;
|
||||
}
|
||||
|
||||
if (Helper.IS_VERBOSE) {
|
||||
System.out.printf("maxArgs = %d%nmaxDrops = %d%n",
|
||||
maxArgs, maxDrops);
|
||||
}
|
||||
constructorSize = TestCase.CONSTRUCTORS.size();
|
||||
}
|
||||
|
||||
private static List<CatchExceptionTest> createTests(int argsCount,
|
||||
int catchDrops) {
|
||||
if (catchDrops > argsCount || argsCount < 0 || catchDrops < 0) {
|
||||
throw new IllegalArgumentException("argsCount = " + argsCount
|
||||
+ ", catchDrops = " + catchDrops
|
||||
);
|
||||
}
|
||||
List<CatchExceptionTest> result = new ArrayList<>(
|
||||
TestCase.CONSTRUCTORS.size());
|
||||
for (Supplier<TestCase> constructor : TestCase.CONSTRUCTORS) {
|
||||
result.add(new CatchExceptionTest(constructor.get(),
|
||||
/* isVararg = */ true,
|
||||
argsCount,
|
||||
catchDrops));
|
||||
result.add(new CatchExceptionTest(constructor.get(),
|
||||
/* isVararg = */ false,
|
||||
argsCount,
|
||||
catchDrops));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return next test from test matrix:
|
||||
* {varArgs, noVarArgs} x TestCase.rtypes x TestCase.THROWABLES x {1, .., maxArgs } x {1, .., maxDrops}
|
||||
*/
|
||||
public CatchExceptionTest nextTest() {
|
||||
if (constructor < constructorSize) {
|
||||
return createTest();
|
||||
}
|
||||
constructor = 0;
|
||||
count++;
|
||||
if (!Helper.IS_THOROUGH && count > Helper.TEST_LIMIT) {
|
||||
System.out.println("test limit is exceeded");
|
||||
return null;
|
||||
}
|
||||
if (dropArgs <= currentMaxDrops) {
|
||||
if (dropArgs == 1) {
|
||||
if (Helper.IS_THOROUGH || Helper.RNG.nextBoolean()) {
|
||||
++dropArgs;
|
||||
return createTest();
|
||||
} else if (Helper.IS_VERBOSE) {
|
||||
System.out.printf(
|
||||
"argsCount=%d : \"drop\" scenarios are skipped%n",
|
||||
args);
|
||||
}
|
||||
} else {
|
||||
++dropArgs;
|
||||
return createTest();
|
||||
}
|
||||
}
|
||||
|
||||
if (args <= maxArgs) {
|
||||
dropArgs = 1;
|
||||
currentMaxDrops = Math.min(args, maxDrops);
|
||||
++args;
|
||||
return createTest();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private CatchExceptionTest createTest() {
|
||||
if (!Helper.IS_THOROUGH) {
|
||||
return new CatchExceptionTest(
|
||||
TestCase.CONSTRUCTORS.get(constructor++).get(),
|
||||
Helper.RNG.nextBoolean(), args, dropArgs);
|
||||
} else {
|
||||
if (isVararg) {
|
||||
isVararg = false;
|
||||
return new CatchExceptionTest(
|
||||
TestCase.CONSTRUCTORS.get(constructor++).get(),
|
||||
isVararg, args, dropArgs);
|
||||
} else {
|
||||
isVararg = true;
|
||||
return new CatchExceptionTest(
|
||||
TestCase.CONSTRUCTORS.get(constructor).get(),
|
||||
isVararg, args, dropArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestCase<T> {
|
||||
private static enum ThrowMode {
|
||||
NOTHING,
|
||||
CAUGHT,
|
||||
UNCAUGHT,
|
||||
ADAPTER
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final List<Supplier<TestCase>> CONSTRUCTORS;
|
||||
private static final MethodHandle FAKE_IDENTITY;
|
||||
private static final MethodHandle THROW_OR_RETURN;
|
||||
private static final MethodHandle CATCHER;
|
||||
|
||||
static {
|
||||
try {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
THROW_OR_RETURN = lookup.findStatic(
|
||||
TestCase.class,
|
||||
"throwOrReturn",
|
||||
MethodType.methodType(Object.class, Object.class,
|
||||
Throwable.class)
|
||||
);
|
||||
CATCHER = lookup.findStatic(
|
||||
TestCase.class,
|
||||
"catcher",
|
||||
MethodType.methodType(Object.class, Object.class));
|
||||
FAKE_IDENTITY = lookup.findVirtual(
|
||||
TestCase.class, "fakeIdentity",
|
||||
MethodType.methodType(Object.class, Object.class));
|
||||
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
PartialConstructor[] constructors = {
|
||||
create(Object.class, Object.class::cast),
|
||||
create(String.class, Objects::toString),
|
||||
create(int[].class, x -> new int[]{Objects.hashCode(x)}),
|
||||
create(long.class,
|
||||
x -> Objects.hashCode(x) & (-1L >>> 32)),
|
||||
create(void.class, TestCase::noop)};
|
||||
Throwable[] throwables = {
|
||||
new ClassCastException("testing"),
|
||||
new java.io.IOException("testing"),
|
||||
new LinkageError("testing")};
|
||||
List<Supplier<TestCase>> list = new ArrayList<>(constructors.length *
|
||||
throwables.length * ThrowMode.values().length);
|
||||
//noinspection unchecked
|
||||
for (PartialConstructor f : constructors) {
|
||||
for (ThrowMode mode : ThrowMode.values()) {
|
||||
for (Throwable t : throwables) {
|
||||
list.add(f.apply(mode, t));
|
||||
}
|
||||
}
|
||||
}
|
||||
CONSTRUCTORS = Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
public final Class<T> rtype;
|
||||
public final ThrowMode throwMode;
|
||||
public final Throwable thrown;
|
||||
public final Class<? extends Throwable> throwableClass;
|
||||
/**
|
||||
* MH which takes 2 args (Object,Throwable), 1st is the return value,
|
||||
* 2nd is the exception which will be thrown, if it's supposed in current
|
||||
* {@link #throwMode}.
|
||||
*/
|
||||
public final MethodHandle thrower;
|
||||
private final Function<Object, T> cast;
|
||||
protected MethodHandle filter;
|
||||
private int fakeIdentityCount;
|
||||
|
||||
private TestCase(Class<T> rtype, Function<Object, T> cast,
|
||||
ThrowMode throwMode, Throwable thrown)
|
||||
throws NoSuchMethodException, IllegalAccessException {
|
||||
this.cast = cast;
|
||||
filter = MethodHandles.lookup().findVirtual(
|
||||
Function.class,
|
||||
"apply",
|
||||
MethodType.methodType(Object.class, Object.class))
|
||||
.bindTo(cast);
|
||||
this.rtype = rtype;
|
||||
this.throwMode = throwMode;
|
||||
this.throwableClass = thrown.getClass();
|
||||
switch (throwMode) {
|
||||
case NOTHING:
|
||||
this.thrown = null;
|
||||
break;
|
||||
case ADAPTER:
|
||||
case UNCAUGHT:
|
||||
this.thrown = new Error("do not catch this");
|
||||
break;
|
||||
default:
|
||||
this.thrown = thrown;
|
||||
}
|
||||
|
||||
MethodHandle throwOrReturn = THROW_OR_RETURN;
|
||||
if (throwMode == ThrowMode.ADAPTER) {
|
||||
MethodHandle fakeIdentity = FAKE_IDENTITY.bindTo(this);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
throwOrReturn = MethodHandles.filterReturnValue(
|
||||
throwOrReturn, fakeIdentity);
|
||||
}
|
||||
}
|
||||
thrower = throwOrReturn.asType(MethodType.genericMethodType(2));
|
||||
}
|
||||
|
||||
private static Void noop(Object x) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static <T2> PartialConstructor create(
|
||||
Class<T2> rtype, Function<Object, T2> cast) {
|
||||
return (t, u) -> () -> {
|
||||
try {
|
||||
return new TestCase<>(rtype, cast, t, u);
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static <T extends Throwable>
|
||||
Object throwOrReturn(Object normal, T exception) throws T {
|
||||
if (exception != null) {
|
||||
Helper.called("throwOrReturn/throw", normal, exception);
|
||||
throw exception;
|
||||
}
|
||||
Helper.called("throwOrReturn/normal", normal, exception);
|
||||
return normal;
|
||||
}
|
||||
|
||||
private static <T extends Throwable>
|
||||
Object catcher(Object o) {
|
||||
Helper.called("catcher", o);
|
||||
return o;
|
||||
}
|
||||
|
||||
public MethodHandle filter(MethodHandle target) {
|
||||
return MethodHandles.filterReturnValue(target, filter);
|
||||
}
|
||||
|
||||
public MethodHandle getCatcher(List<Class<?>> classes) {
|
||||
return MethodHandles.filterReturnValue(Helper.AS_LIST.asType(
|
||||
MethodType.methodType(Object.class, classes)),
|
||||
CATCHER
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TestCase{" +
|
||||
"rtype=" + rtype +
|
||||
", throwMode=" + throwMode +
|
||||
", throwableClass=" + throwableClass +
|
||||
'}';
|
||||
}
|
||||
|
||||
public String callName() {
|
||||
return "throwOrReturn/" +
|
||||
(throwMode == ThrowMode.NOTHING
|
||||
? "normal"
|
||||
: "throw");
|
||||
}
|
||||
|
||||
public void assertReturn(Object returned, Object arg0, Object arg1,
|
||||
int catchDrops, Object... args) {
|
||||
int lag = 0;
|
||||
if (throwMode == ThrowMode.CAUGHT) {
|
||||
lag = 1;
|
||||
}
|
||||
Helper.assertCalled(lag, callName(), arg0, arg1);
|
||||
|
||||
if (throwMode == ThrowMode.NOTHING) {
|
||||
assertEQ(cast.apply(arg0), returned);
|
||||
} else if (throwMode == ThrowMode.CAUGHT) {
|
||||
List<Object> catchArgs = new ArrayList<>(Arrays.asList(args));
|
||||
// catcher receives an initial subsequence of target arguments:
|
||||
catchArgs.subList(args.length - catchDrops, args.length).clear();
|
||||
// catcher also receives the exception, prepended:
|
||||
catchArgs.add(0, thrown);
|
||||
Helper.assertCalled("catcher", catchArgs);
|
||||
assertEQ(cast.apply(catchArgs), returned);
|
||||
}
|
||||
Asserts.assertEQ(0, fakeIdentityCount);
|
||||
}
|
||||
|
||||
private void assertEQ(T t, Object returned) {
|
||||
if (rtype.isArray()) {
|
||||
Asserts.assertEQ(t.getClass(), returned.getClass());
|
||||
int n = Array.getLength(t);
|
||||
Asserts.assertEQ(n, Array.getLength(returned));
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Asserts.assertEQ(Array.get(t, i), Array.get(returned, i));
|
||||
}
|
||||
} else {
|
||||
Asserts.assertEQ(t, returned);
|
||||
}
|
||||
}
|
||||
|
||||
private Object fakeIdentity(Object x) {
|
||||
System.out.println("should throw through this!");
|
||||
++fakeIdentityCount;
|
||||
return x;
|
||||
}
|
||||
|
||||
public void assertCatch(Throwable ex) {
|
||||
try {
|
||||
Asserts.assertSame(thrown, ex,
|
||||
"must get the out-of-band exception");
|
||||
} catch (Throwable t) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public interface PartialConstructor
|
||||
extends BiFunction<ThrowMode, Throwable, Supplier<TestCase>> {
|
||||
}
|
||||
}
|
@ -2405,108 +2405,6 @@ public class MethodHandlesTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCatchException() throws Throwable {
|
||||
if (CAN_SKIP_WORKING) return;
|
||||
startTest("catchException");
|
||||
for (int nargs = 0; nargs < 40; nargs++) {
|
||||
if (CAN_TEST_LIGHTLY && nargs > 11) break;
|
||||
for (int throwMode = 0; throwMode < THROW_MODE_LIMIT; throwMode++) {
|
||||
testCatchException(int.class, new ClassCastException("testing"), throwMode, nargs);
|
||||
if (CAN_TEST_LIGHTLY && nargs > 3) continue;
|
||||
testCatchException(void.class, new java.io.IOException("testing"), throwMode, nargs);
|
||||
testCatchException(String.class, new LinkageError("testing"), throwMode, nargs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final int THROW_NOTHING = 0, THROW_CAUGHT = 1, THROW_UNCAUGHT = 2, THROW_THROUGH_ADAPTER = 3, THROW_MODE_LIMIT = 4;
|
||||
|
||||
void testCatchException(Class<?> returnType, Throwable thrown, int throwMode, int nargs) throws Throwable {
|
||||
testCatchException(returnType, thrown, throwMode, nargs, 0);
|
||||
if (nargs <= 5 || nargs % 10 == 3) {
|
||||
for (int catchDrops = 1; catchDrops <= nargs; catchDrops++)
|
||||
testCatchException(returnType, thrown, throwMode, nargs, catchDrops);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends Throwable>
|
||||
Object throwOrReturn(Object normal, T exception) throws T {
|
||||
if (exception != null) {
|
||||
called("throwOrReturn/throw", normal, exception);
|
||||
throw exception;
|
||||
}
|
||||
called("throwOrReturn/normal", normal, exception);
|
||||
return normal;
|
||||
}
|
||||
private int fakeIdentityCount;
|
||||
private Object fakeIdentity(Object x) {
|
||||
System.out.println("should throw through this!");
|
||||
fakeIdentityCount++;
|
||||
return x;
|
||||
}
|
||||
|
||||
void testCatchException(Class<?> returnType, Throwable thrown, int throwMode, int nargs, int catchDrops) throws Throwable {
|
||||
countTest();
|
||||
if (verbosity >= 3)
|
||||
System.out.println("catchException rt="+returnType+" throw="+throwMode+" nargs="+nargs+" drops="+catchDrops);
|
||||
Class<? extends Throwable> exType = thrown.getClass();
|
||||
if (throwMode > THROW_CAUGHT) thrown = new UnsupportedOperationException("do not catch this");
|
||||
MethodHandle throwOrReturn
|
||||
= PRIVATE.findStatic(MethodHandlesTest.class, "throwOrReturn",
|
||||
MethodType.methodType(Object.class, Object.class, Throwable.class));
|
||||
if (throwMode == THROW_THROUGH_ADAPTER) {
|
||||
MethodHandle fakeIdentity
|
||||
= PRIVATE.findVirtual(MethodHandlesTest.class, "fakeIdentity",
|
||||
MethodType.methodType(Object.class, Object.class)).bindTo(this);
|
||||
for (int i = 0; i < 10; i++)
|
||||
throwOrReturn = MethodHandles.filterReturnValue(throwOrReturn, fakeIdentity);
|
||||
}
|
||||
int nargs1 = Math.max(2, nargs);
|
||||
MethodHandle thrower = throwOrReturn.asType(MethodType.genericMethodType(2));
|
||||
thrower = addTrailingArgs(thrower, nargs, Object.class);
|
||||
int catchArgc = 1 + nargs - catchDrops;
|
||||
MethodHandle catcher = varargsList(catchArgc).asType(MethodType.genericMethodType(catchArgc));
|
||||
Object[] args = randomArgs(nargs, Object.class);
|
||||
Object arg0 = MISSING_ARG;
|
||||
Object arg1 = (throwMode == THROW_NOTHING) ? (Throwable) null : thrown;
|
||||
if (nargs > 0) arg0 = args[0];
|
||||
if (nargs > 1) args[1] = arg1;
|
||||
assertEquals(nargs1, thrower.type().parameterCount());
|
||||
if (nargs < nargs1) {
|
||||
Object[] appendArgs = { arg0, arg1 };
|
||||
appendArgs = Arrays.copyOfRange(appendArgs, nargs, nargs1);
|
||||
thrower = MethodHandles.insertArguments(thrower, nargs, appendArgs);
|
||||
}
|
||||
assertEquals(nargs, thrower.type().parameterCount());
|
||||
MethodHandle target = MethodHandles.catchException(thrower, exType, catcher);
|
||||
assertEquals(thrower.type(), target.type());
|
||||
assertEquals(nargs, target.type().parameterCount());
|
||||
//System.out.println("catching with "+target+" : "+throwOrReturn);
|
||||
Object returned;
|
||||
try {
|
||||
returned = target.invokeWithArguments(args);
|
||||
} catch (Throwable ex) {
|
||||
assertSame("must get the out-of-band exception", thrown, ex);
|
||||
if (throwMode <= THROW_CAUGHT)
|
||||
assertEquals(THROW_UNCAUGHT, throwMode);
|
||||
returned = ex;
|
||||
}
|
||||
assertCalled("throwOrReturn/"+(throwMode == THROW_NOTHING ? "normal" : "throw"), arg0, arg1);
|
||||
//System.out.println("return from "+target+" : "+returned);
|
||||
if (throwMode == THROW_NOTHING) {
|
||||
assertSame(arg0, returned);
|
||||
} else if (throwMode == THROW_CAUGHT) {
|
||||
List<Object> catchArgs = new ArrayList<>(Arrays.asList(args));
|
||||
// catcher receives an initial subsequence of target arguments:
|
||||
catchArgs.subList(nargs - catchDrops, nargs).clear();
|
||||
// catcher also receives the exception, prepended:
|
||||
catchArgs.add(0, thrown);
|
||||
assertEquals(catchArgs, returned);
|
||||
}
|
||||
assertEquals(0, fakeIdentityCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrowException() throws Throwable {
|
||||
if (CAN_SKIP_WORKING) return;
|
||||
|
@ -206,6 +206,34 @@ public class Asserts {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #assertSame(java.lang.Object, java.lang.Object, java.lang.String)} with a default message.
|
||||
*
|
||||
* @param lhs The left hand side of the comparison.
|
||||
* @param rhs The right hand side of the comparison.
|
||||
* @see #assertSame(Object, Object, String)
|
||||
*/
|
||||
public static void assertSame(Object lhs, Object rhs) {
|
||||
assertSame(lhs, rhs, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that {@code lhs} is the same as {@code rhs}.
|
||||
*
|
||||
* @param lhs The left hand side of the comparison.
|
||||
* @param rhs The right hand side of the comparison.
|
||||
* @param msg A description of the assumption; {@code null} for a default message.
|
||||
* @throws RuntimeException if the assertion is not true.
|
||||
*/
|
||||
public static void assertSame(Object lhs, Object rhs, String msg) {
|
||||
if (lhs != rhs) {
|
||||
msg = Objects.toString(msg, "assertSame")
|
||||
+ ": expected " + Objects.toString(lhs)
|
||||
+ " to equal " + Objects.toString(rhs);
|
||||
fail(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for {@link #assertGreaterThanOrEqual(Comparable, Comparable)}.
|
||||
*
|
||||
|
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 com.oracle.testlibrary.jsr292;
|
||||
|
||||
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.*;
|
||||
|
||||
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) : 2_000L;
|
||||
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 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()]));
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user