/ runtime / StructureRareData.cpp
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