Diff
Modified: trunk/JSTests/ChangeLog (205847 => 205848)
--- trunk/JSTests/ChangeLog 2016-09-13 02:12:51 UTC (rev 205847)
+++ trunk/JSTests/ChangeLog 2016-09-13 02:57:37 UTC (rev 205848)
@@ -1,5 +1,23 @@
2016-09-12 Saam Barati <sbar...@apple.com>
+ Speed up Function.prototype.bind a bit by making it a builtin
+ https://bugs.webkit.org/show_bug.cgi?id=161879
+
+ Reviewed by Filip Pizlo.
+
+ * microbenchmarks/function-bind-inlining.js: Added.
+ (assert):
+ (test):
+ (test2):
+ (foo):
+ * microbenchmarks/function-bind-no-inlining.js: Added.
+ (assert):
+ (test):
+ (test2):
+ (foo):
+
+2016-09-12 Saam Barati <sbar...@apple.com>
+
HashMapImpl should take into account m_deleteCount in its load factor and it should be able to rehash the table to be smaller
https://bugs.webkit.org/show_bug.cgi?id=161640
Added: trunk/JSTests/microbenchmarks/function-bind-inlining.js (0 => 205848)
--- trunk/JSTests/microbenchmarks/function-bind-inlining.js (rev 0)
+++ trunk/JSTests/microbenchmarks/function-bind-inlining.js 2016-09-13 02:57:37 UTC (rev 205848)
@@ -0,0 +1,28 @@
+function assert(b) {
+ if (!b)
+ throw new Error("Bad")
+}
+noInline(assert);
+
+function test(f, v, c, d) {
+ return f.bind(v, c, d);
+}
+
+function test2(f, v) {
+ return f.bind(v);
+}
+
+function foo(a,b,c,d,e,f) { return this; }
+let thisValue = {};
+let start = Date.now();
+for (let i = 0; i < 1000000; i++) {
+ let f = test(foo, thisValue, 20, 30);
+ assert(f(foo, thisValue, 20, 30) === thisValue);
+}
+for (let i = 0; i < 1000000; i++) {
+ let f = test2(foo, thisValue);
+ assert(f(foo, thisValue, 20, 30) === thisValue);
+}
+const verbose = false;
+if (verbose)
+ print(Date.now() - start);
Added: trunk/JSTests/microbenchmarks/function-bind-no-inlining.js (0 => 205848)
--- trunk/JSTests/microbenchmarks/function-bind-no-inlining.js (rev 0)
+++ trunk/JSTests/microbenchmarks/function-bind-no-inlining.js 2016-09-13 02:57:37 UTC (rev 205848)
@@ -0,0 +1,31 @@
+
+function assert(b) {
+ if (!b)
+ throw new Error("Bad")
+}
+noInline(assert);
+
+function test(f, v, c, d) {
+ return f.bind(v, c, d);
+}
+noInline(test);
+
+function test2(f, v) {
+ return f.bind(v);
+}
+noInline(test);
+
+function foo(a,b,c,d,e,f) { return this; }
+let thisValue = {};
+let start = Date.now();
+for (let i = 0; i < 1000000; i++) {
+ let f = test(foo, thisValue, 20, 30);
+ assert(f(foo, thisValue, 20, 30) === thisValue);
+}
+for (let i = 0; i < 1000000; i++) {
+ let f = test2(foo, thisValue);
+ assert(f(foo, thisValue, 20, 30) === thisValue);
+}
+const verbose = false;
+if (verbose)
+ print(Date.now() - start);
Modified: trunk/LayoutTests/ChangeLog (205847 => 205848)
--- trunk/LayoutTests/ChangeLog 2016-09-13 02:12:51 UTC (rev 205847)
+++ trunk/LayoutTests/ChangeLog 2016-09-13 02:57:37 UTC (rev 205848)
@@ -1,3 +1,12 @@
+2016-09-12 Saam Barati <sbar...@apple.com>
+
+ Speed up Function.prototype.bind a bit by making it a builtin
+ https://bugs.webkit.org/show_bug.cgi?id=161879
+
+ Reviewed by Filip Pizlo.
+
+ * js/dom/function-bind-expected.txt:
+
2016-09-12 Nan Wang <n_w...@apple.com>
AX: Crash at WebCore::Range::compareBoundaryPoints(WebCore::Range::CompareHow, WebCore::Range const&, int&) const + 23
Modified: trunk/LayoutTests/js/dom/function-bind-expected.txt (205847 => 205848)
--- trunk/LayoutTests/js/dom/function-bind-expected.txt 2016-09-13 02:12:51 UTC (rev 205847)
+++ trunk/LayoutTests/js/dom/function-bind-expected.txt 2016-09-13 02:57:37 UTC (rev 205848)
@@ -23,7 +23,7 @@
PASS "prototype" in F is true
PASS "prototype" in G is false
PASS "prototype" in H is false
-PASS Function.bind.call(undefined) threw exception TypeError: Type error.
+PASS Function.bind.call(undefined) threw exception TypeError: |this| is not a function inside Function.prototype.bind.
PASS abcAt(1) is "b"
PASS new abcAt(1) threw exception TypeError: function is not a constructor (evaluating 'new abcAt(1)').
PASS boundFunctionPrototypeAccessed is false
Modified: trunk/Source/_javascript_Core/builtins/BuiltinNames.h (205847 => 205848)
--- trunk/Source/_javascript_Core/builtins/BuiltinNames.h 2016-09-13 02:12:51 UTC (rev 205847)
+++ trunk/Source/_javascript_Core/builtins/BuiltinNames.h 2016-09-13 02:57:37 UTC (rev 205848)
@@ -160,7 +160,9 @@
macro(regExpTestFast) \
macro(stringIncludesInternal) \
macro(stringSplitFast) \
- macro(stringSubstrInternal)
+ macro(stringSubstrInternal) \
+ macro(makeBoundFunction) \
+ macro(hasOwnLengthProperty) \
#define INITIALIZE_PRIVATE_TO_PUBLIC_ENTRY(name) m_privateToPublicMap.add(m_##name##PrivateName.impl(), &m_##name);
Modified: trunk/Source/_javascript_Core/builtins/FunctionPrototype.js (205847 => 205848)
--- trunk/Source/_javascript_Core/builtins/FunctionPrototype.js 2016-09-13 02:12:51 UTC (rev 205847)
+++ trunk/Source/_javascript_Core/builtins/FunctionPrototype.js 2016-09-13 02:57:37 UTC (rev 205848)
@@ -56,3 +56,40 @@
let target = this.prototype;
return @instanceOf(value, target);
}
+
+function bind(thisValue)
+{
+ "use strict";
+
+ let target = this;
+ if (typeof target !== "function")
+ throw new @TypeError("|this| is not a function inside Function.prototype.bind");
+
+ let argumentCount = arguments.length;
+ let boundArgs = null;
+ let numBoundArgs = 0;
+ if (argumentCount > 1) {
+ numBoundArgs = argumentCount - 1;
+ boundArgs = @newArrayWithSize(numBoundArgs);
+ for (let i = 0; i < numBoundArgs; i++)
+ boundArgs[i] = arguments[i + 1];
+ }
+
+ let length = 0;
+ if (@hasOwnLengthProperty(target)) {
+ let lengthValue = target.length;
+ if (typeof lengthValue === "number") {
+ lengthValue = lengthValue | 0;
+ // Note that we only care about positive lengthValues, however, this comparision
+ // against numBoundArgs suffices to prove we're not a negative number.
+ if (lengthValue > numBoundArgs)
+ length = lengthValue - numBoundArgs;
+ }
+ }
+
+ let name = target.name;
+ if (typeof name !== "string")
+ name = "";
+
+ return @makeBoundFunction(target, arguments[0], boundArgs, length, name);
+}
Modified: trunk/Source/_javascript_Core/runtime/FunctionPrototype.cpp (205847 => 205848)
--- trunk/Source/_javascript_Core/runtime/FunctionPrototype.cpp 2016-09-13 02:12:51 UTC (rev 205847)
+++ trunk/Source/_javascript_Core/runtime/FunctionPrototype.cpp 2016-09-13 02:57:37 UTC (rev 205848)
@@ -40,7 +40,6 @@
const ClassInfo FunctionPrototype::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(FunctionPrototype) };
static EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(ExecState*);
-static EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState*);
FunctionPrototype::FunctionPrototype(VM& vm, Structure* structure)
: InternalFunction(vm, structure)
@@ -63,9 +62,7 @@
*applyFunction = putDirectBuiltinFunctionWithoutTransition(vm, globalObject, vm.propertyNames->builtinNames().applyPublicName(), functionPrototypeApplyCodeGenerator(vm), DontEnum);
*callFunction = putDirectBuiltinFunctionWithoutTransition(vm, globalObject, vm.propertyNames->builtinNames().callPublicName(), functionPrototypeCallCodeGenerator(vm), DontEnum);
*hasInstanceSymbolFunction = putDirectBuiltinFunction(vm, globalObject, vm.propertyNames->hasInstanceSymbol, functionPrototypeSymbolHasInstanceCodeGenerator(vm), DontDelete | ReadOnly | DontEnum);
-
- JSFunction* bindFunction = JSFunction::create(vm, globalObject, 1, vm.propertyNames->bind.string(), functionProtoFuncBind);
- putDirectWithoutTransition(vm, vm.propertyNames->bind, bindFunction, DontEnum);
+ putDirectBuiltinFunctionWithoutTransition(vm, globalObject, vm.propertyNames->bind, functionPrototypeBindCodeGenerator(vm), DontEnum);
}
static EncodedJSValue JSC_HOST_CALL callFunctionPrototype(ExecState*)
@@ -124,58 +121,4 @@
return throwVMTypeError(exec, scope);
}
-// 15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, ...]])
-EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState* exec)
-{
- VM& vm = exec->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
- JSGlobalObject* globalObject = exec->callee()->globalObject();
-
- // Let Target be the this value.
- JSValue target = exec->thisValue();
-
- // If IsCallable(Target) is false, throw a TypeError exception.
- CallData callData;
- CallType callType = getCallData(target, callData);
- if (callType == CallType::None)
- return throwVMTypeError(exec, scope);
- // Primitive values are not callable.
- ASSERT(target.isObject());
- JSObject* targetObject = asObject(target);
-
- // Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order.
- size_t numBoundArgs = exec->argumentCount() > 1 ? exec->argumentCount() - 1 : 0;
- JSArray* boundArgs;
- if (numBoundArgs) {
- boundArgs = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), numBoundArgs);
- if (!boundArgs)
- return JSValue::encode(throwOutOfMemoryError(exec, scope));
-
- for (size_t i = 0; i < numBoundArgs; ++i)
- boundArgs->initializeIndex(vm, i, exec->argument(i + 1));
- } else
- boundArgs = nullptr;
-
- // If the [[Class]] internal property of Target is "Function", then ...
- // Else set the length own property of F to 0.
- unsigned length = 0;
- if (targetObject->hasOwnProperty(exec, exec->propertyNames().length)) {
- if (UNLIKELY(scope.exception()))
- return JSValue::encode(jsUndefined());
-
- // a. Let L be the length property of Target minus the length of A.
- // b. Set the length own property of F to either 0 or L, whichever is larger.
- JSValue lengthValue = target.get(exec, exec->propertyNames().length);
- if (lengthValue.isNumber()) {
- unsigned targetLength = (unsigned)lengthValue.asNumber();
- if (targetLength > numBoundArgs)
- length = targetLength - numBoundArgs;
- }
- }
-
- JSValue nameProp = target.get(exec, exec->propertyNames().name);
- JSString* name = nameProp.isString() ? nameProp.toString(exec) : jsEmptyString(exec);
- return JSValue::encode(JSBoundFunction::create(vm, exec, globalObject, targetObject, exec->argument(0), boundArgs, length, name->value(exec)));
-}
-
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (205847 => 205848)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2016-09-13 02:12:51 UTC (rev 205847)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2016-09-13 02:57:37 UTC (rev 205848)
@@ -199,6 +199,28 @@
return ConsoleObject::create(vm, global, ConsoleObject::createStructure(vm, global, constructEmptyObject(global->globalExec())));
}
+static EncodedJSValue JSC_HOST_CALL makeBoundFunction(ExecState* exec)
+{
+ VM& vm = exec->vm();
+ JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+
+ JSObject* target = asObject(exec->uncheckedArgument(0));
+ JSValue boundThis = exec->uncheckedArgument(1);
+ JSValue boundArgs = exec->uncheckedArgument(2);
+ JSValue length = exec->uncheckedArgument(3);
+ JSString* name = asString(exec->uncheckedArgument(4));
+
+ return JSValue::encode(JSBoundFunction::create(
+ vm, exec, globalObject, target, boundThis, boundArgs.isCell() ? jsCast<JSArray*>(boundArgs) : nullptr, length.asInt32(), name->value(exec)));
+}
+
+static EncodedJSValue JSC_HOST_CALL hasOwnLengthProperty(ExecState* exec)
+{
+ VM& vm = exec->vm();
+ JSObject* target = asObject(exec->uncheckedArgument(0));
+ return JSValue::encode(jsBoolean(target->hasOwnProperty(exec, vm.propertyNames->length)));
+}
+
} // namespace JSC
#include "JSGlobalObject.lut.h"
@@ -768,6 +790,10 @@
GlobalPropertyInfo(vm.propertyNames->builtinNames().stringIncludesInternalPrivateName(), JSFunction::create(vm, this, 1, String(), builtinStringIncludesInternal), DontEnum | DontDelete | ReadOnly),
GlobalPropertyInfo(vm.propertyNames->builtinNames().stringSplitFastPrivateName(), JSFunction::create(vm, this, 2, String(), stringProtoFuncSplitFast), DontEnum | DontDelete | ReadOnly),
GlobalPropertyInfo(vm.propertyNames->builtinNames().stringSubstrInternalPrivateName(), JSFunction::create(vm, this, 2, String(), builtinStringSubstrInternal), DontEnum | DontDelete | ReadOnly),
+
+ // Function prototype helpers.
+ GlobalPropertyInfo(vm.propertyNames->builtinNames().makeBoundFunctionPrivateName(), JSFunction::create(vm, this, 5, String(), makeBoundFunction), DontEnum | DontDelete | ReadOnly),
+ GlobalPropertyInfo(vm.propertyNames->builtinNames().hasOwnLengthPropertyPrivateName(), JSFunction::create(vm, this, 1, String(), hasOwnLengthProperty), DontEnum | DontDelete | ReadOnly),
};
addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals));