Browse Source

Merge pull request #18323 from uschindler/dynamiccallsite_cleanup

painless: cleanup of DynamicCallSite
Robert Muir 9 years ago
parent
commit
520697eb14

+ 83 - 84
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;
-    }
-
-    /**
-     * 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;
+        return new InliningCacheCallSite(name, type, flavor);
     }
 
-    /**
-     * 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);
-        }
-    }
 }