| /* | 
 |  * Copyright (C) 2013-2021 Apple Inc. All rights reserved. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  * 1. Redistributions of source code must retain the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer. | 
 |  * 2. Redistributions in binary form must reproduce the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer in the | 
 |  *    documentation and/or other materials provided with the distribution. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' | 
 |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | 
 |  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
 |  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS | 
 |  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 
 |  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 
 |  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 
 |  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | 
 |  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 
 |  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | 
 |  * THE POSSIBILITY OF SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #include "config.h" | 
 | #include "StructureRareData.h" | 
 |  | 
 | #include "AdaptiveInferredPropertyValueWatchpointBase.h" | 
 | #include "CacheableIdentifierInlines.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" | 
 | #include <wtf/TZoneMalloc.h> | 
 | #include <wtf/TZoneMallocInlines.h> | 
 |  | 
 | namespace JSC { | 
 |  | 
 | const ClassInfo StructureRareData::s_info = { "StructureRareData"_s, 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)) 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_previous(previous, WriteBarrierEarlyInit) | 
 |     , m_maxOffset(invalidOffset) | 
 |     , m_transitionOffset(invalidOffset) | 
 | { | 
 | } | 
 |  | 
 | template<typename Visitor> | 
 | void StructureRareData::visitChildrenImpl(JSCell* cell, Visitor& 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.appendUnbarriered(thisObject->cachedPropertyNameEnumerator()); | 
 |     for (unsigned index = 0; index < numberOfCachedPropertyNames; ++index) { | 
 |         auto* cached = thisObject->m_cachedPropertyNames[index].unvalidatedGet(); | 
 |         if (cached != cachedPropertyNamesSentinel()) | 
 |             visitor.appendUnbarriered(cached); | 
 |     } | 
 | } | 
 |  | 
 | DEFINE_VISIT_CHILDREN(StructureRareData); | 
 |  | 
 | // ----------- Cached special properties helper watchpoint classes ----------- | 
 |  | 
 | class CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint final : public AdaptiveInferredPropertyValueWatchpointBase { | 
 |     WTF_MAKE_TZONE_ALLOCATED(CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint); | 
 | 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(); // Expose valid struct for concurrent threads including concurrent compilers. | 
 |     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; | 
 |     case CachedSpecialPropertyKey::ToJSON: | 
 |         uid = vm.propertyNames->toJSON.impl(); | 
 |         break; | 
 |     } | 
 |  | 
 |     if (!ownStructure->propertyAccessesAreCacheable() || ownStructure->isProxy()) { | 
 |         giveUpOnSpecialPropertyCache(key); | 
 |         return; | 
 |     } | 
 |  | 
 |     ObjectPropertyConditionSet conditionSet; | 
 |     if (slot.isValue()) { | 
 |         // We don't handle the own property case of special properties (toString, valueOf, @@toPrimitive, @@toStringTag) because we would never know if a new | 
 |         // object transitioning to the same structure had the same value stored in that property. | 
 |         // Additionally, this is a super unlikely case anyway. | 
 |         if (!slot.isCacheable() || slot.slotBase()->structure() == ownStructure) | 
 |             return; | 
 |  | 
 |         // This will not create a condition for the current structure but that is good because we know that property | 
 |         // is not on the ownStructure so we will transisition if one is added and this cache will no longer be used. | 
 |         auto cacheStatus = prepareChainForCaching(globalObject, ownStructure, uid, 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, uid, 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()->startWatchingPropertyForReplacements(vm, condition.offset()); | 
 |             equivCondition = condition.attemptToMakeEquivalenceWithoutBarrier(); | 
 |  | 
 |             // The equivalence condition won't be watchable if we have already seen a replacement. | 
 |             if (!equivCondition.isWatchable(PropertyCondition::MakeNoChanges)) { | 
 |                 giveUpOnSpecialPropertyCache(key); | 
 |                 return; | 
 |             } | 
 |         } else if (!condition.isWatchable(PropertyCondition::MakeNoChanges)) { | 
 |             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, CollectionScope) | 
 | { | 
 |     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)); | 
 |     } | 
 | } | 
 |  | 
 | // ------------- Methods for Object.prototype.toString() helper watchpoint classes -------------- | 
 |  | 
 | WTF_MAKE_TZONE_ALLOCATED_IMPL(CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint); | 
 |  | 
 | 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 if (this->key().uid() == vm.propertyNames->toJSON.impl()) | 
 |         key = CachedSpecialPropertyKey::ToJSON; | 
 |     else { | 
 |         ASSERT(this->key().uid() == vm.propertyNames->toPrimitiveSymbol.impl()); | 
 |         key = CachedSpecialPropertyKey::ToPrimitive; | 
 |     } | 
 |     m_structureRareData->clearCachedSpecialProperty(key); | 
 | } | 
 |  | 
 | } // namespace JSC |