StructureRareData.cpp [plain text]
#include "config.h"
#include "StructureRareData.h"
#include "AdaptiveInferredPropertyValueWatchpointBase.h"
#include "CachedSpecialPropertyAdaptiveStructureWatchpoint.h"
#include "JSImmutableButterfly.h"
#include "JSObjectInlines.h"
#include "JSPropertyNameEnumerator.h"
#include "JSString.h"
#include "ObjectPropertyConditionSet.h"
#include "StructureChain.h"
#include "StructureInlines.h"
#include "StructureRareDataInlines.h"
namespace JSC {
const ClassInfo StructureRareData::s_info = { "StructureRareData", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(StructureRareData) };
Structure* StructureRareData::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
{
return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info());
}
StructureRareData* StructureRareData::create(VM& vm, Structure* previous)
{
StructureRareData* rareData = new (NotNull, allocateCell<StructureRareData>(vm.heap)) StructureRareData(vm, previous);
rareData->finishCreation(vm);
return rareData;
}
void StructureRareData::destroy(JSCell* cell)
{
static_cast<StructureRareData*>(cell)->StructureRareData::~StructureRareData();
}
StructureRareData::StructureRareData(VM& vm, Structure* previous)
: JSCell(vm, vm.structureRareDataStructure.get())
, m_maxOffset(invalidOffset)
, m_transitionOffset(invalidOffset)
{
if (previous)
m_previous.set(vm, this, previous);
}
void StructureRareData::visitChildren(JSCell* cell, SlotVisitor& visitor)
{
StructureRareData* thisObject = jsCast<StructureRareData*>(cell);
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
Base::visitChildren(thisObject, visitor);
visitor.append(thisObject->m_previous);
if (thisObject->m_specialPropertyCache) {
for (unsigned index = 0; index < numberOfCachedSpecialPropertyKeys; ++index)
visitor.appendUnbarriered(thisObject->cachedSpecialProperty(static_cast<CachedSpecialPropertyKey>(index)));
}
visitor.append(thisObject->m_cachedPropertyNameEnumerator);
for (unsigned index = 0; index < numberOfCachedPropertyNames; ++index) {
auto* cached = thisObject->m_cachedPropertyNames[index].unvalidatedGet();
if (cached != cachedPropertyNamesSentinel())
visitor.appendUnbarriered(cached);
}
}
class CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint final : public AdaptiveInferredPropertyValueWatchpointBase {
public:
typedef AdaptiveInferredPropertyValueWatchpointBase Base;
CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint(const ObjectPropertyCondition&, StructureRareData*);
private:
bool isValid() const final;
void handleFire(VM&, const FireDetail&) final;
StructureRareData* m_structureRareData;
};
SpecialPropertyCacheEntry::~SpecialPropertyCacheEntry() = default;
SpecialPropertyCache& StructureRareData::ensureSpecialPropertyCacheSlow()
{
ASSERT(!isCompilationThread() && !Thread::mayBeGCThread());
ASSERT(!m_specialPropertyCache);
auto cache = makeUnique<SpecialPropertyCache>();
WTF::storeStoreFence(); m_specialPropertyCache = WTFMove(cache);
return *m_specialPropertyCache.get();
}
inline void StructureRareData::giveUpOnSpecialPropertyCache(CachedSpecialPropertyKey key)
{
ensureSpecialPropertyCache().m_cache[static_cast<unsigned>(key)].m_value.setWithoutWriteBarrier(JSCell::seenMultipleCalleeObjects());
}
void StructureRareData::cacheSpecialPropertySlow(JSGlobalObject* globalObject, VM& vm, Structure* ownStructure, JSValue value, CachedSpecialPropertyKey key, const PropertySlot& slot)
{
UniquedStringImpl* uid = nullptr;
switch (key) {
case CachedSpecialPropertyKey::ToStringTag:
uid = vm.propertyNames->toStringTagSymbol.impl();
break;
case CachedSpecialPropertyKey::ToString:
uid = vm.propertyNames->toString.impl();
break;
case CachedSpecialPropertyKey::ValueOf:
uid = vm.propertyNames->valueOf.impl();
break;
case CachedSpecialPropertyKey::ToPrimitive:
uid = vm.propertyNames->toPrimitiveSymbol.impl();
break;
}
if (!ownStructure->propertyAccessesAreCacheable() || ownStructure->isProxy()) {
giveUpOnSpecialPropertyCache(key);
return;
}
ObjectPropertyConditionSet conditionSet;
if (slot.isValue()) {
if (!slot.isCacheable() || slot.slotBase()->structure(vm) == ownStructure)
return;
auto cacheStatus = prepareChainForCaching(globalObject, ownStructure, slot.slotBase());
if (!cacheStatus) {
giveUpOnSpecialPropertyCache(key);
return;
}
conditionSet = generateConditionsForPrototypePropertyHit(vm, this, globalObject, ownStructure, slot.slotBase(), uid);
ASSERT(!conditionSet.isValid() || conditionSet.hasOneSlotBaseCondition());
} else if (slot.isUnset()) {
if (!ownStructure->propertyAccessesAreCacheableForAbsence()) {
giveUpOnSpecialPropertyCache(key);
return;
}
auto cacheStatus = prepareChainForCaching(globalObject, ownStructure, nullptr);
if (!cacheStatus) {
giveUpOnSpecialPropertyCache(key);
return;
}
conditionSet = generateConditionsForPropertyMiss(vm, this, globalObject, ownStructure, uid);
} else
return;
if (!conditionSet.isValid()) {
giveUpOnSpecialPropertyCache(key);
return;
}
ObjectPropertyCondition equivCondition;
for (const ObjectPropertyCondition& condition : conditionSet) {
if (condition.condition().kind() == PropertyCondition::Presence) {
ASSERT(isValidOffset(condition.offset()));
condition.object()->structure(vm)->startWatchingPropertyForReplacements(vm, condition.offset());
equivCondition = condition.attemptToMakeEquivalenceWithoutBarrier(vm);
if (!equivCondition.isWatchable()) {
giveUpOnSpecialPropertyCache(key);
return;
}
} else if (!condition.isWatchable()) {
giveUpOnSpecialPropertyCache(key);
return;
}
}
ASSERT(conditionSet.structuresEnsureValidity());
auto& cache = ensureSpecialPropertyCache().m_cache[static_cast<unsigned>(key)];
for (ObjectPropertyCondition condition : conditionSet) {
if (condition.condition().kind() == PropertyCondition::Presence) {
cache.m_equivalenceWatchpoint = makeUnique<CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint>(equivCondition, this);
cache.m_equivalenceWatchpoint->install(vm);
} else
cache.m_missWatchpoints.add(condition, this)->install(vm);
}
cache.m_value.set(vm, this, value);
}
void StructureRareData::clearCachedSpecialProperty(CachedSpecialPropertyKey key)
{
auto* objectToStringCache = m_specialPropertyCache.get();
if (!objectToStringCache)
return;
auto& cache = objectToStringCache->m_cache[static_cast<unsigned>(key)];
cache.m_missWatchpoints.clear();
cache.m_equivalenceWatchpoint.reset();
if (cache.m_value.get() != JSCell::seenMultipleCalleeObjects())
cache.m_value.clear();
}
void StructureRareData::finalizeUnconditionally(VM& vm)
{
if (m_specialPropertyCache) {
auto clearCacheIfInvalidated = [&](CachedSpecialPropertyKey key) {
auto& cache = m_specialPropertyCache->m_cache[static_cast<unsigned>(key)];
if (cache.m_equivalenceWatchpoint) {
if (!cache.m_equivalenceWatchpoint->key().isStillLive(vm)) {
clearCachedSpecialProperty(key);
return;
}
}
for (auto* watchpoint : cache.m_missWatchpoints) {
if (!watchpoint->key().isStillLive(vm)) {
clearCachedSpecialProperty(key);
return;
}
}
};
for (unsigned index = 0; index < numberOfCachedSpecialPropertyKeys; ++index)
clearCacheIfInvalidated(static_cast<CachedSpecialPropertyKey>(index));
}
}
CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint::CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint(const ObjectPropertyCondition& key, StructureRareData* structureRareData)
: Base(key)
, m_structureRareData(structureRareData)
{
}
bool CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint::isValid() const
{
return m_structureRareData->isLive();
}
void CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint::handleFire(VM& vm, const FireDetail&)
{
CachedSpecialPropertyKey key = CachedSpecialPropertyKey::ToStringTag;
if (this->key().uid() == vm.propertyNames->toStringTagSymbol.impl())
key = CachedSpecialPropertyKey::ToStringTag;
else if (this->key().uid() == vm.propertyNames->toString.impl())
key = CachedSpecialPropertyKey::ToString;
else if (this->key().uid() == vm.propertyNames->valueOf.impl())
key = CachedSpecialPropertyKey::ValueOf;
else {
ASSERT(this->key().uid() == vm.propertyNames->toPrimitiveSymbol.impl());
key = CachedSpecialPropertyKey::ToPrimitive;
}
m_structureRareData->clearCachedSpecialProperty(key);
}
}