Modified: trunk/Source/_javascript_Core/ChangeLog (210744 => 210745)
--- trunk/Source/_javascript_Core/ChangeLog 2017-01-13 23:02:58 UTC (rev 210744)
+++ trunk/Source/_javascript_Core/ChangeLog 2017-01-13 23:30:45 UTC (rev 210745)
@@ -1,3 +1,39 @@
+2017-01-13 Saam Barati <sbar...@apple.com>
+
+ Initialize the ArraySpecies watchpoint as Clear and transition to IsWatched once slice is called for the first time
+ https://bugs.webkit.org/show_bug.cgi?id=167017
+ <rdar://problem/30019309>
+
+ Reviewed by Keith Miller and Filip Pizlo.
+
+ This patch is to reverse the JSBench regression from r210695.
+
+ The new state diagram for the array species watchpoint is as
+ follows:
+
+ 1. On GlobalObject construction, it starts life out as ClearWatchpoint.
+ 2. When slice is called for the first time, we observe the state
+ of the world, and either transition it to IsWatched if we were able
+ to set up the object property conditions, or to IsInvalidated if we
+ were not.
+ 3. The DFG compiler will now only lower slice as an intrinsic if
+ it observed the speciesWatchpoint.state() as IsWatched.
+ 4. The IsWatched => IsInvalidated transition happens only when
+ one of the object property condition watchpoints fire.
+
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+ * runtime/ArrayPrototype.cpp:
+ (JSC::speciesWatchpointIsValid):
+ (JSC::speciesConstructArray):
+ (JSC::arrayProtoPrivateFuncConcatMemcpy):
+ (JSC::ArrayPrototype::tryInitializeSpeciesWatchpoint):
+ (JSC::ArrayPrototype::initializeSpeciesWatchpoint): Deleted.
+ * runtime/ArrayPrototype.h:
+ * runtime/JSGlobalObject.cpp:
+ (JSC::JSGlobalObject::JSGlobalObject):
+ (JSC::JSGlobalObject::init):
+
2017-01-13 Yusuke Suzuki <utatane....@gmail.com>
Reserve capacity for StringBuilder in unescape
Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp (210744 => 210745)
--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2017-01-13 23:02:58 UTC (rev 210744)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2017-01-13 23:30:45 UTC (rev 210745)
@@ -191,12 +191,19 @@
throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError));
}
-inline bool speciesWatchpointIsValid(JSObject* thisObject)
+ALWAYS_INLINE bool speciesWatchpointIsValid(ExecState* exec, JSObject* thisObject)
{
- ArrayPrototype* arrayPrototype = thisObject->globalObject()->arrayPrototype();
+ JSGlobalObject* globalObject = thisObject->globalObject();
+ ArrayPrototype* arrayPrototype = globalObject->arrayPrototype();
+
+ if (globalObject->arraySpeciesWatchpoint().stateOnJSThread() == ClearWatchpoint) {
+ arrayPrototype->tryInitializeSpeciesWatchpoint(exec);
+ ASSERT(globalObject->arraySpeciesWatchpoint().stateOnJSThread() != ClearWatchpoint);
+ }
+
return !thisObject->hasCustomProperties()
&& arrayPrototype == thisObject->getPrototypeDirect()
- && arrayPrototype->globalObject()->arraySpeciesWatchpoint().isStillValid();
+ && globalObject->arraySpeciesWatchpoint().stateOnJSThread() == IsWatched;
}
enum class SpeciesConstructResult {
@@ -221,7 +228,7 @@
if (LIKELY(thisIsArray)) {
// 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.
- bool isValid = speciesWatchpointIsValid(thisObject);
+ bool isValid = speciesWatchpointIsValid(exec, thisObject);
if (LIKELY(isValid))
return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
@@ -1239,7 +1246,7 @@
return JSValue::encode(jsNull());
// We need to check the species constructor here since checking it in the JS wrapper is too expensive for the non-optimizing tiers.
- bool isValid = speciesWatchpointIsValid(firstArray);
+ bool isValid = speciesWatchpointIsValid(exec, firstArray);
if (UNLIKELY(!isValid))
return JSValue::encode(jsNull());
@@ -1331,7 +1338,7 @@
ArrayPrototype* m_arrayPrototype;
};
-void ArrayPrototype::initializeSpeciesWatchpoint(ExecState* exec)
+void ArrayPrototype::tryInitializeSpeciesWatchpoint(ExecState* exec)
{
VM& vm = exec->vm();
@@ -1339,7 +1346,6 @@
RELEASE_ASSERT(!m_constructorSpeciesWatchpoint);
auto scope = DECLARE_THROW_SCOPE(vm);
- UNUSED_PARAM(scope);
if (verbose)
dataLog("Initializing 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");
@@ -1355,12 +1361,19 @@
JSGlobalObject* globalObject = this->globalObject();
ArrayConstructor* arrayConstructor = globalObject->arrayConstructor();
+ auto invalidateWatchpoint = [&] {
+ globalObject->arraySpeciesWatchpoint().invalidate(vm, StringFireDetail("Was not able to set up array species watchpoint."));
+ };
+
PropertySlot constructorSlot(this, PropertySlot::InternalMethodType::VMInquiry);
this->getOwnPropertySlot(this, exec, vm.propertyNames->constructor, constructorSlot);
- ASSERT(!scope.exception());
- ASSERT(constructorSlot.slotBase() == this);
- ASSERT(constructorSlot.isCacheableValue());
- RELEASE_ASSERT(constructorSlot.getValue(exec, vm.propertyNames->constructor) == arrayConstructor);
+ ASSERT_UNUSED(scope, !scope.exception());
+ if (constructorSlot.slotBase() != this
+ || !constructorSlot.isCacheableValue()
+ || constructorSlot.getValue(exec, vm.propertyNames->constructor) != arrayConstructor) {
+ invalidateWatchpoint();
+ return;
+ }
Structure* constructorStructure = arrayConstructor->structure(vm);
if (constructorStructure->isDictionary())
@@ -1368,10 +1381,13 @@
PropertySlot speciesSlot(arrayConstructor, PropertySlot::InternalMethodType::VMInquiry);
arrayConstructor->getOwnPropertySlot(arrayConstructor, exec, vm.propertyNames->speciesSymbol, speciesSlot);
- ASSERT(!scope.exception());
- ASSERT(speciesSlot.slotBase() == arrayConstructor);
- ASSERT(speciesSlot.isCacheableGetter());
- RELEASE_ASSERT(speciesSlot.getterSetter() == globalObject->speciesGetterSetter());
+ ASSERT_UNUSED(scope, !scope.exception());
+ if (speciesSlot.slotBase() != arrayConstructor
+ || !speciesSlot.isCacheableGetter()
+ || speciesSlot.getterSetter() != globalObject->speciesGetterSetter()) {
+ invalidateWatchpoint();
+ return;
+ }
// Now we need to setup the watchpoints to make sure these conditions remain valid.
prototypeStructure->startWatchingPropertyForReplacements(vm, constructorSlot.cachedOffset());
@@ -1380,8 +1396,10 @@
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());
- RELEASE_ASSERT(constructorCondition.isWatchable());
- RELEASE_ASSERT(speciesCondition.isWatchable());
+ if (!constructorCondition.isWatchable() || !speciesCondition.isWatchable()) {
+ invalidateWatchpoint();
+ return;
+ }
m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(constructorCondition, this);
m_constructorWatchpoint->install();
@@ -1388,6 +1406,10 @@
m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(speciesCondition, this);
m_constructorSpeciesWatchpoint->install();
+
+ // We only watch this from the DFG, and the DFG makes sure to only start watching if the watchpoint is in the IsWatched state.
+ RELEASE_ASSERT(!globalObject->arraySpeciesWatchpoint().isBeingWatched());
+ globalObject->arraySpeciesWatchpoint().touch(vm, "Set up array species watchpoint.");
}
ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.h (210744 => 210745)
--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.h 2017-01-13 23:02:58 UTC (rev 210744)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.h 2017-01-13 23:30:45 UTC (rev 210745)
@@ -49,7 +49,7 @@
return Structure::create(vm, globalObject, prototype, TypeInfo(DerivedArrayType, StructureFlags), info(), ArrayClass);
}
- void initializeSpeciesWatchpoint(ExecState*);
+ void tryInitializeSpeciesWatchpoint(ExecState*);
static const bool needsDestruction = false;
// We don't need destruction since we use a finalizer.
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (210744 => 210745)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2017-01-13 23:02:58 UTC (rev 210744)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2017-01-13 23:30:45 UTC (rev 210745)
@@ -332,7 +332,7 @@
, m_varInjectionWatchpoint(adoptRef(new WatchpointSet(IsWatched)))
, m_weakRandom(Options::forceWeakRandomSeed() ? Options::forcedWeakRandomSeed() : static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0)))
, m_arrayIteratorProtocolWatchpoint(IsWatched)
- , m_arraySpeciesWatchpoint(IsWatched)
+ , m_arraySpeciesWatchpoint(ClearWatchpoint)
, m_templateRegistry(vm)
, m_evalEnabled(true)
, m_runtimeFlags()
@@ -947,8 +947,6 @@
m_arrayPrototypeSymbolIteratorWatchpoint = std::make_unique<ArrayIteratorAdaptiveWatchpoint>(condition, this);
m_arrayPrototypeSymbolIteratorWatchpoint->install();
}
-
- this->arrayPrototype()->initializeSpeciesWatchpoint(exec);
}
resetPrototype(vm, getPrototypeDirect());