Skip to content

Instantly share code, notes, and snippets.

@forax
Created November 1, 2016 15:10
Show Gist options
  • Save forax/cab12206323ca2e5866c9f8e20ee6d40 to your computer and use it in GitHub Desktop.
Save forax/cab12206323ca2e5866c9f8e20ee6d40 to your computer and use it in GitHub Desktop.
package java.lang.invoke;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.invoke.MethodHandles.Lookup;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
public class FrameworkAccessor {
private FrameworkAccessor() { throw new InternalError(); }
private static final int MODES = Lookup.PRIVATE | Lookup.PACKAGE | Lookup.PROTECTED | Lookup.PUBLIC;
private static void checkMode(Lookup lookup) {
if ((lookup.lookupModes() & MODES) != MODES) {
throw new IllegalArgumentException("invalid lookup");
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface GrantAccess {
Class<?>[] friends() default {};
}
private final static ClassValue<Function<Class<?>, Optional<Lookup>>> cache = new ClassValue<Function<Class<?>, Optional<Lookup>>>() {
@Override
protected Function<Class<?>, Optional<Lookup>> computeValue(Class<?> type) {
GrantAccess access = type.getDeclaredAnnotation(GrantAccess.class);
if (access == null) {
return __ -> Optional.empty();
}
Set<Class<?>> friends = Set.of(access.friends());
Predicate<Class<?>> predicate = friends.isEmpty()? __ -> true: friends::contains;
Optional<Lookup> lookup = Optional.of(new Lookup(type));
return declaringClass -> lookup.filter(__ -> predicate.test(declaringClass));
}
};
public static Optional<Lookup> getAccess(Class<?> declaringClass, Lookup clientLookup) {
Objects.requireNonNull(declaringClass);
checkMode(clientLookup);
return cache.get(declaringClass).apply(clientLookup.lookupClass());
}
}
import java.lang.invoke.FrameworkAccessor;
import java.lang.invoke.FrameworkAccessor.GrantAccess;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
public class FrameworkAccessorMain {
@GrantAccess(friends = B.class)
static class A {
private void foo() { System.out.println("hello"); }
}
static class B {
public static void bar() {
// access A members from B
Lookup lookup = FrameworkAccessor.getAccess(A.class, MethodHandles.lookup()).get();
MethodHandle mh;
try {
mh = lookup.findVirtual(A.class, "foo", MethodType.methodType(void.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new AssertionError(e);
}
try {
mh.invokeExact(new A());
} catch (Throwable e) {
throw new AssertionError(e);
}
}
}
public static void main(String[] args) {
B.bar();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment