Path: blob/master/test/jdk/java/lang/invoke/MethodHandles/CatchExceptionTest.java
41152 views
/*1* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package test.java.lang.invoke.MethodHandles;2425import jdk.test.lib.TimeLimitedRunner;26import jdk.test.lib.Asserts;27import jdk.test.lib.Utils;28import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;29import test.java.lang.invoke.lib.Helper;3031import java.lang.invoke.MethodHandle;32import java.lang.invoke.MethodHandles;33import java.lang.invoke.MethodType;34import java.lang.reflect.Array;35import java.util.ArrayList;36import java.util.Arrays;37import java.util.Collections;38import java.util.List;39import java.util.Objects;40import java.util.function.BiFunction;41import java.util.function.Function;42import java.util.function.Supplier;4344/* @test45* @library /java/lang/invoke/common /test/lib46* @build jdk.test.lib.TimeLimitedRunner47* @compile CatchExceptionTest.java48* @run main/othervm -esa test.java.lang.invoke.MethodHandles.CatchExceptionTest49* @key intermittent randomness50*/51public class CatchExceptionTest {52private static final List<Class<?>> ARGS_CLASSES;53protected static final int MAX_ARITY = Helper.MAX_ARITY - 1;5455static {56Class<?> classes[] = {57Object.class,58long.class,59int.class,60byte.class,61Integer[].class,62double[].class,63String.class,64};65ARGS_CLASSES = Collections.unmodifiableList(66Helper.randomClasses(classes, MAX_ARITY));67}6869private final TestCase testCase;70private final int nargs;71private final int argsCount;72private final MethodHandle catcher;73private int dropped;74private MethodHandle thrower;7576public CatchExceptionTest(TestCase testCase, final boolean isVararg,77final int argsCount, final int catchDrops) {78this.testCase = testCase;79this.dropped = catchDrops;80MethodHandle thrower = testCase.thrower;81int throwerLen = thrower.type().parameterCount();82List<Class<?>> classes;83int extra = Math.max(0, argsCount - throwerLen);84classes = getThrowerParams(isVararg, extra);85this.argsCount = throwerLen + classes.size();86thrower = Helper.addTrailingArgs(thrower, this.argsCount, classes);87if (isVararg && argsCount > throwerLen) {88MethodType mt = thrower.type();89Class<?> lastParam = mt.parameterType(mt.parameterCount() - 1);90thrower = thrower.asVarargsCollector(lastParam);91}92this.thrower = thrower;93this.dropped = Math.min(this.argsCount, catchDrops);94catcher = testCase.getCatcher(getCatcherParams());95nargs = Math.max(2, this.argsCount);96}9798public static void main(String[] args) throws Throwable {99CodeCacheOverflowProcessor.runMHTest(CatchExceptionTest::test);100}101102public static void test() throws Throwable {103System.out.println("classes = " + ARGS_CLASSES);104105TestFactory factory = new TestFactory();106long timeout = Helper.IS_THOROUGH ? 0L : Utils.adjustTimeout(Utils.DEFAULT_TEST_TIMEOUT);107// subtract vm init time and reserve time for vm exit108timeout *= 0.9;109TimeLimitedRunner runner = new TimeLimitedRunner(timeout, 2.0d,110() -> {111CatchExceptionTest test = factory.nextTest();112if (test != null) {113test.runTest();114return true;115}116return false;117});118for (CatchExceptionTest test : TestFactory.MANDATORY_TEST_CASES) {119test.runTest();120}121runner.call();122}123124private List<Class<?>> getThrowerParams(boolean isVararg, int argsCount) {125return Helper.getParams(ARGS_CLASSES, isVararg, argsCount);126}127128private List<Class<?>> getCatcherParams() {129int catchArgc = 1 + this.argsCount - dropped;130List<Class<?>> result = new ArrayList<>(131thrower.type().parameterList().subList(0, catchArgc - 1));132// prepend throwable133result.add(0, testCase.throwableClass);134return result;135}136137private void runTest() {138if (Helper.IS_VERBOSE) {139System.out.printf("CatchException(%s, isVararg=%b argsCount=%d " +140"dropped=%d)%n",141testCase, thrower.isVarargsCollector(), argsCount, dropped);142}143144Helper.clear();145146Object[] args = Helper.randomArgs(147argsCount, thrower.type().parameterArray());148Object arg0 = Helper.MISSING_ARG;149Object arg1 = testCase.thrown;150if (argsCount > 0) {151arg0 = args[0];152}153if (argsCount > 1) {154args[1] = arg1;155}156Asserts.assertEQ(nargs, thrower.type().parameterCount());157if (argsCount < nargs) {158Object[] appendArgs = {arg0, arg1};159appendArgs = Arrays.copyOfRange(appendArgs, argsCount, nargs);160thrower = MethodHandles.insertArguments(161thrower, argsCount, appendArgs);162}163Asserts.assertEQ(argsCount, thrower.type().parameterCount());164165MethodHandle target = MethodHandles.catchException(166testCase.filter(thrower), testCase.throwableClass,167testCase.filter(catcher));168169Asserts.assertEQ(thrower.type(), target.type());170Asserts.assertEQ(argsCount, target.type().parameterCount());171172Object returned;173try {174returned = target.invokeWithArguments(args);175} catch (Throwable ex) {176if (CodeCacheOverflowProcessor.isThrowableCausedByVME(ex)) {177// This error will be treated by CodeCacheOverflowProcessor178// to prevent the test from failing because of code cache overflow.179throw new Error(ex);180}181testCase.assertCatch(ex);182returned = ex;183}184185testCase.assertReturn(returned, arg0, arg1, dropped, args);186}187}188189class TestFactory {190public static final List<CatchExceptionTest> MANDATORY_TEST_CASES = new ArrayList<>();191192private static final int MIN_TESTED_ARITY = 10;193194static {195for (int[] args : new int[][]{196{0, 0},197{MIN_TESTED_ARITY, 0},198{MIN_TESTED_ARITY, MIN_TESTED_ARITY},199{CatchExceptionTest.MAX_ARITY, 0},200{CatchExceptionTest.MAX_ARITY, CatchExceptionTest.MAX_ARITY},201}) {202MANDATORY_TEST_CASES.addAll(createTests(args[0], args[1]));203}204}205206private int count;207private int args;208private int dropArgs;209private int currentMaxDrops;210private int maxArgs;211private int maxDrops;212private int constructor;213private int constructorSize;214private boolean isVararg;215216public TestFactory() {217if (Helper.IS_THOROUGH) {218maxArgs = maxDrops = CatchExceptionTest.MAX_ARITY;219} else {220maxArgs = MIN_TESTED_ARITY221+ Helper.RNG.nextInt(CatchExceptionTest.MAX_ARITY222- MIN_TESTED_ARITY)223+ 1;224maxDrops = MIN_TESTED_ARITY225+ Helper.RNG.nextInt(maxArgs - MIN_TESTED_ARITY)226+ 1;227args = 1;228}229230System.out.printf("maxArgs = %d%nmaxDrops = %d%n", maxArgs, maxDrops);231constructorSize = TestCase.CONSTRUCTORS.size();232}233234private static List<CatchExceptionTest> createTests(int argsCount,235int catchDrops) {236if (catchDrops > argsCount || argsCount < 0 || catchDrops < 0) {237throw new IllegalArgumentException("argsCount = " + argsCount238+ ", catchDrops = " + catchDrops239);240}241List<CatchExceptionTest> result = new ArrayList<>(242TestCase.CONSTRUCTORS.size());243for (Supplier<TestCase> constructor : TestCase.CONSTRUCTORS) {244result.add(new CatchExceptionTest(constructor.get(),245/* isVararg = */ true,246argsCount,247catchDrops));248result.add(new CatchExceptionTest(constructor.get(),249/* isVararg = */ false,250argsCount,251catchDrops));252}253return result;254}255256/**257* @return next test from test matrix:258* {varArgs, noVarArgs} x TestCase.rtypes x TestCase.THROWABLES x {1, .., maxArgs } x {0, .., maxDrops}259*/260public CatchExceptionTest nextTest() {261if (constructor < constructorSize) {262return createTest();263}264constructor = 0;265count++;266if (!Helper.IS_THOROUGH && count > Helper.TEST_LIMIT) {267System.out.println("test limit is exceeded");268return null;269}270if (dropArgs <= currentMaxDrops) {271if (dropArgs == 0) {272if (Helper.IS_THOROUGH || Helper.RNG.nextBoolean()) {273++dropArgs;274return createTest();275} else if (Helper.IS_VERBOSE) {276System.out.printf(277"argsCount=%d : \"drop\" scenarios are skipped%n",278args);279}280} else {281++dropArgs;282return createTest();283}284}285286if (args < maxArgs) {287dropArgs = 0;288currentMaxDrops = Math.min(args, maxDrops);289++args;290return createTest();291}292return null;293}294295private CatchExceptionTest createTest() {296if (!Helper.IS_THOROUGH) {297return new CatchExceptionTest(298TestCase.CONSTRUCTORS.get(constructor++).get(),299Helper.RNG.nextBoolean(), args, dropArgs);300} else {301if (isVararg) {302isVararg = false;303return new CatchExceptionTest(304TestCase.CONSTRUCTORS.get(constructor++).get(),305isVararg, args, dropArgs);306} else {307isVararg = true;308return new CatchExceptionTest(309TestCase.CONSTRUCTORS.get(constructor).get(),310isVararg, args, dropArgs);311}312}313}314}315316class TestCase<T> {317private static enum ThrowMode {318NOTHING,319CAUGHT,320UNCAUGHT,321ADAPTER322}323324@SuppressWarnings("unchecked")325public static final List<Supplier<TestCase>> CONSTRUCTORS;326private static final MethodHandle FAKE_IDENTITY;327private static final MethodHandle THROW_OR_RETURN;328private static final MethodHandle CATCHER;329330static {331try {332MethodHandles.Lookup lookup = MethodHandles.lookup();333THROW_OR_RETURN = lookup.findStatic(334TestCase.class,335"throwOrReturn",336MethodType.methodType(Object.class, Object.class,337Throwable.class)338);339CATCHER = lookup.findStatic(340TestCase.class,341"catcher",342MethodType.methodType(Object.class, Object.class));343FAKE_IDENTITY = lookup.findVirtual(344TestCase.class, "fakeIdentity",345MethodType.methodType(Object.class, Object.class));346347} catch (NoSuchMethodException | IllegalAccessException e) {348throw new Error(e);349}350PartialConstructor[] constructors = {351create(Object.class, Object.class::cast),352create(String.class, Objects::toString),353create(int[].class, x -> new int[]{Objects.hashCode(x)}),354create(long.class,355x -> Objects.hashCode(x) & (-1L >>> 32)),356create(void.class, TestCase::noop)};357Throwable[] throwables = {358new ClassCastException("testing"),359new java.io.IOException("testing"),360new LinkageError("testing")};361List<Supplier<TestCase>> list = new ArrayList<>(constructors.length362* throwables.length * ThrowMode.values().length);363//noinspection unchecked364for (PartialConstructor f : constructors) {365for (ThrowMode mode : ThrowMode.values()) {366for (Throwable t : throwables) {367list.add(f.apply(mode, t));368}369}370}371CONSTRUCTORS = Collections.unmodifiableList(list);372}373374public final Class<T> rtype;375public final ThrowMode throwMode;376public final Throwable thrown;377public final Class<? extends Throwable> throwableClass;378/**379* MH which takes 2 args (Object,Throwable), 1st is the return value,380* 2nd is the exception which will be thrown, if it's supposed in current381* {@link #throwMode}.382*/383public final MethodHandle thrower;384private final Function<Object, T> cast;385protected MethodHandle filter;386private int fakeIdentityCount;387388private TestCase(Class<T> rtype, Function<Object, T> cast,389ThrowMode throwMode, Throwable thrown)390throws NoSuchMethodException, IllegalAccessException {391this.cast = cast;392filter = MethodHandles.lookup().findVirtual(393Function.class,394"apply",395MethodType.methodType(Object.class, Object.class))396.bindTo(cast);397this.rtype = rtype;398this.throwMode = throwMode;399this.throwableClass = thrown.getClass();400switch (throwMode) {401case NOTHING:402this.thrown = null;403break;404case ADAPTER:405case UNCAUGHT:406this.thrown = new Error("do not catch this");407break;408default:409this.thrown = thrown;410}411412MethodHandle throwOrReturn = THROW_OR_RETURN;413if (throwMode == ThrowMode.ADAPTER) {414MethodHandle fakeIdentity = FAKE_IDENTITY.bindTo(this);415for (int i = 0; i < 10; ++i) {416throwOrReturn = MethodHandles.filterReturnValue(417throwOrReturn, fakeIdentity);418}419}420thrower = throwOrReturn.asType(MethodType.genericMethodType(2));421}422423private static Void noop(Object x) {424return null;425}426427private static <T2> PartialConstructor create(428Class<T2> rtype, Function<Object, T2> cast) {429return (t, u) -> () -> {430try {431return new TestCase<>(rtype, cast, t, u);432} catch (NoSuchMethodException | IllegalAccessException e) {433throw new Error(e);434}435};436}437438private static <T extends Throwable>439Object throwOrReturn(Object normal, T exception) throws T {440if (exception != null) {441Helper.called("throwOrReturn/throw", normal, exception);442throw exception;443}444Helper.called("throwOrReturn/normal", normal, exception);445return normal;446}447448private static <T extends Throwable>449Object catcher(Object o) {450Helper.called("catcher", o);451return o;452}453454public MethodHandle filter(MethodHandle target) {455return MethodHandles.filterReturnValue(target, filter);456}457458public MethodHandle getCatcher(List<Class<?>> classes) {459return MethodHandles.filterReturnValue(Helper.AS_LIST.asType(460MethodType.methodType(Object.class, classes)),461CATCHER462);463}464465@Override466public String toString() {467return "TestCase{" +468"rtype=" + rtype +469", throwMode=" + throwMode +470", throwableClass=" + throwableClass +471'}';472}473474public String callName() {475return "throwOrReturn/" +476(throwMode == ThrowMode.NOTHING477? "normal"478: "throw");479}480481public void assertReturn(Object returned, Object arg0, Object arg1,482int catchDrops, Object... args) {483int lag = 0;484if (throwMode == ThrowMode.CAUGHT) {485lag = 1;486}487Helper.assertCalled(lag, callName(), arg0, arg1);488489if (throwMode == ThrowMode.NOTHING) {490assertEQ(cast.apply(arg0), returned);491} else if (throwMode == ThrowMode.CAUGHT) {492List<Object> catchArgs = new ArrayList<>(Arrays.asList(args));493// catcher receives an initial subsequence of target arguments:494catchArgs.subList(args.length - catchDrops, args.length).clear();495// catcher also receives the exception, prepended:496catchArgs.add(0, thrown);497Helper.assertCalled("catcher", catchArgs);498assertEQ(cast.apply(catchArgs), returned);499}500Asserts.assertEQ(0, fakeIdentityCount);501}502503private void assertEQ(T t, Object returned) {504if (rtype.isArray()) {505Asserts.assertEQ(t.getClass(), returned.getClass());506int n = Array.getLength(t);507Asserts.assertEQ(n, Array.getLength(returned));508for (int i = 0; i < n; ++i) {509Asserts.assertEQ(Array.get(t, i), Array.get(returned, i));510}511} else {512Asserts.assertEQ(t, returned);513}514}515516private Object fakeIdentity(Object x) {517System.out.println("should throw through this!");518++fakeIdentityCount;519return x;520}521522public void assertCatch(Throwable ex) {523try {524Asserts.assertSame(thrown, ex,525"must get the out-of-band exception");526} catch (Throwable t) {527ex.printStackTrace();528}529}530531public interface PartialConstructor532extends BiFunction<ThrowMode, Throwable, Supplier<TestCase>> {533}534}535536537