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;
 }
 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);
 }
 static {
   Class<?> classes[] = {
     Object.class,
     long.class,
     int.class,
     byte.class,
     Integer[].class,
     double[].class,
     String.class,
   };
   ARGS_CLASSES = Collections.unmodifiableList(Helper.randomClasses(classes, MAX_ARITY));
 }
  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 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);
  }
 private static <T extends Throwable> Object catcher(Object o) {
   Helper.called("catcher", o);
   return o;
 }
 private List<Class<?>> getThrowerParams(boolean isVararg, int argsCount) {
   return Helper.getParams(ARGS_CLASSES, isVararg, argsCount);
 }