| /* |
| * 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<Class<?>> 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<Class<?>> 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> data, TestMethods.Kind kind) { |
| MethodType mtTarget = (MethodType) data.get("mtTarget"); |
| return MethodHandles.invoker(mtTarget); |
| } |
| }, |
| EXACT_INVOKER("exactInvoker", Helper.MAX_ARITY - 1) { |
| @Override |
| public Map<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> data, TestMethods.Kind kind) { |
| MethodType mtTarget = (MethodType) data.get("mtTarget"); |
| return MethodHandles.exactInvoker(mtTarget); |
| } |
| }, |
| SPREAD_INVOKER("spreadInvoker", Helper.MAX_ARITY - 1) { |
| @Override |
| public Map<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<String, Object> getTestCaseData() { |
| Map<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<Class<?>> 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<Class<?>> 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<Class<?>> reduceArgListToSlotsCount(List<Class<?>> list, |
| int desiredSlotCount) { |
| List<Class<?>> 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; |
| } |
| } |