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);