/ runtime / SparseArrayValueMap.cpp
SparseArrayValueMap.cpp
  1  /*
  2   * Copyright (C) 2011-2017 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. ``AS IS'' AND ANY
 14   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 15   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 17   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 18   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 19   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 20   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 21   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 22   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 23   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 24   */
 25  
 26  #include "config.h"
 27  #include "SparseArrayValueMap.h"
 28  
 29  #include "GetterSetter.h"
 30  #include "JSCJSValueInlines.h"
 31  #include "JSObjectInlines.h"
 32  #include "PropertySlot.h"
 33  #include "StructureInlines.h"
 34  #include "TypeError.h"
 35  
 36  namespace JSC {
 37  
 38  const ClassInfo SparseArrayValueMap::s_info = { "SparseArrayValueMap", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(SparseArrayValueMap) };
 39  
 40  SparseArrayValueMap::SparseArrayValueMap(VM& vm)
 41      : Base(vm, vm.sparseArrayValueMapStructure.get())
 42  {
 43  }
 44  
 45  void SparseArrayValueMap::finishCreation(VM& vm)
 46  {
 47      Base::finishCreation(vm);
 48  }
 49  
 50  SparseArrayValueMap* SparseArrayValueMap::create(VM& vm)
 51  {
 52      SparseArrayValueMap* result = new (NotNull, allocateCell<SparseArrayValueMap>(vm.heap)) SparseArrayValueMap(vm);
 53      result->finishCreation(vm);
 54      return result;
 55  }
 56  
 57  void SparseArrayValueMap::destroy(JSCell* cell)
 58  {
 59      static_cast<SparseArrayValueMap*>(cell)->SparseArrayValueMap::~SparseArrayValueMap();
 60  }
 61  
 62  Structure* SparseArrayValueMap::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
 63  {
 64      return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info());
 65  }
 66  
 67  SparseArrayValueMap::AddResult SparseArrayValueMap::add(JSObject* array, unsigned i)
 68  {
 69      AddResult result;
 70      size_t increasedCapacity = 0;
 71      {
 72          auto locker = holdLock(cellLock());
 73          result = m_map.add(i, SparseArrayEntry());
 74          size_t capacity = m_map.capacity();
 75          if (capacity > m_reportedCapacity) {
 76              increasedCapacity = capacity - m_reportedCapacity;
 77              m_reportedCapacity = capacity;
 78          }
 79      }
 80      if (increasedCapacity)
 81          Heap::heap(array)->reportExtraMemoryAllocated(increasedCapacity * sizeof(Map::KeyValuePairType));
 82      return result;
 83  }
 84  
 85  void SparseArrayValueMap::remove(iterator it)
 86  {
 87      auto locker = holdLock(cellLock());
 88      m_map.remove(it);
 89  }
 90  
 91  void SparseArrayValueMap::remove(unsigned i)
 92  {
 93      auto locker = holdLock(cellLock());
 94      m_map.remove(i);
 95  }
 96  
 97  bool SparseArrayValueMap::putEntry(JSGlobalObject* globalObject, JSObject* array, unsigned i, JSValue value, bool shouldThrow)
 98  {
 99      VM& vm = globalObject->vm();
100      auto scope = DECLARE_THROW_SCOPE(vm);
101      ASSERT(value);
102      
103      AddResult result = add(array, i);
104      SparseArrayEntry& entry = result.iterator->value;
105  
106      // To save a separate find & add, we first always add to the sparse map.
107      // In the uncommon case that this is a new property, and the array is not
108      // extensible, this is not the right thing to have done - so remove again.
109      if (result.isNewEntry && !array->isStructureExtensible(vm)) {
110          remove(result.iterator);
111          return typeError(globalObject, scope, shouldThrow, ReadonlyPropertyWriteError);
112      }
113      
114      RELEASE_AND_RETURN(scope, entry.put(globalObject, array, this, value, shouldThrow));
115  }
116  
117  bool SparseArrayValueMap::putDirect(JSGlobalObject* globalObject, JSObject* array, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode)
118  {
119      VM& vm = globalObject->vm();
120      auto scope = DECLARE_THROW_SCOPE(vm);
121      ASSERT(value);
122      
123      bool shouldThrow = (mode == PutDirectIndexShouldThrow);
124  
125      AddResult result = add(array, i);
126      SparseArrayEntry& entry = result.iterator->value;
127  
128      // To save a separate find & add, we first always add to the sparse map.
129      // In the uncommon case that this is a new property, and the array is not
130      // extensible, this is not the right thing to have done - so remove again.
131      if (mode != PutDirectIndexLikePutDirect && result.isNewEntry && !array->isStructureExtensible(vm)) {
132          remove(result.iterator);
133          return typeError(globalObject, scope, shouldThrow, NonExtensibleObjectPropertyDefineError);
134      }
135  
136      if (entry.attributes() & PropertyAttribute::ReadOnly)
137          return typeError(globalObject, scope, shouldThrow, ReadonlyPropertyWriteError);
138  
139      entry.forceSet(vm, this, value, attributes);
140      return true;
141  }
142  
143  JSValue SparseArrayValueMap::getConcurrently(unsigned i)
144  {
145      auto locker = holdLock(cellLock());
146      auto iterator = m_map.find(i);
147      if (iterator == m_map.end())
148          return JSValue();
149      return iterator->value.getConcurrently();
150  }
151  
152  void SparseArrayEntry::get(JSObject* thisObject, PropertySlot& slot) const
153  {
154      JSValue value = Base::get();
155      ASSERT(value);
156  
157      if (LIKELY(!value.isGetterSetter())) {
158          slot.setValue(thisObject, m_attributes, value);
159          return;
160      }
161  
162      slot.setGetterSlot(thisObject, m_attributes, jsCast<GetterSetter*>(value));
163  }
164  
165  void SparseArrayEntry::get(PropertyDescriptor& descriptor) const
166  {
167      descriptor.setDescriptor(Base::get(), m_attributes);
168  }
169  
170  JSValue SparseArrayEntry::getConcurrently() const
171  {
172      // These attributes and value can be updated while executing getConcurrently.
173      // But this is OK since attributes should be never weaken once it gets DontDelete and ReadOnly.
174      // By emitting store-store-fence and load-load-fence between value setting and attributes setting,
175      // we can ensure that the value is what we want once the attributes get ReadOnly & DontDelete:
176      // once attributes get this state, the value should not be changed.
177      unsigned attributes = m_attributes;
178      Dependency attributesDependency = Dependency::fence(attributes);
179      if (attributes & PropertyAttribute::Accessor)
180          return JSValue();
181  
182      if (!(attributes & PropertyAttribute::ReadOnly))
183          return JSValue();
184  
185      if (!(attributes & PropertyAttribute::DontDelete))
186          return JSValue();
187  
188      return attributesDependency.consume(this)->Base::get();
189  }
190  
191  bool SparseArrayEntry::put(JSGlobalObject* globalObject, JSValue thisValue, SparseArrayValueMap* map, JSValue value, bool shouldThrow)
192  {
193      VM& vm = globalObject->vm();
194      auto scope = DECLARE_THROW_SCOPE(vm);
195  
196      if (!(m_attributes & PropertyAttribute::Accessor)) {
197          if (m_attributes & PropertyAttribute::ReadOnly)
198              return typeError(globalObject, scope, shouldThrow, ReadonlyPropertyWriteError);
199  
200          set(vm, map, value);
201          return true;
202      }
203  
204      RELEASE_AND_RETURN(scope, callSetter(globalObject, thisValue, Base::get(), value, shouldThrow ? ECMAMode::strict() : ECMAMode::sloppy()));
205  }
206  
207  JSValue SparseArrayEntry::getNonSparseMode() const
208  {
209      ASSERT(!m_attributes);
210      return Base::get();
211  }
212  
213  void SparseArrayValueMap::visitChildren(JSCell* cell, SlotVisitor& visitor)
214  {
215      SparseArrayValueMap* thisObject = jsCast<SparseArrayValueMap*>(cell);
216      ASSERT_GC_OBJECT_INHERITS(thisObject, info());
217      Base::visitChildren(cell, visitor);
218      {
219          auto locker = holdLock(thisObject->cellLock());
220          for (auto& entry : thisObject->m_map)
221              visitor.append(entry.value.asValue());
222      }
223      visitor.reportExtraMemoryVisited(thisObject->m_reportedCapacity * sizeof(Map::KeyValuePairType));
224  }
225  
226  } // namespace JSC
227