changeset 9287:f0d3a72a7289

8038186: [TESTBUG] improvements of test j.l.i.MethodHandles Reviewed-by: iveresov, twisti, vlivanov
author iignatyev
date Sat, 29 Mar 2014 12:29:21 +0400
parents f46d9d01422c
children de395dde0d4e
files test/java/lang/invoke/MethodHandles/CatchExceptionTest.java test/java/lang/invoke/MethodHandlesTest.java test/lib/testlibrary/jdk/testlibrary/Asserts.java test/lib/testlibrary/jsr292/com/oracle/testlibrary/jsr292/Helper.java
diffstat 4 files changed, 846 insertions(+), 102 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/MethodHandles/CatchExceptionTest.java	Sat Mar 29 12:29:21 2014 +0400
@@ -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>> {
+    }
+}
--- a/test/java/lang/invoke/MethodHandlesTest.java	Fri Mar 28 20:34:46 2014 +0400
+++ b/test/java/lang/invoke/MethodHandlesTest.java	Sat Mar 29 12:29:21 2014 +0400
@@ -2406,108 +2406,6 @@
     }
 
     @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;
         startTest("throwException");
--- a/test/lib/testlibrary/jdk/testlibrary/Asserts.java	Fri Mar 28 20:34:46 2014 +0400
+++ b/test/lib/testlibrary/jdk/testlibrary/Asserts.java	Sat Mar 29 12:29:21 2014 +0400
@@ -171,6 +171,34 @@
     }
 
     /**
+     * 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(T, T)}.
      *
      * @see #assertGreaterThanOrEqual(T, T)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/lib/testlibrary/jsr292/com/oracle/testlibrary/jsr292/Helper.java	Sat Mar 29 12:29:21 2014 +0400
@@ -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;
+    }
+}