blob: 2b5ac7c9a0276949ec7633d36b77b7fb33f168ae [file] [log] [blame]
/*
* 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.
*
* 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 class Nestmate {
private static Lookup localLookup() { return lookup(); }
}
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/1", mems, publicLookup());
// CS methods have to be revealed with a matching lookupClass
testOnMembersNoReveal("testCallerSensitiveNegative/2", mems, Simple.localLookup(), publicLookup());
testOnMembersNoReveal("testCallerSensitiveNegative/3", mems, Simple.localLookup(), Nestmate.localLookup());
}
@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 (IllegalArgumentException|SecurityException 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...
}
}
}
}
}