8006293: Reduce ScriptObject.findCallMethodMethod
authorattila
Tue, 15 Jan 2013 13:10:20 +0100
changeset 16173 c41d062f7d2a
parent 16172 25c8da53438f
child 16174 675bf35d8931
8006293: Reduce ScriptObject.findCallMethodMethod Reviewed-by: lagergren, jlaskey
nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java
nashorn/src/jdk/nashorn/internal/runtime/WithObject.java
nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java
nashorn/src/jdk/nashorn/internal/runtime/linker/MethodHandleFactory.java
nashorn/src/jdk/nashorn/internal/runtime/linker/MethodHandleFunctionality.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Mon Jan 14 21:30:13 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Tue Jan 15 13:10:20 2013 +0100
@@ -62,9 +62,6 @@
     /** Method handle for name getter for this ScriptFunction */
     public static final MethodHandle G$NAME       = findOwnMH("G$name",       Object.class, Object.class);
 
-    /** Method handle for invokehelper for this ScriptFunction - used from applies */
-    public static final MethodHandle INVOKEHELPER = findOwnMH("invokeHelper", Object.class, Object.class, Object.class, Object[].class);
-
     /** Method handle for allocate function for this ScriptFunction */
     public static final MethodHandle ALLOCATE     = findOwnMH("allocate", Object.class);
 
@@ -387,24 +384,6 @@
     }
 
     /**
-     * Helper function used for implementation of apply
-     * @param func       script function
-     * @param self       reference to {@code this} for apply
-     * @param args       arguments to apply
-     * @return           result of apply
-     * @throws Throwable if invocation throws an error or exception
-     */
-    public static Object invokeHelper(final Object func, final Object self, final Object... args) throws Throwable {
-        if (func instanceof ScriptFunction) {
-            return ((ScriptFunction)func).invoke(self, args);
-        }
-
-        typeError(Context.getGlobal(), "not.a.function", ScriptRuntime.safeToString(func));
-
-        return UNDEFINED;
-    }
-
-    /**
      * Construct new object using this constructor.
      * @param self  Target object.
      * @param args  Call arguments.
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java	Mon Jan 14 21:30:13 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java	Tue Jan 15 13:10:20 2013 +0100
@@ -69,7 +69,6 @@
 import org.dynalang.dynalink.CallSiteDescriptor;
 import org.dynalang.dynalink.linker.GuardedInvocation;
 import org.dynalang.dynalink.support.CallSiteDescriptorFactory;
-import org.dynalang.dynalink.support.Guards;
 
 /**
  * Base class for generic JavaScript objects.
@@ -1587,7 +1586,10 @@
     }
 
     /**
-     * Find the appropriate CALL method for an invoke dynamic call.
+     * Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses
+     * "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another
+     * one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external
+     * callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle.
      *
      * @param desc The call site descriptor.
      * @param megaMorphic is this call site megaMorphic, as reported by Dynalink - then just do apply
@@ -1595,49 +1597,18 @@
      * @return GuardedInvocation to be invoked at call site.
      */
     protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final boolean megaMorphic) {
-        final String       name     = desc.getNameToken(2);
-        final MethodType   callType = desc.getMethodType();
-        final FindProperty find     = findProperty(name, true);
-
-        if (find == null) {
-            return createNoSuchMethodInvocation(desc);
-        }
-
-        if (find.getProperty().hasGetterFunction()) {
-            final GuardedInvocation link   = findGetMethod(CallSiteDescriptorFactory.changeReturnType(desc, Object.class), megaMorphic, "getMethod");
-            final MethodHandle      getter = link.getInvocation(); //this cannot be anything but an object as this is the function
-
-            MethodHandle invoker = ScriptFunction.INVOKEHELPER;
-            invoker = MH.asCollector(invoker, Object[].class, callType.parameterCount() - 1); //deduct self
-            invoker = MH.foldArguments(invoker, MH.asType(getter, getter.type().changeReturnType(Object.class))); //getter->func to first arguments. self and object parameters remain
-
-            return new GuardedInvocation(invoker, link.getSwitchPoint(), link.getGuard());
-        }
-
-        //retrieve the appropriate scriptfunction
-        final Object value = getObjectValue(find);
-
-        MethodHandle methodHandle = getCallMethodHandle(value, callType, null);
-
-        if (methodHandle != null) {
-            if (find.isScope()) {
-                final boolean strictCallee = ((ScriptFunction)value).isStrict();
-                if (strictCallee && NashornCallSiteDescriptor.isScope(desc)) {
-                    methodHandle = bindTo(methodHandle, UNDEFINED);
-                } else {
-                    methodHandle = bindTo(methodHandle, Context.getGlobal());
-                }
-            }
-
-            final MethodHandle guard       = find.isSelf() ? Guards.getIdentityGuard(this) : NashornGuards.getMapGuard(getMap());
-            final int          invokeFlags = ((ScriptFunction)value).isStrict()? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
-
-            return new NashornGuardedInvocation(methodHandle, null, guard, invokeFlags);
-        }
-
-        typeError(Context.getGlobal(), "no.such.function", name, ScriptRuntime.safeToString(this));
-
-        throw new AssertionError("should not reach here");
+        // R(P0, P1, ...)
+        final MethodType callType = desc.getMethodType();
+        // use type Object(P0) for the getter
+        final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
+        final GuardedInvocation getter = findGetMethod(getterType, megaMorphic, "getMethod");
+
+        // Object(P0) => Object(P0, P1, ...)
+        final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
+        // R(Object, P0, P1, ...)
+        final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
+        // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
+        return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
     }
 
     /**
@@ -1923,41 +1894,6 @@
     }
 
     /**
-     * Create an invocation that will raise NoSuchMethod error
-     *
-     * @param desc call site descriptor
-     *
-     * @return Guarded invocation to be invoked at the call site
-     */
-    public GuardedInvocation createNoSuchMethodInvocation(final CallSiteDescriptor desc) {
-        final String name = desc.getNameToken(2);
-        final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true);
-        final boolean scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc);
-
-        if (find != null) {
-            final ScriptFunction func = (ScriptFunction)getObjectValue(find);
-            MethodHandle methodHandle = getCallMethodHandle(func, desc.getMethodType(), name);
-
-            if (methodHandle != null) {
-                if (scopeCall && func.isStrict()) {
-                    methodHandle = bindTo(methodHandle, UNDEFINED);
-                }
-                return new GuardedInvocation(methodHandle,
-                        find.isInherited()? getMap().getProtoGetSwitchPoint(NO_SUCH_PROPERTY_NAME) : null,
-                        getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func));
-            }
-        }
-
-        if (scopeCall) {
-            referenceError(Context.getGlobal(), "not.defined", name);
-        } else {
-            typeError(Context.getGlobal(), "no.such.function", name, ScriptRuntime.safeToString(this));
-        }
-
-        return null;
-    }
-
-    /**
      * Fall back if a property is not found.
      * @param desc the call site descriptor.
      * @return GuardedInvocation to be invoked at call site.
--- a/nashorn/src/jdk/nashorn/internal/runtime/WithObject.java	Mon Jan 14 21:30:13 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/WithObject.java	Tue Jan 15 13:10:20 2013 +0100
@@ -131,12 +131,13 @@
         // the property is not found - now check for
         // __noSuchProperty__ and __noSuchMethod__ in expression
         if (self != null) {
-            String fallBack;
+            final String fallBack;
 
             final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
 
             switch (operator) {
             case "callMethod":
+                throw new AssertionError(); // Nashorn never emits callMethod
             case "getMethod":
                 fallBack = NO_SUCH_METHOD_NAME;
                 break;
@@ -153,9 +154,6 @@
                 find = self.findProperty(fallBack, true);
                 if (find != null) {
                     switch (operator) {
-                    case "callMethod":
-                        link = self.createNoSuchMethodInvocation(desc);
-                        break;
                     case "getMethod":
                         link = self.noSuchMethod(desc);
                         break;
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Mon Jan 14 21:30:13 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Tue Jan 15 13:10:20 2013 +0100
@@ -79,7 +79,7 @@
     }
 
     /**
-     * Return a dynamic invoker for a specified dynamic operation. You can use this method to create a method handle
+     * Returns a dynamic invoker for a specified dynamic operation. You can use this method to create a method handle
      * that when invoked acts completely as if it were a Nashorn-linked call site. An overview of available dynamic
      * operations can be found in the <a href="https://github.com/szegedi/dynalink/wiki/User-Guide-0.4">Dynalink User Guide</a>,
      * but we'll show few examples here:
@@ -170,12 +170,24 @@
      * delegating to the function will be returned.</li>
      * </ul>
      * @param opDesc Dynalink dynamic operation descriptor.
-     * @param rtype return type of the operation
-     * @param ptypes parameter types of the operation
+     * @param rtype the return type for the operation
+     * @param ptypes the parameter types for the operation
      * @return MethodHandle for invoking the operation.
      */
     public static MethodHandle createDynamicInvoker(final String opDesc, final Class<?> rtype, final Class<?>... ptypes) {
-        return bootstrap(null, opDesc, MethodType.methodType(rtype, ptypes), 0).dynamicInvoker();
+        return createDynamicInvoker(opDesc, MethodType.methodType(rtype, ptypes));
+    }
+
+    /**
+     * Returns a dynamic invoker for a specified dynamic operation. Similar to
+     * {@link #createDynamicInvoker(String, Class, Class...)} but with return and parameter types composed into a
+     * method type in the signature. See the discussion of that method for details.
+     * @param opDesc Dynalink dynamic operation descriptor.
+     * @param type the method type for the operation
+     * @return MethodHandle for invoking the operation.
+     */
+    public static MethodHandle createDynamicInvoker(final String opDesc, final MethodType type) {
+        return bootstrap(null, opDesc, type, 0).dynamicInvoker();
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/MethodHandleFactory.java	Mon Jan 14 21:30:13 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/MethodHandleFactory.java	Tue Jan 15 13:10:20 2013 +0100
@@ -25,12 +25,6 @@
 
 package jdk.nashorn.internal.runtime.linker;
 
-import jdk.nashorn.internal.runtime.ConsString;
-import jdk.nashorn.internal.runtime.Debug;
-import jdk.nashorn.internal.runtime.DebugLogger;
-import jdk.nashorn.internal.runtime.ScriptObject;
-import jdk.nashorn.internal.runtime.options.Options;
-
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 import java.lang.invoke.MethodHandle;
@@ -42,6 +36,11 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.logging.Level;
+import jdk.nashorn.internal.runtime.ConsString;
+import jdk.nashorn.internal.runtime.Debug;
+import jdk.nashorn.internal.runtime.DebugLogger;
+import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.options.Options;
 
 /**
  * This class is abstraction for all method handle, switchpoint and method type
@@ -299,6 +298,11 @@
         }
 
         @Override
+        public MethodHandle dropArguments(final MethodHandle target, final int pos, final List<Class<?>> valueTypes) {
+            return MethodHandles.dropArguments(target, pos, valueTypes);
+        }
+
+        @Override
         public MethodHandle asType(final MethodHandle handle, final MethodType type) {
             return handle.asType(type);
         }
@@ -500,6 +504,12 @@
         }
 
         @Override
+        public MethodHandle dropArguments(final MethodHandle target, final int pos, final List<Class<?>> values) {
+            final MethodHandle mh = super.dropArguments(target, pos, values);
+            return debug(mh, "dropArguments", target, pos, values);
+        }
+
+        @Override
         public MethodHandle asType(final MethodHandle handle, final MethodType type) {
             final MethodHandle mh = super.asType(handle, type);
             return debug(mh, "asType", handle, type);
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/MethodHandleFunctionality.java	Mon Jan 14 21:30:13 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/MethodHandleFunctionality.java	Tue Jan 15 13:10:20 2013 +0100
@@ -30,6 +30,7 @@
 import java.lang.invoke.MethodType;
 import java.lang.invoke.SwitchPoint;
 import java.lang.reflect.Method;
+import java.util.List;
 
 /**
  * Wrapper for all method handle related functions used in Nashorn. This interface only exists
@@ -92,6 +93,17 @@
     public MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes);
 
     /**
+     * Wrapper for {@link MethodHandles#dropArguments(MethodHandle, int, List)}
+     *
+     * @param target     target method handle
+     * @param pos        start argument index
+     * @param valueTypes valueTypes of arguments to drop
+     *
+     * @return handle with dropped arguments
+     */
+    public MethodHandle dropArguments(final MethodHandle target, final int pos, final List<Class<?>> valueTypes);
+
+    /**
      * Wrapper for {@link MethodHandles#foldArguments(MethodHandle, MethodHandle)}
      *
      * @param target   target method handle