Skip to content

Instantly share code, notes, and snippets.

@forax
Created February 2, 2018 17:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save forax/1e0734f9aa976eab8a1fe982371a44a7 to your computer and use it in GitHub Desktop.
Save forax/1e0734f9aa976eab8a1fe982371a44a7 to your computer and use it in GitHub Desktop.
Transform a non constant method handle to a constant one by introducing an inlining cache in the middle
import static java.lang.invoke.MethodHandles.constant;
import static java.lang.invoke.MethodHandles.dropArguments;
import static java.lang.invoke.MethodHandles.guardWithTest;
import static java.lang.invoke.MethodHandles.lookup;
import static java.lang.invoke.MethodType.methodType;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MutableCallSite;
import java.lang.reflect.UndeclaredThrowableException;
public class InliningCacheInvokerMain {
public interface InliningCacheInvoker {
MethodHandle cache(MethodHandle mh);
static InliningCacheInvoker get() {
MethodHandle invoker = new CS().dynamicInvoker();
return mh -> {
try {
return (MethodHandle)invoker.invokeExact(mh);
} catch (Throwable e) {
throw rethrow(e);
}
};
}
private static UndeclaredThrowableException rethrow(Throwable t) {
if (t instanceof RuntimeException) {
throw (RuntimeException)t;
}
if (t instanceof Error) {
throw (Error)t;
}
return new UndeclaredThrowableException(t);
}
}
static class CS extends MutableCallSite {
private static final MethodHandle FALLBACK, IDENTITY_CHECK;
static {
Lookup lookup = lookup();
try {
FALLBACK = lookup.findVirtual(CS.class, "fallback", methodType(MethodHandle.class, MethodHandle.class));
IDENTITY_CHECK = lookup.findStatic(CS.class, "identityCheck", methodType(boolean.class, MethodHandle.class, MethodHandle.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new AssertionError(e);
}
}
public CS() {
super(methodType(MethodHandle.class, MethodHandle.class));
setTarget(FALLBACK.bindTo(this));
}
@SuppressWarnings("unused")
private MethodHandle fallback(MethodHandle mh) {
setTarget(guardWithTest(IDENTITY_CHECK.bindTo(mh),
dropArguments(constant(MethodHandle.class, mh), 0, MethodHandle.class),
new CS().dynamicInvoker()));
return mh;
}
@SuppressWarnings("unused")
private static boolean identityCheck(MethodHandle mh1, MethodHandle mh2) {
return mh1 == mh2;
}
}
// -----------------------------
private static final InliningCacheInvoker INVOKER = InliningCacheInvoker.get();
private static int counter;
@SuppressWarnings("unused")
private static void count() {
counter++;
}
public static void main(String[] args) throws Throwable {
MethodHandle mh = MethodHandles.lookup().findStatic(InliningCacheInvokerMain.class, "count", methodType(void.class));
for(int i = 0; i < 1_000_000; i++) {
// mh.invokeExact(); // non constant mh
INVOKER.cache(mh).invokeExact(); // constant mh !
}
System.out.println(counter);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment