mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-21 03:24:38 +02:00
8057719: Develop new tests for LambdaForm Reduction and Caching feature
Reviewed-by: iignatyev, vlivanov, psandoz
This commit is contained in:
parent
9e30841d28
commit
881fa79cfe
6 changed files with 1184 additions and 0 deletions
78
jdk/test/java/lang/invoke/LFCaching/LFCachingTestCase.java
Normal file
78
jdk/test/java/lang/invoke/LFCaching/LFCachingTestCase.java
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Abstract class for lambda forms caching testing.
|
||||
*
|
||||
* @author kshefov
|
||||
*/
|
||||
public abstract class LFCachingTestCase extends LambdaFormTestCase {
|
||||
|
||||
/**
|
||||
* Constructor for lambda forms caching test case.
|
||||
*
|
||||
* @param testMethod A method from {@code j.l.i.MethodHandles} class that
|
||||
* returns a {@code j.l.i.MethodHandle} instance.
|
||||
*/
|
||||
protected LFCachingTestCase(TestMethods testMethod) {
|
||||
super(testMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the lambda forms of the two adapter method handles adapter1
|
||||
* and adapter2 are the same.
|
||||
*
|
||||
* @param adapter1 First method handle.
|
||||
* @param adapter2 Second method handle.
|
||||
*/
|
||||
public void checkLFCaching(MethodHandle adapter1, MethodHandle adapter2) {
|
||||
try {
|
||||
|
||||
if (!adapter1.type().equals(adapter2.type())) {
|
||||
throw new Error("TESTBUG: Types of the two method handles are not the same");
|
||||
}
|
||||
|
||||
Object lambdaForm0 = LambdaFormTestCase.INTERNAL_FORM.invoke(adapter1);
|
||||
Object lambdaForm1 = LambdaFormTestCase.INTERNAL_FORM.invoke(adapter2);
|
||||
|
||||
if (lambdaForm0 == null || lambdaForm1 == null) {
|
||||
throw new Error("Unexpected error: One or both lambda forms of the method handles are null");
|
||||
}
|
||||
|
||||
if (lambdaForm0 != lambdaForm1) {
|
||||
System.err.println("Lambda form 0 toString is:");
|
||||
System.err.println(lambdaForm0);
|
||||
System.err.println("Lambda form 1 toString is:");
|
||||
System.err.println(lambdaForm1);
|
||||
throw new AssertionError("Error: Lambda forms of the two method handles"
|
||||
+ " are not the same. LF cahing does not work");
|
||||
}
|
||||
} catch (IllegalAccessException | IllegalArgumentException |
|
||||
SecurityException | InvocationTargetException ex) {
|
||||
throw new Error("Unexpected exception: ", ex);
|
||||
}
|
||||
}
|
||||
}
|
102
jdk/test/java/lang/invoke/LFCaching/LFGarbageCollectedTest.java
Normal file
102
jdk/test/java/lang/invoke/LFCaching/LFGarbageCollectedTest.java
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test LFGarbageCollectedTest
|
||||
* @bug 8046703
|
||||
* @summary Test verifies that lambda forms are garbage collected
|
||||
* @author kshefov
|
||||
* @library /lib/testlibrary/jsr292 /lib/testlibrary
|
||||
* @build TestMethods
|
||||
* @build LambdaFormTestCase
|
||||
* @build LFGarbageCollectedTest
|
||||
* @run main/othervm/timeout=600 -Djava.lang.invoke.MethodHandle.USE_LF_EDITOR=true -DtestLimit=150 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI LFGarbageCollectedTest
|
||||
*/
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.ref.PhantomReference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Lambda forms garbage collection test class.
|
||||
*/
|
||||
public final class LFGarbageCollectedTest extends LambdaFormTestCase {
|
||||
|
||||
/**
|
||||
* Constructor for a lambda forms garbage collection test case.
|
||||
*
|
||||
* @param testMethod A method from {@code j.l.i.MethodHandles} class that
|
||||
* returns a {@code j.l.i.MethodHandle} instance.
|
||||
*/
|
||||
public LFGarbageCollectedTest(TestMethods testMethod) {
|
||||
super(testMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doTest() {
|
||||
try {
|
||||
Map<String, Object> data = getTestMethod().getTestCaseData();
|
||||
MethodHandle adapter;
|
||||
try {
|
||||
adapter = getTestMethod().getTestCaseMH(data, TestMethods.Kind.ONE);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
throw new Error("Unexpected exception: ", ex);
|
||||
}
|
||||
Object lambdaForm = LambdaFormTestCase.INTERNAL_FORM.invoke(adapter);
|
||||
if (lambdaForm == null) {
|
||||
throw new Error("Unexpected error: Lambda form of the method handle is null");
|
||||
}
|
||||
ReferenceQueue rq = new ReferenceQueue();
|
||||
PhantomReference ph = new PhantomReference(lambdaForm, rq);
|
||||
lambdaForm = null;
|
||||
data = null;
|
||||
adapter = null;
|
||||
for (int i = 0; i < 1000 && !ph.isEnqueued(); i++) {
|
||||
System.gc();
|
||||
}
|
||||
if (!ph.isEnqueued()) {
|
||||
throw new AssertionError("Error: Lambda form is not garbage collected");
|
||||
}
|
||||
} catch (IllegalAccessException | IllegalArgumentException |
|
||||
InvocationTargetException ex) {
|
||||
throw new Error("Unexpected exception: ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main routine for lambda forms garbage collection test.
|
||||
*
|
||||
* @param args Accepts no arguments.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// The "identity" and "constant" methods should be removed from this test,
|
||||
// because their lambda forms are stored in a static filed and are not GC'ed.
|
||||
// There can be only 5 such LFs for each method, so no memory leak happens.
|
||||
EnumSet<TestMethods> testMethods = EnumSet.complementOf(EnumSet.of(TestMethods.IDENTITY, TestMethods.CONSTANT));
|
||||
LambdaFormTestCase.runTests(LFGarbageCollectedTest::new, testMethods);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test LFMultiThreadCachingTest
|
||||
* @bug 8046703
|
||||
* @summary Test verifies that lambda forms are cached when run with multiple threads
|
||||
* @author kshefov
|
||||
* @library /lib/testlibrary/jsr292 /lib/testlibrary
|
||||
* @build TestMethods
|
||||
* @build LambdaFormTestCase
|
||||
* @build LFCachingTestCase
|
||||
* @build LFMultiThreadCachingTest
|
||||
* @run main/othervm/timeout=300 -Djava.lang.invoke.MethodHandle.USE_LF_EDITOR=true LFMultiThreadCachingTest
|
||||
*/
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
|
||||
/**
|
||||
* Multiple threaded lambda forms caching test class.
|
||||
*/
|
||||
public final class LFMultiThreadCachingTest extends LFCachingTestCase {
|
||||
private static final TestMethods.Kind[] KINDS;
|
||||
static {
|
||||
EnumSet<TestMethods.Kind> set = EnumSet.complementOf(EnumSet.of(TestMethods.Kind.EXCEPT));
|
||||
KINDS = set.toArray(new TestMethods.Kind[set.size()]);
|
||||
if (KINDS.length < 2) {
|
||||
throw new Error("TESTBUG: KINDS.length[" + KINDS.length + "] should be at least 2");
|
||||
}
|
||||
}
|
||||
private static final int CORES = Math.max(KINDS.length, Runtime.getRuntime().availableProcessors());
|
||||
|
||||
/**
|
||||
* Constructor a for multiple threaded lambda forms caching test case.
|
||||
*
|
||||
* @param testMethod A method from {@code j.l.i.MethodHandles} class that
|
||||
* returns a {@code j.l.i.MethodHandle} instance.
|
||||
*/
|
||||
public LFMultiThreadCachingTest(TestMethods testMethod) {
|
||||
super(testMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doTest() {
|
||||
Map<String, Object> data = getTestMethod().getTestCaseData();
|
||||
ConcurrentLinkedQueue<MethodHandle> adapters = new ConcurrentLinkedQueue<>();
|
||||
CyclicBarrier begin = new CyclicBarrier(CORES);
|
||||
CountDownLatch end = new CountDownLatch(CORES);
|
||||
for (int i = 0; i < CORES; ++i) {
|
||||
TestMethods.Kind kind = KINDS[i % KINDS.length];
|
||||
new Thread(() -> {
|
||||
try {
|
||||
begin.await();
|
||||
adapters.add(getTestMethod().getTestCaseMH(data, kind));
|
||||
} catch (InterruptedException | BrokenBarrierException | IllegalAccessException | NoSuchMethodException ex) {
|
||||
throw new Error("Unexpected exception: ", ex);
|
||||
} finally {
|
||||
end.countDown();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
try {
|
||||
end.await();
|
||||
} catch (InterruptedException ex) {
|
||||
throw new Error("Unexpected exception: ", ex);
|
||||
}
|
||||
if (adapters.size() < CORES) {
|
||||
throw new Error("adapters size[" + adapters.size() + "] is less than " + CORES);
|
||||
}
|
||||
MethodHandle prev = adapters.poll();
|
||||
for (MethodHandle current : adapters) {
|
||||
checkLFCaching(prev, current);
|
||||
prev = current;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main routine for multiple threaded lambda forms caching test.
|
||||
*
|
||||
* @param args Accepts no arguments.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
LambdaFormTestCase.runTests(LFMultiThreadCachingTest::new, EnumSet.allOf(TestMethods.class));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test LFSingleThreadCachingTest
|
||||
* @bug 8046703
|
||||
* @summary Test verifies that lambda forms are cached when run with single thread
|
||||
* @author kshefov
|
||||
* @library /lib/testlibrary/jsr292 /lib/testlibrary
|
||||
* @build TestMethods
|
||||
* @build LambdaFormTestCase
|
||||
* @build LFCachingTestCase
|
||||
* @build LFSingleThreadCachingTest
|
||||
* @run main/othervm/timeout=300 -Djava.lang.invoke.MethodHandle.USE_LF_EDITOR=true LFSingleThreadCachingTest
|
||||
*/
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Single threaded lambda forms caching test class.
|
||||
*/
|
||||
public final class LFSingleThreadCachingTest extends LFCachingTestCase {
|
||||
|
||||
/**
|
||||
* Constructor for a single threaded lambda forms caching test case.
|
||||
*
|
||||
* @param testMethod A method from {@code j.l.i.MethodHandles} class that
|
||||
* returns a {@code j.l.i.MethodHandle} instance.
|
||||
*/
|
||||
public LFSingleThreadCachingTest(TestMethods testMethod) {
|
||||
super(testMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doTest() {
|
||||
MethodHandle adapter1;
|
||||
MethodHandle adapter2;
|
||||
Map<String, Object> data = getTestMethod().getTestCaseData();
|
||||
try {
|
||||
adapter1 = getTestMethod().getTestCaseMH(data, TestMethods.Kind.ONE);
|
||||
adapter2 = getTestMethod().getTestCaseMH(data, TestMethods.Kind.TWO);
|
||||
} catch (NoSuchMethodException | IllegalAccessException ex) {
|
||||
throw new Error("Unexpected exception: ", ex);
|
||||
}
|
||||
checkLFCaching(adapter1, adapter2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main routine for single threaded lambda forms caching test.
|
||||
*
|
||||
* @param args Accepts no arguments.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
LambdaFormTestCase.runTests(LFSingleThreadCachingTest::new, EnumSet.allOf(TestMethods.class));
|
||||
}
|
||||
}
|
117
jdk/test/java/lang/invoke/LFCaching/LambdaFormTestCase.java
Normal file
117
jdk/test/java/lang/invoke/LFCaching/LambdaFormTestCase.java
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import com.oracle.testlibrary.jsr292.Helper;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Lambda forms caching test case class. Contains all necessary test routines to
|
||||
* test lambda forms caching in method handles returned by methods of
|
||||
* MethodHandles class.
|
||||
*
|
||||
* @author kshefov
|
||||
*/
|
||||
public abstract class LambdaFormTestCase {
|
||||
|
||||
private final static String METHOD_HANDLE_CLASS_NAME = "java.lang.invoke.MethodHandle";
|
||||
private final static String INTERNAL_FORM_METHOD_NAME = "internalForm";
|
||||
|
||||
/**
|
||||
* Reflection link to {@code j.l.i.MethodHandle.internalForm} method. It is
|
||||
* used to get a lambda form from a method handle.
|
||||
*/
|
||||
protected final static Method INTERNAL_FORM;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class mhClass = Class.forName(METHOD_HANDLE_CLASS_NAME);
|
||||
INTERNAL_FORM = mhClass.getDeclaredMethod(INTERNAL_FORM_METHOD_NAME);
|
||||
INTERNAL_FORM.setAccessible(true);
|
||||
} catch (Exception ex) {
|
||||
throw new Error("Unexpected exception: ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private final TestMethods testMethod;
|
||||
|
||||
/**
|
||||
* Test case constructor. Generates test cases with random method types for
|
||||
* given methods form {@code j.l.i.MethodHandles} class.
|
||||
*
|
||||
* @param testMethod A method from {@code j.l.i.MethodHandles} class which
|
||||
* returns a {@code j.l.i.MethodHandle}.
|
||||
*/
|
||||
protected LambdaFormTestCase(TestMethods testMethod) {
|
||||
this.testMethod = testMethod;
|
||||
}
|
||||
|
||||
public TestMethods getTestMethod() {
|
||||
return testMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Routine that executes a test case.
|
||||
*/
|
||||
public abstract void doTest();
|
||||
|
||||
/**
|
||||
* Runs a number of test cases defined by the size of testCases list.
|
||||
*
|
||||
* @param ctor constructor of LambdaFormCachingTest or its child classes
|
||||
* object.
|
||||
* @param testMethods list of test methods
|
||||
*/
|
||||
public static void runTests(Function<TestMethods, LambdaFormTestCase> ctor, Collection<TestMethods> testMethods) {
|
||||
boolean passed = true;
|
||||
int testCounter = 0;
|
||||
int failCounter = 0;
|
||||
long iterations = Math.max(1, Helper.TEST_LIMIT / testMethods.size());
|
||||
for (long i = 0; i < iterations; i++) {
|
||||
System.err.println(String.format("Iteration %d:", i));
|
||||
for (TestMethods testMethod : testMethods) {
|
||||
LambdaFormTestCase testCase = ctor.apply(testMethod);
|
||||
try {
|
||||
System.err.printf("Tested LF caching feature with MethodHandles.%s method.%n",
|
||||
testCase.getTestMethod().name);
|
||||
testCase.doTest();
|
||||
System.err.println("PASSED");
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
System.err.println("FAILED");
|
||||
passed = false;
|
||||
failCounter++;
|
||||
}
|
||||
testCounter++;
|
||||
}
|
||||
}
|
||||
if (!passed) {
|
||||
throw new Error(String.format("%d of %d test cases FAILED! %n"
|
||||
+ "Rerun the test with the same \"-Dseed=\" option as in the log file!",
|
||||
failCounter, testCounter));
|
||||
} else {
|
||||
System.err.println(String.format("All %d test cases PASSED!", testCounter));
|
||||
}
|
||||
}
|
||||
}
|
698
jdk/test/java/lang/invoke/LFCaching/TestMethods.java
Normal file
698
jdk/test/java/lang/invoke/LFCaching/TestMethods.java
Normal file
|
@ -0,0 +1,698 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import com.oracle.testlibrary.jsr292.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(Helper.MAX_ARITY);
|
||||
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(Helper.MAX_ARITY);
|
||||
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(Helper.MAX_ARITY - 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 > Helper.MAX_ARITY - 1) {
|
||||
fakeParList = TestMethods.reduceArgListToSlotsCount(mtDropArgs.parameterList(),
|
||||
Helper.MAX_ARITY - mtTgtSlotsCount - 1);
|
||||
} else {
|
||||
fakeParList = mtDropArgs.parameterList();
|
||||
}
|
||||
return MethodHandles.dropArguments(target, dropArgsPos, fakeParList);
|
||||
}
|
||||
},
|
||||
EXPLICIT_CAST_ARGUMENTS("explicitCastArguments") {
|
||||
@Override
|
||||
public Map<String, Object> getTestCaseData() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY / 2);
|
||||
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") {
|
||||
@Override
|
||||
public Map<String, Object> getTestCaseData() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY / 2);
|
||||
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(Helper.MAX_ARITY);
|
||||
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") {
|
||||
@Override
|
||||
public Map<String, Object> getTestCaseData() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY);
|
||||
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") {
|
||||
@Override
|
||||
public Map<String, Object> getTestCaseData() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY / 2);
|
||||
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 mtParmuteArgsNum = Helper.RNG.nextInt(Helper.MAX_ARITY);
|
||||
mtParmuteArgsNum = mtParmuteArgsNum == 0 ? 1 : mtParmuteArgsNum;
|
||||
MethodType mtPermuteArgs = TestMethods.randomMethodTypeGenerator(mtParmuteArgsNum);
|
||||
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(Helper.MAX_ARITY);
|
||||
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(Helper.MAX_ARITY);
|
||||
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(Helper.MAX_ARITY);
|
||||
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") {
|
||||
@Override
|
||||
public Map<String, Object> getTestCaseData() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY);
|
||||
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") {
|
||||
@Override
|
||||
public Map<String, Object> getTestCaseData() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY);
|
||||
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") {
|
||||
@Override
|
||||
public Map<String, Object> getTestCaseData() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY);
|
||||
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(Helper.MAX_ARITY);
|
||||
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(Helper.MAX_ARITY);
|
||||
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(Helper.MAX_ARITY);
|
||||
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(Helper.MAX_ARITY);
|
||||
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 TestMethods(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
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) {
|
||||
final Class<?>[] CLASSES = {
|
||||
Object.class,
|
||||
int.class,
|
||||
boolean.class,
|
||||
byte.class,
|
||||
short.class,
|
||||
char.class,
|
||||
long.class,
|
||||
float.class,
|
||||
double.class
|
||||
};
|
||||
if (arity > Helper.MAX_ARITY) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Arity should not exceed %d!", Helper.MAX_ARITY));
|
||||
}
|
||||
List<Class<?>> list = Helper.randomClasses(CLASSES, arity);
|
||||
list = Helper.getParams(list, false, arity);
|
||||
int i = Helper.RNG.nextInt(CLASSES.length + 1);
|
||||
Class<?> rtype = i == CLASSES.length ? void.class : CLASSES[i];
|
||||
return MethodType.methodType(rtype, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue