/* * 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. */ import test.java.lang.invoke.lib.Helper; 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.HashMap; import java.util.List; import java.util.Map; /** * Enumeration containing information about methods from * {@code j.l.i.MethodHandles} class that are used for testing lambda forms * caching. * * @author kshefov */ public enum TestMethods { FOLD_ARGUMENTS("foldArguments") { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); // Arity after reducing because of long and double take 2 slots. int realArity = mtTarget.parameterCount(); int modifierMHArgNum = Helper.RNG.nextInt(realArity + 1); data.put("modifierMHArgNum", modifierMHArgNum); Class combinerReturnType; if (realArity == 0) { combinerReturnType = void.class; } else { combinerReturnType = Helper.RNG.nextBoolean() ? void.class : mtTarget.parameterType(0); } data.put("combinerReturnType", combinerReturnType); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException { MethodType mtTarget = (MethodType) data.get("mtTarget"); Class combinerReturnType = (Class) data.get("combinerReturnType"); int modifierMHArgNum = (int) data.get("modifierMHArgNum"); MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(), mtTarget.parameterList(), kind); Class rType = mtTarget.returnType(); int combListStart = (combinerReturnType == void.class) ? 0 : 1; if (modifierMHArgNum < combListStart) { modifierMHArgNum = combListStart; } MethodHandle combiner = TestMethods.methodHandleGenerator(combinerReturnType, mtTarget.parameterList().subList(combListStart, modifierMHArgNum), kind); return MethodHandles.foldArguments(target, combiner); } }, DROP_ARGUMENTS("dropArguments") { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); // Arity after reducing because of long and double take 2 slots. int realArity = mtTarget.parameterCount(); int dropArgsPos = Helper.RNG.nextInt(realArity + 1); data.put("dropArgsPos", dropArgsPos); MethodType mtDropArgs = TestMethods.randomMethodTypeGenerator( Helper.RNG.nextInt(super.maxArity - realArity)); data.put("mtDropArgs", mtDropArgs); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException { MethodType mtTarget = (MethodType) data.get("mtTarget"); MethodType mtDropArgs = (MethodType) data.get("mtDropArgs"); int dropArgsPos = (int) data.get("dropArgsPos"); MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(), mtTarget.parameterList(), kind); int mtTgtSlotsCount = TestMethods.argSlotsCount(mtTarget); int mtDASlotsCount = TestMethods.argSlotsCount(mtDropArgs); List> fakeParList; if (mtTgtSlotsCount + mtDASlotsCount > super.maxArity - 1) { fakeParList = TestMethods.reduceArgListToSlotsCount(mtDropArgs.parameterList(), super.maxArity - mtTgtSlotsCount - 1); } else { fakeParList = mtDropArgs.parameterList(); } return MethodHandles.dropArguments(target, dropArgsPos, fakeParList); } }, EXPLICIT_CAST_ARGUMENTS("explicitCastArguments", Helper.MAX_ARITY / 2) { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); // Arity after reducing because of long and double take 2 slots. int realArity = mtTarget.parameterCount(); MethodType mtExcplCastArgs = TestMethods.randomMethodTypeGenerator(realArity); if (mtTarget.returnType() == void.class) { mtExcplCastArgs = MethodType.methodType(void.class, mtExcplCastArgs.parameterArray()); } if (mtExcplCastArgs.returnType() == void.class) { mtExcplCastArgs = MethodType.methodType(mtTarget.returnType(), mtExcplCastArgs.parameterArray()); } data.put("mtExcplCastArgs", mtExcplCastArgs); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException { MethodType mtTarget = (MethodType) data.get("mtTarget"); MethodType mtExcplCastArgs = (MethodType) data.get("mtExcplCastArgs"); MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(), mtTarget.parameterList(), kind); return MethodHandles.explicitCastArguments(target, mtExcplCastArgs); } }, FILTER_ARGUMENTS("filterArguments", Helper.MAX_ARITY / 2) { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); // Arity after reducing because of long and double take 2 slots. int realArity = mtTarget.parameterCount(); int filterArgsPos = Helper.RNG.nextInt(realArity + 1); data.put("filterArgsPos", filterArgsPos); int filtersArgsArrayLength = Helper.RNG.nextInt(realArity + 1 - filterArgsPos); data.put("filtersArgsArrayLength", filtersArgsArrayLength); MethodType mtFilter = TestMethods.randomMethodTypeGenerator(filtersArgsArrayLength); data.put("mtFilter", mtFilter); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException { MethodType mtTarget = (MethodType) data.get("mtTarget"); MethodType mtFilter = (MethodType) data.get("mtFilter"); int filterArgsPos = (int) data.get("filterArgsPos"); int filtersArgsArrayLength = (int) data.get("filtersArgsArrayLength"); MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(), mtTarget.parameterList(), kind); MethodHandle[] filters = new MethodHandle[filtersArgsArrayLength]; for (int i = 0; i < filtersArgsArrayLength; i++) { filters[i] = TestMethods.filterGenerator(mtFilter.parameterType(i), mtTarget.parameterType(filterArgsPos + i), kind); } return MethodHandles.filterArguments(target, filterArgsPos, filters); } }, FILTER_RETURN_VALUE("filterReturnValue") { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); // Arity after reducing because of long and double take 2 slots. int realArity = mtTarget.parameterCount(); int filterArgsPos = Helper.RNG.nextInt(realArity + 1); int filtersArgsArrayLength = Helper.RNG.nextInt(realArity + 1 - filterArgsPos); MethodType mtFilter = TestMethods.randomMethodTypeGenerator(filtersArgsArrayLength); data.put("mtFilter", mtFilter); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException { MethodType mtTarget = (MethodType) data.get("mtTarget"); MethodType mtFilter = (MethodType) data.get("mtFilter"); MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(), mtTarget.parameterList(), kind); MethodHandle filter = TestMethods.filterGenerator(mtTarget.returnType(), mtFilter.returnType(), kind); return MethodHandles.filterReturnValue(target, filter); } }, INSERT_ARGUMENTS("insertArguments", Helper.MAX_ARITY - 3) { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); // Arity after reducing because of long and double take 2 slots. int realArity = mtTarget.parameterCount(); int insertArgsPos = Helper.RNG.nextInt(realArity + 1); data.put("insertArgsPos", insertArgsPos); int insertArgsArrayLength = Helper.RNG.nextInt(realArity + 1 - insertArgsPos); MethodType mtInsertArgs = MethodType.methodType(void.class, mtTarget.parameterList() .subList(insertArgsPos, insertArgsPos + insertArgsArrayLength)); data.put("mtInsertArgs", mtInsertArgs); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException { MethodType mtTarget = (MethodType) data.get("mtTarget"); MethodType mtInsertArgs = (MethodType) data.get("mtInsertArgs"); int insertArgsPos = (int) data.get("insertArgsPos"); MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(), mtTarget.parameterList(), kind); Object[] insertList = Helper.randomArgs(mtInsertArgs.parameterList()); return MethodHandles.insertArguments(target, insertArgsPos, insertList); } }, PERMUTE_ARGUMENTS("permuteArguments", Helper.MAX_ARITY / 2) { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); // Arity after reducing because of long and double take 2 slots. int realArity = mtTarget.parameterCount(); int[] permuteArgsReorderArray = new int[realArity]; int mtPermuteArgsNum = Helper.RNG.nextInt(Helper.MAX_ARITY); mtPermuteArgsNum = mtPermuteArgsNum == 0 ? 1 : mtPermuteArgsNum; MethodType mtPermuteArgs = TestMethods.randomMethodTypeGenerator(mtPermuteArgsNum); mtTarget = mtTarget.changeReturnType(mtPermuteArgs.returnType()); for (int i = 0; i < realArity; i++) { int mtPermuteArgsParNum = Helper.RNG.nextInt(mtPermuteArgs.parameterCount()); permuteArgsReorderArray[i] = mtPermuteArgsParNum; mtTarget = mtTarget.changeParameterType( i, mtPermuteArgs.parameterType(mtPermuteArgsParNum)); } data.put("mtTarget", mtTarget); data.put("permuteArgsReorderArray", permuteArgsReorderArray); data.put("mtPermuteArgs", mtPermuteArgs); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException { MethodType mtTarget = (MethodType) data.get("mtTarget"); MethodType mtPermuteArgs = (MethodType) data.get("mtPermuteArgs"); int[] permuteArgsReorderArray = (int[]) data.get("permuteArgsReorderArray"); MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(), mtTarget.parameterList(), kind); return MethodHandles.permuteArguments(target, mtPermuteArgs, permuteArgsReorderArray); } }, THROW_EXCEPTION("throwException") { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) { MethodType mtTarget = (MethodType) data.get("mtTarget"); Class rType = mtTarget.returnType(); return MethodHandles.throwException(rType, Exception.class ); } }, GUARD_WITH_TEST("guardWithTest") { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); // Arity after reducing because of long and double take 2 slots. int realArity = mtTarget.parameterCount(); int modifierMHArgNum = Helper.RNG.nextInt(realArity + 1); data.put("modifierMHArgNum", modifierMHArgNum); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException { MethodType mtTarget = (MethodType) data.get("mtTarget"); int modifierMHArgNum = (int) data.get("modifierMHArgNum"); TestMethods.Kind targetKind; TestMethods.Kind fallbackKind; if (kind.equals(TestMethods.Kind.ONE)) { targetKind = TestMethods.Kind.ONE; fallbackKind = TestMethods.Kind.TWO; } else { targetKind = TestMethods.Kind.TWO; fallbackKind = TestMethods.Kind.ONE; } MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(), mtTarget.parameterList(), targetKind); MethodHandle fallback = TestMethods.methodHandleGenerator(mtTarget.returnType(), mtTarget.parameterList(), fallbackKind); MethodHandle test = TestMethods.methodHandleGenerator(boolean.class, mtTarget.parameterList().subList(0, modifierMHArgNum), kind); return MethodHandles.guardWithTest(test, target, fallback); } }, CATCH_EXCEPTION("catchException") { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); // Arity after reducing because of long and double take 2 slots. int realArity = mtTarget.parameterCount(); int modifierMHArgNum = Helper.RNG.nextInt(realArity + 1); data.put("modifierMHArgNum", modifierMHArgNum); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException { MethodType mtTarget = (MethodType) data.get("mtTarget"); int modifierMHArgNum = (int) data.get("modifierMHArgNum"); MethodHandle target; if (kind.equals(TestMethods.Kind.ONE)) { target = TestMethods.methodHandleGenerator(mtTarget.returnType(), mtTarget.parameterList(), TestMethods.Kind.ONE); } else { target = TestMethods.methodHandleGenerator(mtTarget.returnType(), mtTarget.parameterList(), TestMethods.Kind.EXCEPT); } List> handlerParamList = new ArrayList<>(mtTarget.parameterCount() + 1); handlerParamList.add(Exception.class); handlerParamList.addAll(mtTarget.parameterList().subList(0, modifierMHArgNum)); MethodHandle handler = TestMethods.methodHandleGenerator( mtTarget.returnType(), handlerParamList, TestMethods.Kind.TWO); return MethodHandles.catchException(target, Exception.class, handler); } }, INVOKER("invoker", Helper.MAX_ARITY - 1) { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) { MethodType mtTarget = (MethodType) data.get("mtTarget"); return MethodHandles.invoker(mtTarget); } }, EXACT_INVOKER("exactInvoker", Helper.MAX_ARITY - 1) { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) { MethodType mtTarget = (MethodType) data.get("mtTarget"); return MethodHandles.exactInvoker(mtTarget); } }, SPREAD_INVOKER("spreadInvoker", Helper.MAX_ARITY - 1) { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); // Arity after reducing because of long and double take 2 slots. int realArity = mtTarget.parameterCount(); int modifierMHArgNum = Helper.RNG.nextInt(realArity + 1); data.put("modifierMHArgNum", modifierMHArgNum); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) { MethodType mtTarget = (MethodType) data.get("mtTarget"); int modifierMHArgNum = (int) data.get("modifierMHArgNum"); return MethodHandles.spreadInvoker(mtTarget, modifierMHArgNum); } }, ARRAY_ELEMENT_GETTER("arrayElementGetter") { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) { MethodType mtTarget = (MethodType) data.get("mtTarget"); Class rType = mtTarget.returnType(); if (rType == void.class) { rType = Object.class; } return MethodHandles.arrayElementGetter(Array.newInstance(rType, 2).getClass()); } }, ARRAY_ELEMENT_SETTER("arrayElementSetter") { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) { MethodType mtTarget = (MethodType) data.get("mtTarget"); Class rType = mtTarget.returnType(); if (rType == void.class) { rType = Object.class; } return MethodHandles.arrayElementSetter(Array.newInstance(rType, 2).getClass()); } }, CONSTANT("constant") { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) { MethodType mtTarget = (MethodType) data.get("mtTarget"); Class rType = mtTarget.returnType(); if (rType == void.class) { rType = Object.class; } if (rType.equals(boolean.class)) { // There should be the same return values because for default values there are special "zero" forms return MethodHandles.constant(rType, true); } else { return MethodHandles.constant(rType, kind.getValue(rType)); } } }, IDENTITY("identity") { @Override public Map getTestCaseData() { Map data = new HashMap<>(); int desiredArity = Helper.RNG.nextInt(super.maxArity); MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity); data.put("mtTarget", mtTarget); return data; } @Override protected MethodHandle getMH(Map data, TestMethods.Kind kind) { MethodType mtTarget = (MethodType) data.get("mtTarget"); Class rType = mtTarget.returnType(); if (rType == void.class) { rType = Object.class; } return MethodHandles.identity(rType); } }; /** * Test method's name. */ public final String name; private final int maxArity; private TestMethods(String name, int maxArity) { this.name = name; this.maxArity = maxArity; } private TestMethods(String name) { this(name, Helper.MAX_ARITY); } protected MethodHandle getMH(Map data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException { throw new UnsupportedOperationException("TESTBUG: getMH method is not implemented for test method " + this); } /** * Creates an adapter method handle depending on a test method from * MethodHandles class. Adapter is what is returned by the test method. This * method is able to create two kinds of adapters, their type will be the * same, but return values are different. * * @param data a Map containing data to create a method handle, can be * obtained by {@link #getTestCaseData} method * @param kind defines whether adapter ONE or adapter TWO will be * initialized. Should be equal to TestMethods.Kind.ONE or * TestMethods.Kind.TWO * @return Method handle adapter that behaves according to * TestMethods.Kind.ONE or TestMethods.Kind.TWO * @throws java.lang.NoSuchMethodException * @throws java.lang.IllegalAccessException */ public MethodHandle getTestCaseMH(Map data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException { if (data == null) { throw new Error(String.format("TESTBUG: Data for test method %s is not prepared", this.name)); } if (!kind.equals(TestMethods.Kind.ONE) && !kind.equals(TestMethods.Kind.TWO)) { throw new IllegalArgumentException("TESTBUG: Wrong \"kind\" (" + kind + ") arg to getTestCaseMH function." + " Should be Kind.ONE or Kind.TWO"); } return getMH(data, kind); } /** * Returns a data Map needed for {@link #getTestCaseMH} method. * * @return data Map needed for {@link #getTestCaseMH} method */ public Map getTestCaseData() { throw new UnsupportedOperationException( "TESTBUG: getTestCaseData method is not implemented for test method " + this); } /** * Enumeration used in methodHandleGenerator to define whether a MH returned * by this method returns "2" in different type representations, "4", or * throw an Exception. */ public static enum Kind { ONE(2), TWO(4), EXCEPT(0); private final int value; private Object getValue(Class cl) { return Helper.castToWrapper(value, cl); } private MethodHandle getBasicMH(Class rType) throws NoSuchMethodException, IllegalAccessException { MethodHandle result = null; switch (this) { case ONE: case TWO: if (rType.equals(void.class)) { result = MethodHandles.lookup().findVirtual(Kind.class, "returnVoid", MethodType.methodType(void.class)); result = MethodHandles.insertArguments(result, 0, this); } else { result = MethodHandles.constant(rType, getValue(rType)); } break; case EXCEPT: result = MethodHandles.throwException(rType, Exception.class); result = MethodHandles.insertArguments(result, 0, new Exception()); break; } return result; } private void returnVoid() { } private Kind(int value) { this.value = value; } } /** * Routine used to obtain a randomly generated method type. * * @param arity Arity of returned method type. * @return MethodType generated randomly. */ private static MethodType randomMethodTypeGenerator(int arity) { return Helper.randomMethodTypeGenerator(arity); } /** * Routine used to obtain a method handles of a given type an kind (return * value). * * @param returnType Type of MH return value. * @param argTypes Types of MH args. * @param kind Defines whether the obtained MH returns "1" or "2". * @return Method handle of the given type. * @throws NoSuchMethodException * @throws IllegalAccessException */ private static MethodHandle methodHandleGenerator(Class returnType, List> argTypes, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException { MethodHandle result; result = kind.getBasicMH(returnType); return Helper.addTrailingArgs(result, argTypes.size(), argTypes); } /** * Routine that generates filter method handles to test * MethodHandles.filterArguments method. * * @param inputType Filter's argument type. * @param returnType Filter's return type. * @param kind Filter's return value definer. * @return A filter method handle, that takes one argument. * @throws NoSuchMethodException * @throws IllegalAccessException */ private static MethodHandle filterGenerator(Class inputType, Class returnType, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException { MethodHandle tmpMH = kind.getBasicMH(returnType); if (inputType.equals(void.class)) { return tmpMH; } ArrayList> inputTypeList = new ArrayList<>(1); inputTypeList.add(inputType); return Helper.addTrailingArgs(tmpMH, 1, inputTypeList); } private static int argSlotsCount(MethodType mt) { int result = 0; for (Class cl : mt.parameterArray()) { if (cl.equals(long.class) || cl.equals(double.class)) { result += 2; } else { result++; } } return result; } private static List> reduceArgListToSlotsCount(List> list, int desiredSlotCount) { List> result = new ArrayList<>(desiredSlotCount); int count = 0; for (Class cl : list) { if (count >= desiredSlotCount) { break; } if (cl.equals(long.class) || cl.equals(double.class)) { count += 2; } else { count++; } result.add(cl); } return result; } }