8007273: Creation of ScriptFunctions can be refactored

Wed, 06 Feb 2013 10:31:58 +0100

author
hannesw
date
Wed, 06 Feb 2013 10:31:58 +0100
changeset 72
f6fae6de6f4f
parent 71
f05d4dae30f7
child 73
fcf541418304

8007273: Creation of ScriptFunctions can be refactored
Reviewed-by: lagergren, attila

src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/objects/ObjectCreator.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/objects/Global.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/PropertyMap.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/ScriptFunction.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/ScriptFunctionData.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java	Tue Feb 05 22:07:04 2013 +0530
     1.2 +++ b/src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java	Wed Feb 06 10:31:58 2013 +0100
     1.3 @@ -28,7 +28,6 @@
     1.4  import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.HANDLE_STATIC;
     1.5  import static jdk.nashorn.internal.codegen.Compiler.SCRIPTFUNCTION_IMPL_OBJECT;
     1.6  import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
     1.7 -import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
     1.8  import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
     1.9  import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
    1.10  
    1.11 @@ -40,14 +39,10 @@
    1.12  import jdk.nashorn.internal.codegen.FunctionSignature;
    1.13  import jdk.nashorn.internal.codegen.MethodEmitter;
    1.14  import jdk.nashorn.internal.ir.FunctionNode;
    1.15 -import jdk.nashorn.internal.ir.IdentNode;
    1.16  import jdk.nashorn.internal.ir.Symbol;
    1.17 -import jdk.nashorn.internal.parser.Token;
    1.18 -import jdk.nashorn.internal.parser.TokenType;
    1.19  import jdk.nashorn.internal.runtime.PropertyMap;
    1.20 -import jdk.nashorn.internal.runtime.ScriptFunction;
    1.21 +import jdk.nashorn.internal.runtime.ScriptFunctionData;
    1.22  import jdk.nashorn.internal.runtime.ScriptObject;
    1.23 -import jdk.nashorn.internal.runtime.Source;
    1.24  
    1.25  /**
    1.26   * Analyze a function object's characteristics for appropriate code
    1.27 @@ -79,58 +74,28 @@
    1.28       */
    1.29      @Override
    1.30      public void makeObject(final MethodEmitter method) {
    1.31 -        makeMap();
    1.32  
    1.33 -        final IdentNode identNode  = functionNode.getIdent();
    1.34 -        final String    signature  = new FunctionSignature(true, functionNode.needsCallee(), functionNode.getReturnType(), functionNode.isVarArg() ? null : functionNode.getParameters()).toString();
    1.35 -        final long      firstToken = functionNode.getFirstToken();
    1.36 -        final long      lastToken  = functionNode.getLastToken();
    1.37 -        final int       position   = Token.descPosition(firstToken);
    1.38 -        final int       length     = Token.descPosition(lastToken) - position + Token.descLength(lastToken);
    1.39 -        final long      token      = Token.toDesc(TokenType.FUNCTION, position, length);
    1.40 +        final PropertyMap map = makeMap();
    1.41 +        final String signature = new FunctionSignature(true, functionNode.needsCallee(), functionNode.getReturnType(), functionNode.isVarArg() ? null : functionNode.getParameters()).toString();
    1.42 +        final ScriptFunctionData scriptFunctionData = new ScriptFunctionData(functionNode, map);
    1.43  
    1.44          /*
    1.45 -         * Instantiate the function object, must be referred to by name as
    1.46 -         * class is not available at compile time
    1.47 +         * Instantiate the function object
    1.48           */
    1.49          method._new(SCRIPTFUNCTION_IMPL_OBJECT).dup();
    1.50 -        method.load(functionNode.isAnonymous() ? "" : identNode.getName());
    1.51 +        codegen.loadConstant(scriptFunctionData);
    1.52          loadHandle(method, signature);
    1.53          if(functionNode.needsParentScope()) {
    1.54              method.loadScope();
    1.55          } else {
    1.56              method.loadNull();
    1.57          }
    1.58 -        method.getStatic(compileUnit.getUnitClassName(), SOURCE.tag(), SOURCE.descriptor());
    1.59 -        method.load(token);
    1.60          method.loadHandle(getClassName(), ALLOCATE.tag(), methodDescriptor(ScriptObject.class, PropertyMap.class), EnumSet.of(HANDLE_STATIC));
    1.61  
    1.62          /*
    1.63 -         * Emit code for the correct property map for the object
    1.64 -         */
    1.65 -        loadMap(method);
    1.66 -
    1.67 -        /*
    1.68           * Invoke the constructor
    1.69           */
    1.70 -        method.load(functionNode.needsCallee());
    1.71 -        method.load(functionNode.isStrictMode());
    1.72 -        method.invoke(constructorNoLookup(SCRIPTFUNCTION_IMPL_OBJECT,
    1.73 -                    String.class,
    1.74 -                    MethodHandle.class,
    1.75 -                    ScriptObject.class,
    1.76 -                    Source.class,
    1.77 -                    long.class,
    1.78 -                    MethodHandle.class,
    1.79 -                    PropertyMap.class,
    1.80 -                    boolean.class,
    1.81 -                    boolean.class));
    1.82 +        method.invoke(constructorNoLookup(SCRIPTFUNCTION_IMPL_OBJECT, ScriptFunctionData.class, MethodHandle.class, ScriptObject.class, MethodHandle.class));
    1.83  
    1.84 -
    1.85 -        if (functionNode.isVarArg()) {
    1.86 -            method.dup();
    1.87 -            method.load(functionNode.getParameters().size());
    1.88 -            method.invoke(ScriptFunction.SET_ARITY);
    1.89 -        }
    1.90      }
    1.91  }
     2.1 --- a/src/jdk/nashorn/internal/codegen/objects/ObjectCreator.java	Tue Feb 05 22:07:04 2013 +0530
     2.2 +++ b/src/jdk/nashorn/internal/codegen/objects/ObjectCreator.java	Wed Feb 06 10:31:58 2013 +0100
     2.3 @@ -127,14 +127,15 @@
     2.4  
     2.5      /**
     2.6       * Construct the property map appropriate for the object.
     2.7 +     * @return the newly created property map
     2.8       */
     2.9 -    protected void makeMap() {
    2.10 +    protected PropertyMap makeMap() {
    2.11          if (keys.isEmpty()) { //empty map
    2.12              propertyMap = PropertyMap.newMap(fieldObjectClass);
    2.13 -            return;
    2.14 +        } else {
    2.15 +            propertyMap = newMapCreator(fieldObjectClass).makeMap(isVarArg());
    2.16          }
    2.17 -
    2.18 -        propertyMap = newMapCreator(fieldObjectClass).makeMap(isVarArg());
    2.19 +        return propertyMap;
    2.20      }
    2.21  
    2.22      /**
     3.1 --- a/src/jdk/nashorn/internal/objects/Global.java	Tue Feb 05 22:07:04 2013 +0530
     3.2 +++ b/src/jdk/nashorn/internal/objects/Global.java	Wed Feb 06 10:31:58 2013 +0100
     3.3 @@ -34,7 +34,6 @@
     3.4  import java.lang.invoke.MethodHandle;
     3.5  import java.lang.invoke.MethodHandles;
     3.6  import java.lang.ref.SoftReference;
     3.7 -import java.util.HashMap;
     3.8  import java.util.LinkedHashMap;
     3.9  import java.util.List;
    3.10  import java.util.Map;
    3.11 @@ -394,7 +393,7 @@
    3.12  
    3.13      @Override
    3.14      public ScriptFunction newScriptFunction(final String name, final MethodHandle handle, final ScriptObject scope, final boolean strict) {
    3.15 -        return new ScriptFunctionImpl(name, handle, scope, strict, null);
    3.16 +        return new ScriptFunctionImpl(name, handle, scope, null, strict, false);
    3.17      }
    3.18  
    3.19      @Override
     4.1 --- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Tue Feb 05 22:07:04 2013 +0530
     4.2 +++ b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Wed Feb 06 10:31:58 2013 +0100
     4.3 @@ -30,6 +30,8 @@
     4.4  
     4.5  import java.lang.invoke.MethodHandle;
     4.6  import java.lang.invoke.MethodHandles;
     4.7 +
     4.8 +import jdk.nashorn.internal.runtime.ScriptFunctionData;
     4.9  import jdk.nashorn.internal.codegen.objects.FunctionObjectCreator;
    4.10  import jdk.nashorn.internal.runtime.GlobalFunctions;
    4.11  import jdk.nashorn.internal.runtime.Property;
    4.12 @@ -37,7 +39,6 @@
    4.13  import jdk.nashorn.internal.runtime.ScriptFunction;
    4.14  import jdk.nashorn.internal.runtime.ScriptObject;
    4.15  import jdk.nashorn.internal.runtime.ScriptRuntime;
    4.16 -import jdk.nashorn.internal.runtime.Source;
    4.17  import jdk.nashorn.internal.runtime.linker.Lookup;
    4.18  import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
    4.19  
    4.20 @@ -46,109 +47,68 @@
    4.21   * function objects -- to expose properties like "prototype", "length" etc.
    4.22   */
    4.23  public class ScriptFunctionImpl extends ScriptFunction {
    4.24 -    // per-function object flags
    4.25 -    private static final int IS_STRICT  = 0b0000_0001;
    4.26 -    private static final int IS_BUILTIN = 0b0000_0010;
    4.27 -    private static final int HAS_CALLEE = 0b0000_0100;
    4.28 -
    4.29 -    // set this function to be a builtin function
    4.30 -    private void setIsBuiltin() {
    4.31 -        flags |= IS_BUILTIN;
    4.32 -    }
    4.33 -
    4.34 -    // set this function to be a ECMAScript strict function
    4.35 -    private void setIsStrict() {
    4.36 -        flags |= IS_STRICT;
    4.37 -    }
    4.38  
    4.39      private static final MethodHandle BOUND_FUNCTION    = findOwnMH("boundFunction",    Object.class, ScriptFunction.class, Object.class, Object[].class, Object.class, Object[].class);
    4.40      private static final MethodHandle BOUND_CONSTRUCTOR = findOwnMH("boundConstructor", Object.class, ScriptFunction.class, Object[].class, Object.class, Object[].class);
    4.41  
    4.42      private static final PropertyMap nasgenmap$;
    4.43  
    4.44 -    private int flags;
    4.45 -
    4.46      /**
    4.47 -     * Constructor
    4.48 -     *
    4.49 -     * Called by Nasgen generated code, no membercount, use the default map
    4.50 -     * Creates builtin functions only
    4.51 +     * Constructor called by Nasgen generated code, no membercount, use the default map.
    4.52 +     * Creates builtin functions only.
    4.53       *
    4.54       * @param name name of function
    4.55       * @param invokeHandle handle for invocation
    4.56       * @param specs specialized versions of this method, if available, null otherwise
    4.57       */
    4.58      ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs) {
    4.59 -        this(name, invokeHandle, nasgenmap$, specs);
    4.60 -    }
    4.61 -
    4.62 -    /**
    4.63 -     * Constructor
    4.64 -     *
    4.65 -     * Called by Nasgen generated code, no membercount, use the default map
    4.66 -     * Creates builtin functions only
    4.67 -     *
    4.68 -     * @param name name of function
    4.69 -     * @param methodHandle handle for invocation
    4.70 -     * @param map initial property map
    4.71 -     * @param specs specialized versions of this method, if available, null otherwise
    4.72 -     */
    4.73 -    ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final PropertyMap map, final MethodHandle[] specs) {
    4.74 -        super(name, methodHandle, (nasgenmap$ == map) ? nasgenmap$ : map.addAll(nasgenmap$), null, null, 0, false, specs);
    4.75 -        this.setIsBuiltin();
    4.76 +        super(name, invokeHandle, nasgenmap$, null, specs, false, true);
    4.77          init();
    4.78      }
    4.79  
    4.80      /**
    4.81 -     * Constructor
    4.82 +     * Constructor called by Nasgen generated code, no membercount, use the map passed as argument.
    4.83 +     * Creates builtin functions only.
    4.84       *
    4.85 -     * Called by Global.newScriptFunction (runtime)
    4.86 +     * @param name name of function
    4.87 +     * @param invokeHandle handle for invocation
    4.88 +     * @param map initial property map
    4.89 +     * @param specs specialized versions of this method, if available, null otherwise
    4.90 +     */
    4.91 +    ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final MethodHandle[] specs) {
    4.92 +        super(name, invokeHandle, map.addAll(nasgenmap$), null, specs, false, true);
    4.93 +        init();
    4.94 +    }
    4.95 +
    4.96 +    /**
    4.97 +     * Constructor called by Global.newScriptFunction (runtime).
    4.98       *
    4.99       * @param name name of function
   4.100       * @param methodHandle handle for invocation
   4.101       * @param scope scope object
   4.102 +     * @param specs specialized versions of this method, if available, null otherwise
   4.103       * @param strict are we in strict mode
   4.104 -     * @param specs specialized versions of this method, if available, null otherwise
   4.105 +     * @param builtin is this a built-in function
   4.106       */
   4.107 -    ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final boolean strict, final MethodHandle[] specs) {
   4.108 -        super(name, methodHandle, getMap(strict), scope, specs);
   4.109 -        if (strict) {
   4.110 -            this.setIsStrict();
   4.111 -        }
   4.112 +    ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean strict, final boolean builtin) {
   4.113 +        super(name, methodHandle, getMap(strict), scope, specs, strict, builtin);
   4.114          init();
   4.115      }
   4.116  
   4.117      /**
   4.118 -     * Constructor
   4.119 +     * Constructor called by (compiler) generated code for {@link ScriptObject}s.
   4.120 +     * Code is generated by {@link FunctionObjectCreator}
   4.121       *
   4.122 -     * Called by (compiler) generated code for {@link ScriptObject}s. Code is
   4.123 -     * generated by {@link FunctionObjectCreator}
   4.124 -     *
   4.125 -     * TODO this is a horrible constructor - can we do it with fewer args?
   4.126 -     *
   4.127 -     * @param name name of function
   4.128 +     * @param data static function data
   4.129       * @param methodHandle handle for invocation
   4.130       * @param scope scope object
   4.131 -     * @param source source
   4.132 -     * @param token token
   4.133       * @param allocator instance constructor for function
   4.134 -     * @param allocatorMap initial map that constructor will keep reference to for future instantiations
   4.135 -     * @param needCallee does the function use the {@code callee} variable
   4.136 -     * @param strict are we in strict mode
   4.137       */
   4.138 -    public ScriptFunctionImpl(
   4.139 -            final String name,
   4.140 -            final MethodHandle methodHandle,
   4.141 -            final ScriptObject scope,
   4.142 -            final Source source,
   4.143 -            final long token,
   4.144 -            final MethodHandle allocator,
   4.145 -            final PropertyMap allocatorMap,
   4.146 -            final boolean needCallee,
   4.147 -            final boolean strict) {
   4.148 -        super(name, methodHandle, getMap(strict), scope, source, token, allocator, allocatorMap, needCallee, null);
   4.149 -        if (strict) {
   4.150 -            this.setIsStrict();
   4.151 +    public ScriptFunctionImpl(final ScriptFunctionData data, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle allocator) {
   4.152 +        super(data, getMap(data.isStrict()), scope);
   4.153 +        // Set method handles in script data
   4.154 +        if (data.getInvoker() == null) {
   4.155 +            data.setMethodHandles(methodHandle, allocator);
   4.156          }
   4.157          init();
   4.158      }
   4.159 @@ -167,11 +127,11 @@
   4.160      static synchronized ScriptFunction getTypeErrorThrower() {
   4.161          if (typeErrorThrower == null) {
   4.162              //name handle
   4.163 -            final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_SETTER, null, false, null);
   4.164 +            final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_SETTER, null, null, false, false);
   4.165              // clear constructor handle...
   4.166 -            func.constructHandle = null;
   4.167 -            func.prototype       = UNDEFINED;
   4.168 -            typeErrorThrower     = func;
   4.169 +            func.setConstructHandle(null);
   4.170 +            func.setPrototype(UNDEFINED);
   4.171 +            typeErrorThrower = func;
   4.172          }
   4.173  
   4.174          return typeErrorThrower;
   4.175 @@ -216,28 +176,8 @@
   4.176          return new AnonymousFunction();
   4.177      }
   4.178  
   4.179 -    @Override
   4.180 -    public final boolean isStrict() {
   4.181 -        return (flags & IS_STRICT) != 0;
   4.182 -    }
   4.183 -
   4.184 -    @Override
   4.185 -    public final boolean hasCalleeParameter() {
   4.186 -        return (flags & HAS_CALLEE) != 0;
   4.187 -    }
   4.188 -
   4.189 -    @Override
   4.190 -    protected void setHasCalleeParameter() {
   4.191 -        flags |= HAS_CALLEE;
   4.192 -    }
   4.193 -
   4.194 -    @Override
   4.195 -    public final boolean isBuiltin() {
   4.196 -        return (flags & IS_BUILTIN) != 0;
   4.197 -    }
   4.198 -
   4.199      /**
   4.200 -     * Factory method for non-constructor functions
   4.201 +     * Factory method for non-constructor built-in functions
   4.202       *
   4.203       * @param name   function name
   4.204       * @param methodHandle handle for invocation
   4.205 @@ -246,9 +186,7 @@
   4.206       * @return new ScriptFunction
   4.207       */
   4.208      public static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict) {
   4.209 -        final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, strict, specs);
   4.210 -
   4.211 -        func.setIsBuiltin();
   4.212 +        final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, strict, true);
   4.213          func.setConstructHandle(null);
   4.214          func.setPrototype(UNDEFINED);
   4.215  
   4.216 @@ -256,7 +194,7 @@
   4.217      }
   4.218  
   4.219      /**
   4.220 -     * Factory method for non-constructor functions
   4.221 +     * Factory method for non-constructor built-in functions
   4.222       *
   4.223       * @param name   function name
   4.224       * @param methodHandle handle for invocation
   4.225 @@ -268,7 +206,7 @@
   4.226      }
   4.227  
   4.228      /**
   4.229 -     * Factory method for non-constructor functions
   4.230 +     * Factory method for non-constructor built-in functions
   4.231       *
   4.232       * @param name   function name
   4.233       * @param methodHandle handle for invocation
     5.1 --- a/src/jdk/nashorn/internal/runtime/PropertyMap.java	Tue Feb 05 22:07:04 2013 +0530
     5.2 +++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java	Wed Feb 06 10:31:58 2013 +0100
     5.3 @@ -383,6 +383,7 @@
     5.4       * @return New {@link PropertyMap} with added properties.
     5.5       */
     5.6      public PropertyMap addAll(final PropertyMap other) {
     5.7 +        assert this != other : "adding property map to itself";
     5.8          final Property[] otherProperties = other.properties.getProperties();
     5.9          final PropertyHashMap newProperties = properties.immutableAdd(otherProperties);
    5.10  
     6.1 --- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Tue Feb 05 22:07:04 2013 +0530
     6.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Wed Feb 06 10:31:58 2013 +0100
     6.3 @@ -37,7 +37,6 @@
     6.4  import jdk.nashorn.internal.codegen.types.Type;
     6.5  import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
     6.6  import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
     6.7 -import jdk.nashorn.internal.parser.Token;
     6.8  import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
     6.9  import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
    6.10  import jdk.nashorn.internal.runtime.linker.NashornGuards;
    6.11 @@ -70,144 +69,53 @@
    6.12  
    6.13      private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
    6.14  
    6.15 -    /** method handle to arity setter for this ScriptFunction */
    6.16 -    public static final Call SET_ARITY = virtualCallNoLookup(ScriptFunction.class, "setArity", void.class, int.class);
    6.17      /** method handle to scope getter for this ScriptFunction */
    6.18      public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
    6.19  
    6.20      /** Should specialized function and specialized constructors for the builtin be used if available? */
    6.21      private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable");
    6.22  
    6.23 -    /** Name of function or null. */
    6.24 -    private final String name;
    6.25 -
    6.26 -    /** Source of function. */
    6.27 -    private final Source source;
    6.28 -
    6.29 -    /** Start position and length in source. */
    6.30 -    private final long token;
    6.31 -
    6.32 -    /** Reference to code for this method. */
    6.33 -    private final MethodHandle invokeHandle;
    6.34 -
    6.35 -    /** Reference to code for this method when called to create "new" object */
    6.36 -    protected MethodHandle constructHandle;
    6.37 +    private final ScriptFunctionData data;
    6.38  
    6.39      /** Reference to constructor prototype. */
    6.40      protected Object prototype;
    6.41  
    6.42 -    /** Constructor to create a new instance. */
    6.43 -    private MethodHandle allocator;
    6.44 -
    6.45 -    /** Map for new instance constructor. */
    6.46 -    private PropertyMap allocatorMap;
    6.47 -
    6.48      /** The parent scope. */
    6.49      private final ScriptObject scope;
    6.50  
    6.51 -    /** Specializations - see @SpecializedFunction */
    6.52 -    private MethodHandle[] invokeSpecializations;
    6.53 -
    6.54 -    /** Specializations - see @SpecializedFunction */
    6.55 -    private MethodHandle[] constructSpecializations;
    6.56 -
    6.57 -    /** This field is either computed in constructor or set explicitly by calling setArity method. */
    6.58 -    private int arity;
    6.59 -
    6.60      /**
    6.61       * Constructor
    6.62       *
    6.63 -     * @param name          function name
    6.64 -     * @param methodHandle  method handle to function (if specializations are present, assumed to be most generic)
    6.65 -     * @param map           property map
    6.66 -     * @param scope         scope
    6.67 -     * @param specs         specialized version of this function - other method handles
    6.68 +     * @param name         function name
    6.69 +     * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
    6.70 +     * @param map          property map
    6.71 +     * @param scope        scope
    6.72 +     * @param specs        specialized version of this function - other method handles
    6.73 +     *
    6.74       */
    6.75      protected ScriptFunction(
    6.76              final String name,
    6.77              final MethodHandle methodHandle,
    6.78              final PropertyMap map,
    6.79              final ScriptObject scope,
    6.80 -            final MethodHandle[] specs) {
    6.81 -        this(name, methodHandle, map, scope, null, 0, needsCallee(methodHandle), specs);
    6.82 -    }
    6.83 +            final MethodHandle[] specs,
    6.84 +            final boolean strict,
    6.85 +            final boolean builtin) {
    6.86  
    6.87 -    /**
    6.88 -     * Heuristic to figure out if the method handle has a callee argument. If it's type is either
    6.89 -     * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
    6.90 -     * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
    6.91 -     * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
    6.92 -     * they also always receive a callee.
    6.93 -     * @param methodHandle the examined method handle
    6.94 -     * @return true if the method handle expects a callee, false otherwise
    6.95 -     */
    6.96 -    private static boolean needsCallee(MethodHandle methodHandle) {
    6.97 -        final MethodType type = methodHandle.type();
    6.98 -        final int len = type.parameterCount();
    6.99 -        if(len == 0) {
   6.100 -            return false;
   6.101 -        }
   6.102 -        if(type.parameterType(0) == boolean.class) {
   6.103 -            return len > 2 && type.parameterType(2) == ScriptFunction.class;
   6.104 -        }
   6.105 -        return len > 1 && type.parameterType(1) == ScriptFunction.class;
   6.106 +        this (new ScriptFunctionData(name, methodHandle, specs, strict, builtin), map, scope);
   6.107      }
   6.108  
   6.109      /**
   6.110       * Constructor
   6.111       *
   6.112 -     * @param name          function name
   6.113 -     * @param methodHandle  method handle to function (if specializations are present, assumed to be most generic)
   6.114 +     * @param data          static function data
   6.115       * @param map           property map
   6.116       * @param scope         scope
   6.117 -     * @param source        the source
   6.118 -     * @param token         token
   6.119 -     * @param allocator     method handle to this function's allocator - see JO$ classes
   6.120 -     * @param allocatorMap  property map to be used for all constructors
   6.121 -     * @param needsCallee   does this method use the {@code callee} variable
   6.122 -     * @param specs         specialized version of this function - other method handles
   6.123       */
   6.124      protected ScriptFunction(
   6.125 -            final String name,
   6.126 -            final MethodHandle methodHandle,
   6.127 +            final ScriptFunctionData data,
   6.128              final PropertyMap map,
   6.129 -            final ScriptObject scope,
   6.130 -            final Source source,
   6.131 -            final long token,
   6.132 -            final MethodHandle allocator,
   6.133 -            final PropertyMap allocatorMap,
   6.134 -            final boolean needsCallee,
   6.135 -            final MethodHandle[] specs) {
   6.136 -
   6.137 -        this(name, methodHandle, map, scope, source, token, needsCallee, specs);
   6.138 -
   6.139 -        //this is the internal constructor
   6.140 -
   6.141 -        this.allocator    = allocator;
   6.142 -        this.allocatorMap = allocatorMap;
   6.143 -    }
   6.144 -
   6.145 -    /**
   6.146 -     * Constructor
   6.147 -     *
   6.148 -     * @param name              function name
   6.149 -     * @param methodHandle      method handle to function (if specializations are present, assumed to be most generic)
   6.150 -     * @param map               property map
   6.151 -     * @param scope             scope
   6.152 -     * @param source            the source
   6.153 -     * @param token             token
   6.154 -     * @param needsCallee       does this method use the {@code callee} variable
   6.155 -     * @param specs             specialized version of this function - other method handles
   6.156 -     */
   6.157 -    protected ScriptFunction(
   6.158 -            final String name,
   6.159 -            final MethodHandle methodHandle,
   6.160 -            final PropertyMap map,
   6.161 -            final ScriptObject scope,
   6.162 -            final Source source,
   6.163 -            final long token,
   6.164 -            final boolean needsCallee,
   6.165 -            final MethodHandle[] specs) {
   6.166 +            final ScriptObject scope) {
   6.167  
   6.168          super(map);
   6.169  
   6.170 @@ -215,89 +123,8 @@
   6.171              constructorCount++;
   6.172          }
   6.173  
   6.174 -        this.name   = name;
   6.175 -        this.source = source;
   6.176 -        this.token  = token;
   6.177 +        this.data = data;
   6.178          this.scope  = scope;
   6.179 -        if(needsCallee) {
   6.180 -            setHasCalleeParameter();
   6.181 -        }
   6.182 -
   6.183 -        final MethodType type       = methodHandle.type();
   6.184 -        final int        paramCount = type.parameterCount();
   6.185 -        final boolean    isVarArg   = type.parameterType(paramCount - 1).isArray();
   6.186 -
   6.187 -        final MethodHandle mh = MH.asType(methodHandle, adaptType(type, needsCallee, isVarArg));
   6.188 -
   6.189 -        this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
   6.190 -
   6.191 -        if (needsCallee && !isVarArg) {
   6.192 -            this.arity--;
   6.193 -        }
   6.194 -
   6.195 -        if (scope != null) {
   6.196 -            this.invokeHandle    = mh;
   6.197 -            this.constructHandle = mh;
   6.198 -        } else if (isConstructor(mh)) {
   6.199 -            if (!isVarArg) {
   6.200 -                this.arity--;    // drop the boolean flag for arity
   6.201 -            }
   6.202 -            /*
   6.203 -             * We insert a boolean argument to tell if the method was invoked as
   6.204 -             * constructor or not if the method handle's first argument is boolean.
   6.205 -             */
   6.206 -            this.invokeHandle    = MH.insertArguments(mh, 0, false);
   6.207 -            this.constructHandle = MH.insertArguments(mh, 0, true);
   6.208 -
   6.209 -            if (specs != null) {
   6.210 -                this.invokeSpecializations    = new MethodHandle[specs.length];
   6.211 -                this.constructSpecializations = new MethodHandle[specs.length];
   6.212 -                for (int i = 0; i < specs.length; i++) {
   6.213 -                    this.invokeSpecializations[i]    = MH.insertArguments(specs[i], 0, false);
   6.214 -                    this.constructSpecializations[i] = MH.insertArguments(specs[i], 0, true);
   6.215 -                }
   6.216 -            }
   6.217 -        } else {
   6.218 -            this.invokeHandle             = mh;
   6.219 -            this.constructHandle          = mh;
   6.220 -            this.invokeSpecializations    = specs;
   6.221 -            this.constructSpecializations = specs;
   6.222 -        }
   6.223 -    }
   6.224 -
   6.225 -    /**
   6.226 -     * Takes a method type, and returns a (potentially different method type) that the method handles used by
   6.227 -     * ScriptFunction must conform to in order to be usable in {@link #invoke(Object, Object...)} and
   6.228 -     * {@link #construct(Object, Object...)}. The returned method type will be sure to return {@code Object}, and will
   6.229 -     * have all its parameters turned into {@code Object} as well, except for the following ones:
   6.230 -     * <ul>
   6.231 -     * <li>an optional first {@code boolean} parameter, used for some functions to distinguish method and constructor
   6.232 -     * invocation,</li>
   6.233 -     * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
   6.234 -     * <li>the second (or, in presence of boolean parameter, third) argument, which is forced to be
   6.235 -     * {@link ScriptFunction}, in case the function receives itself (callee) as an argument</li>
   6.236 -     * @param type the original type
   6.237 -     * @param hasCallee true if the function uses the callee argument
   6.238 -     * @param isVarArg if the function is a vararg
   6.239 -     * @return the new type, conforming to the rules above.
   6.240 -     */
   6.241 -    private static MethodType adaptType(final MethodType type, final boolean hasCallee, final boolean isVarArg) {
   6.242 -        // Generify broadly
   6.243 -        MethodType newType = type.generic().changeReturnType(Object.class);
   6.244 -        if(isVarArg) {
   6.245 -            // Change back to vararg if we over-generified it
   6.246 -            newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
   6.247 -        }
   6.248 -        final boolean hasBoolean = type.parameterType(0) == boolean.class;
   6.249 -        if(hasBoolean) {
   6.250 -            // Restore the initial boolean argument
   6.251 -            newType = newType.changeParameterType(0, boolean.class);
   6.252 -        }
   6.253 -        if(hasCallee) {
   6.254 -            // Restore the ScriptFunction argument
   6.255 -            newType = newType.changeParameterType(hasBoolean ? 2 : 1, ScriptFunction.class);
   6.256 -        }
   6.257 -        return newType;
   6.258      }
   6.259  
   6.260      @Override
   6.261 @@ -329,7 +156,7 @@
   6.262       * @return arity
   6.263       */
   6.264      public final int getArity() {
   6.265 -        return arity;
   6.266 +        return data.getArity();
   6.267      }
   6.268  
   6.269      /**
   6.270 @@ -337,27 +164,32 @@
   6.271       * @param arity arity
   6.272       */
   6.273      public final void setArity(final int arity) {
   6.274 -        this.arity = arity;
   6.275 +        data.setArity(arity);
   6.276      }
   6.277  
   6.278      /**
   6.279       * Is this a ECMAScript 'use strict' function?
   6.280       * @return true if function is in strict mode
   6.281       */
   6.282 -    public abstract boolean isStrict();
   6.283 +    public boolean isStrict() {
   6.284 +        return data.isStrict();
   6.285 +    }
   6.286  
   6.287      /**
   6.288       * Is this a ECMAScript built-in function (like parseInt, Array.isArray) ?
   6.289       * @return true if built-in
   6.290       */
   6.291 -    public abstract boolean isBuiltin();
   6.292 +    public boolean isBuiltin() {
   6.293 +        return data.isBuiltin();
   6.294 +    }
   6.295  
   6.296      /**
   6.297 -     * Is this a non-strict and not-built-in script function?
   6.298 -     * @return true if neither strict nor built-in
   6.299 +     * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
   6.300 +     * according to ECMA 10.4.3.
   6.301 +     * @return true if this argument must be an object
   6.302       */
   6.303 -    public boolean isNonStrictFunction() {
   6.304 -        return !isStrict() && !isBuiltin();
   6.305 +    public boolean needsWrappedThis() {
   6.306 +        return data.needsWrappedThis();
   6.307      }
   6.308  
   6.309      /**
   6.310 @@ -372,43 +204,44 @@
   6.311              invokes++;
   6.312          }
   6.313  
   6.314 +        final MethodHandle invoker = data.getGenericInvoker();
   6.315          final Object selfObj = convertThisObject(self);
   6.316          final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
   6.317  
   6.318 -        if (isVarArg(invokeHandle)) {
   6.319 -            if (hasCalleeParameter()) {
   6.320 -                return invokeHandle.invokeExact(selfObj, this, args);
   6.321 +        if (data.isVarArg()) {
   6.322 +            if (data.needsCallee()) {
   6.323 +                return invoker.invokeExact(selfObj, this, args);
   6.324              }
   6.325 -            return invokeHandle.invokeExact(selfObj, args);
   6.326 +            return invoker.invokeExact(selfObj, args);
   6.327          }
   6.328  
   6.329 -        final int paramCount = invokeHandle.type().parameterCount();
   6.330 -        if (hasCalleeParameter()) {
   6.331 +        final int paramCount = invoker.type().parameterCount();
   6.332 +        if (data.needsCallee()) {
   6.333              switch (paramCount) {
   6.334              case 2:
   6.335 -                return invokeHandle.invokeExact(selfObj, this);
   6.336 +                return invoker.invokeExact(selfObj, this);
   6.337              case 3:
   6.338 -                return invokeHandle.invokeExact(selfObj, this, getArg(args, 0));
   6.339 +                return invoker.invokeExact(selfObj, this, getArg(args, 0));
   6.340              case 4:
   6.341 -                return invokeHandle.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1));
   6.342 +                return invoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1));
   6.343              case 5:
   6.344 -                return invokeHandle.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
   6.345 +                return invoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
   6.346              default:
   6.347 -                return invokeHandle.invokeWithArguments(withArguments(selfObj, this, paramCount, args));
   6.348 +                return invoker.invokeWithArguments(withArguments(selfObj, this, paramCount, args));
   6.349              }
   6.350          }
   6.351  
   6.352          switch (paramCount) {
   6.353          case 1:
   6.354 -            return invokeHandle.invokeExact(selfObj);
   6.355 +            return invoker.invokeExact(selfObj);
   6.356          case 2:
   6.357 -            return invokeHandle.invokeExact(selfObj, getArg(args, 0));
   6.358 +            return invoker.invokeExact(selfObj, getArg(args, 0));
   6.359          case 3:
   6.360 -            return invokeHandle.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
   6.361 +            return invoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
   6.362          case 4:
   6.363 -            return invokeHandle.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
   6.364 +            return invoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
   6.365          default:
   6.366 -            return invokeHandle.invokeWithArguments(withArguments(selfObj, null, paramCount, args));
   6.367 +            return invoker.invokeWithArguments(withArguments(selfObj, null, paramCount, args));
   6.368          }
   6.369      }
   6.370  
   6.371 @@ -424,44 +257,45 @@
   6.372       * @throws Throwable if there is an exception/error with the constructor invocation or thrown from it
   6.373       */
   6.374      public Object construct(final Object self, final Object... args) throws Throwable {
   6.375 -        if (constructHandle == null) {
   6.376 +        if (data.getConstructor() == null) {
   6.377              typeError("not.a.constructor", ScriptRuntime.safeToString(this));
   6.378          }
   6.379  
   6.380 -        if (isVarArg(constructHandle)) {
   6.381 -            if (hasCalleeParameter()) {
   6.382 -                return constructHandle.invokeExact(self, this, args);
   6.383 +        final MethodHandle constructor = data.getGenericConstructor();
   6.384 +        if (data.isVarArg()) {
   6.385 +            if (data.needsCallee()) {
   6.386 +                return constructor.invokeExact(self, this, args);
   6.387              }
   6.388 -            return constructHandle.invokeExact(self, args);
   6.389 +            return constructor.invokeExact(self, args);
   6.390          }
   6.391  
   6.392 -        final int paramCount = constructHandle.type().parameterCount();
   6.393 -        if (hasCalleeParameter()) {
   6.394 +        final int paramCount = constructor.type().parameterCount();
   6.395 +        if (data.needsCallee()) {
   6.396              switch (paramCount) {
   6.397              case 2:
   6.398 -                return constructHandle.invokeExact(self, this);
   6.399 +                return constructor.invokeExact(self, this);
   6.400              case 3:
   6.401 -                return constructHandle.invokeExact(self, this, getArg(args, 0));
   6.402 +                return constructor.invokeExact(self, this, getArg(args, 0));
   6.403              case 4:
   6.404 -                return constructHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1));
   6.405 +                return constructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1));
   6.406              case 5:
   6.407 -                return constructHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
   6.408 +                return constructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
   6.409              default:
   6.410 -                return constructHandle.invokeWithArguments(withArguments(self, this, args));
   6.411 +                return constructor.invokeWithArguments(withArguments(self, this, args));
   6.412              }
   6.413          }
   6.414  
   6.415          switch(paramCount) {
   6.416          case 1:
   6.417 -            return constructHandle.invokeExact(self);
   6.418 +            return constructor.invokeExact(self);
   6.419          case 2:
   6.420 -            return constructHandle.invokeExact(self, getArg(args, 0));
   6.421 +            return constructor.invokeExact(self, getArg(args, 0));
   6.422          case 3:
   6.423 -            return constructHandle.invokeExact(self, getArg(args, 0), getArg(args, 1));
   6.424 +            return constructor.invokeExact(self, getArg(args, 0), getArg(args, 1));
   6.425          case 4:
   6.426 -            return constructHandle.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
   6.427 +            return constructor.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
   6.428          default:
   6.429 -            return constructHandle.invokeWithArguments(withArguments(self, null, args));
   6.430 +            return constructor.invokeWithArguments(withArguments(self, null, args));
   6.431          }
   6.432      }
   6.433  
   6.434 @@ -509,9 +343,9 @@
   6.435  
   6.436          ScriptObject object = null;
   6.437  
   6.438 -        if (allocator != null) {
   6.439 +        if (data.getAllocator() != null) {
   6.440              try {
   6.441 -                object = (ScriptObject)allocator.invokeExact(allocatorMap);
   6.442 +                object = (ScriptObject)data.getAllocator().invokeExact(data.getAllocatorMap());
   6.443              } catch (final RuntimeException | Error e) {
   6.444                  throw e;
   6.445              } catch (final Throwable t) {
   6.446 @@ -546,25 +380,6 @@
   6.447       */
   6.448      public abstract ScriptFunction makeBoundFunction(Object self, Object[] args);
   6.449  
   6.450 -    /**
   6.451 -     * Test if a methodHandle refers to a constructor.
   6.452 -     * @param methodHandle MethodHandle to test.
   6.453 -     * @return True if method is a constructor.
   6.454 -     */
   6.455 -    private static boolean isConstructor(final MethodHandle methodHandle) {
   6.456 -        return methodHandle.type().parameterCount() >= 1 && methodHandle.type().parameterType(0) == boolean.class;
   6.457 -    }
   6.458 -
   6.459 -    /**
   6.460 -     * Test if a methodHandle refers to a variable argument method.
   6.461 -     * @param methodHandle MethodHandle to test.
   6.462 -     * @return True if variable arguments.
   6.463 -     */
   6.464 -    public boolean isVarArg(final MethodHandle methodHandle) {
   6.465 -        return hasCalleeParameter()
   6.466 -                ? methodHandle.type().parameterCount() == 3 && methodHandle.type().parameterType(2).isArray()
   6.467 -                : methodHandle.type().parameterCount() == 2 && methodHandle.type().parameterType(1).isArray();
   6.468 -    }
   6.469  
   6.470      @Override
   6.471      public final String safeToString() {
   6.472 @@ -573,23 +388,7 @@
   6.473  
   6.474      @Override
   6.475      public String toString() {
   6.476 -        final StringBuilder sb = new StringBuilder();
   6.477 -
   6.478 -        sb.append(super.toString())
   6.479 -            .append(" [ ")
   6.480 -            .append(invokeHandle)
   6.481 -            .append(", ")
   6.482 -            .append((name == null || name.isEmpty()) ? "<anonymous>" : name);
   6.483 -
   6.484 -        if (source != null) {
   6.485 -            sb.append(" @ ")
   6.486 -               .append(source.getName())
   6.487 -               .append(':')
   6.488 -               .append(source.getLine(Token.descPosition(token)));
   6.489 -        }
   6.490 -        sb.append(" ]");
   6.491 -
   6.492 -        return sb.toString();
   6.493 +        return data.toString();
   6.494      }
   6.495  
   6.496      /**
   6.497 @@ -598,11 +397,7 @@
   6.498       * @return string representation of this function's source
   6.499       */
   6.500      public final String toSource() {
   6.501 -        if (source != null && token != 0) {
   6.502 -            return source.getString(Token.descPosition(token), Token.descLength(token));
   6.503 -        }
   6.504 -
   6.505 -        return "function " + (name == null ? "" : name) + "() { [native code] }";
   6.506 +        return data.toSource();
   6.507      }
   6.508  
   6.509      /**
   6.510 @@ -696,7 +491,7 @@
   6.511       * @return invoke method handle
   6.512       */
   6.513      public final MethodHandle getBestSpecializedInvokeHandle(final MethodType type) {
   6.514 -        return candidateWithLowestWeight(type, getInvokeHandle(), invokeSpecializations);
   6.515 +        return candidateWithLowestWeight(type, getInvokeHandle(), data.getInvokeSpecializations());
   6.516      }
   6.517  
   6.518      /**
   6.519 @@ -706,7 +501,7 @@
   6.520       * @return invokeHandle
   6.521       */
   6.522      public final MethodHandle getInvokeHandle() {
   6.523 -        return invokeHandle;
   6.524 +        return data.getInvoker();
   6.525      }
   6.526  
   6.527      /**
   6.528 @@ -718,19 +513,9 @@
   6.529       */
   6.530      public final MethodHandle getBoundInvokeHandle(final ScriptObject self) {
   6.531          final MethodHandle bound = MH.bindTo(getInvokeHandle(), self);
   6.532 -        return hasCalleeParameter() ? MH.bindTo(bound, this) : bound;
   6.533 +        return data.needsCallee() ? MH.bindTo(bound, this) : bound;
   6.534      }
   6.535  
   6.536 -    /**
   6.537 -     * Check whether the ScriptFunction has callee parameter
   6.538 -     * @return true if callee parameter
   6.539 -     */
   6.540 -    protected abstract boolean hasCalleeParameter();
   6.541 -
   6.542 -    /**
   6.543 -     * Flag ScriptFunction as needing a callee parameter
   6.544 -     */
   6.545 -    protected abstract void setHasCalleeParameter();
   6.546  
   6.547      /**
   6.548       * Get the construct handle - the most generic (and if no specializations are in place, only) constructor
   6.549 @@ -740,7 +525,7 @@
   6.550       * @return construct handle
   6.551       */
   6.552      public final MethodHandle getConstructHandle(final MethodType type) {
   6.553 -        return candidateWithLowestWeight(type, getConstructHandle(), constructSpecializations);
   6.554 +        return candidateWithLowestWeight(type, getConstructHandle(), data.getConstructSpecializations());
   6.555      }
   6.556  
   6.557      /**
   6.558 @@ -748,7 +533,7 @@
   6.559       * @return constructor handle
   6.560       */
   6.561      public final MethodHandle getConstructHandle() {
   6.562 -        return constructHandle;
   6.563 +        return data.getConstructor();
   6.564      }
   6.565  
   6.566      /**
   6.567 @@ -756,8 +541,7 @@
   6.568       * @param constructHandle constructor handle
   6.569       */
   6.570      public final void setConstructHandle(final MethodHandle constructHandle) {
   6.571 -        this.constructHandle = constructHandle;
   6.572 -        this.constructSpecializations = null;
   6.573 +        data.setConstructor(constructHandle);
   6.574      }
   6.575  
   6.576      /**
   6.577 @@ -765,7 +549,7 @@
   6.578       * @return the name
   6.579       */
   6.580      public final String getName() {
   6.581 -        return name;
   6.582 +        return data.getName();
   6.583      }
   6.584  
   6.585      /**
   6.586 @@ -775,7 +559,7 @@
   6.587       * @return true if this needs compilation
   6.588       */
   6.589      public final boolean needsCompilation() {
   6.590 -        return invokeHandle == null;
   6.591 +        return data.getInvoker() == null;
   6.592      }
   6.593  
   6.594      /**
   6.595 @@ -783,7 +567,7 @@
   6.596       * @return token
   6.597       */
   6.598      public final long getToken() {
   6.599 -        return token;
   6.600 +        return data.getToken();
   6.601      }
   6.602  
   6.603      /**
   6.604 @@ -907,7 +691,7 @@
   6.605          final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray(); // drop self
   6.606          MethodHandle handle = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), constructor);
   6.607  
   6.608 -        if (hasCalleeParameter()) {
   6.609 +        if (data.needsCallee()) {
   6.610              handle = MH.foldArguments(handle, ALLOCATE);
   6.611          } else {
   6.612              handle = MH.filterArguments(handle, 0, ALLOCATE);
   6.613 @@ -958,12 +742,12 @@
   6.614          MethodHandle boundHandle;
   6.615          MethodHandle guard = null;
   6.616  
   6.617 -        if (hasCalleeParameter()) {
   6.618 +        if (data.needsCallee()) {
   6.619              final MethodHandle callHandle = getBestSpecializedInvokeHandle(type);
   6.620  
   6.621              if(NashornCallSiteDescriptor.isScope(desc)) {
   6.622                  // (this, callee, args...) => (callee, args...) => (callee, [this], args...)
   6.623 -                boundHandle = MH.bindTo(callHandle, isNonStrictFunction() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
   6.624 +                boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
   6.625                  boundHandle = MH.dropArguments(boundHandle, 1, Object.class);
   6.626              } else {
   6.627                  // (this, callee, args...) permute => (callee, this, args...) which is what we get in
   6.628 @@ -980,7 +764,7 @@
   6.629                  // For non-strict functions, check whether this-object is primitive type.
   6.630                  // If so add a to-object-wrapper argument filter.
   6.631                  // Else install a guard that will trigger a relink when the argument becomes primitive.
   6.632 -                if (isNonStrictFunction()) {
   6.633 +                if (needsWrappedThis()) {
   6.634                      if (isPrimitiveThis(request.getArguments()[1])) {
   6.635                          boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER);
   6.636                      } else {
   6.637 @@ -992,7 +776,7 @@
   6.638              final MethodHandle callHandle = getBestSpecializedInvokeHandle(type.dropParameterTypes(0, 1));
   6.639  
   6.640              if(NashornCallSiteDescriptor.isScope(desc)) {
   6.641 -                boundHandle = MH.bindTo(callHandle, isNonStrictFunction() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
   6.642 +                boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
   6.643                  boundHandle = MH.dropArguments(boundHandle, 0, Object.class, Object.class);
   6.644              } else {
   6.645                  boundHandle = MH.dropArguments(callHandle, 0, Object.class);
   6.646 @@ -1012,13 +796,13 @@
   6.647          MethodHandle methodHandle = getBestSpecializedInvokeHandle(type);
   6.648  
   6.649          if (bindName != null) {
   6.650 -            if (hasCalleeParameter()) {
   6.651 +            if (data.needsCallee()) {
   6.652                  methodHandle = MH.insertArguments(methodHandle, 1, this, bindName);
   6.653              } else {
   6.654                  methodHandle = MH.insertArguments(methodHandle, 1, bindName);
   6.655              }
   6.656          } else {
   6.657 -            if (hasCalleeParameter()) {
   6.658 +            if (data.needsCallee()) {
   6.659                  methodHandle = MH.insertArguments(methodHandle, 1, this);
   6.660              }
   6.661          }
   6.662 @@ -1034,7 +818,7 @@
   6.663       * @return the converted this object
   6.664       */
   6.665      protected Object convertThisObject(final Object thiz) {
   6.666 -        if (!(thiz instanceof ScriptObject) && isNonStrictFunction()) {
   6.667 +        if (!(thiz instanceof ScriptObject) && needsWrappedThis()) {
   6.668              if (JSType.nullOrUndefined(thiz)) {
   6.669                  return Context.getGlobalTrusted();
   6.670              }
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Wed Feb 06 10:31:58 2013 +0100
     7.3 @@ -0,0 +1,442 @@
     7.4 +/*
     7.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     7.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     7.7 + *
     7.8 + * This code is free software; you can redistribute it and/or modify it
     7.9 + * under the terms of the GNU General Public License version 2 only, as
    7.10 + * published by the Free Software Foundation.  Oracle designates this
    7.11 + * particular file as subject to the "Classpath" exception as provided
    7.12 + * by Oracle in the LICENSE file that accompanied this code.
    7.13 + *
    7.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    7.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    7.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    7.17 + * version 2 for more details (a copy is included in the LICENSE file that
    7.18 + * accompanied this code).
    7.19 + *
    7.20 + * You should have received a copy of the GNU General Public License version
    7.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    7.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    7.23 + *
    7.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    7.25 + * or visit www.oracle.com if you need additional information or have any
    7.26 + * questions.
    7.27 + */
    7.28 +
    7.29 +package jdk.nashorn.internal.runtime;
    7.30 +
    7.31 +import jdk.nashorn.internal.ir.FunctionNode;
    7.32 +import jdk.nashorn.internal.parser.Token;
    7.33 +import jdk.nashorn.internal.parser.TokenType;
    7.34 +
    7.35 +import java.lang.invoke.MethodHandle;
    7.36 +import java.lang.invoke.MethodType;
    7.37 +
    7.38 +import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
    7.39 +
    7.40 +/**
    7.41 + * A container for data needed to instantiate a specific {@link ScriptFunction} at runtime.
    7.42 + * Instances of this class are created during codegen and stored in script classes'
    7.43 + * constants array to reduce function instantiation overhead during runtime.
    7.44 + */
    7.45 +public class ScriptFunctionData {
    7.46 +
    7.47 +    // per-function object flags
    7.48 +    private static final int IS_STRICT  = 0b0000_0001;
    7.49 +    private static final int IS_BUILTIN = 0b0000_0010;
    7.50 +    private static final int HAS_CALLEE = 0b0000_0100;
    7.51 +    private static final int IS_VARARGS = 0b0000_1000;
    7.52 +
    7.53 +    /** Name of the function or "" */
    7.54 +    private final String name;
    7.55 +    /** Source of this function, or null */
    7.56 +    private final Source source;
    7.57 +    /** Map for new instance constructor */
    7.58 +    private PropertyMap  allocatorMap;
    7.59 +    /** Start position and length in source */
    7.60 +    private final long   token;
    7.61 +    /** Number of expected arguments, either taken from FunctionNode or calculated from method handle signature*/
    7.62 +    private int          arity;
    7.63 +    /** Does this function need a callee argument? */
    7.64 +    private final int    flags;
    7.65 +
    7.66 +    /** Reference to code for this method. */
    7.67 +    private MethodHandle invoker;
    7.68 +    /** Reference to code for this method when called to create "new" object */
    7.69 +    private MethodHandle constructor;
    7.70 +    /** Constructor to create a new instance. */
    7.71 +    private MethodHandle allocator;
    7.72 +    /** Generic invoker to used in {@link ScriptFunction#invoke(Object, Object...)}. */
    7.73 +    private MethodHandle genericInvoker;
    7.74 +    /** Generic constructor used in {@link ScriptFunction#construct(Object, Object...)}. */
    7.75 +    private MethodHandle genericConstructor;
    7.76 +    /** Specializations - see @SpecializedFunction */
    7.77 +    private MethodHandle[] invokeSpecializations;
    7.78 +    /** Specializations - see @SpecializedFunction */
    7.79 +    private MethodHandle[] constructSpecializations;
    7.80 +
    7.81 +    /**
    7.82 +     * Constructor
    7.83 +     * @param fn the function node
    7.84 +     * @param allocatorMap the allocator property map
    7.85 +     */
    7.86 +    public ScriptFunctionData(final FunctionNode fn, final PropertyMap allocatorMap) {
    7.87 +
    7.88 +        final long firstToken = fn.getFirstToken();
    7.89 +        final long lastToken  = fn.getLastToken();
    7.90 +        final int  position   = Token.descPosition(firstToken);
    7.91 +        final int  length     = Token.descPosition(lastToken) - position + Token.descLength(lastToken);
    7.92 +
    7.93 +        this.name         = fn.isAnonymous() ? "" : fn.getIdent().getName();
    7.94 +        this.source       = fn.getSource();
    7.95 +        this.allocatorMap = allocatorMap;
    7.96 +        this.token        = Token.toDesc(TokenType.FUNCTION, position, length);
    7.97 +        this.arity        = fn.getParameters().size();
    7.98 +        this.flags        = makeFlags(fn.needsCallee(), fn.isVarArg(), fn.isStrictMode(), false);
    7.99 +    }
   7.100 +
   7.101 +    /**
   7.102 +     * Constructor
   7.103 +     * @param name the function name
   7.104 +     * @param methodHandle the method handle
   7.105 +     * @param specs array of specialized method handles
   7.106 +     * @param strict strict flag
   7.107 +     * @param builtin builtin flag
   7.108 +     */
   7.109 +    public ScriptFunctionData(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict, final boolean builtin) {
   7.110 +        this.name        = name;
   7.111 +        this.source      = null;
   7.112 +        this.token       = 0;
   7.113 +
   7.114 +        final MethodType type     = methodHandle.type();
   7.115 +        final int paramCount      = type.parameterCount();
   7.116 +        final boolean isVarArg    = type.parameterType(paramCount - 1).isArray();
   7.117 +        final boolean needsCallee = needsCallee(methodHandle);
   7.118 +
   7.119 +        this.flags = makeFlags(needsCallee, isVarArg, strict, builtin);
   7.120 +        this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
   7.121 +
   7.122 +        if (needsCallee && !isVarArg) {
   7.123 +            this.arity--;
   7.124 +        }
   7.125 +
   7.126 +        if (isConstructor(methodHandle)) {
   7.127 +            if (!isVarArg) {
   7.128 +                this.arity--;    // drop the boolean flag for arity
   7.129 +            }
   7.130 +            /*
   7.131 +             * We insert a boolean argument to tell if the method was invoked as
   7.132 +             * constructor or not if the method handle's first argument is boolean.
   7.133 +             */
   7.134 +            this.invoker     = MH.insertArguments(methodHandle, 0, false);
   7.135 +            this.constructor = MH.insertArguments(methodHandle, 0, true);
   7.136 +
   7.137 +            if (specs != null) {
   7.138 +                this.invokeSpecializations    = new MethodHandle[specs.length];
   7.139 +                this.constructSpecializations = new MethodHandle[specs.length];
   7.140 +                for (int i = 0; i < specs.length; i++) {
   7.141 +                    this.invokeSpecializations[i]    = MH.insertArguments(specs[i], 0, false);
   7.142 +                    this.constructSpecializations[i] = MH.insertArguments(specs[i], 0, true);
   7.143 +                }
   7.144 +            }
   7.145 +        } else {
   7.146 +            this.invoker                  = methodHandle;
   7.147 +            this.constructor              = methodHandle;
   7.148 +            this.invokeSpecializations    = specs;
   7.149 +            this.constructSpecializations = specs;
   7.150 +        }
   7.151 +    }
   7.152 +
   7.153 +    /**
   7.154 +     * Get the arity of the function.
   7.155 +     * @return the arity
   7.156 +     */
   7.157 +    public int getArity() {
   7.158 +        return arity;
   7.159 +    }
   7.160 +
   7.161 +    /**
   7.162 +     * Set the arity of the function.
   7.163 +     * @param arity the arity
   7.164 +     */
   7.165 +    public void setArity(int arity) {
   7.166 +        this.arity = arity;
   7.167 +    }
   7.168 +
   7.169 +    /**
   7.170 +     * Get the function name.
   7.171 +     * @return function name
   7.172 +     */
   7.173 +    public String getName() {
   7.174 +        return name;
   7.175 +    }
   7.176 +
   7.177 +    /**
   7.178 +     * Get the source of the function.
   7.179 +     * @return the source
   7.180 +     */
   7.181 +    public Source getSource() {
   7.182 +        return source;
   7.183 +    }
   7.184 +
   7.185 +    /**
   7.186 +     * Get this function as a String containing its source code. If no source code
   7.187 +     * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
   7.188 +     * @return string representation of this function's source
   7.189 +     */
   7.190 +    public String toSource() {
   7.191 +        if (source != null && token != 0) {
   7.192 +            return source.getString(Token.descPosition(token), Token.descLength(token));
   7.193 +        }
   7.194 +
   7.195 +        return "function " + (name == null ? "" : name) + "() { [native code] }";
   7.196 +    }
   7.197 +
   7.198 +    @Override
   7.199 +    public String toString() {
   7.200 +        final StringBuilder sb = new StringBuilder();
   7.201 +
   7.202 +        sb.append(super.toString())
   7.203 +                .append(" [ ")
   7.204 +                .append(invoker)
   7.205 +                .append(", ")
   7.206 +                .append((name == null || name.isEmpty()) ? "<anonymous>" : name);
   7.207 +
   7.208 +        if (source != null) {
   7.209 +            sb.append(" @ ")
   7.210 +                    .append(source.getName())
   7.211 +                    .append(':')
   7.212 +                    .append(source.getLine(Token.descPosition(token)));
   7.213 +        }
   7.214 +        sb.append(" ]");
   7.215 +
   7.216 +        return sb.toString();
   7.217 +    }
   7.218 +
   7.219 +    /**
   7.220 +     * Get the allocator property map.
   7.221 +     * @return the allocator map
   7.222 +     */
   7.223 +    public PropertyMap getAllocatorMap() {
   7.224 +        return allocatorMap;
   7.225 +    }
   7.226 +
   7.227 +    /**
   7.228 +     * Get the function's parse token.
   7.229 +     * @return the token
   7.230 +     */
   7.231 +    public long getToken() {
   7.232 +        return token;
   7.233 +    }
   7.234 +
   7.235 +    /**
   7.236 +     * Returns true if the function needs a callee argument.
   7.237 +     * @return the needsCallee flag
   7.238 +     */
   7.239 +    public boolean needsCallee() {
   7.240 +        return (flags & HAS_CALLEE) != 0;
   7.241 +    }
   7.242 +
   7.243 +    /**
   7.244 +     * Returns true if this is a strict-mode function.
   7.245 +     * @return the strict flag
   7.246 +     */
   7.247 +    public boolean isStrict() {
   7.248 +        return (flags & IS_STRICT) != 0;
   7.249 +    }
   7.250 +
   7.251 +    /**
   7.252 +     * Returns true if this is a built-in function.
   7.253 +     * @return the built-in flag
   7.254 +     */
   7.255 +    public boolean isBuiltin() {
   7.256 +        return (flags & IS_BUILTIN) != 0;
   7.257 +    }
   7.258 +
   7.259 +    /**
   7.260 +     * Returns true if this is a var-arg function.
   7.261 +     * @return the var-arg flag
   7.262 +     */
   7.263 +    public boolean isVarArg() {
   7.264 +        return (flags & IS_VARARGS) != 0;
   7.265 +    }
   7.266 +
   7.267 +    /**
   7.268 +     * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
   7.269 +     * according to ECMA 10.4.3.
   7.270 +     * @return true if this argument must be an object
   7.271 +     */
   7.272 +    public boolean needsWrappedThis() {
   7.273 +        return (flags & (IS_STRICT | IS_BUILTIN)) == 0;
   7.274 +    }
   7.275 +
   7.276 +    /**
   7.277 +     * Get the method handle used to invoke this function.
   7.278 +     * @return the invoke handle
   7.279 +     */
   7.280 +    public MethodHandle getInvoker() {
   7.281 +        return invoker;
   7.282 +    }
   7.283 +
   7.284 +    /**
   7.285 +     * Get the method handle used to invoke this function as a constructor.
   7.286 +     * @return the constructor handle
   7.287 +     */
   7.288 +    public MethodHandle getConstructor() {
   7.289 +        return constructor;
   7.290 +    }
   7.291 +
   7.292 +    /**
   7.293 +     * Set the constructor method handle.
   7.294 +     * @param constructor the constructor handle
   7.295 +     */
   7.296 +    public void setConstructor(MethodHandle constructor) {
   7.297 +        this.constructor = constructor;
   7.298 +        this.constructSpecializations = null;
   7.299 +    }
   7.300 +
   7.301 +    /**
   7.302 +     * Get the method handle used to allocate a new object for this constructor.
   7.303 +     * @return the allocator handle
   7.304 +     */
   7.305 +    public MethodHandle getAllocator() {
   7.306 +        return allocator;
   7.307 +    }
   7.308 +
   7.309 +    /**
   7.310 +     * Get an adapted version of the invoker handle that only uses {@code Object} as parameter and return types.
   7.311 +     * @return the generic invoke handle
   7.312 +     */
   7.313 +    public MethodHandle getGenericInvoker() {
   7.314 +        if (genericInvoker == null) {
   7.315 +            assert invoker != null : "invoker is null";
   7.316 +            genericInvoker = adaptMethodType(invoker);
   7.317 +        }
   7.318 +        return genericInvoker;
   7.319 +    }
   7.320 +
   7.321 +    /**
   7.322 +     * Get an adapted version of the constructor handle that only uses {@code Object} as parameter and return types.
   7.323 +     * @return the generic constructor handle
   7.324 +     */
   7.325 +    public MethodHandle getGenericConstructor() {
   7.326 +        if (genericConstructor == null) {
   7.327 +            assert constructor != null : "constructor is null";
   7.328 +            genericConstructor = adaptMethodType(constructor);
   7.329 +        }
   7.330 +        return genericConstructor;
   7.331 +    }
   7.332 +
   7.333 +    /**
   7.334 +     * Get the specialized invoke handles for this function.
   7.335 +     * @return array of specialized invoke handles
   7.336 +     */
   7.337 +    public MethodHandle[] getInvokeSpecializations() {
   7.338 +        return invokeSpecializations;
   7.339 +    }
   7.340 +
   7.341 +    /**
   7.342 +     * Get the specialized construct handles for this function.
   7.343 +     * @return array of specialized construct handles
   7.344 +     */
   7.345 +    public MethodHandle[] getConstructSpecializations() {
   7.346 +        return constructSpecializations;
   7.347 +    }
   7.348 +
   7.349 +    /**
   7.350 +     * Set the method handles for this function.
   7.351 +     * @param invoker the invoker handle
   7.352 +     * @param allocator the allocator handle
   7.353 +     */
   7.354 +    public void setMethodHandles(MethodHandle invoker, MethodHandle allocator) {
   7.355 +        // We can't make method handle fields final because they're not available during codegen
   7.356 +        // and they're set when first called, so we enforce set-once here.
   7.357 +        if (this.invoker == null) {
   7.358 +            this.invoker     = invoker;
   7.359 +            this.constructor = invoker;
   7.360 +            this.allocator   = allocator;
   7.361 +        }
   7.362 +    }
   7.363 +
   7.364 +    /**
   7.365 +     * Convert boolean flags to int.
   7.366 +     * @param needsCallee needs-callee flag
   7.367 +     * @param isVarArg var-arg flag
   7.368 +     * @param isStrict strict flag
   7.369 +     * @param isBuiltin builtin flag
   7.370 +     * @return int flags
   7.371 +     */
   7.372 +    private static int makeFlags(final boolean needsCallee, final boolean isVarArg, final boolean isStrict, final boolean isBuiltin) {
   7.373 +        int flags = 0;
   7.374 +        if (needsCallee) {
   7.375 +            flags |= HAS_CALLEE;
   7.376 +        }
   7.377 +        if (isVarArg) {
   7.378 +            flags |= IS_VARARGS;
   7.379 +        }
   7.380 +        if (isStrict) {
   7.381 +            flags |= IS_STRICT;
   7.382 +        }
   7.383 +        if (isBuiltin) {
   7.384 +            flags |= IS_BUILTIN;
   7.385 +        }
   7.386 +        return flags;
   7.387 +    }
   7.388 +
   7.389 +    /**
   7.390 +     * Test if a methodHandle refers to a constructor.
   7.391 +     * @param methodHandle MethodHandle to test.
   7.392 +     * @return True if method is a constructor.
   7.393 +     */
   7.394 +    private static boolean isConstructor(final MethodHandle methodHandle) {
   7.395 +        return methodHandle.type().parameterCount() >= 1 && methodHandle.type().parameterType(0) == boolean.class;
   7.396 +    }
   7.397 +
   7.398 +    /**
   7.399 +     * Heuristic to figure out if the method handle has a callee argument. If it's type is either
   7.400 +     * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
   7.401 +     * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
   7.402 +     * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
   7.403 +     * they also always receive a callee.
   7.404 +     * @param methodHandle the examined method handle
   7.405 +     * @return true if the method handle expects a callee, false otherwise
   7.406 +     */
   7.407 +    private static boolean needsCallee(MethodHandle methodHandle) {
   7.408 +        final MethodType type = methodHandle.type();
   7.409 +        final int len = type.parameterCount();
   7.410 +        if(len == 0) {
   7.411 +            return false;
   7.412 +        }
   7.413 +        if(type.parameterType(0) == boolean.class) {
   7.414 +            return len > 2 && type.parameterType(2) == ScriptFunction.class;
   7.415 +        }
   7.416 +        return len > 1 && type.parameterType(1) == ScriptFunction.class;
   7.417 +    }
   7.418 +
   7.419 +    /**
   7.420 +     * Takes a method handle, and returns a potentially different method handle that can be used in
   7.421 +     * {@link ScriptFunction#invoke(Object, Object...)} or {@link ScriptFunction#construct(Object, Object...)}.
   7.422 +     * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
   7.423 +     * {@code Object} as well, except for the following ones:
   7.424 +     * <ul>
   7.425 +     *   <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
   7.426 +     *   <li>the second argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
   7.427 +     *   (callee) as an argument</li>
   7.428 +     * </ul>
   7.429 +     *
   7.430 +     * @param handle the original method handle
   7.431 +     * @return the new handle, conforming to the rules above.
   7.432 +     */
   7.433 +    private MethodHandle adaptMethodType(final MethodHandle handle) {
   7.434 +        final MethodType type = handle.type();
   7.435 +        MethodType newType = type.generic();
   7.436 +        if (isVarArg()) {
   7.437 +            newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
   7.438 +        }
   7.439 +        if (needsCallee()) {
   7.440 +            newType = newType.changeParameterType(1, ScriptFunction.class);
   7.441 +        }
   7.442 +        return type.equals(newType) ? handle : handle.asType(newType);
   7.443 +    }
   7.444 +
   7.445 +}

mercurial