Skip to content

Instantly share code, notes, and snippets.

@forax
Last active November 1, 2016 12:07
Show Gist options
  • Save forax/1511fae2273f04273ff9463c6fbbdfbc to your computer and use it in GitHub Desktop.
Save forax/1511fae2273f04273ff9463c6fbbdfbc to your computer and use it in GitHub Desktop.
package org.openjdk.meta;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.MethodHandles.Lookup;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
public class Vault {
private Vault() { /* empty */ }
private static final Vault INSTANCE = new Vault();
public static Vault getInstance() { return INSTANCE; }
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");
}
}
public interface AccessControl {
public boolean grantAccess(Class<?> type);
}
private final ClassValue<AtomicReference<Function<Class<?>, Optional<Lookup>>>> cache = new ClassValue<AtomicReference<Function<Class<?>, Optional<Lookup>>>>() {
@Override
protected AtomicReference<Function<Class<?>, Optional<Lookup>>> computeValue(Class<?> type) {
return new AtomicReference<>(__ -> Optional.empty());
}
};
public void grantAccess(Lookup lookup, AccessControl accessControl) {
Objects.requireNonNull(lookup);
Objects.requireNonNull(accessControl);
checkMode(lookup);
cache.get(lookup.lookupClass())
.updateAndGet(fun -> clientClass -> fun.apply(clientClass)
.or(() -> Optional.of(lookup).filter(__ -> accessControl.grantAccess(clientClass))));
}
public Optional<Lookup> getAccess(Class<?> declaringClass, Lookup clientLookup) {
Objects.requireNonNull(declaringClass);
checkMode(clientLookup);
try {
Class.forName(declaringClass.getName(), true, declaringClass.getClassLoader()); // force init, maybe use unsafe here ?
} catch (ClassNotFoundException e) {
throw (NoClassDefFoundError)new NoClassDefFoundError().initCause(e);
}
return cache.get(declaringClass).get().apply(clientLookup.lookupClass());
}
//////////////////////////////////////////////////////////////////////////////////////////////
static class A {
private void foo() { System.out.println("hello"); }
static {
// grant access to B
Vault.getInstance().grantAccess(MethodHandles.lookup(), type -> type == B.class);
}
}
static class B {
public static void bar() {
// access A members from B
Lookup lookup = Vault.getInstance().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