8008688: Make MethodHandleInfo public
Summary: A major overhaul to MethodHandleInfo and method handles in general.
Reviewed-by: vlivanov, twisti
Contributed-by: john.r.rose@oracle.com
diff --git a/test/java/lang/invoke/7087570/Test7087570.java b/test/java/lang/invoke/7087570/Test7087570.java
index 572facc..732467b 100644
--- a/test/java/lang/invoke/7087570/Test7087570.java
+++ b/test/java/lang/invoke/7087570/Test7087570.java
@@ -35,20 +35,9 @@
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
+import static java.lang.invoke.MethodHandleInfo.*;
public class Test7087570 {
- // XXX may remove the following constant declarations when MethodHandleInfo is made public
- private static final int
- REF_getField = 1,
- REF_getStatic = 2,
- REF_putField = 3,
- REF_putStatic = 4,
- REF_invokeVirtual = 5,
- REF_invokeStatic = 6,
- REF_invokeSpecial = 7,
- REF_newInvokeSpecial = 8,
- REF_invokeInterface = 9,
- REF_LIMIT = 10;
private static final TestMethodData[] TESTS = new TestMethodData[] {
// field accessors
@@ -87,17 +76,17 @@
}
private static void doTest(MethodHandle mh, TestMethodData testMethod) {
- Object mhi = newMethodHandleInfo(mh);
+ MethodHandleInfo mhi = LOOKUP.revealDirect(mh);
System.out.printf("%s.%s: %s, nominal refKind: %s, actual refKind: %s\n",
testMethod.clazz.getName(), testMethod.name, testMethod.methodType,
- REF_KIND_NAMES[testMethod.referenceKind],
- REF_KIND_NAMES[getReferenceKind(mhi)]);
- assertEquals(testMethod.name, getName(mhi));
- assertEquals(testMethod.methodType, getMethodType(mhi));
- assertEquals(testMethod.declaringClass, getDeclaringClass(mhi));
+ referenceKindToString(testMethod.referenceKind),
+ referenceKindToString(mhi.getReferenceKind()));
+ assertEquals(testMethod.name, mhi.getName());
+ assertEquals(testMethod.methodType, mhi.getMethodType());
+ assertEquals(testMethod.declaringClass, mhi.getDeclaringClass());
assertEquals(testMethod.referenceKind == REF_invokeSpecial, isInvokeSpecial(mh));
- assertRefKindEquals(testMethod.referenceKind, getReferenceKind(mhi));
+ assertRefKindEquals(testMethod.referenceKind, mhi.getReferenceKind());
}
private static void testWithLookup() throws Throwable {
@@ -122,50 +111,8 @@
return methodType(void.class, clazz);
}
- private static final String[] REF_KIND_NAMES = {
- "MH::invokeBasic",
- "REF_getField", "REF_getStatic", "REF_putField", "REF_putStatic",
- "REF_invokeVirtual", "REF_invokeStatic", "REF_invokeSpecial",
- "REF_newInvokeSpecial", "REF_invokeInterface"
- };
-
private static final Lookup LOOKUP = lookup();
- // XXX may remove the following reflective logic when MethodHandleInfo is made public
- private static final MethodHandle MH_IS_INVOKESPECIAL;
- private static final MethodHandle MHI_CONSTRUCTOR;
- private static final MethodHandle MHI_GET_NAME;
- private static final MethodHandle MHI_GET_METHOD_TYPE;
- private static final MethodHandle MHI_GET_DECLARING_CLASS;
- private static final MethodHandle MHI_GET_REFERENCE_KIND;
-
- static {
- try {
- // This is white box testing. Use reflection to grab private implementation bits.
- String magicName = "IMPL_LOOKUP";
- Field magicLookup = MethodHandles.Lookup.class.getDeclaredField(magicName);
- // This unit test will fail if a security manager is installed.
- magicLookup.setAccessible(true);
- // Forbidden fruit...
- Lookup directInvokeLookup = (Lookup) magicLookup.get(null);
- Class<?> mhiClass = Class.forName("java.lang.invoke.MethodHandleInfo", false, MethodHandle.class.getClassLoader());
- MH_IS_INVOKESPECIAL = directInvokeLookup
- .findVirtual(MethodHandle.class, "isInvokeSpecial", methodType(boolean.class));
- MHI_CONSTRUCTOR = directInvokeLookup
- .findConstructor(mhiClass, methodType(void.class, MethodHandle.class));
- MHI_GET_NAME = directInvokeLookup
- .findVirtual(mhiClass, "getName", methodType(String.class));
- MHI_GET_METHOD_TYPE = directInvokeLookup
- .findVirtual(mhiClass, "getMethodType", methodType(MethodType.class));
- MHI_GET_DECLARING_CLASS = directInvokeLookup
- .findVirtual(mhiClass, "getDeclaringClass", methodType(Class.class));
- MHI_GET_REFERENCE_KIND = directInvokeLookup
- .findVirtual(mhiClass, "getReferenceKind", methodType(int.class));
- } catch (ReflectiveOperationException ex) {
- throw new Error(ex);
- }
- }
-
private static class TestMethodData {
final Class<?> clazz;
final String name;
@@ -208,7 +155,9 @@
return LOOKUP.findStatic(testMethod.clazz, testMethod.name, testMethod.methodType);
case REF_invokeSpecial:
Class<?> thisClass = LOOKUP.lookupClass();
- return LOOKUP.findSpecial(testMethod.clazz, testMethod.name, testMethod.methodType, thisClass);
+ MethodHandle smh = LOOKUP.findSpecial(testMethod.clazz, testMethod.name, testMethod.methodType, thisClass);
+ noteInvokeSpecial(smh);
+ return smh;
case REF_newInvokeSpecial:
return LOOKUP.findConstructor(testMethod.clazz, testMethod.methodType);
default:
@@ -238,7 +187,9 @@
case REF_invokeSpecial: {
Method m = testMethod.clazz.getDeclaredMethod(testMethod.name, testMethod.methodType.parameterArray());
Class<?> thisClass = LOOKUP.lookupClass();
- return LOOKUP.unreflectSpecial(m, thisClass);
+ MethodHandle smh = LOOKUP.unreflectSpecial(m, thisClass);
+ noteInvokeSpecial(smh);
+ return smh;
}
case REF_newInvokeSpecial: {
Constructor c = testMethod.clazz.getDeclaredConstructor(testMethod.methodType.parameterArray());
@@ -249,59 +200,20 @@
}
}
- private static Object newMethodHandleInfo(MethodHandle mh) {
- try {
- return MHI_CONSTRUCTOR.invoke(mh);
- } catch (Throwable ex) {
- throw new Error(ex);
- }
+ private static List<MethodHandle> specialMethodHandles = new ArrayList<>();
+ private static void noteInvokeSpecial(MethodHandle mh) {
+ specialMethodHandles.add(mh);
+ assert(isInvokeSpecial(mh));
}
-
private static boolean isInvokeSpecial(MethodHandle mh) {
- try {
- return (boolean) MH_IS_INVOKESPECIAL.invokeExact(mh);
- } catch (Throwable ex) {
- throw new Error(ex);
- }
- }
-
- private static String getName(Object mhi) {
- try {
- return (String) MHI_GET_NAME.invoke(mhi);
- } catch (Throwable ex) {
- throw new Error(ex);
- }
- }
-
- private static MethodType getMethodType(Object mhi) {
- try {
- return (MethodType) MHI_GET_METHOD_TYPE.invoke(mhi);
- } catch (Throwable ex) {
- throw new Error(ex);
- }
- }
-
- private static Class<?> getDeclaringClass(Object mhi) {
- try {
- return (Class<?>) MHI_GET_DECLARING_CLASS.invoke(mhi);
- } catch (Throwable ex) {
- throw new Error(ex);
- }
- }
-
- private static int getReferenceKind(Object mhi) {
- try {
- return (int) MHI_GET_REFERENCE_KIND.invoke(mhi);
- } catch (Throwable ex) {
- throw new Error(ex);
- }
+ return specialMethodHandles.contains(mh);
}
private static void assertRefKindEquals(int expect, int observed) {
if (expect == observed) return;
- String msg = "expected " + REF_KIND_NAMES[(int) expect] +
- " but observed " + REF_KIND_NAMES[(int) observed];
+ String msg = "expected " + referenceKindToString(expect) +
+ " but observed " + referenceKindToString(observed);
System.out.println("FAILED: " + msg);
throw new AssertionError(msg);
}
diff --git a/test/java/lang/invoke/RevealDirectTest.java b/test/java/lang/invoke/RevealDirectTest.java
new file mode 100644
index 0000000..f05b190
--- /dev/null
+++ b/test/java/lang/invoke/RevealDirectTest.java
@@ -0,0 +1,753 @@
+/*
+ * Copyright (c) 2013, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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
+ * @summary verify Lookup.revealDirect on a variety of input handles
+ * @compile -XDignore.symbol.file RevealDirectTest.java
+ * @run junit/othervm -ea -esa test.java.lang.invoke.RevealDirectTest
+ *
+ * @test
+ * @summary verify Lookup.revealDirect on a variety of input handles, with security manager
+ * @run main/othervm/policy=jtreg.security.policy/secure=java.lang.SecurityManager -ea -esa test.java.lang.invoke.RevealDirectTest
+ */
+
+/* To run manually:
+ * $ $JAVA8X_HOME/bin/javac -cp $JUNIT4_JAR -d ../../../.. -XDignore.symbol.file RevealDirectTest.java
+ * $ $JAVA8X_HOME/bin/java -cp $JUNIT4_JAR:../../../.. -ea -esa org.junit.runner.JUnitCore test.java.lang.invoke.RevealDirectTest
+ * $ $JAVA8X_HOME/bin/java -cp $JUNIT4_JAR:../../../.. -ea -esa -Djava.security.manager test.java.lang.invoke.RevealDirectTest
+ */
+
+package test.java.lang.invoke;
+
+import java.lang.reflect.*;
+import java.lang.invoke.*;
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+import static java.lang.invoke.MethodHandleInfo.*;
+import java.util.*;
+import static org.junit.Assert.*;
+import org.junit.*;
+
+public class RevealDirectTest {
+ public static void main(String... av) throws Throwable {
+ // Run the @Test methods explicitly, in case we don't want to use the JUnitCore driver.
+ // This appears to be necessary when running with a security manager.
+ Throwable fail = null;
+ for (Method test : RevealDirectTest.class.getDeclaredMethods()) {
+ if (!test.isAnnotationPresent(Test.class)) continue;
+ try {
+ test.invoke(new RevealDirectTest());
+ } catch (Throwable ex) {
+ if (ex instanceof InvocationTargetException)
+ ex = ex.getCause();
+ if (fail == null) fail = ex;
+ System.out.println("Testcase: "+test.getName()
+ +"("+test.getDeclaringClass().getName()
+ +"):\tCaused an ERROR");
+ System.out.println(ex);
+ ex.printStackTrace(System.out);
+ }
+ }
+ if (fail != null) throw fail;
+ }
+
+ public interface SimpleSuperInterface {
+ public abstract int getInt();
+ public static void printAll(String... args) {
+ System.out.println(Arrays.toString(args));
+ }
+ public int NICE_CONSTANT = 42;
+ }
+ public interface SimpleInterface extends SimpleSuperInterface {
+ default float getFloat() { return getInt(); }
+ public static void printAll(String[] args) {
+ System.out.println(Arrays.toString(args));
+ }
+ }
+ public static class Simple implements SimpleInterface, Cloneable {
+ public int intField;
+ public final int finalField;
+ private static String stringField;
+ public int getInt() { return NICE_CONSTANT; }
+ private static Number getNum() { return 804; }
+ public Simple clone() {
+ try {
+ return (Simple) super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ Simple() { finalField = -NICE_CONSTANT; }
+ private static Lookup localLookup() { return lookup(); }
+ private static List<Member> members() { return getMembers(lookup().lookupClass()); };
+ }
+
+ static boolean VERBOSE = false;
+
+ @Test public void testSimple() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testSimple");
+ testOnMembers("testSimple", Simple.members(), Simple.localLookup());
+ }
+ @Test public void testPublicLookup() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testPublicLookup");
+ List<Member> mems = publicOnly(Simple.members());
+ Lookup pubLookup = publicLookup(), privLookup = Simple.localLookup();
+ testOnMembers("testPublicLookup/1", mems, pubLookup);
+ // reveal using publicLookup:
+ testOnMembers("testPublicLookup/2", mems, privLookup, pubLookup);
+ // lookup using publicLookup, but reveal using private:
+ testOnMembers("testPublicLookup/3", mems, pubLookup, privLookup);
+ }
+ @Test public void testPublicLookupNegative() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testPublicLookupNegative");
+ List<Member> mems = nonPublicOnly(Simple.members());
+ Lookup pubLookup = publicLookup(), privLookup = Simple.localLookup();
+ testOnMembersNoLookup("testPublicLookupNegative/1", mems, pubLookup);
+ testOnMembersNoReveal("testPublicLookupNegative/2", mems, privLookup, pubLookup);
+ testOnMembersNoReflect("testPublicLookupNegative/3", mems, privLookup, pubLookup);
+ }
+ @Test public void testJavaLangClass() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testJavaLangClass");
+ List<Member> mems = callerSensitive(false, publicOnly(getMembers(Class.class)));
+ mems = limit(20, mems);
+ testOnMembers("testJavaLangClass", mems, Simple.localLookup());
+ }
+ @Test public void testCallerSensitive() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testCallerSensitive");
+ List<Member> mems = union(getMembers(MethodHandles.class, "lookup"),
+ getMembers(Method.class, "invoke"),
+ getMembers(Field.class, "get", "set", "getLong"),
+ getMembers(Class.class));
+ mems = callerSensitive(true, publicOnly(mems));
+ mems = limit(10, mems);
+ testOnMembers("testCallerSensitive", mems, Simple.localLookup());
+ }
+ @Test public void testCallerSensitiveNegative() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testCallerSensitiveNegative");
+ List<Member> mems = union(getMembers(MethodHandles.class, "lookup"),
+ getMembers(Class.class, "forName"),
+ getMembers(Method.class, "invoke"));
+ mems = callerSensitive(true, publicOnly(mems));
+ // CS methods cannot be looked up with publicLookup
+ testOnMembersNoLookup("testCallerSensitiveNegative", mems, publicLookup());
+ }
+ @Test public void testMethodHandleNatives() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testMethodHandleNatives");
+ List<Member> mems = getMembers(MethodHandle.class, "invoke", "invokeExact");
+ testOnMembers("testMethodHandleNatives", mems, Simple.localLookup());
+ }
+ @Test public void testMethodHandleInvokes() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testMethodHandleInvokes");
+ List<MethodType> types = new ArrayList<>();
+ Class<?>[] someParamTypes = { void.class, int.class, Object.class, Object[].class };
+ for (Class<?> rt : someParamTypes) {
+ for (Class<?> p0 : someParamTypes) {
+ if (p0 == void.class) { types.add(methodType(rt)); continue; }
+ for (Class<?> p1 : someParamTypes) {
+ if (p1 == void.class) { types.add(methodType(rt, p0)); continue; }
+ for (Class<?> p2 : someParamTypes) {
+ if (p2 == void.class) { types.add(methodType(rt, p0, p1)); continue; }
+ types.add(methodType(rt, p0, p1, p2));
+ }
+ }
+ }
+ }
+ List<Member> mems = union(getPolyMembers(MethodHandle.class, "invoke", types),
+ getPolyMembers(MethodHandle.class, "invokeExact", types));
+ testOnMembers("testMethodHandleInvokes/1", mems, Simple.localLookup());
+ testOnMembers("testMethodHandleInvokes/2", mems, publicLookup());
+ }
+
+ static List<Member> getPolyMembers(Class<?> cls, String name, List<MethodType> types) {
+ assert(cls == MethodHandle.class);
+ ArrayList<Member> mems = new ArrayList<>();
+ for (MethodType type : types) {
+ mems.add(new SignaturePolymorphicMethod(name, type));
+ }
+ return mems;
+ }
+ static List<Member> getMembers(Class<?> cls) {
+ return getMembers(cls, (String[]) null);
+ }
+ static List<Member> getMembers(Class<?> cls, String... onlyNames) {
+ List<String> names = (onlyNames == null || onlyNames.length == 0 ? null : Arrays.asList(onlyNames));
+ ArrayList<Member> res = new ArrayList<>();
+ for (Class<?> sup : getSupers(cls)) {
+ res.addAll(getDeclaredMembers(sup, "getDeclaredFields"));
+ res.addAll(getDeclaredMembers(sup, "getDeclaredMethods"));
+ res.addAll(getDeclaredMembers(sup, "getDeclaredConstructors"));
+ }
+ res = new ArrayList<>(new LinkedHashSet<>(res));
+ for (int i = 0; i < res.size(); i++) {
+ Member mem = res.get(i);
+ if (!canBeReached(mem, cls) ||
+ res.indexOf(mem) != i ||
+ mem.isSynthetic() ||
+ (names != null && !names.contains(mem.getName()))
+ ) {
+ res.remove(i--);
+ }
+ }
+ return res;
+ }
+ static List<Class<?>> getSupers(Class<?> cls) {
+ ArrayList<Class<?>> res = new ArrayList<>();
+ ArrayList<Class<?>> intfs = new ArrayList<>();
+ for (Class<?> sup = cls; sup != null; sup = sup.getSuperclass()) {
+ res.add(sup);
+ for (Class<?> intf : cls.getInterfaces()) {
+ if (!intfs.contains(intf))
+ intfs.add(intf);
+ }
+ }
+ for (int i = 0; i < intfs.size(); i++) {
+ for (Class<?> intf : intfs.get(i).getInterfaces()) {
+ if (!intfs.contains(intf))
+ intfs.add(intf);
+ }
+ }
+ res.addAll(intfs);
+ //System.out.println("getSupers => "+res);
+ return res;
+ }
+ static boolean hasSM() {
+ return (System.getSecurityManager() != null);
+ }
+ static List<Member> getDeclaredMembers(Class<?> cls, String accessor) {
+ Member[] mems = {};
+ Method getter = getMethod(Class.class, accessor);
+ if (hasSM()) {
+ try {
+ mems = (Member[]) invokeMethod(getter, cls);
+ } catch (SecurityException ex) {
+ //if (VERBOSE) ex.printStackTrace();
+ accessor = accessor.replace("Declared", "");
+ getter = getMethod(Class.class, accessor);
+ if (VERBOSE) System.out.println("replaced accessor: "+getter);
+ }
+ }
+ if (mems.length == 0) {
+ try {
+ mems = (Member[]) invokeMethod(getter, cls);
+ } catch (SecurityException ex) {
+ ex.printStackTrace();
+ }
+ }
+ if (VERBOSE) System.out.println(accessor+" "+cls.getName()+" => "+mems.length+" members");
+ return Arrays.asList(mems);
+ }
+ static Method getMethod(Class<?> cls, String name) {
+ try {
+ return cls.getMethod(name);
+ } catch (ReflectiveOperationException ex) {
+ throw new AssertionError(ex);
+ }
+ }
+ static Object invokeMethod(Method m, Object recv, Object... args) {
+ try {
+ return m.invoke(recv, args);
+ } catch (InvocationTargetException ex) {
+ Throwable ex2 = ex.getCause();
+ if (ex2 instanceof RuntimeException) throw (RuntimeException) ex2;
+ if (ex2 instanceof Error) throw (Error) ex2;
+ throw new AssertionError(ex);
+ } catch (ReflectiveOperationException ex) {
+ throw new AssertionError(ex);
+ }
+ }
+
+ static List<Member> limit(int len, List<Member> mems) {
+ if (mems.size() <= len) return mems;
+ return mems.subList(0, len);
+ }
+ @SafeVarargs
+ static List<Member> union(List<Member> mems, List<Member>... mem2s) {
+ for (List<Member> mem2 : mem2s) {
+ for (Member m : mem2) {
+ if (!mems.contains(m))
+ mems.add(m);
+ }
+ }
+ return mems;
+ }
+ static List<Member> callerSensitive(boolean cond, List<Member> members) {
+ for (Iterator<Member> i = members.iterator(); i.hasNext(); ) {
+ Member mem = i.next();
+ if (isCallerSensitive(mem) != cond)
+ i.remove();
+ }
+ if (members.isEmpty()) throw new AssertionError("trivial result");
+ return members;
+ }
+ static boolean isCallerSensitive(Member mem) {
+ if (!(mem instanceof AnnotatedElement)) return false;
+ AnnotatedElement ae = (AnnotatedElement) mem;
+ if (CS_CLASS != null)
+ return ae.isAnnotationPresent(sun.reflect.CallerSensitive.class);
+ for (java.lang.annotation.Annotation a : ae.getDeclaredAnnotations()) {
+ if (a.toString().contains(".CallerSensitive"))
+ return true;
+ }
+ return false;
+ }
+ static final Class<?> CS_CLASS;
+ static {
+ Class<?> c = null;
+ try {
+ c = sun.reflect.CallerSensitive.class;
+ } catch (SecurityException | LinkageError ex) {
+ }
+ CS_CLASS = c;
+ }
+ static List<Member> publicOnly(List<Member> members) {
+ return removeMods(members, Modifier.PUBLIC, 0);
+ }
+ static List<Member> nonPublicOnly(List<Member> members) {
+ return removeMods(members, Modifier.PUBLIC, -1);
+ }
+ static List<Member> removeMods(List<Member> members, int mask, int bits) {
+ int publicMods = (mask & Modifier.PUBLIC);
+ members = new ArrayList<>(members);
+ for (Iterator<Member> i = members.iterator(); i.hasNext(); ) {
+ Member mem = i.next();
+ int mods = mem.getModifiers();
+ if ((publicMods & mods) != 0 &&
+ (publicMods & mem.getDeclaringClass().getModifiers()) == 0)
+ mods -= publicMods;
+ if ((mods & mask) == (bits & mask))
+ i.remove();
+ }
+ return members;
+ }
+
+ void testOnMembers(String tname, List<Member> mems, Lookup lookup, Lookup... lookups) throws Throwable {
+ if (VERBOSE) System.out.println("testOnMembers "+mems);
+ Lookup revLookup = (lookups.length > 0) ? lookups[0] : null;
+ if (revLookup == null) revLookup = lookup;
+ Lookup refLookup = (lookups.length > 1) ? lookups[1] : null;
+ if (refLookup == null) refLookup = lookup;
+ assert(lookups.length <= 2);
+ testOnMembersImpl(tname, mems, lookup, revLookup, refLookup, NO_FAIL);
+ }
+ void testOnMembersNoLookup(String tname, List<Member> mems, Lookup lookup) throws Throwable {
+ if (VERBOSE) System.out.println("testOnMembersNoLookup "+mems);
+ testOnMembersImpl(tname, mems, lookup, null, null, FAIL_LOOKUP);
+ }
+ void testOnMembersNoReveal(String tname, List<Member> mems,
+ Lookup lookup, Lookup negLookup) throws Throwable {
+ if (VERBOSE) System.out.println("testOnMembersNoReveal "+mems);
+ testOnMembersImpl(tname, mems, lookup, negLookup, null, FAIL_REVEAL);
+ }
+ void testOnMembersNoReflect(String tname, List<Member> mems,
+ Lookup lookup, Lookup negLookup) throws Throwable {
+ if (VERBOSE) System.out.println("testOnMembersNoReflect "+mems);
+ testOnMembersImpl(tname, mems, lookup, lookup, negLookup, FAIL_REFLECT);
+ }
+ void testOnMembersImpl(String tname, List<Member> mems,
+ Lookup lookup,
+ Lookup revLookup,
+ Lookup refLookup,
+ int failureMode) throws Throwable {
+ Throwable fail = null;
+ int failCount = 0;
+ failureModeCounts = new int[FAIL_MODE_COUNT];
+ long tm0 = System.currentTimeMillis();
+ for (Member mem : mems) {
+ try {
+ testWithMember(mem, lookup, revLookup, refLookup, failureMode);
+ } catch (Throwable ex) {
+ if (fail == null) fail = ex;
+ if (++failCount > 10) { System.out.println("*** FAIL: too many failures"); break; }
+ System.out.println("*** FAIL: "+mem+" => "+ex);
+ if (VERBOSE) ex.printStackTrace(System.out);
+ }
+ }
+ long tm1 = System.currentTimeMillis();
+ System.out.printf("@Test %s executed %s tests in %d ms",
+ tname, testKinds(failureModeCounts), (tm1-tm0)).println();
+ if (fail != null) throw fail;
+ }
+ static String testKinds(int[] modes) {
+ int pos = modes[0], neg = -pos;
+ for (int n : modes) neg += n;
+ if (neg == 0) return pos + " positive";
+ String negs = "";
+ for (int n : modes) negs += "/"+n;
+ negs = negs.replaceFirst("/"+pos+"/", "");
+ negs += " negative";
+ if (pos == 0) return negs;
+ return pos + " positive, " + negs;
+ }
+ static class SignaturePolymorphicMethod implements Member { // non-reflected instance of MH.invoke*
+ final String name;
+ final MethodType type;
+ SignaturePolymorphicMethod(String name, MethodType type) {
+ this.name = name;
+ this.type = type;
+ }
+ public String toString() {
+ String typeStr = type.toString();
+ if (isVarArgs()) typeStr = typeStr.replaceFirst("\\[\\])$", "...)");
+ return (Modifier.toString(getModifiers())
+ +typeStr.substring(0, typeStr.indexOf('('))+" "
+ +getDeclaringClass().getTypeName()+"."
+ +getName()+typeStr.substring(typeStr.indexOf('(')));
+ }
+ public boolean equals(Object x) {
+ return (x instanceof SignaturePolymorphicMethod && equals((SignaturePolymorphicMethod)x));
+ }
+ public boolean equals(SignaturePolymorphicMethod that) {
+ return this.name.equals(that.name) && this.type.equals(that.type);
+ }
+ public int hashCode() {
+ return name.hashCode() * 31 + type.hashCode();
+ }
+ public Class<?> getDeclaringClass() { return MethodHandle.class; }
+ public String getName() { return name; }
+ public MethodType getMethodType() { return type; }
+ public int getModifiers() { return Modifier.PUBLIC | Modifier.FINAL | Modifier.NATIVE | SYNTHETIC; }
+ public boolean isVarArgs() { return Modifier.isTransient(getModifiers()); }
+ public boolean isSynthetic() { return true; }
+ public Class<?> getReturnType() { return type.returnType(); }
+ public Class<?>[] getParameterTypes() { return type.parameterArray(); }
+ static final int SYNTHETIC = 0x00001000;
+ }
+ static class UnreflectResult { // a tuple
+ final MethodHandle mh;
+ final Throwable ex;
+ final byte kind;
+ final Member mem;
+ final int var;
+ UnreflectResult(MethodHandle mh, byte kind, Member mem, int var) {
+ this.mh = mh;
+ this.ex = null;
+ this.kind = kind;
+ this.mem = mem;
+ this.var = var;
+ }
+ UnreflectResult(Throwable ex, byte kind, Member mem, int var) {
+ this.mh = null;
+ this.ex = ex;
+ this.kind = kind;
+ this.mem = mem;
+ this.var = var;
+ }
+ public String toString() {
+ return toInfoString()+"/v"+var;
+ }
+ public String toInfoString() {
+ return String.format("%s %s.%s:%s", MethodHandleInfo.referenceKindToString(kind),
+ mem.getDeclaringClass().getName(), name(mem), type(mem, kind));
+ }
+ static String name(Member mem) {
+ if (mem instanceof Constructor) return "<init>";
+ return mem.getName();
+ }
+ static MethodType type(Member mem, byte kind) {
+ if (mem instanceof Field) {
+ Class<?> type = ((Field)mem).getType();
+ if (kind == REF_putStatic || kind == REF_putField)
+ return methodType(void.class, type);
+ return methodType(type);
+ } else if (mem instanceof SignaturePolymorphicMethod) {
+ return ((SignaturePolymorphicMethod)mem).getMethodType();
+ }
+ Class<?>[] params = ((Executable)mem).getParameterTypes();
+ if (mem instanceof Constructor)
+ return methodType(void.class, params);
+ Class<?> type = ((Method)mem).getReturnType();
+ return methodType(type, params);
+ }
+ }
+ static UnreflectResult unreflectMember(Lookup lookup, Member mem, int variation) {
+ byte[] refKind = {0};
+ try {
+ return unreflectMemberOrThrow(lookup, mem, variation, refKind);
+ } catch (ReflectiveOperationException|SecurityException ex) {
+ return new UnreflectResult(ex, refKind[0], mem, variation);
+ }
+ }
+ static UnreflectResult unreflectMemberOrThrow(Lookup lookup, Member mem, int variation,
+ byte[] refKind) throws ReflectiveOperationException {
+ Class<?> cls = lookup.lookupClass();
+ Class<?> defc = mem.getDeclaringClass();
+ String name = mem.getName();
+ int mods = mem.getModifiers();
+ boolean isStatic = Modifier.isStatic(mods);
+ MethodHandle mh = null;
+ byte kind = 0;
+ if (mem instanceof Method) {
+ Method m = (Method) mem;
+ MethodType type = methodType(m.getReturnType(), m.getParameterTypes());
+ boolean canBeSpecial = (!isStatic &&
+ (lookup.lookupModes() & Modifier.PRIVATE) != 0 &&
+ defc.isAssignableFrom(cls) &&
+ (!defc.isInterface() || Arrays.asList(cls.getInterfaces()).contains(defc)));
+ if (variation >= 2)
+ kind = REF_invokeSpecial;
+ else if (isStatic)
+ kind = REF_invokeStatic;
+ else if (defc.isInterface())
+ kind = REF_invokeInterface;
+ else
+ kind = REF_invokeVirtual;
+ refKind[0] = kind;
+ switch (variation) {
+ case 0:
+ mh = lookup.unreflect(m);
+ break;
+ case 1:
+ if (defc == MethodHandle.class &&
+ !isStatic &&
+ m.isVarArgs() &&
+ Modifier.isFinal(mods) &&
+ Modifier.isNative(mods)) {
+ break;
+ }
+ if (isStatic)
+ mh = lookup.findStatic(defc, name, type);
+ else
+ mh = lookup.findVirtual(defc, name, type);
+ break;
+ case 2:
+ if (!canBeSpecial)
+ break;
+ mh = lookup.unreflectSpecial(m, lookup.lookupClass());
+ break;
+ case 3:
+ if (!canBeSpecial)
+ break;
+ mh = lookup.findSpecial(defc, name, type, lookup.lookupClass());
+ break;
+ }
+ } else if (mem instanceof SignaturePolymorphicMethod) {
+ SignaturePolymorphicMethod m = (SignaturePolymorphicMethod) mem;
+ MethodType type = methodType(m.getReturnType(), m.getParameterTypes());
+ kind = REF_invokeVirtual;
+ refKind[0] = kind;
+ switch (variation) {
+ case 0:
+ mh = lookup.findVirtual(defc, name, type);
+ break;
+ }
+ } else if (mem instanceof Constructor) {
+ name = "<init>"; // not used
+ Constructor<?> m = (Constructor<?>) mem;
+ MethodType type = methodType(void.class, m.getParameterTypes());
+ kind = REF_newInvokeSpecial;
+ refKind[0] = kind;
+ switch (variation) {
+ case 0:
+ mh = lookup.unreflectConstructor(m);
+ break;
+ case 1:
+ mh = lookup.findConstructor(defc, type);
+ break;
+ }
+ } else if (mem instanceof Field) {
+ Field m = (Field) mem;
+ Class<?> type = m.getType();
+ boolean canHaveSetter = !Modifier.isFinal(mods);
+ if (variation >= 2)
+ kind = (byte)(isStatic ? REF_putStatic : REF_putField);
+ else
+ kind = (byte)(isStatic ? REF_getStatic : REF_getField);
+ refKind[0] = kind;
+ switch (variation) {
+ case 0:
+ mh = lookup.unreflectGetter(m);
+ break;
+ case 1:
+ if (isStatic)
+ mh = lookup.findStaticGetter(defc, name, type);
+ else
+ mh = lookup.findGetter(defc, name, type);
+ break;
+ case 3:
+ if (!canHaveSetter)
+ break;
+ mh = lookup.unreflectSetter(m);
+ break;
+ case 2:
+ if (!canHaveSetter)
+ break;
+ if (isStatic)
+ mh = lookup.findStaticSetter(defc, name, type);
+ else
+ mh = lookup.findSetter(defc, name, type);
+ break;
+ }
+ } else {
+ throw new IllegalArgumentException(String.valueOf(mem));
+ }
+ if (mh == null)
+ // ran out of valid variations; return null to caller
+ return null;
+ return new UnreflectResult(mh, kind, mem, variation);
+ }
+ static boolean canBeReached(Member mem, Class<?> cls) {
+ Class<?> defc = mem.getDeclaringClass();
+ String name = mem.getName();
+ int mods = mem.getModifiers();
+ if (mem instanceof Constructor) {
+ name = "<init>"; // according to 292 spec.
+ }
+ if (defc == cls)
+ return true;
+ if (name.startsWith("<"))
+ return false; // only my own constructors
+ if (Modifier.isPrivate(mods))
+ return false; // only my own constructors
+ if (defc.getPackage() == cls.getPackage())
+ return true; // package access or greater OK
+ if (Modifier.isPublic(mods))
+ return true; // publics always OK
+ if (Modifier.isProtected(mods) && defc.isAssignableFrom(cls))
+ return true; // protected OK
+ return false;
+ }
+ static boolean consistent(UnreflectResult res, MethodHandleInfo info) {
+ assert(res.mh != null);
+ assertEquals(res.kind, info.getReferenceKind());
+ assertEquals(res.mem.getModifiers(), info.getModifiers());
+ assertEquals(res.mem.getDeclaringClass(), info.getDeclaringClass());
+ String expectName = res.mem.getName();
+ if (res.kind == REF_newInvokeSpecial)
+ expectName = "<init>";
+ assertEquals(expectName, info.getName());
+ MethodType expectType = res.mh.type();
+ if ((res.kind & 1) == (REF_getField & 1))
+ expectType = expectType.dropParameterTypes(0, 1);
+ if (res.kind == REF_newInvokeSpecial)
+ expectType = expectType.changeReturnType(void.class);
+ assertEquals(expectType, info.getMethodType());
+ assertEquals(res.mh.isVarargsCollector(), isVarArgs(info));
+ assertEquals(res.toInfoString(), info.toString());
+ assertEquals(res.toInfoString(), MethodHandleInfo.toString(info.getReferenceKind(), info.getDeclaringClass(), info.getName(), info.getMethodType()));
+ return true;
+ }
+ static boolean isVarArgs(MethodHandleInfo info) {
+ return info.isVarArgs();
+ }
+ static boolean consistent(Member mem, Member mem2) {
+ assertEquals(mem, mem2);
+ return true;
+ }
+ static boolean consistent(MethodHandleInfo info, MethodHandleInfo info2) {
+ assertEquals(info.getReferenceKind(), info2.getReferenceKind());
+ assertEquals(info.getModifiers(), info2.getModifiers());
+ assertEquals(info.getDeclaringClass(), info2.getDeclaringClass());
+ assertEquals(info.getName(), info2.getName());
+ assertEquals(info.getMethodType(), info2.getMethodType());
+ assertEquals(isVarArgs(info), isVarArgs(info));
+ return true;
+ }
+ static boolean consistent(MethodHandle mh, MethodHandle mh2) {
+ assertEquals(mh.type(), mh2.type());
+ assertEquals(mh.isVarargsCollector(), mh2.isVarargsCollector());
+ return true;
+ }
+ int[] failureModeCounts;
+ static final int NO_FAIL=0, FAIL_LOOKUP=1, FAIL_REVEAL=2, FAIL_REFLECT=3, FAIL_MODE_COUNT=4;
+ void testWithMember(Member mem,
+ Lookup lookup, // initial lookup of member => MH
+ Lookup revLookup, // reveal MH => info
+ Lookup refLookup, // reflect info => member
+ int failureMode) throws Throwable {
+ boolean expectEx1 = (failureMode == FAIL_LOOKUP); // testOnMembersNoLookup
+ boolean expectEx2 = (failureMode == FAIL_REVEAL); // testOnMembersNoReveal
+ boolean expectEx3 = (failureMode == FAIL_REFLECT); // testOnMembersNoReflect
+ for (int variation = 0; ; variation++) {
+ UnreflectResult res = unreflectMember(lookup, mem, variation);
+ failureModeCounts[failureMode] += 1;
+ if (variation == 0) assert(res != null);
+ if (res == null) break;
+ if (VERBOSE && variation == 0)
+ System.out.println("from "+mem.getDeclaringClass().getSimpleName());
+ MethodHandle mh = res.mh;
+ Throwable ex1 = res.ex;
+ if (VERBOSE) System.out.println(" "+variation+": "+res+" << "+(mh != null ? mh : ex1));
+ if (expectEx1 && ex1 != null)
+ continue; // this is OK; we expected that lookup to fail
+ if (expectEx1)
+ throw new AssertionError("unexpected lookup for negative test");
+ if (ex1 != null && !expectEx1) {
+ if (failureMode != NO_FAIL)
+ throw new AssertionError("unexpected lookup failure for negative test", ex1);
+ throw ex1;
+ }
+ MethodHandleInfo info;
+ try {
+ info = revLookup.revealDirect(mh);
+ if (expectEx2) throw new AssertionError("unexpected revelation for negative test");
+ } catch (Throwable ex2) {
+ if (VERBOSE) System.out.println(" "+variation+": "+res+" => "+mh.getClass().getName()+" => (EX2)"+ex2);
+ if (expectEx2)
+ continue; // this is OK; we expected the reflect to fail
+ if (failureMode != NO_FAIL)
+ throw new AssertionError("unexpected revelation failure for negative test", ex2);
+ throw ex2;
+ }
+ assert(consistent(res, info));
+ Member mem2;
+ try {
+ mem2 = info.reflectAs(Member.class, refLookup);
+ if (expectEx3) throw new AssertionError("unexpected reflection for negative test");
+ assert(!(mem instanceof SignaturePolymorphicMethod));
+ } catch (IllegalArgumentException ex3) {
+ if (VERBOSE) System.out.println(" "+variation+": "+info+" => (EX3)"+ex3);
+ if (expectEx3)
+ continue; // this is OK; we expected the reflect to fail
+ if (mem instanceof SignaturePolymorphicMethod)
+ continue; // this is OK; we cannot reflect MH.invokeExact(a,b,c)
+ if (failureMode != NO_FAIL)
+ throw new AssertionError("unexpected reflection failure for negative test", ex3);
+ throw ex3;
+ }
+ assert(consistent(mem, mem2));
+ UnreflectResult res2 = unreflectMember(lookup, mem2, variation);
+ MethodHandle mh2 = res2.mh;
+ assert(consistent(mh, mh2));
+ MethodHandleInfo info2 = lookup.revealDirect(mh2);
+ assert(consistent(info, info2));
+ assert(consistent(res, info2));
+ Member mem3;
+ if (hasSM())
+ mem3 = info2.reflectAs(Member.class, lookup);
+ else
+ mem3 = MethodHandles.reflectAs(Member.class, mh2);
+ assert(consistent(mem2, mem3));
+ if (hasSM()) {
+ try {
+ MethodHandles.reflectAs(Member.class, mh2);
+ throw new AssertionError("failed to throw on "+mem3);
+ } catch (SecurityException ex3) {
+ // OK...
+ }
+ }
+ }
+ }
+}
diff --git a/test/java/lang/invoke/jtreg.security.policy b/test/java/lang/invoke/jtreg.security.policy
new file mode 100644
index 0000000..d32c7af
--- /dev/null
+++ b/test/java/lang/invoke/jtreg.security.policy
@@ -0,0 +1,9 @@
+/*
+ * security policy used by the test process
+ * must allow file reads so that jtreg itself can run
+ */
+
+grant {
+ // standard test activation permissions
+ permission java.io.FilePermission "*", "read";
+};