Wed, 06 Feb 2013 10:31:58 +0100
8007273: Creation of ScriptFunctions can be refactored
Reviewed-by: lagergren, attila
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 +}