下面列出了怎么用java.lang.invoke.MethodHandles.Lookup的API类实例代码及写法,或者点击链接到github查看源代码。
static MethodHandle[] makeLists() {
ArrayList<MethodHandle> lists = new ArrayList<>();
MethodHandles.Lookup lookup = IMPL_LOOKUP;
for (;;) {
int nargs = lists.size();
MethodType type = MethodType.genericMethodType(nargs).changeReturnType(List.class);
String name = "list";
MethodHandle list = null;
try {
list = lookup.findStatic(ValueConversions.class, name, type);
} catch (ReflectiveOperationException ex) {
// break from loop!
}
if (list == null) break;
lists.add(list);
}
assertTrue(lists.size() == 11); // current number of methods
return lists.toArray(new MethodHandle[0]);
}
public static void main(String[] args) throws Throwable {
Lookup LOOKUP = T3.lookup();
Class<IllegalAccessException> IAE = IllegalAccessException.class;
assertFailure(IAE, () -> LOOKUP.findVirtual(T1.class, "m1", MethodType.methodType(void.class)));
assertFailure(IAE, () -> LOOKUP.findStatic(T1.class, "m2", MethodType.methodType(void.class)));
assertSuccess(() -> LOOKUP.findVirtual(T2.class, "m1", MethodType.methodType(void.class)));
assertSuccess(() -> LOOKUP.findVirtual(T3.class, "m1", MethodType.methodType(void.class)));
assertSuccess(() -> LOOKUP.findStatic(T2.class, "m2", MethodType.methodType(void.class)));
assertSuccess(() -> LOOKUP.findStatic(T3.class, "m2", MethodType.methodType(void.class)));
assertFailure(IAE, () -> LOOKUP.unreflect(T1.class.getDeclaredMethod("m1")));
assertFailure(IAE, () -> LOOKUP.unreflect(T1.class.getDeclaredMethod("m2")));
System.out.println("TEST PASSED");
}
static MethodHandle[] makeArrays() {
ArrayList<MethodHandle> arrays = new ArrayList<>();
MethodHandles.Lookup lookup = IMPL_LOOKUP;
for (;;) {
int nargs = arrays.size();
MethodType type = MethodType.genericMethodType(nargs).changeReturnType(Object[].class);
String name = "array";
MethodHandle array = null;
try {
array = lookup.findStatic(ValueConversions.class, name, type);
} catch (ReflectiveOperationException ex) {
// break from loop!
}
if (array == null) break;
arrays.add(array);
}
assertTrue(arrays.size() == 11); // current number of methods
return arrays.toArray(new MethodHandle[0]);
}
public void testGetter(int testMode) throws Throwable {
Lookup lookup = PRIVATE; // FIXME: test more lookups than this one
for (Object[] c : HasFields.CASES) {
boolean positive = (c[1] != Error.class);
testGetter(positive, lookup, c[0], c[1], testMode);
if (positive)
testGetter(positive, lookup, c[0], c[1], testMode | TEST_NPE);
}
testGetter(true, lookup,
new Object[]{ true, System.class, "out", java.io.PrintStream.class },
System.out, testMode);
for (int isStaticN = 0; isStaticN <= 1; isStaticN++) {
testGetter(false, lookup,
new Object[]{ (isStaticN != 0), System.class, "bogus", char.class },
null, testMode);
}
}
public static void main(String... args) throws Throwable {
// Get a full power lookup
Lookup lookup1 = MethodHandles.lookup();
MethodHandle mh1 = lookup1.findStatic(lookup1.lookupClass(),
"foo",
methodType(String.class));
assertEquals((String) mh1.invokeExact(), foo());
Method lookupMethod = MethodHandles.class.getMethod("lookup");
System.out.println("reflection method: " + lookupMethod);
if (!lookupMethod.getName().equals("lookup")) {
throw new RuntimeException("Unexpected name: " + lookupMethod.getName());
}
// Get a full power Lookup reflectively.
Lookup lookup2 = (Lookup) lookupMethod.invoke(null);
assertEquals(lookup1.lookupClass(), lookup2.lookupClass());
assertEquals(lookup1.lookupModes(), lookup2.lookupModes());
MethodHandle mh2 = lookup2.findStatic(lookup2.lookupClass(),
"foo",
methodType(String.class));
assertEquals((String) mh2.invokeExact(), foo());
}
static MethodHandle[] makeArrays() {
ArrayList<MethodHandle> arrays = new ArrayList<>();
MethodHandles.Lookup lookup = IMPL_LOOKUP;
for (;;) {
int nargs = arrays.size();
MethodType type = MethodType.genericMethodType(nargs).changeReturnType(Object[].class);
String name = "array";
MethodHandle array = null;
try {
array = lookup.findStatic(ValueConversions.class, name, type);
} catch (ReflectiveOperationException ex) {
// break from loop!
}
if (array == null) break;
arrays.add(array);
}
assertTrue(arrays.size() == 11); // current number of methods
return arrays.toArray(new MethodHandle[0]);
}
/**
* Produces a {@link MethodHandle} using either the context or {@link #targetLoader} class
* loader, depending on {@code target}.
*/
private MethodHandle toMethodHandle(Lookup lookup, Handle asmHandle, boolean target)
throws ReflectiveOperationException {
Class<?> owner = loadFromInternal(asmHandle.getOwner());
MethodType signature =
MethodType.fromMethodDescriptorString(
asmHandle.getDesc(),
target ? targetLoader : Thread.currentThread().getContextClassLoader());
switch (asmHandle.getTag()) {
case Opcodes.H_INVOKESTATIC:
return lookup.findStatic(owner, asmHandle.getName(), signature);
case Opcodes.H_INVOKEVIRTUAL:
case Opcodes.H_INVOKEINTERFACE:
return lookup.findVirtual(owner, asmHandle.getName(), signature);
case Opcodes.H_INVOKESPECIAL: // we end up calling these using invokevirtual
return lookup.findSpecial(owner, asmHandle.getName(), signature, owner);
case Opcodes.H_NEWINVOKESPECIAL:
return lookup.findConstructor(owner, signature);
default:
throw new UnsupportedOperationException("Cannot resolve " + asmHandle);
}
}
/**
* For a particular type name, get the JCasClassInfo
* - by fetching the cached value
* - by loading the class
* - return null if no JCas class for this name
* only called for non-Pear callers
* @param ti -
* @param cl -
* @param type2jcci -
* @param lookup -
* @return - jcci or null, if no JCas class for this type was able to be loaded
*/
public static JCasClassInfo getOrCreateJCasClassInfo(
TypeImpl ti,
ClassLoader cl,
Map<String, JCasClassInfo> type2jcci,
Lookup lookup) {
JCasClassInfo jcci = type2jcci.get(ti.getJCasClassName());
if (jcci == null) {
jcci = maybeCreateJCasClassInfo(ti, cl, type2jcci, lookup);
}
// do this setup for new type systems using previously loaded jcci, as well as
// for new jccis
if (jcci != null && jcci.jcasType >= 0) {
ti.getTypeSystem().setJCasRegisteredType(jcci.jcasType, ti);
}
return jcci;
}
public static CallSite constructorBootstrap(Lookup lookup, String selector,
MethodType type, String referenceString) throws Throwable {
Reference reference = Reference.factory.value_(referenceString);
Constructor constructor = ImageBootstrapper.systemMapping.classMappingAtReference_(reference).identityClass().getConstructor();
MethodHandle constructorHandle = lookup.unreflectConstructor(constructor);
return new ConstantCallSite(constructorHandle.asType(type));
}
public void testRunnableProxy0() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("testRunnableProxy");
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle run = lookup.findStatic(lookup.lookupClass(), "runForRunnable", MethodType.methodType(void.class));
Runnable r = MethodHandleProxies.asInterfaceInstance(Runnable.class, run);
testRunnableProxy(r);
assertCalled("runForRunnable");
}
public void testFindSpecial0() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("findSpecial");
testFindSpecial(SubExample.class, Example.class, void.class, "v0");
testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0");
testFindSpecial(RemoteExample.class, PubExample.class, void.class, "Pub/pro_v0");
// Do some negative testing:
for (Lookup lookup : new Lookup[]{ PRIVATE, EXAMPLE, PACKAGE, PUBLIC }) {
testFindSpecial(false, lookup, Object.class, Example.class, void.class, "v0");
testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "bogus");
testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", int.class);
testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", Void.class);
testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0");
}
}
void testFindConstructor(boolean positive, Lookup lookup,
Class<?> defc, Class<?>... params) throws Throwable {
countTest(positive);
MethodType type = MethodType.methodType(void.class, params);
MethodHandle target = null;
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" <init>"+type);
target = lookup.findConstructor(defc, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
assertTrue(noAccess.getClass().getName(), noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("findConstructor "+defc.getName()+".<init>/"+type+" => "+target
+(target == null ? "" : target.type())
+(noAccess == null ? "" : " !! "+noAccess));
if (positive && noAccess != null) throw noAccess;
assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
if (!positive) return; // negative test failed as expected
assertEquals(type.changeReturnType(defc), target.type());
Object[] args = randomArgs(params);
printCalled(target, defc.getSimpleName(), args);
Object obj = target.invokeWithArguments(args);
if (!(defc == Example.class && params.length < 2))
assertCalled(defc.getSimpleName()+".<init>", args);
assertTrue("instance of "+defc.getName(), defc.isInstance(obj));
}
private static boolean lookupsEqual(final Lookup l1, final Lookup l2) {
if(l1 == l2) {
return true;
}
if(l1.lookupClass() != l2.lookupClass()) {
return false;
}
return l1.lookupModes() == l2.lookupModes();
}
void testBind(boolean positive, Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
countTest(positive);
String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo
MethodType type = MethodType.methodType(ret, params);
Object receiver = randomArg(defc);
MethodHandle target = null;
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = maybeMoveIn(lookup, defc).bind(receiver, methodName, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
assertExceptionClass(
(name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
? NoSuchMethodException.class
: IllegalAccessException.class,
noAccess);
if (verbosity >= 5) ex.printStackTrace(System.out);
}
if (verbosity >= 3)
System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target
+(noAccess == null ? "" : " !! "+noAccess));
if (positive && noAccess != null) throw noAccess;
assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
if (!positive) return; // negative test failed as expected
assertEquals(type, target.type());
Object[] args = randomArgs(params);
printCalled(target, name, args);
target.invokeWithArguments(args);
Object[] argsWithReceiver = cat(array(Object[].class, receiver), args);
assertCalled(name, argsWithReceiver);
if (verbosity >= 1)
System.out.print(':');
}
@Test
public void testUserClassInSignature() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("testUserClassInSignature");
Lookup lookup = MethodHandles.lookup();
String name; MethodType mt; MethodHandle mh;
Object[] args;
// Try a static method.
name = "userMethod";
mt = MethodType.methodType(Example.class, Object.class, String.class, int.class);
mh = lookup.findStatic(lookup.lookupClass(), name, mt);
assertEquals(mt, mh.type());
assertEquals(Example.class, mh.type().returnType());
args = randomArgs(mh.type().parameterArray());
mh.invokeWithArguments(args);
assertCalled(name, args);
// Try a virtual method.
name = "v2";
mt = MethodType.methodType(Object.class, Object.class, int.class);
mh = lookup.findVirtual(Example.class, name, mt);
assertEquals(mt, mh.type().dropParameterTypes(0,1));
assertTrue(mh.type().parameterList().contains(Example.class));
args = randomArgs(mh.type().parameterArray());
mh.invokeWithArguments(args);
assertCalled(name, args);
}
private static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String operator, final String operand, final MethodType methodType, final int flags) {
final NashornCallSiteDescriptor csd = new NashornCallSiteDescriptor(lookup, operator, operand, methodType, flags);
// Many of these call site descriptors are identical (e.g. every getter for a property color will be
// "dyn:getProp:color(Object)Object", so it makes sense canonicalizing them.
final ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> classCanonicals = canonicals.get(lookup.lookupClass());
final NashornCallSiteDescriptor canonical = classCanonicals.putIfAbsent(csd, csd);
return canonical != null ? canonical : csd;
}
public void testRunnableProxy0() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("testRunnableProxy");
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle run = lookup.findStatic(lookup.lookupClass(), "runForRunnable", MethodType.methodType(void.class));
Runnable r = MethodHandleProxies.asInterfaceInstance(Runnable.class, run);
testRunnableProxy(r);
assertCalled("runForRunnable");
}
public void testSetter(int testMode) throws Throwable {
Lookup lookup = PRIVATE; // FIXME: test more lookups than this one
startTest("unreflectSetter");
for (Object[] c : HasFields.CASES) {
boolean positive = (c[1] != Error.class);
testSetter(positive, lookup, c[0], c[1], testMode);
if (positive)
testSetter(positive, lookup, c[0], c[1], testMode | TEST_NPE);
}
for (int isStaticN = 0; isStaticN <= 1; isStaticN++) {
testSetter(false, lookup,
new Object[]{ (isStaticN != 0), System.class, "bogus", char.class },
null, testMode);
}
}
@Test
public void testUserClassInSignature() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("testUserClassInSignature");
Lookup lookup = MethodHandles.lookup();
String name; MethodType mt; MethodHandle mh;
Object[] args;
// Try a static method.
name = "userMethod";
mt = MethodType.methodType(Example.class, Object.class, String.class, int.class);
mh = lookup.findStatic(lookup.lookupClass(), name, mt);
assertEquals(mt, mh.type());
assertEquals(Example.class, mh.type().returnType());
args = randomArgs(mh.type().parameterArray());
mh.invokeWithArguments(args);
assertCalled(name, args);
// Try a virtual method.
name = "v2";
mt = MethodType.methodType(Object.class, Object.class, int.class);
mh = lookup.findVirtual(Example.class, name, mt);
assertEquals(mt, mh.type().dropParameterTypes(0,1));
assertTrue(mh.type().parameterList().contains(Example.class));
args = randomArgs(mh.type().parameterArray());
mh.invokeWithArguments(args);
assertCalled(name, args);
}
@Override
public Lookup apply(Class<?> clazz) {
try {
return (Lookup) privateLookupInMethod.invoke(null, clazz, MethodHandles.lookup());
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException("Unable to get lookup for " + clazz.getName(), e);
}
}
void testBind(boolean positive, Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
countTest(positive);
String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo
MethodType type = MethodType.methodType(ret, params);
Object receiver = randomArg(defc);
MethodHandle target = null;
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = maybeMoveIn(lookup, defc).bind(receiver, methodName, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
assertExceptionClass(
(name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
? NoSuchMethodException.class
: IllegalAccessException.class,
noAccess);
if (verbosity >= 5) ex.printStackTrace(System.out);
}
if (verbosity >= 3)
System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target
+(noAccess == null ? "" : " !! "+noAccess));
if (positive && noAccess != null) throw noAccess;
assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
if (!positive) return; // negative test failed as expected
assertEquals(type, target.type());
Object[] args = randomArgs(params);
printCalled(target, name, args);
target.invokeWithArguments(args);
Object[] argsWithReceiver = cat(array(Object[].class, receiver), args);
assertCalled(name, argsWithReceiver);
if (verbosity >= 1)
System.out.print(':');
}
void testFindConstructor(boolean positive, Lookup lookup,
Class<?> defc, Class<?>... params) throws Throwable {
countTest(positive);
MethodType type = MethodType.methodType(void.class, params);
MethodHandle target = null;
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" <init>"+type);
target = lookup.findConstructor(defc, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
assertTrue(noAccess.getClass().getName(), noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("findConstructor "+defc.getName()+".<init>/"+type+" => "+target
+(target == null ? "" : target.type())
+(noAccess == null ? "" : " !! "+noAccess));
if (positive && noAccess != null) throw noAccess;
assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
if (!positive) return; // negative test failed as expected
assertEquals(type.changeReturnType(defc), target.type());
Object[] args = randomArgs(params);
printCalled(target, defc.getSimpleName(), args);
Object obj = target.invokeWithArguments(args);
if (!(defc == Example.class && params.length < 2))
assertCalled(defc.getSimpleName()+".<init>", args);
assertTrue("instance of "+defc.getName(), defc.isInstance(obj));
}
void testFindStatic(boolean positive, Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
countTest(positive);
String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo
MethodType type = MethodType.methodType(ret, params);
MethodHandle target = null;
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = maybeMoveIn(lookup, defc).findStatic(defc, methodName, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
assertExceptionClass(
(name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
? NoSuchMethodException.class
: IllegalAccessException.class,
noAccess);
if (verbosity >= 5) ex.printStackTrace(System.out);
}
if (verbosity >= 3)
System.out.println("findStatic "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
+(noAccess == null ? "" : " !! "+noAccess));
if (positive && noAccess != null) throw noAccess;
assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
if (!positive) return; // negative test failed as expected
assertEquals(type, target.type());
assertNameStringContains(target, methodName);
Object[] args = randomArgs(params);
printCalled(target, name, args);
target.invokeWithArguments(args);
assertCalled(name, args);
if (verbosity >= 1)
System.out.print(':');
}
private void introspectFields(ArrayList<MarshalField> fieldList,
PodImport moduleImport,
Class<?> sourceClass)
{
if (sourceClass == null || Object.class.equals(sourceClass)) {
return;
}
introspectFields(fieldList,
moduleImport,
sourceClass.getSuperclass());
Lookup lookup = MethodHandles.lookup();
for (Field sourceField : sourceClass.getDeclaredFields()) {
if (Modifier.isStatic(sourceField.getModifiers())) {
continue;
}
try {
sourceField.setAccessible(true);
ModuleMarshal marshal;
marshal = moduleImport.marshalArg(sourceField.getType(),
Object.class);
MethodHandle sourceHandle = lookup.unreflectGetter(sourceField);
sourceHandle = sourceHandle.asType(MethodType.methodType(Object.class, Object.class));
MarshalField fieldMarshal = new MarshalFieldObject(sourceField.getName(),
marshal,
sourceHandle);
fieldList.add(fieldMarshal);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public int hashCode() {
final MethodHandles.Lookup lookup = getLookup();
int h = lookup.lookupClass().hashCode() + 31 * lookup.lookupModes();
final int c = getNameTokenCount();
for(int i = 0; i < c; ++i) {
h = h * 31 + getNameToken(i).hashCode();
}
return h * 31 + getMethodType().hashCode();
}
public void testRunnableProxy0() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("testRunnableProxy");
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle run = lookup.findStatic(lookup.lookupClass(), "runForRunnable", MethodType.methodType(void.class));
Runnable r = MethodHandleProxies.asInterfaceInstance(Runnable.class, run);
testRunnableProxy(r);
assertCalled("runForRunnable");
}
/**
* The JVM is resolving a CONSTANT_MethodHandle CP entry. And it wants our help.
* It will make an up-call to this method. (Do not change the name or signature.)
* The type argument is a Class for field requests and a MethodType for non-fields.
* <p>
* Recent versions of the JVM may also pass a resolved MemberName for the type.
* In that case, the name is ignored and may be null.
*/
static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind,
Class<?> defc, String name, Object type) {
try {
Lookup lookup = IMPL_LOOKUP.in(callerClass);
assert(refKindIsValid(refKind));
return lookup.linkMethodHandleConstant((byte) refKind, defc, name, type);
} catch (ReflectiveOperationException ex) {
Error err = new IncompatibleClassChangeError();
err.initCause(ex);
throw err;
}
}
void testFindConstructor(boolean positive, Lookup lookup,
Class<?> defc, Class<?>... params) throws Throwable {
countTest(positive);
MethodType type = MethodType.methodType(void.class, params);
MethodHandle target = null;
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" <init>"+type);
target = lookup.findConstructor(defc, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
assertTrue(noAccess.getClass().getName(), noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("findConstructor "+defc.getName()+".<init>/"+type+" => "+target
+(target == null ? "" : target.type())
+(noAccess == null ? "" : " !! "+noAccess));
if (positive && noAccess != null) throw noAccess;
assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null);
if (!positive) return; // negative test failed as expected
assertEquals(type.changeReturnType(defc), target.type());
Object[] args = randomArgs(params);
printCalled(target, defc.getSimpleName(), args);
Object obj = target.invokeWithArguments(args);
if (!(defc == Example.class && params.length < 2))
assertCalled(defc.getSimpleName()+".<init>", args);
assertTrue("instance of "+defc.getName(), defc.isInstance(obj));
}
@Override
public int hashCode() {
final MethodHandles.Lookup lookup = getLookup();
int h = lookup.lookupClass().hashCode() + 31 * lookup.lookupModes();
final int c = getNameTokenCount();
for(int i = 0; i < c; ++i) {
h = h * 31 + getNameToken(i).hashCode();
}
return h * 31 + getMethodType().hashCode();
}
private static boolean lookupsEqual(final Lookup l1, final Lookup l2) {
if(l1 == l2) {
return true;
}
if(l1.lookupClass() != l2.lookupClass()) {
return false;
}
return l1.lookupModes() == l2.lookupModes();
}