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/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...
+ }
+ }
+ }
+ }
+}