package st.gravel.support.jvm.runtime; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.lang.invoke.MutableCallSite; import java.util.Collections; import java.util.Set; import java.util.WeakHashMap; import st.gravel.core.Symbol; import st.gravel.support.compiler.ast.SelectorConverter; public abstract class BaseCallSite extends MutableCallSite { private static final SelectorConverter selectorConverter = SelectorConverter.factory .basicNew(); protected BaseCallSite(Lookup lookup, MethodType type, String selector) { super(type); this.selector = selector; this.lookup = lookup; this.type = type; this.fallback = getFallbackMethod(); reset(); } protected final String selector; protected final MethodHandles.Lookup lookup; protected final MethodType type; public MethodHandle wrapDNUHandle(MethodHandle dnuHandle) { Symbol sym = selectorConverter.functionNameAsSelector_(selector); MethodHandle withBoundSymbol = MethodHandles.insertArguments(dnuHandle, 1, sym); MethodHandle message = withBoundSymbol.asCollector(Object[].class, numArgs()); return message; } int numArgs() { return type.parameterCount() - 1; } private static final Set<BaseCallSite> callsites = Collections .newSetFromMap(new WeakHashMap<BaseCallSite, Boolean>()); public synchronized static void register(BaseCallSite callsite) { callsites.add(callsite); } protected final MethodHandle fallback; public synchronized static void resetAll() { for (BaseCallSite callsite : callsites) { callsite.reset(); } MutableCallSite.syncAll(callsites.toArray(new MutableCallSite[callsites.size()])); } public static Object invocationFallback(BaseCallSite site, Object receiver) throws Throwable { site.addTargetToCache(receiver); return site.getTarget().invoke(receiver); } public static Object invocationFallback(BaseCallSite site, Object receiver, Object arg1) throws Throwable { site.addTargetToCache(receiver); return site.getTarget().invoke(receiver, arg1); } public static Object invocationFallback(BaseCallSite site, Object receiver, Object arg1, Object arg2) throws Throwable { site.addTargetToCache(receiver); return site.getTarget().invoke(receiver, arg1, arg2); } public static Object invocationFallback(BaseCallSite site, Object receiver, Object arg1, Object arg2, Object arg3) throws Throwable { site.addTargetToCache(receiver); return site.getTarget().invoke(receiver, arg1, arg2, arg3); } public static Object invocationFallback(BaseCallSite site, Object receiver, Object arg1, Object arg2, Object arg3, Object arg4) throws Throwable { site.addTargetToCache(receiver); return site.getTarget().invoke(receiver, arg1, arg2, arg3, arg4); } public static Object invocationFallback(BaseCallSite site, Object receiver, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) throws Throwable { site.addTargetToCache(receiver); return site.getTarget().invoke(receiver, arg1, arg2, arg3, arg4, arg5); } public static Object invocationFallback(BaseCallSite site, Object receiver, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) throws Throwable { site.addTargetToCache(receiver); return site.getTarget().invoke(receiver, arg1, arg2, arg3, arg4, arg5, arg6); } public static Object invocationFallback(BaseCallSite site, Object receiver, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) throws Throwable { site.addTargetToCache(receiver); return site.getTarget().invoke(receiver, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } public static Object invocationFallback(BaseCallSite site, Object receiver, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) throws Throwable { site.addTargetToCache(receiver); return site.getTarget().invoke(receiver, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } private void reset() { resetCache(); setTarget(fallback); } protected abstract void resetCache(); private MethodHandle getFallbackMethod() { try { final MethodType fallbackType = MethodType.genericMethodType( type.parameterCount()).insertParameterTypes(0, BaseCallSite.class); final MethodHandle fallbackHandle = MethodHandles.insertArguments( MethodHandles.lookup().findStatic(BaseCallSite.class, "invocationFallback", fallbackType), 0, this); return fallbackHandle.asType(type); } catch (NoSuchMethodException | IllegalAccessException e) { throw new RuntimeException(e); } } protected abstract void addTargetToCache(Object receiver); }