Diff
Modified: trunk/LayoutTests/ChangeLog (202066 => 202067)
--- trunk/LayoutTests/ChangeLog 2016-06-14 22:09:33 UTC (rev 202066)
+++ trunk/LayoutTests/ChangeLog 2016-06-14 22:34:09 UTC (rev 202067)
@@ -1,3 +1,18 @@
+2016-06-14 Keith Miller <[email protected]>
+
+ The Array species constructor watchpoints should be created the first time they are needed rather than on creation
+ https://bugs.webkit.org/show_bug.cgi?id=158754
+
+ Reviewed by Benjamin Poulain.
+
+ Add new micro-benchmark that tests the impact of lazily
+ initializing the array species watchpoints.
+
+ * js/regress/lazy-array-species-watchpoints-expected.txt: Added.
+ * js/regress/lazy-array-species-watchpoints.html: Added.
+ * js/regress/script-tests/lazy-array-species-watchpoints.js: Added.
+ (test):
+
2016-06-14 Benjamin Poulain <[email protected]>
Add the unprefixed version of the pseudo element ::placeholder
Added: trunk/LayoutTests/js/regress/lazy-array-species-watchpoints-expected.txt (0 => 202067)
--- trunk/LayoutTests/js/regress/lazy-array-species-watchpoints-expected.txt (rev 0)
+++ trunk/LayoutTests/js/regress/lazy-array-species-watchpoints-expected.txt 2016-06-14 22:34:09 UTC (rev 202067)
@@ -0,0 +1,10 @@
+JSRegress/lazy-array-species-watchpoints
+
+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/lazy-array-species-watchpoints.html (0 => 202067)
--- trunk/LayoutTests/js/regress/lazy-array-species-watchpoints.html (rev 0)
+++ trunk/LayoutTests/js/regress/lazy-array-species-watchpoints.html 2016-06-14 22:34:09 UTC (rev 202067)
@@ -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/lazy-array-species-watchpoints.js (0 => 202067)
--- trunk/LayoutTests/js/regress/script-tests/lazy-array-species-watchpoints.js (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/lazy-array-species-watchpoints.js 2016-06-14 22:34:09 UTC (rev 202067)
@@ -0,0 +1,12 @@
+Object.defineProperty(Array.prototype, "sort", { writable: false, value: Array.prototype.sort });
+
+function test(array) {
+ array = array.splice(2, 2);
+ array = array.slice(0, 5);
+ array = array.concat([1,2,3]);
+ return array;
+}
+noInline(test);
+
+for (let i = 0; i < 100000; i++)
+ test([1,2,3,4,5,6,7,8,9]);
Modified: trunk/Source/_javascript_Core/ChangeLog (202066 => 202067)
--- trunk/Source/_javascript_Core/ChangeLog 2016-06-14 22:09:33 UTC (rev 202066)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-06-14 22:34:09 UTC (rev 202067)
@@ -1,5 +1,47 @@
2016-06-14 Keith Miller <[email protected]>
+ The Array species constructor watchpoints should be created the first time they are needed rather than on creation
+ https://bugs.webkit.org/show_bug.cgi?id=158754
+
+ Reviewed by Benjamin Poulain.
+
+ We use adaptive watchpoints for some Array prototype functions to
+ ensure that the user has not overridden the value of the
+ Array.prototype.constructor or Array[Symbol.species]. This patch
+ changes when the Array species constructor watchpoints are
+ initialized. Before, those watchpoints would be created when the
+ global object is initialized. This had the advantage that it did
+ not require validating the constructor and Symbol.species
+ properties. On the other hand, it also meant that if the user were
+ to reconfigure properties Array.prototype, which would cause the
+ structure of the property to become an uncachable dictionary,
+ prior to running code that the watchpoints would be
+ invalidated. It turns out that JSBench amazon, for instance, does
+ reconfigure some of Array.prototype's properties. This patch
+ initializes the watchpoints the first time they are needed. Since
+ we only initialize once we also flatten the structure of Array and
+ Array.prototype.
+
+ * runtime/ArrayPrototype.cpp:
+ (JSC::speciesConstructArray):
+ (JSC::ArrayPrototype::attemptToInitializeSpeciesWatchpoint):
+ (JSC::ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire):
+ (JSC::ArrayPrototype::setConstructor): Deleted.
+ * runtime/ArrayPrototype.h:
+ (JSC::ArrayPrototype::speciesWatchpointStatus):
+ (JSC::ArrayPrototype::didChangeConstructorOrSpeciesProperties): Deleted.
+ * runtime/JSGlobalObject.cpp:
+ (JSC::JSGlobalObject::init):
+ * runtime/JSGlobalObject.h:
+ (JSC::JSGlobalObject::speciesGetterSetter):
+ (JSC::JSGlobalObject::arrayConstructor):
+ * tests/stress/array-symbol-species-lazy-watchpoints.js: Added.
+ (test):
+ (arrayEq):
+ (A):
+
+2016-06-14 Keith Miller <[email protected]>
+
REGRESSION(202002-202014): 845 32-bit JSC Stress Test failures
https://bugs.webkit.org/show_bug.cgi?id=158737
Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp (202066 => 202067)
--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2016-06-14 22:09:33 UTC (rev 202066)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2016-06-14 22:34:09 UTC (rev 202067)
@@ -32,6 +32,7 @@
#include "CodeBlock.h"
#include "CopiedSpaceInlines.h"
#include "Error.h"
+#include "GetterSetter.h"
#include "Interpreter.h"
#include "JIT.h"
#include "JSArrayIterator.h"
@@ -183,11 +184,16 @@
// ECMA 9.4.2.3: https://tc39.github.io/ecma262/#sec-arrayspeciescreate
JSValue constructor = jsUndefined();
if (LIKELY(isArray(exec, thisObject))) {
+ ArrayPrototype* arrayPrototype = thisObject->globalObject()->arrayPrototype();
+ ArrayPrototype::SpeciesWatchpointStatus status = arrayPrototype->speciesWatchpointStatus();
+ if (UNLIKELY(status == ArrayPrototype::SpeciesWatchpointStatus::Uninitialized))
+ status = arrayPrototype->attemptToInitializeSpeciesWatchpoint(exec);
+ ASSERT(status != ArrayPrototype::SpeciesWatchpointStatus::Uninitialized);
// Fast path in the normal case where the user has not set an own constructor and the Array.prototype.constructor is normal.
// We need prototype check for subclasses of Array, which are Array objects but have a different prototype by default.
if (LIKELY(!thisObject->hasCustomProperties()
- && thisObject->globalObject()->arrayPrototype() == thisObject->getPrototypeDirect()
- && !thisObject->globalObject()->arrayPrototype()->didChangeConstructorOrSpeciesProperties()))
+ && arrayPrototype == thisObject->getPrototypeDirect()
+ && status == ArrayPrototype::SpeciesWatchpointStatus::Initialized))
return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
constructor = thisObject->get(exec, exec->propertyNames().constructor);
@@ -1079,6 +1085,8 @@
// -------------------- ArrayPrototype.constructor Watchpoint ------------------
+static bool verbose = false;
+
class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
public:
typedef AdaptiveInferredPropertyValueWatchpointBase Base;
@@ -1090,32 +1098,61 @@
ArrayPrototype* m_arrayPrototype;
};
-void ArrayPrototype::setConstructor(VM& vm, JSObject* constructorProperty, unsigned attributes)
+ArrayPrototype::SpeciesWatchpointStatus ArrayPrototype::attemptToInitializeSpeciesWatchpoint(ExecState* exec)
{
- putDirectWithoutTransition(vm, vm.propertyNames->constructor, constructorProperty, attributes);
+ ASSERT(m_speciesWatchpointStatus == SpeciesWatchpointStatus::Uninitialized);
- // Do the watchpoint on our constructor property
- PropertyOffset offset = this->structure()->get(vm, vm.propertyNames->constructor);
- ASSERT(isValidOffset(offset));
- this->structure()->startWatchingPropertyForReplacements(vm, offset);
+ VM& vm = exec->vm();
- ObjectPropertyCondition condition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), constructorProperty);
- ASSERT(condition.isWatchable());
+ if (verbose)
+ dataLog("Attempting to initialize Array species watchpoints for Array.prototype: ", pointerDump(this), " with structure: ", pointerDump(this->structure()), "\nand Array: ", pointerDump(this->globalObject()->arrayConstructor()), " with structure: ", pointerDump(this->globalObject()->arrayConstructor()->structure()), "\n");
+ // First we need to make sure that the Array.prototype.constructor property points to Array
+ // and that Array[Symbol.species] is the primordial GetterSetter.
- m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(condition, this);
+ // We only initialize once so flattening the structures does not have any real cost.
+ Structure* prototypeStructure = this->structure(vm);
+ if (prototypeStructure->isDictionary())
+ prototypeStructure = prototypeStructure->flattenDictionaryStructure(vm, this);
+ RELEASE_ASSERT(!prototypeStructure->isDictionary());
+
+ JSGlobalObject* globalObject = this->globalObject();
+ ArrayConstructor* arrayConstructor = globalObject->arrayConstructor();
+
+ PropertySlot constructorSlot(this, PropertySlot::InternalMethodType::VMInquiry);
+ JSValue(this).get(exec, vm.propertyNames->constructor, constructorSlot);
+ if (constructorSlot.slotBase() != this
+ || !constructorSlot.isCacheableValue()
+ || constructorSlot.getValue(exec, vm.propertyNames->constructor) != arrayConstructor)
+ return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Fired;
+
+ Structure* constructorStructure = arrayConstructor->structure(vm);
+ if (constructorStructure->isDictionary())
+ constructorStructure = constructorStructure->flattenDictionaryStructure(vm, arrayConstructor);
+
+ PropertySlot speciesSlot(arrayConstructor, PropertySlot::InternalMethodType::VMInquiry);
+ JSValue(arrayConstructor).get(exec, vm.propertyNames->speciesSymbol, speciesSlot);
+ if (speciesSlot.slotBase() != arrayConstructor
+ || !speciesSlot.isCacheableGetter()
+ || speciesSlot.getterSetter() != globalObject->speciesGetterSetter())
+ return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Fired;
+
+ // Now we need to setup the watchpoints to make sure these conditions remain valid.
+ prototypeStructure->startWatchingPropertyForReplacements(vm, constructorSlot.cachedOffset());
+ constructorStructure->startWatchingPropertyForReplacements(vm, speciesSlot.cachedOffset());
+
+ ObjectPropertyCondition constructorCondition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), arrayConstructor);
+ ObjectPropertyCondition speciesCondition = ObjectPropertyCondition::equivalence(vm, this, arrayConstructor, vm.propertyNames->speciesSymbol.impl(), globalObject->speciesGetterSetter());
+
+ if (!constructorCondition.isWatchable() || !speciesCondition.isWatchable())
+ return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Fired;
+
+ m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(constructorCondition, this);
m_constructorWatchpoint->install();
-
- // Do the watchpoint on the constructor's Symbol.species property
- offset = constructorProperty->structure()->get(vm, vm.propertyNames->speciesSymbol);
- ASSERT(isValidOffset(offset));
- constructorProperty->structure()->startWatchingPropertyForReplacements(vm, offset);
- ASSERT(constructorProperty->getDirect(offset).isGetterSetter());
- condition = ObjectPropertyCondition::equivalence(vm, this, constructorProperty, vm.propertyNames->speciesSymbol.impl(), constructorProperty->getDirect(offset));
- ASSERT(condition.isWatchable());
+ m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(speciesCondition, this);
+ m_constructorSpeciesWatchpoint->install();
- m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(condition, this);
- m_constructorSpeciesWatchpoint->install();
+ return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Initialized;
}
ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
@@ -1131,7 +1168,10 @@
StringFireDetail stringDetail(out.toCString().data());
- m_arrayPrototype->m_didChangeConstructorOrSpeciesProperties = true;
+ if (verbose)
+ WTF::dataLog(stringDetail, "\n");
+
+ m_arrayPrototype->m_speciesWatchpointStatus = ArrayPrototype::SpeciesWatchpointStatus::Fired;
}
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.h (202066 => 202067)
--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.h 2016-06-14 22:09:33 UTC (rev 202066)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.h 2016-06-14 22:34:09 UTC (rev 202067)
@@ -35,6 +35,12 @@
public:
typedef JSArray Base;
+ enum class SpeciesWatchpointStatus {
+ Uninitialized,
+ Initialized,
+ Fired
+ };
+
static ArrayPrototype* create(VM&, JSGlobalObject*, Structure*);
DECLARE_INFO;
@@ -44,10 +50,9 @@
return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info(), ArrayClass);
}
- void setConstructor(VM&, JSObject* constructorProperty, unsigned attributes);
+ SpeciesWatchpointStatus speciesWatchpointStatus() const { return m_speciesWatchpointStatus; }
+ SpeciesWatchpointStatus attemptToInitializeSpeciesWatchpoint(ExecState*);
- bool didChangeConstructorOrSpeciesProperties() const { return m_didChangeConstructorOrSpeciesProperties; }
-
static const bool needsDestruction = false;
// We don't need destruction since we use a finalizer.
static void destroy(JSC::JSCell*);
@@ -60,7 +65,7 @@
friend ArrayPrototypeAdaptiveInferredPropertyWatchpoint;
std::unique_ptr<ArrayPrototypeAdaptiveInferredPropertyWatchpoint> m_constructorWatchpoint;
std::unique_ptr<ArrayPrototypeAdaptiveInferredPropertyWatchpoint> m_constructorSpeciesWatchpoint;
- bool m_didChangeConstructorOrSpeciesProperties = false;
+ SpeciesWatchpointStatus m_speciesWatchpointStatus { SpeciesWatchpointStatus::Uninitialized };
};
EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*);
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (202066 => 202067)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2016-06-14 22:09:33 UTC (rev 202066)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2016-06-14 22:34:09 UTC (rev 202067)
@@ -561,7 +561,8 @@
m_definePropertyFunction.set(vm, this, definePropertyFunction);
JSCell* functionConstructor = FunctionConstructor::create(vm, FunctionConstructor::createStructure(vm, this, m_functionPrototype.get()), m_functionPrototype.get());
- JSObject* arrayConstructor = ArrayConstructor::create(vm, this, ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_arrayPrototype.get(), m_speciesGetterSetter.get());
+ ArrayConstructor* arrayConstructor = ArrayConstructor::create(vm, this, ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_arrayPrototype.get(), m_speciesGetterSetter.get());
+ m_arrayConstructor.set(vm, this, arrayConstructor);
m_regExpConstructor.set(vm, this, RegExpConstructor::create(vm, RegExpConstructor::createStructure(vm, this, m_functionPrototype.get()), m_regExpPrototype.get(), m_speciesGetterSetter.get()));
@@ -608,7 +609,7 @@
m_objectPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, objectConstructor, DontEnum);
m_functionPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, functionConstructor, DontEnum);
- m_arrayPrototype->setConstructor(vm, arrayConstructor, DontEnum);
+ m_arrayPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, arrayConstructor, DontEnum);
m_regExpPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, m_regExpConstructor.get(), DontEnum);
putDirectWithoutTransition(vm, vm.propertyNames->Object, objectConstructor, DontEnum);
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.h (202066 => 202067)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.h 2016-06-14 22:09:33 UTC (rev 202066)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.h 2016-06-14 22:34:09 UTC (rev 202067)
@@ -55,6 +55,7 @@
namespace JSC {
+class ArrayConstructor;
class ArrayPrototype;
class BooleanPrototype;
class ConsoleClient;
@@ -226,6 +227,7 @@
WriteBarrier<NativeErrorConstructor> m_typeErrorConstructor;
LazyProperty<JSGlobalObject, NativeErrorConstructor> m_URIErrorConstructor;
WriteBarrier<ObjectConstructor> m_objectConstructor;
+ WriteBarrier<ArrayConstructor> m_arrayConstructor;
WriteBarrier<JSPromiseConstructor> m_promiseConstructor;
WriteBarrier<JSInternalPromiseConstructor> m_internalPromiseConstructor;
@@ -458,9 +460,12 @@
// The following accessors return pristine values, even if a script
// replaces the global object's associated property.
+ GetterSetter* speciesGetterSetter() const { return m_speciesGetterSetter.get(); }
+
RegExpConstructor* regExpConstructor() const { return m_regExpConstructor.get(); }
ErrorConstructor* errorConstructor() const { return m_errorConstructor.get(); }
+ ArrayConstructor* arrayConstructor() const { return m_arrayConstructor.get(); }
ObjectConstructor* objectConstructor() const { return m_objectConstructor.get(); }
JSPromiseConstructor* promiseConstructor() const { return m_promiseConstructor.get(); }
JSInternalPromiseConstructor* internalPromiseConstructor() const { return m_internalPromiseConstructor.get(); }
Added: trunk/Source/_javascript_Core/tests/stress/array-symbol-species-lazy-watchpoints.js (0 => 202067)
--- trunk/Source/_javascript_Core/tests/stress/array-symbol-species-lazy-watchpoints.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/array-symbol-species-lazy-watchpoints.js 2016-06-14 22:34:09 UTC (rev 202067)
@@ -0,0 +1,47 @@
+// This tests that the lazy watchpoints we set for Symbol.species in our Builtin arrayPrototype functions work.
+
+
+function test(array) {
+ array = array.splice(2, 2);
+ array = array.slice(0, 5);
+ array = array.concat([1,2,3]);
+ return array;
+}
+noInline(test);
+
+function arrayEq(a, b) {
+ if (a.length !== b.length)
+ throw new Error();
+
+ for (let i = 0; i < a.length; i++) {
+ if (a[i] !== b[i])
+ throw new Error();
+ }
+}
+
+for (let i = 0; i < 100; i++)
+ arrayEq(test([1,2,3,4,5,6,7,8,9]), [3,4,1,2,3]);
+
+class A extends Array { }
+
+for (let i = 0; i < 100; i++) {
+ let result = test(new A(1,2,3,4,5,6,7,8,9));
+ arrayEq(result, [3,4,1,2,3]);
+ if (!(result instanceof A))
+ throw new Error();
+}
+
+for (let i = 0; i < 100; i++)
+ arrayEq(test([1,2,3,4,5,6,7,8,9]), [3,4,1,2,3]);
+
+delete Array.prototype.sort;
+
+for (let i = 0; i < 100; i++)
+ arrayEq(test([1,2,3,4,5,6,7,8,9]), [3,4,1,2,3]);
+
+for (let i = 0; i < 100; i++) {
+ let result = test(new A(1,2,3,4,5,6,7,8,9));
+ arrayEq(result, [3,4,1,2,3]);
+ if (!(result instanceof A))
+ throw new Error();
+}