Created
February 2, 2018 17:54
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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