src/jdk/nashorn/internal/runtime/ScriptFunction.java

Print this page

        

@@ -32,10 +32,11 @@
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
+import jdk.nashorn.internal.codegen.ScriptFunctionData;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
 import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
 import jdk.nashorn.internal.parser.Token;
 import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;

@@ -68,12 +69,10 @@
 
     private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
 
     private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
 
-    /** method handle to arity setter for this ScriptFunction */
-    public static final Call SET_ARITY = virtualCallNoLookup(ScriptFunction.class, "setArity", void.class, int.class);
     /** method handle to scope getter for this ScriptFunction */
     public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
 
     /** Should specialized function and specialized constructors for the builtin be used if available? */
     private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable");

@@ -86,14 +85,20 @@
 
     /** Start position and length in source. */
     private final long token;
 
     /** Reference to code for this method. */
-    private final MethodHandle invokeHandle;
+    private final MethodHandle invoker;
 
     /** Reference to code for this method when called to create "new" object */
-    protected MethodHandle constructHandle;
+    protected MethodHandle constructor;
+
+    /** Generic invoker to used in {@link #invoke(Object, Object...)}. */
+    private MethodHandle genericInvoker;
+
+    /** Generic constructor used in {@link #construct(Object, Object...)}. */
+    private MethodHandle genericConstructor;
 
     /** Reference to constructor prototype. */
     protected Object prototype;
 
     /** Constructor to create a new instance. */

@@ -127,11 +132,11 @@
             final String name,
             final MethodHandle methodHandle,
             final PropertyMap map,
             final ScriptObject scope,
             final MethodHandle[] specs) {
-        this(name, methodHandle, map, scope, null, 0, needsCallee(methodHandle), specs);
+        this(name, methodHandle, map, scope, needsCallee(methodHandle), specs);
     }
 
     /**
      * Heuristic to figure out if the method handle has a callee argument. If it's type is either
      * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has

@@ -154,152 +159,138 @@
     }
 
     /**
      * Constructor
      *
-     * @param name          function name
+     * @param data          static function data
      * @param methodHandle  method handle to function (if specializations are present, assumed to be most generic)
      * @param map           property map
      * @param scope         scope
-     * @param source        the source
-     * @param token         token
      * @param allocator     method handle to this function's allocator - see JO$ classes
-     * @param allocatorMap  property map to be used for all constructors
-     * @param needsCallee   does this method use the {@code callee} variable
-     * @param specs         specialized version of this function - other method handles
      */
     protected ScriptFunction(
-            final String name,
+            final ScriptFunctionData data,
             final MethodHandle methodHandle,
             final PropertyMap map,
             final ScriptObject scope,
-            final Source source,
-            final long token,
-            final MethodHandle allocator,
-            final PropertyMap allocatorMap,
-            final boolean needsCallee,
-            final MethodHandle[] specs) {
+            final MethodHandle allocator) {
+
+        super(map);
+
+        if (Context.DEBUG) {
+            constructorCount++;
+        }
 
-        this(name, methodHandle, map, scope, source, token, needsCallee, specs);
+        this.name   = data.getName();
+        this.source = data.getSource();
+        this.token  = data.getToken();
+        this.arity  = data.getArity();
+        this.scope  = scope;
 
-        //this is the internal constructor
+        if(data.needsCallee()) {
+            setHasCalleeParameter();
+        }
 
+        this.invoker      = methodHandle;
+        this.constructor  = methodHandle;
         this.allocator    = allocator;
-        this.allocatorMap = allocatorMap;
+        this.allocatorMap = data.getAllocatorMap();
     }
 
     /**
      * Constructor
      *
      * @param name              function name
      * @param methodHandle      method handle to function (if specializations are present, assumed to be most generic)
      * @param map               property map
      * @param scope             scope
-     * @param source            the source
-     * @param token             token
      * @param needsCallee       does this method use the {@code callee} variable
      * @param specs             specialized version of this function - other method handles
      */
     protected ScriptFunction(
             final String name,
             final MethodHandle methodHandle,
             final PropertyMap map,
             final ScriptObject scope,
-            final Source source,
-            final long token,
             final boolean needsCallee,
             final MethodHandle[] specs) {
 
         super(map);
 
         if (Context.DEBUG) {
             constructorCount++;
         }
 
         this.name   = name;
-        this.source = source;
-        this.token  = token;
+        this.source = null;
+        this.token  = 0;
         this.scope  = scope;
         if(needsCallee) {
             setHasCalleeParameter();
         }
 
         final MethodType type       = methodHandle.type();
         final int        paramCount = type.parameterCount();
         final boolean    isVarArg   = type.parameterType(paramCount - 1).isArray();
 
-        final MethodHandle mh = MH.asType(methodHandle, adaptType(type, needsCallee, isVarArg));
-
         this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
 
         if (needsCallee && !isVarArg) {
             this.arity--;
         }
 
-        if (scope != null) {
-            this.invokeHandle    = mh;
-            this.constructHandle = mh;
-        } else if (isConstructor(mh)) {
+        if (isConstructor(methodHandle)) {
             if (!isVarArg) {
                 this.arity--;    // drop the boolean flag for arity
             }
             /*
              * We insert a boolean argument to tell if the method was invoked as
              * constructor or not if the method handle's first argument is boolean.
              */
-            this.invokeHandle    = MH.insertArguments(mh, 0, false);
-            this.constructHandle = MH.insertArguments(mh, 0, true);
+            this.invoker     = MH.insertArguments(methodHandle, 0, false);
+            this.constructor = MH.insertArguments(methodHandle, 0, true);
 
             if (specs != null) {
                 this.invokeSpecializations    = new MethodHandle[specs.length];
                 this.constructSpecializations = new MethodHandle[specs.length];
                 for (int i = 0; i < specs.length; i++) {
                     this.invokeSpecializations[i]    = MH.insertArguments(specs[i], 0, false);
                     this.constructSpecializations[i] = MH.insertArguments(specs[i], 0, true);
                 }
             }
         } else {
-            this.invokeHandle             = mh;
-            this.constructHandle          = mh;
+            this.invoker                  = methodHandle;
+            this.constructor              = methodHandle;
             this.invokeSpecializations    = specs;
             this.constructSpecializations = specs;
         }
     }
 
     /**
-     * Takes a method type, and returns a (potentially different method type) that the method handles used by
-     * ScriptFunction must conform to in order to be usable in {@link #invoke(Object, Object...)} and
-     * {@link #construct(Object, Object...)}. The returned method type will be sure to return {@code Object}, and will
-     * have all its parameters turned into {@code Object} as well, except for the following ones:
+     * Takes a method handle, and returns a potentially different method handle that can be used in
+     * {@link #invoke(Object, Object...)} or {@link #construct(Object, Object...)}. The returned method handle
+     * will be sure to return {@code Object}, and will have all its parameters turned into {@code Object} as well,
+     * except for the following ones:
      * <ul>
-     * <li>an optional first {@code boolean} parameter, used for some functions to distinguish method and constructor
-     * invocation,</li>
      * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
-     * <li>the second (or, in presence of boolean parameter, third) argument, which is forced to be
-     * {@link ScriptFunction}, in case the function receives itself (callee) as an argument</li>
-     * @param type the original type
-     * @param hasCallee true if the function uses the callee argument
-     * @param isVarArg if the function is a vararg
-     * @return the new type, conforming to the rules above.
-     */
-    private static MethodType adaptType(final MethodType type, final boolean hasCallee, final boolean isVarArg) {
-        // Generify broadly
-        MethodType newType = type.generic().changeReturnType(Object.class);
-        if(isVarArg) {
-            // Change back to vararg if we over-generified it
+     *   <li>the second argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
+     *   (callee) as an argument</li>
+     * </ul>
+     *
+     * @param handle the original method handle
+     * @return the new handle, conforming to the rules above.
+     */
+    private MethodHandle adaptMethodType(final MethodHandle handle) {
+        final MethodType type = handle.type();
+        MethodType newType = type.generic();
+        if (isVarArg(handle)) {
             newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
         }
-        final boolean hasBoolean = type.parameterType(0) == boolean.class;
-        if(hasBoolean) {
-            // Restore the initial boolean argument
-            newType = newType.changeParameterType(0, boolean.class);
-        }
-        if(hasCallee) {
-            // Restore the ScriptFunction argument
-            newType = newType.changeParameterType(hasBoolean ? 2 : 1, ScriptFunction.class);
+        if (hasCalleeParameter()) {
+            newType = newType.changeParameterType(1, ScriptFunction.class);
         }
-        return newType;
+        return type.equals(newType) ? handle : handle.asType(newType);
     }
 
     @Override
     public String getClassName() {
         return "Function";

@@ -370,47 +361,51 @@
     public Object invoke(final Object self, final Object... arguments) throws Throwable {
         if (Context.DEBUG) {
             invokes++;
         }
 
+        if (genericInvoker == null) {
+            genericInvoker = adaptMethodType(invoker);
+        }
+
         final Object selfObj = convertThisObject(self);
         final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
 
-        if (isVarArg(invokeHandle)) {
+        if (isVarArg(genericInvoker)) {
             if (hasCalleeParameter()) {
-                return invokeHandle.invokeExact(selfObj, this, args);
+                return genericInvoker.invokeExact(selfObj, this, args);
             }
-            return invokeHandle.invokeExact(selfObj, args);
+            return genericInvoker.invokeExact(selfObj, args);
         }
 
-        final int paramCount = invokeHandle.type().parameterCount();
+        final int paramCount = genericInvoker.type().parameterCount();
         if (hasCalleeParameter()) {
             switch (paramCount) {
             case 2:
-                return invokeHandle.invokeExact(selfObj, this);
+                return genericInvoker.invokeExact(selfObj, this);
             case 3:
-                return invokeHandle.invokeExact(selfObj, this, getArg(args, 0));
+                return genericInvoker.invokeExact(selfObj, this, getArg(args, 0));
             case 4:
-                return invokeHandle.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1));
+                return genericInvoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1));
             case 5:
-                return invokeHandle.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+                return genericInvoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
             default:
-                return invokeHandle.invokeWithArguments(withArguments(selfObj, this, paramCount, args));
+                return genericInvoker.invokeWithArguments(withArguments(selfObj, this, paramCount, args));
             }
         }
 
         switch (paramCount) {
         case 1:
-            return invokeHandle.invokeExact(selfObj);
+            return genericInvoker.invokeExact(selfObj);
         case 2:
-            return invokeHandle.invokeExact(selfObj, getArg(args, 0));
+            return genericInvoker.invokeExact(selfObj, getArg(args, 0));
         case 3:
-            return invokeHandle.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
+            return genericInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
         case 4:
-            return invokeHandle.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+            return genericInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
         default:
-            return invokeHandle.invokeWithArguments(withArguments(selfObj, null, paramCount, args));
+            return genericInvoker.invokeWithArguments(withArguments(selfObj, null, paramCount, args));
         }
     }
 
     private static Object getArg(final Object[] args, final int i) {
         return i < args.length ? args[i] : UNDEFINED;

@@ -422,48 +417,51 @@
      * @param args  Call arguments.
      * @return ScriptFunction result.
      * @throws Throwable if there is an exception/error with the constructor invocation or thrown from it
      */
     public Object construct(final Object self, final Object... args) throws Throwable {
-        if (constructHandle == null) {
+        if (constructor == null) {
             typeError("not.a.constructor", ScriptRuntime.safeToString(this));
         }
 
-        if (isVarArg(constructHandle)) {
+        if (genericConstructor == null) {
+            genericConstructor = adaptMethodType(constructor);
+        }
+        if (isVarArg(genericConstructor)) {
             if (hasCalleeParameter()) {
-                return constructHandle.invokeExact(self, this, args);
+                return genericConstructor.invokeExact(self, this, args);
             }
-            return constructHandle.invokeExact(self, args);
+            return genericConstructor.invokeExact(self, args);
         }
 
-        final int paramCount = constructHandle.type().parameterCount();
+        final int paramCount = genericConstructor.type().parameterCount();
         if (hasCalleeParameter()) {
             switch (paramCount) {
             case 2:
-                return constructHandle.invokeExact(self, this);
+                return genericConstructor.invokeExact(self, this);
             case 3:
-                return constructHandle.invokeExact(self, this, getArg(args, 0));
+                return genericConstructor.invokeExact(self, this, getArg(args, 0));
             case 4:
-                return constructHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1));
+                return genericConstructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1));
             case 5:
-                return constructHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+                return genericConstructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
             default:
-                return constructHandle.invokeWithArguments(withArguments(self, this, args));
+                return genericConstructor.invokeWithArguments(withArguments(self, this, args));
             }
         }
 
         switch(paramCount) {
         case 1:
-            return constructHandle.invokeExact(self);
+            return genericConstructor.invokeExact(self);
         case 2:
-            return constructHandle.invokeExact(self, getArg(args, 0));
+            return genericConstructor.invokeExact(self, getArg(args, 0));
         case 3:
-            return constructHandle.invokeExact(self, getArg(args, 0), getArg(args, 1));
+            return genericConstructor.invokeExact(self, getArg(args, 0), getArg(args, 1));
         case 4:
-            return constructHandle.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+            return genericConstructor.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
         default:
-            return constructHandle.invokeWithArguments(withArguments(self, null, args));
+            return genericConstructor.invokeWithArguments(withArguments(self, null, args));
         }
     }
 
     private static Object[] withArguments(final Object self, final ScriptFunction function, final Object... args) {
         return withArguments(self, function, args.length + (function == null ? 1 : 2), args); // + 2 to include self and function

@@ -575,11 +573,11 @@
     public String toString() {
         final StringBuilder sb = new StringBuilder();
 
         sb.append(super.toString())
             .append(" [ ")
-            .append(invokeHandle)
+            .append(invoker)
             .append(", ")
             .append((name == null || name.isEmpty()) ? "<anonymous>" : name);
 
         if (source != null) {
             sb.append(" @ ")

@@ -704,11 +702,11 @@
      * method handle for this ScriptFunction
      * @see SpecializedFunction
      * @return invokeHandle
      */
     public final MethodHandle getInvokeHandle() {
-        return invokeHandle;
+        return invoker;
     }
 
     /**
      * Return the invoke handle bound to a given ScriptObject self reference.
      * If callee parameter is required result is rebound to this.

@@ -738,19 +736,19 @@
     /**
      * Get a method handle to the constructor for this function
      * @return constructor handle
      */
     public final MethodHandle getConstructHandle() {
-        return constructHandle;
+        return constructor;
     }
 
     /**
      * Set a method handle to the constructor for this function
      * @param constructHandle constructor handle
      */
     public final void setConstructHandle(final MethodHandle constructHandle) {
-        this.constructHandle = constructHandle;
+        this.constructor = constructHandle;
         this.constructSpecializations = null;
     }
 
     /**
      * Get the name for this function

@@ -765,11 +763,11 @@
      * null checking invokeHandle
      *
      * @return true if this needs compilation
      */
     public final boolean needsCompilation() {
-        return invokeHandle == null;
+        return invoker == null;
     }
 
     /**
      * Get token for this function
      * @return token