Title: [204321] trunk
Revision
204321
Author
sbar...@apple.com
Date
2016-08-09 20:39:05 -0700 (Tue, 09 Aug 2016)

Log Message

JSBoundFunction should lazily generate its name string
https://bugs.webkit.org/show_bug.cgi?id=160678
<rdar://problem/27043194>

Reviewed by Mark Lam.

JSTests:

* stress/bound-function-lazy-name-generation.js: Added.
(assert):
(test.let.f):
(test.f):
(test):

Source/_javascript_Core:

We were eagerly allocating the BoundFunction's 'name' string
by prepending the "bound " prefix. This patch makes the 'name'
string creation lazy like we do with ordinary JSFunctions.

This is a 25% speedup on the microbenchmark I added that measures
bound function creation speed. Hopefully this also helps us recover
from a 1% Speedometer regression that was introduced in the original
bound function "bound " prefixing patch.

* runtime/JSBoundFunction.cpp:
(JSC::JSBoundFunction::create):
(JSC::JSBoundFunction::JSBoundFunction):
(JSC::JSBoundFunction::finishCreation):
* runtime/JSBoundFunction.h:
* runtime/JSFunction.cpp:
(JSC::JSFunction::finishCreation):
(JSC::JSFunction::getOwnPropertySlot):
(JSC::JSFunction::getOwnNonIndexPropertyNames):
(JSC::JSFunction::put):
(JSC::JSFunction::deleteProperty):
(JSC::JSFunction::defineOwnProperty):
(JSC::JSFunction::reifyLazyPropertyIfNeeded):
(JSC::JSFunction::reifyBoundNameIfNeeded):
* runtime/JSFunction.h:

LayoutTests:

* js/regress/bound-function-construction-performance-expected.txt: Added.
* js/regress/bound-function-construction-performance.html: Added.
* js/regress/script-tests/bound-function-construction-performance.js: Added.
(makeBoundFunc):
(foo.f):
(foo):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (204320 => 204321)


--- trunk/JSTests/ChangeLog	2016-08-10 01:31:17 UTC (rev 204320)
+++ trunk/JSTests/ChangeLog	2016-08-10 03:39:05 UTC (rev 204321)
@@ -1,3 +1,17 @@
+2016-08-09  Saam Barati  <sbar...@apple.com>
+
+        JSBoundFunction should lazily generate its name string
+        https://bugs.webkit.org/show_bug.cgi?id=160678
+        <rdar://problem/27043194>
+
+        Reviewed by Mark Lam.
+
+        * stress/bound-function-lazy-name-generation.js: Added.
+        (assert):
+        (test.let.f):
+        (test.f):
+        (test):
+
 2016-08-08  Mark Lam  <mark....@apple.com>
 
         ASSERTION FAILED: hasInlineStorage() in JSFinalObject::visitChildren().

Added: trunk/JSTests/stress/bound-function-lazy-name-generation.js (0 => 204321)


--- trunk/JSTests/stress/bound-function-lazy-name-generation.js	                        (rev 0)
+++ trunk/JSTests/stress/bound-function-lazy-name-generation.js	2016-08-10 03:39:05 UTC (rev 204321)
@@ -0,0 +1,23 @@
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion!");
+}
+
+function test() {
+    let f = function foo() { }.bind({});
+    assert(f.name === "bound foo");
+
+    f = function () { }.bind({});
+    assert(f.name === "bound ");
+
+    f = function foo() { }.bind({});
+    assert(Reflect.ownKeys(f).includes("name"));
+    assert(f.name === "bound foo");
+    assert(Reflect.ownKeys(f).includes("name"));
+
+    f = function foo() { }.bind({});
+    assert(f.name === "bound foo");
+    assert(Reflect.ownKeys(f).includes("name"));
+}
+for (let i = 0; i < 10000; i++)
+    test();

Modified: trunk/LayoutTests/ChangeLog (204320 => 204321)


--- trunk/LayoutTests/ChangeLog	2016-08-10 01:31:17 UTC (rev 204320)
+++ trunk/LayoutTests/ChangeLog	2016-08-10 03:39:05 UTC (rev 204321)
@@ -1,3 +1,18 @@
+2016-08-09  Saam Barati  <sbar...@apple.com>
+
+        JSBoundFunction should lazily generate its name string
+        https://bugs.webkit.org/show_bug.cgi?id=160678
+        <rdar://problem/27043194>
+
+        Reviewed by Mark Lam.
+
+        * js/regress/bound-function-construction-performance-expected.txt: Added.
+        * js/regress/bound-function-construction-performance.html: Added.
+        * js/regress/script-tests/bound-function-construction-performance.js: Added.
+        (makeBoundFunc):
+        (foo.f):
+        (foo):
+
 2016-08-09  Chris Dumez  <cdu...@apple.com>
 
         CharacterData.data setter optimization is not spec-compliant and is observable

Modified: trunk/LayoutTests/TestExpectations (204320 => 204321)


--- trunk/LayoutTests/TestExpectations	2016-08-10 01:31:17 UTC (rev 204320)
+++ trunk/LayoutTests/TestExpectations	2016-08-10 03:39:05 UTC (rev 204321)
@@ -1012,6 +1012,9 @@
 
 [ Debug ] js/regress/misc-bugs-847389-jpeg2000.html [ Skip ]
 
+# This runs slowly in debug.
+[ Debug ] js/regress/bound-function-construction-performance.html [ Skip ]
+
 webkit.org/b/159678 http/tests/preload/single_download_preload_runner.html [ Timeout ]
 
 # This test is way too slow for debug.

Added: trunk/LayoutTests/js/regress/bound-function-construction-performance-expected.txt (0 => 204321)


--- trunk/LayoutTests/js/regress/bound-function-construction-performance-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/regress/bound-function-construction-performance-expected.txt	2016-08-10 03:39:05 UTC (rev 204321)
@@ -0,0 +1,10 @@
+JSRegress/bound-function-construction-performance
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/js/regress/bound-function-construction-performance.html (0 => 204321)


--- trunk/LayoutTests/js/regress/bound-function-construction-performance.html	                        (rev 0)
+++ trunk/LayoutTests/js/regress/bound-function-construction-performance.html	2016-08-10 03:39:05 UTC (rev 204321)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/js/regress/script-tests/bound-function-construction-performance.js (0 => 204321)


--- trunk/LayoutTests/js/regress/script-tests/bound-function-construction-performance.js	                        (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/bound-function-construction-performance.js	2016-08-10 03:39:05 UTC (rev 204321)
@@ -0,0 +1,12 @@
+function makeBoundFunc(f) {
+    return f.bind(f);
+}
+noInline(makeBoundFunc);
+
+function foo() {
+    function f() { }
+    for (let i = 0; i < 15000000; i++) {
+        makeBoundFunc(f); 
+    }
+}
+foo();

Modified: trunk/Source/_javascript_Core/ChangeLog (204320 => 204321)


--- trunk/Source/_javascript_Core/ChangeLog	2016-08-10 01:31:17 UTC (rev 204320)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-08-10 03:39:05 UTC (rev 204321)
@@ -1,3 +1,36 @@
+2016-08-09  Saam Barati  <sbar...@apple.com>
+
+        JSBoundFunction should lazily generate its name string
+        https://bugs.webkit.org/show_bug.cgi?id=160678
+        <rdar://problem/27043194>
+
+        Reviewed by Mark Lam.
+
+        We were eagerly allocating the BoundFunction's 'name' string
+        by prepending the "bound " prefix. This patch makes the 'name'
+        string creation lazy like we do with ordinary JSFunctions.
+
+        This is a 25% speedup on the microbenchmark I added that measures
+        bound function creation speed. Hopefully this also helps us recover
+        from a 1% Speedometer regression that was introduced in the original
+        bound function "bound " prefixing patch.
+
+        * runtime/JSBoundFunction.cpp:
+        (JSC::JSBoundFunction::create):
+        (JSC::JSBoundFunction::JSBoundFunction):
+        (JSC::JSBoundFunction::finishCreation):
+        * runtime/JSBoundFunction.h:
+        * runtime/JSFunction.cpp:
+        (JSC::JSFunction::finishCreation):
+        (JSC::JSFunction::getOwnPropertySlot):
+        (JSC::JSFunction::getOwnNonIndexPropertyNames):
+        (JSC::JSFunction::put):
+        (JSC::JSFunction::deleteProperty):
+        (JSC::JSFunction::defineOwnProperty):
+        (JSC::JSFunction::reifyLazyPropertyIfNeeded):
+        (JSC::JSFunction::reifyBoundNameIfNeeded):
+        * runtime/JSFunction.h:
+
 2016-08-09  George Ruan  <gr...@apple.com>
 
         Implement functionality of media capture on iOS

Modified: trunk/Source/_javascript_Core/runtime/JSBoundFunction.cpp (204320 => 204321)


--- trunk/Source/_javascript_Core/runtime/JSBoundFunction.cpp	2016-08-10 01:31:17 UTC (rev 204320)
+++ trunk/Source/_javascript_Core/runtime/JSBoundFunction.cpp	2016-08-10 03:39:05 UTC (rev 204321)
@@ -174,7 +174,7 @@
         return nullptr;
     JSBoundFunction* function = new (NotNull, allocateCell<JSBoundFunction>(vm.heap)) JSBoundFunction(vm, globalObject, structure, targetFunction, boundThis, boundArgs);
 
-    function->finishCreation(vm, executable, length, makeString("bound ", name));
+    function->finishCreation(vm, executable, length);
     return function;
 }
 
@@ -191,8 +191,9 @@
 {
 }
 
-void JSBoundFunction::finishCreation(VM& vm, NativeExecutable* executable, int length, const String& name)
+void JSBoundFunction::finishCreation(VM& vm, NativeExecutable* executable, int length)
 {
+    String name; // We lazily create our 'name' string property.
     Base::finishCreation(vm, executable, length, name);
     ASSERT(inherits(info()));
 

Modified: trunk/Source/_javascript_Core/runtime/JSBoundFunction.h (204320 => 204321)


--- trunk/Source/_javascript_Core/runtime/JSBoundFunction.h	2016-08-10 01:31:17 UTC (rev 204320)
+++ trunk/Source/_javascript_Core/runtime/JSBoundFunction.h	2016-08-10 03:39:05 UTC (rev 204321)
@@ -67,7 +67,7 @@
 private:
     JSBoundFunction(VM&, JSGlobalObject*, Structure*, JSObject* targetFunction, JSValue boundThis, JSArray* boundArgs);
     
-    void finishCreation(VM&, NativeExecutable*, int length, const String& name);
+    void finishCreation(VM&, NativeExecutable*, int length);
 
     WriteBarrier<JSObject> m_targetFunction;
     WriteBarrier<Unknown> m_boundThis;

Modified: trunk/Source/_javascript_Core/runtime/JSFunction.cpp (204320 => 204321)


--- trunk/Source/_javascript_Core/runtime/JSFunction.cpp	2016-08-10 01:31:17 UTC (rev 204320)
+++ trunk/Source/_javascript_Core/runtime/JSFunction.cpp	2016-08-10 03:39:05 UTC (rev 204321)
@@ -102,7 +102,9 @@
     Base::finishCreation(vm);
     ASSERT(inherits(info()));
     m_executable.set(vm, this, executable);
-    putDirect(vm, vm.propertyNames->name, jsString(&vm, name), ReadOnly | DontEnum);
+    // Some NativeExecutable functions, like JSBoundFunction, decide to lazily allocate their name string.
+    if (!name.isNull())
+        putDirect(vm, vm.propertyNames->name, jsString(&vm, name), ReadOnly | DontEnum);
     putDirect(vm, vm.propertyNames->length, jsNumber(length), ReadOnly | DontEnum);
 }
 
@@ -336,8 +338,10 @@
 bool JSFunction::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
     JSFunction* thisObject = jsCast<JSFunction*>(object);
-    if (thisObject->isHostOrBuiltinFunction())
+    if (thisObject->isHostOrBuiltinFunction()) {
+        thisObject->reifyBoundNameIfNeeded(exec, propertyName);
         return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
+    }
 
     if (propertyName == exec->propertyNames().prototype && !thisObject->jsExecutable()->isArrowFunction()) {
         VM& vm = exec->vm();
@@ -409,7 +413,8 @@
             propertyNames.add(vm.propertyNames->length);
         if (!thisObject->hasReifiedName())
             propertyNames.add(vm.propertyNames->name);
-    }
+    } else if (thisObject->isHostOrBuiltinFunction() && mode.includeDontEnumProperties() && thisObject->inherits(JSBoundFunction::info()) && !thisObject->hasReifiedName())
+        propertyNames.add(exec->vm().propertyNames->name);
     Base::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
 }
 
@@ -420,8 +425,10 @@
     if (UNLIKELY(isThisValueAltered(slot, thisObject)))
         return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
 
-    if (thisObject->isHostOrBuiltinFunction())
+    if (thisObject->isHostOrBuiltinFunction()) {
+        thisObject->reifyBoundNameIfNeeded(exec, propertyName);
         return Base::put(thisObject, exec, propertyName, value, slot);
+    }
 
     if (propertyName == exec->propertyNames().prototype) {
         // Make sure prototype has been reified, such that it can only be overwritten
@@ -452,8 +459,10 @@
 bool JSFunction::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
 {
     JSFunction* thisObject = jsCast<JSFunction*>(cell);
-    // For non-host functions, don't let these properties by deleted - except by DefineOwnProperty.
-    if (!thisObject->isHostOrBuiltinFunction() && exec->vm().deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable) {
+    if (thisObject->isHostOrBuiltinFunction())
+        thisObject->reifyBoundNameIfNeeded(exec, propertyName);
+    else if (exec->vm().deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable) {
+        // For non-host functions, don't let these properties by deleted - except by DefineOwnProperty.
         FunctionExecutable* executable = thisObject->jsExecutable();
         if (propertyName == exec->propertyNames().arguments
             || (propertyName == exec->propertyNames().prototype && !executable->isArrowFunction())
@@ -469,8 +478,10 @@
 bool JSFunction::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
 {
     JSFunction* thisObject = jsCast<JSFunction*>(object);
-    if (thisObject->isHostOrBuiltinFunction())
+    if (thisObject->isHostOrBuiltinFunction()) {
+        thisObject->reifyBoundNameIfNeeded(exec, propertyName);
         return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
+    }
 
     if (propertyName == exec->propertyNames().prototype) {
         // Make sure prototype has been reified, such that it can only be overwritten
@@ -647,4 +658,23 @@
     }
 }
 
+void JSFunction::reifyBoundNameIfNeeded(ExecState* exec, PropertyName propertyName)
+{
+    const Identifier& nameIdent = exec->propertyNames().name;
+    if (propertyName != nameIdent)
+        return;
+
+    if (hasReifiedName())
+        return;
+
+    if (this->inherits(JSBoundFunction::info())) {
+        VM& vm = exec->vm();
+        FunctionRareData* rareData = this->rareData(vm);
+        String name = makeString("bound ", static_cast<NativeExecutable*>(m_executable.get())->name());
+        unsigned initialAttributes = DontEnum | ReadOnly;
+        putDirect(vm, nameIdent, jsString(exec, name), initialAttributes);
+        rareData->setHasReifiedName();
+    }
+}
+
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/JSFunction.h (204320 => 204321)


--- trunk/Source/_javascript_Core/runtime/JSFunction.h	2016-08-10 01:31:17 UTC (rev 204320)
+++ trunk/Source/_javascript_Core/runtime/JSFunction.h	2016-08-10 03:39:05 UTC (rev 204321)
@@ -191,6 +191,7 @@
     bool hasReifiedName() const;
     void reifyLength(ExecState*);
     void reifyName(ExecState*);
+    void reifyBoundNameIfNeeded(ExecState*, PropertyName);
     void reifyName(ExecState*, String name);
     void reifyLazyPropertyIfNeeded(ExecState*, PropertyName propertyName);
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to