package tc.oc.commons.core.reflect; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import com.google.common.base.Throwables; import com.google.common.cache.LoadingCache; import tc.oc.commons.core.util.CacheUtils; public final class MethodHandleUtils { private MethodHandleUtils() {} private static final Constructor<MethodHandles.Lookup> LOOKUP_CONSTRUCTOR; static { try { LOOKUP_CONSTRUCTOR = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class); if(!LOOKUP_CONSTRUCTOR.isAccessible()) { LOOKUP_CONSTRUCTOR.setAccessible(true); } } catch (NoSuchMethodException e) { throw new IllegalStateException(e); } } private static final LoadingCache<Class<?>, MethodHandles.Lookup> PRIVATE_LOOKUP_CACHE = CacheUtils.newCache(declaringClass -> { try { return LOOKUP_CONSTRUCTOR.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new IllegalStateException(e); } }); /** * Returns a {@link MethodHandles.Lookup} that doesn't do "access" checks (since it allows private calls). * A work-around for Proxy classes to access "default" interface methods. * * See http://rmannibucau.wordpress.com/2014/03/27/java-8-default-interface-methods-and-jdk-dynamic-proxies/ */ public static MethodHandles.Lookup privateLookup(Class<?> declaringClass) { return PRIVATE_LOOKUP_CACHE.getUnchecked(declaringClass); } public static MethodHandle privateUnreflect(Class<?> declaringClass, Method method) { try { return privateLookup(declaringClass).unreflect(method); } catch(IllegalAccessException e) { throw Throwables.propagate(e); // impossible } } public static MethodHandle privateUnreflect(Method method) { return privateUnreflect(method.getDeclaringClass(), method); } public static MethodHandle privateUnreflectGetter(Class<?> declaringClass, Field field) { try { return privateLookup(declaringClass).unreflectGetter(field); } catch(IllegalAccessException e) { throw Throwables.propagate(e); // impossible } } public static MethodHandle privateUnreflectGetter(Field field) { return privateUnreflectGetter(field.getDeclaringClass(), field); } /** * Returns a {@link MethodHandle} to the specified interface default-method */ public static MethodHandle defaultMethodHandle(Method method) { final Class<?> declaringClass = method.getDeclaringClass(); try { return privateLookup(declaringClass).unreflectSpecial(method, declaringClass); } catch (IllegalAccessException e) { throw new IllegalStateException(e); } } }