StructureRareData.cpp
1 /* 2 * Copyright (C) 2013-2020 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "StructureRareData.h" 28 29 #include "AdaptiveInferredPropertyValueWatchpointBase.h" 30 #include "CachedSpecialPropertyAdaptiveStructureWatchpoint.h" 31 #include "JSImmutableButterfly.h" 32 #include "JSObjectInlines.h" 33 #include "JSPropertyNameEnumerator.h" 34 #include "JSString.h" 35 #include "ObjectPropertyConditionSet.h" 36 #include "StructureChain.h" 37 #include "StructureInlines.h" 38 #include "StructureRareDataInlines.h" 39 40 namespace JSC { 41 42 const ClassInfo StructureRareData::s_info = { "StructureRareData", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(StructureRareData) }; 43 44 Structure* StructureRareData::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) 45 { 46 return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); 47 } 48 49 StructureRareData* StructureRareData::create(VM& vm, Structure* previous) 50 { 51 StructureRareData* rareData = new (NotNull, allocateCell<StructureRareData>(vm.heap)) StructureRareData(vm, previous); 52 rareData->finishCreation(vm); 53 return rareData; 54 } 55 56 void StructureRareData::destroy(JSCell* cell) 57 { 58 static_cast<StructureRareData*>(cell)->StructureRareData::~StructureRareData(); 59 } 60 61 StructureRareData::StructureRareData(VM& vm, Structure* previous) 62 : JSCell(vm, vm.structureRareDataStructure.get()) 63 , m_maxOffset(invalidOffset) 64 , m_transitionOffset(invalidOffset) 65 { 66 if (previous) 67 m_previous.set(vm, this, previous); 68 } 69 70 void StructureRareData::visitChildren(JSCell* cell, SlotVisitor& visitor) 71 { 72 StructureRareData* thisObject = jsCast<StructureRareData*>(cell); 73 ASSERT_GC_OBJECT_INHERITS(thisObject, info()); 74 75 Base::visitChildren(thisObject, visitor); 76 visitor.append(thisObject->m_previous); 77 if (thisObject->m_specialPropertyCache) { 78 for (unsigned index = 0; index < numberOfCachedSpecialPropertyKeys; ++index) 79 visitor.appendUnbarriered(thisObject->cachedSpecialProperty(static_cast<CachedSpecialPropertyKey>(index))); 80 } 81 visitor.append(thisObject->m_cachedPropertyNameEnumerator); 82 for (unsigned index = 0; index < numberOfCachedPropertyNames; ++index) { 83 auto* cached = thisObject->m_cachedPropertyNames[index].unvalidatedGet(); 84 if (cached != cachedPropertyNamesSentinel()) 85 visitor.appendUnbarriered(cached); 86 } 87 } 88 89 // ----------- Cached special properties helper watchpoint classes ----------- 90 91 class CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint final : public AdaptiveInferredPropertyValueWatchpointBase { 92 public: 93 typedef AdaptiveInferredPropertyValueWatchpointBase Base; 94 CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint(const ObjectPropertyCondition&, StructureRareData*); 95 96 private: 97 bool isValid() const final; 98 void handleFire(VM&, const FireDetail&) final; 99 100 StructureRareData* m_structureRareData; 101 }; 102 103 SpecialPropertyCacheEntry::~SpecialPropertyCacheEntry() = default; 104 105 SpecialPropertyCache& StructureRareData::ensureSpecialPropertyCacheSlow() 106 { 107 ASSERT(!isCompilationThread() && !Thread::mayBeGCThread()); 108 ASSERT(!m_specialPropertyCache); 109 auto cache = makeUnique<SpecialPropertyCache>(); 110 WTF::storeStoreFence(); // Expose valid struct for concurrent threads including concurrent compilers. 111 m_specialPropertyCache = WTFMove(cache); 112 return *m_specialPropertyCache.get(); 113 } 114 115 inline void StructureRareData::giveUpOnSpecialPropertyCache(CachedSpecialPropertyKey key) 116 { 117 ensureSpecialPropertyCache().m_cache[static_cast<unsigned>(key)].m_value.setWithoutWriteBarrier(JSCell::seenMultipleCalleeObjects()); 118 } 119 120 void StructureRareData::cacheSpecialPropertySlow(JSGlobalObject* globalObject, VM& vm, Structure* ownStructure, JSValue value, CachedSpecialPropertyKey key, const PropertySlot& slot) 121 { 122 UniquedStringImpl* uid = nullptr; 123 switch (key) { 124 case CachedSpecialPropertyKey::ToStringTag: 125 uid = vm.propertyNames->toStringTagSymbol.impl(); 126 break; 127 case CachedSpecialPropertyKey::ToString: 128 uid = vm.propertyNames->toString.impl(); 129 break; 130 case CachedSpecialPropertyKey::ValueOf: 131 uid = vm.propertyNames->valueOf.impl(); 132 break; 133 case CachedSpecialPropertyKey::ToPrimitive: 134 uid = vm.propertyNames->toPrimitiveSymbol.impl(); 135 break; 136 } 137 138 if (!ownStructure->propertyAccessesAreCacheable() || ownStructure->isProxy()) { 139 giveUpOnSpecialPropertyCache(key); 140 return; 141 } 142 143 ObjectPropertyConditionSet conditionSet; 144 if (slot.isValue()) { 145 // We don't handle the own property case of special properties (toString, valueOf, @@toPrimitive, @@toStringTag) because we would never know if a new 146 // object transitioning to the same structure had the same value stored in that property. 147 // Additionally, this is a super unlikely case anyway. 148 if (!slot.isCacheable() || slot.slotBase()->structure(vm) == ownStructure) 149 return; 150 151 // This will not create a condition for the current structure but that is good because we know that property 152 // is not on the ownStructure so we will transisition if one is added and this cache will no longer be used. 153 auto cacheStatus = prepareChainForCaching(globalObject, ownStructure, slot.slotBase()); 154 if (!cacheStatus) { 155 giveUpOnSpecialPropertyCache(key); 156 return; 157 } 158 conditionSet = generateConditionsForPrototypePropertyHit(vm, this, globalObject, ownStructure, slot.slotBase(), uid); 159 ASSERT(!conditionSet.isValid() || conditionSet.hasOneSlotBaseCondition()); 160 } else if (slot.isUnset()) { 161 if (!ownStructure->propertyAccessesAreCacheableForAbsence()) { 162 giveUpOnSpecialPropertyCache(key); 163 return; 164 } 165 166 auto cacheStatus = prepareChainForCaching(globalObject, ownStructure, nullptr); 167 if (!cacheStatus) { 168 giveUpOnSpecialPropertyCache(key); 169 return; 170 } 171 conditionSet = generateConditionsForPropertyMiss(vm, this, globalObject, ownStructure, uid); 172 } else 173 return; 174 175 if (!conditionSet.isValid()) { 176 giveUpOnSpecialPropertyCache(key); 177 return; 178 } 179 180 ObjectPropertyCondition equivCondition; 181 for (const ObjectPropertyCondition& condition : conditionSet) { 182 if (condition.condition().kind() == PropertyCondition::Presence) { 183 ASSERT(isValidOffset(condition.offset())); 184 condition.object()->structure(vm)->startWatchingPropertyForReplacements(vm, condition.offset()); 185 equivCondition = condition.attemptToMakeEquivalenceWithoutBarrier(vm); 186 187 // The equivalence condition won't be watchable if we have already seen a replacement. 188 if (!equivCondition.isWatchable()) { 189 giveUpOnSpecialPropertyCache(key); 190 return; 191 } 192 } else if (!condition.isWatchable()) { 193 giveUpOnSpecialPropertyCache(key); 194 return; 195 } 196 } 197 198 ASSERT(conditionSet.structuresEnsureValidity()); 199 auto& cache = ensureSpecialPropertyCache().m_cache[static_cast<unsigned>(key)]; 200 for (ObjectPropertyCondition condition : conditionSet) { 201 if (condition.condition().kind() == PropertyCondition::Presence) { 202 cache.m_equivalenceWatchpoint = makeUnique<CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint>(equivCondition, this); 203 cache.m_equivalenceWatchpoint->install(vm); 204 } else 205 cache.m_missWatchpoints.add(condition, this)->install(vm); 206 } 207 cache.m_value.set(vm, this, value); 208 } 209 210 void StructureRareData::clearCachedSpecialProperty(CachedSpecialPropertyKey key) 211 { 212 auto* objectToStringCache = m_specialPropertyCache.get(); 213 if (!objectToStringCache) 214 return; 215 auto& cache = objectToStringCache->m_cache[static_cast<unsigned>(key)]; 216 cache.m_missWatchpoints.clear(); 217 cache.m_equivalenceWatchpoint.reset(); 218 if (cache.m_value.get() != JSCell::seenMultipleCalleeObjects()) 219 cache.m_value.clear(); 220 } 221 222 void StructureRareData::finalizeUnconditionally(VM& vm) 223 { 224 if (m_specialPropertyCache) { 225 auto clearCacheIfInvalidated = [&](CachedSpecialPropertyKey key) { 226 auto& cache = m_specialPropertyCache->m_cache[static_cast<unsigned>(key)]; 227 if (cache.m_equivalenceWatchpoint) { 228 if (!cache.m_equivalenceWatchpoint->key().isStillLive(vm)) { 229 clearCachedSpecialProperty(key); 230 return; 231 } 232 } 233 for (auto* watchpoint : cache.m_missWatchpoints) { 234 if (!watchpoint->key().isStillLive(vm)) { 235 clearCachedSpecialProperty(key); 236 return; 237 } 238 } 239 }; 240 241 for (unsigned index = 0; index < numberOfCachedSpecialPropertyKeys; ++index) 242 clearCacheIfInvalidated(static_cast<CachedSpecialPropertyKey>(index)); 243 } 244 } 245 246 // ------------- Methods for Object.prototype.toString() helper watchpoint classes -------------- 247 248 CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint::CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint(const ObjectPropertyCondition& key, StructureRareData* structureRareData) 249 : Base(key) 250 , m_structureRareData(structureRareData) 251 { 252 } 253 254 bool CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint::isValid() const 255 { 256 return m_structureRareData->isLive(); 257 } 258 259 void CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint::handleFire(VM& vm, const FireDetail&) 260 { 261 CachedSpecialPropertyKey key = CachedSpecialPropertyKey::ToStringTag; 262 if (this->key().uid() == vm.propertyNames->toStringTagSymbol.impl()) 263 key = CachedSpecialPropertyKey::ToStringTag; 264 else if (this->key().uid() == vm.propertyNames->toString.impl()) 265 key = CachedSpecialPropertyKey::ToString; 266 else if (this->key().uid() == vm.propertyNames->valueOf.impl()) 267 key = CachedSpecialPropertyKey::ValueOf; 268 else { 269 ASSERT(this->key().uid() == vm.propertyNames->toPrimitiveSymbol.impl()); 270 key = CachedSpecialPropertyKey::ToPrimitive; 271 } 272 m_structureRareData->clearCachedSpecialProperty(key); 273 } 274 275 } // namespace JSC