From a1d0fb5008e9d25c2ca66253a69fd108c597f869 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Fri, 13 May 2016 12:42:18 +0200 Subject: [PATCH] painless: cleanup of DynamicCallSite to have all implementation encapsulated in inner class, outer class only has bootstrap method --- .../painless/DynamicCallSite.java | 167 +++++++++--------- 1 file changed, 83 insertions(+), 84 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DynamicCallSite.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DynamicCallSite.java index 235bfedf8f7..330a64e2a99 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DynamicCallSite.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DynamicCallSite.java @@ -39,10 +39,12 @@ import java.lang.invoke.MutableCallSite; * Based on the cascaded inlining cache from the JSR 292 cookbook * (https://code.google.com/archive/p/jsr292-cookbook/, BSD license) */ -// NOTE: this class must be public, because generated painless classes are in a different package, +// NOTE: this class must be public, because generated painless classes are in a different classloader, // and it needs to be accessible by that code. public final class DynamicCallSite { + private DynamicCallSite() {} // no instance! + // NOTE: these must be primitive types, see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic /** static bootstrap parameter indicating a dynamic method call, e.g. foo.bar(...) */ public static final int METHOD_CALL = 0; @@ -55,20 +57,92 @@ public final class DynamicCallSite { /** static bootstrap parameter indicating a dynamic array store, e.g. foo[bar] = baz */ public static final int ARRAY_STORE = 4; - static class InliningCacheCallSite extends MutableCallSite { + static final class InliningCacheCallSite extends MutableCallSite { /** maximum number of types before we go megamorphic */ static final int MAX_DEPTH = 5; - final Lookup lookup; - final String name; - final int flavor; - int depth; + private final String name; + private final int flavor; + int depth; // pkg-protected for testing - InliningCacheCallSite(Lookup lookup, String name, MethodType type, int flavor) { + InliningCacheCallSite(String name, MethodType type, int flavor) { super(type); - this.lookup = lookup; this.name = name; this.flavor = flavor; + + MethodHandle fallback = FALLBACK.bindTo(this); + fallback = fallback.asCollector(Object[].class, type.parameterCount()); + fallback = fallback.asType(type); + + setTarget(fallback); + } + + /** + * guard method for inline caching: checks the receiver's class is the same + * as the cached class + */ + static boolean checkClass(Class clazz, Object receiver) { + return receiver.getClass() == clazz; + } + + /** + * Does a slow lookup against the whitelist. + */ + private static MethodHandle lookup(int flavor, Class clazz, String name) { + switch(flavor) { + case METHOD_CALL: + return Def.lookupMethod(clazz, name, Definition.INSTANCE); + case LOAD: + return Def.lookupGetter(clazz, name, Definition.INSTANCE); + case STORE: + return Def.lookupSetter(clazz, name, Definition.INSTANCE); + case ARRAY_LOAD: + return Def.lookupArrayLoad(clazz); + case ARRAY_STORE: + return Def.lookupArrayStore(clazz); + default: throw new AssertionError(); + } + } + + /** + * Called when a new type is encountered (or, when we have encountered more than {@code MAX_DEPTH} + * types at this call site and given up on caching). + */ + Object fallback(Object[] args) throws Throwable { + MethodType type = type(); + Object receiver = args[0]; + Class receiverClass = receiver.getClass(); + MethodHandle target = lookup(flavor, receiverClass, name); + target = target.asType(type); + + if (depth >= MAX_DEPTH) { + // revert to a vtable call + setTarget(target); + return target.invokeWithArguments(args); + } + + MethodHandle test = CHECK_CLASS.bindTo(receiverClass); + test = test.asType(test.type().changeParameterType(0, type.parameterType(0))); + + MethodHandle guard = MethodHandles.guardWithTest(test, target, getTarget()); + depth++; + + setTarget(guard); + return target.invokeWithArguments(args); + } + + private static final MethodHandle CHECK_CLASS; + private static final MethodHandle FALLBACK; + static { + final Lookup lookup = MethodHandles.lookup(); + try { + CHECK_CLASS = lookup.findStatic(lookup.lookupClass(), "checkClass", + MethodType.methodType(boolean.class, Class.class, Object.class)); + FALLBACK = lookup.findVirtual(lookup.lookupClass(), "fallback", + MethodType.methodType(Object.class, Object[].class)); + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); + } } } @@ -81,82 +155,7 @@ public final class DynamicCallSite { * see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic */ public static CallSite bootstrap(Lookup lookup, String name, MethodType type, int flavor) { - InliningCacheCallSite callSite = new InliningCacheCallSite(lookup, name, type, flavor); - - MethodHandle fallback = FALLBACK.bindTo(callSite); - fallback = fallback.asCollector(Object[].class, type.parameterCount()); - fallback = fallback.asType(type); - - callSite.setTarget(fallback); - return callSite; + return new InliningCacheCallSite(name, type, flavor); } - /** - * guard method for inline caching: checks the receiver's class is the same - * as the cached class - */ - static boolean checkClass(Class clazz, Object receiver) { - return receiver.getClass() == clazz; - } - - /** - * Does a slow lookup against the whitelist. - */ - private static MethodHandle lookup(int flavor, Class clazz, String name) { - switch(flavor) { - case METHOD_CALL: - return Def.lookupMethod(clazz, name, Definition.INSTANCE); - case LOAD: - return Def.lookupGetter(clazz, name, Definition.INSTANCE); - case STORE: - return Def.lookupSetter(clazz, name, Definition.INSTANCE); - case ARRAY_LOAD: - return Def.lookupArrayLoad(clazz); - case ARRAY_STORE: - return Def.lookupArrayStore(clazz); - default: throw new AssertionError(); - } - } - - /** - * Called when a new type is encountered (or, when we have encountered more than {@code MAX_DEPTH} - * types at this call site and given up on caching). - */ - static Object fallback(InliningCacheCallSite callSite, Object[] args) throws Throwable { - MethodType type = callSite.type(); - Object receiver = args[0]; - Class receiverClass = receiver.getClass(); - MethodHandle target = lookup(callSite.flavor, receiverClass, callSite.name); - target = target.asType(type); - - if (callSite.depth >= InliningCacheCallSite.MAX_DEPTH) { - // revert to a vtable call - callSite.setTarget(target); - return target.invokeWithArguments(args); - } - - MethodHandle test = CHECK_CLASS.bindTo(receiverClass); - test = test.asType(test.type().changeParameterType(0, type.parameterType(0))); - - MethodHandle guard = MethodHandles.guardWithTest(test, target, callSite.getTarget()); - callSite.depth++; - - callSite.setTarget(guard); - return target.invokeWithArguments(args); - } - - private static final MethodHandle CHECK_CLASS; - private static final MethodHandle FALLBACK; - - static { - Lookup lookup = MethodHandles.lookup(); - try { - CHECK_CLASS = lookup.findStatic(DynamicCallSite.class, "checkClass", - MethodType.methodType(boolean.class, Class.class, Object.class)); - FALLBACK = lookup.findStatic(DynamicCallSite.class, "fallback", - MethodType.methodType(Object.class, InliningCacheCallSite.class, Object[].class)); - } catch (ReflectiveOperationException e) { - throw new AssertionError(e); - } - } }