-// We keep track of the size of the last array after it was grown. We use this
-// as a simple heuristic for as the value to grow the next array from size 0.
-// This value is capped by the constant FIRST_VECTOR_GROW defined above.
-static unsigned lastArraySize = 0;
-
-static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
-{
- return length <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues;
-}
-
-static bool reject(ExecState* exec, bool throwException, const char* message)
-{
- if (throwException)
- throwTypeError(exec, message);
- return false;
-}
-
-#if !CHECK_ARRAY_CONSISTENCY
-
-inline void JSArray::checkConsistency(ConsistencyCheckType)
-{
-}
-
-#endif
-
-void JSArray::finishCreation(JSGlobalData& globalData, unsigned initialLength)
-{
- Base::finishCreation(globalData);
- ASSERT(inherits(&s_info));
-
- unsigned initialVectorLength = BASE_VECTOR_LEN;
- unsigned initialStorageSize = storageSize(initialVectorLength);
-
- void* newStorage = 0;
- if (!globalData.heap.tryAllocateStorage(initialStorageSize, &newStorage))
- CRASH();
-
- m_storage = static_cast<ArrayStorage*>(newStorage);
- m_storage->m_allocBase = m_storage;
- m_storage->m_length = initialLength;
- m_vectorLength = initialVectorLength;
- m_storage->m_numValuesInVector = 0;
-#if CHECK_ARRAY_CONSISTENCY
- m_storage->m_inCompactInitialization = false;
-#endif
-
- checkConsistency();
-}
-
-JSArray* JSArray::tryFinishCreationUninitialized(JSGlobalData& globalData, unsigned initialLength)
-{
- Base::finishCreation(globalData);
- ASSERT(inherits(&s_info));
-
- // Check for lengths larger than we can handle with a vector.
- if (initialLength > MAX_STORAGE_VECTOR_LENGTH)
- return 0;
-
- unsigned initialVectorLength = max(initialLength, BASE_VECTOR_LEN);
- unsigned initialStorageSize = storageSize(initialVectorLength);
-
- void* newStorage = 0;
- if (!globalData.heap.tryAllocateStorage(initialStorageSize, &newStorage))
- CRASH();
-
- m_storage = static_cast<ArrayStorage*>(newStorage);
- m_storage->m_allocBase = m_storage;
- m_storage->m_length = initialLength;
- m_vectorLength = initialVectorLength;
- m_storage->m_numValuesInVector = initialLength;
-
-#if CHECK_ARRAY_CONSISTENCY
- m_storage->m_initializationIndex = 0;
- m_storage->m_inCompactInitialization = true;
-#endif
-
- return this;
-}
-
-// This function can be called multiple times on the same object.
-void JSArray::finalize(JSCell* cell)
-{
- JSArray* thisObject = jsCast<JSArray*>(cell);
- thisObject->checkConsistency(DestructorConsistencyCheck);
- thisObject->deallocateSparseMap();
-}
-
-inline SparseArrayValueMap::AddResult SparseArrayValueMap::add(JSArray* array, unsigned i)
-{
- SparseArrayEntry entry;
- entry.setWithoutWriteBarrier(jsUndefined());
-
- AddResult result = m_map.add(i, entry);
- size_t capacity = m_map.capacity();
- if (capacity != m_reportedCapacity) {
- Heap::heap(array)->reportExtraMemoryCost((capacity - m_reportedCapacity) * (sizeof(unsigned) + sizeof(WriteBarrier<Unknown>)));
- m_reportedCapacity = capacity;
- }
- return result;
-}
-
-inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i, JSValue value, bool shouldThrow)
-{
- AddResult result = add(array, i);
- SparseArrayEntry& entry = result.iterator->second;
-
- // To save a separate find & add, we first always add to the sparse map.
- // In the uncommon case that this is a new property, and the array is not
- // extensible, this is not the right thing to have done - so remove again.
- if (result.isNewEntry && !array->isExtensible()) {
- remove(result.iterator);
- if (shouldThrow)
- throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
- return;
- }
-
- if (!(entry.attributes & Accessor)) {
- if (entry.attributes & ReadOnly) {
- if (shouldThrow)
- throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
- return;
- }
-
- entry.set(exec->globalData(), array, value);
- return;
- }
-
- JSValue accessor = entry.Base::get();
- ASSERT(accessor.isGetterSetter());
- JSObject* setter = asGetterSetter(accessor)->setter();
-
- if (!setter) {
- if (shouldThrow)
- throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
- return;
- }
-
- CallData callData;
- CallType callType = setter->methodTable()->getCallData(setter, callData);
- MarkedArgumentBuffer args;
- args.append(value);
- call(exec, setter, callType, callData, array, args);
-}
-
-inline bool SparseArrayValueMap::putDirect(ExecState* exec, JSArray* array, unsigned i, JSValue value, bool shouldThrow)
-{
- AddResult result = add(array, i);
- SparseArrayEntry& entry = result.iterator->second;
-
- // To save a separate find & add, we first always add to the sparse map.
- // In the uncommon case that this is a new property, and the array is not
- // extensible, this is not the right thing to have done - so remove again.
- if (result.isNewEntry && !array->isExtensible()) {
- remove(result.iterator);
- return reject(exec, shouldThrow, "Attempting to define property on object that is not extensible.");
- }
-
- entry.attributes = 0;
- entry.set(exec->globalData(), array, value);
- return true;
-}
-
-inline void SparseArrayEntry::get(PropertySlot& slot) const
-{
- JSValue value = Base::get();
- ASSERT(value);
-
- if (LIKELY(!value.isGetterSetter())) {
- slot.setValue(value);
- return;
- }
-
- JSObject* getter = asGetterSetter(value)->getter();
- if (!getter) {
- slot.setUndefined();
- return;
- }
-
- slot.setGetterSlot(getter);
-}
-
-inline void SparseArrayEntry::get(PropertyDescriptor& descriptor) const
-{
- descriptor.setDescriptor(Base::get(), attributes);
-}
-
-inline JSValue SparseArrayEntry::get(ExecState* exec, JSArray* array) const
-{
- JSValue result = Base::get();
- ASSERT(result);
-
- if (LIKELY(!result.isGetterSetter()))
- return result;
-
- JSObject* getter = asGetterSetter(result)->getter();
- if (!getter)
- return jsUndefined();
-
- CallData callData;
- CallType callType = getter->methodTable()->getCallData(getter, callData);
- return call(exec, getter, callType, callData, array, exec->emptyList());
-}
-
-inline JSValue SparseArrayEntry::getNonSparseMode() const
-{
- ASSERT(!attributes);
- return Base::get();
-}
-
-inline void SparseArrayValueMap::visitChildren(SlotVisitor& visitor)
-{
- iterator end = m_map.end();
- for (iterator it = m_map.begin(); it != end; ++it)
- visitor.append(&it->second);
-}
-
-void JSArray::allocateSparseMap(JSGlobalData& globalData)
-{
- m_sparseValueMap = new SparseArrayValueMap;
- globalData.heap.addFinalizer(this, finalize);
-}
-
-void JSArray::deallocateSparseMap()
-{
- delete m_sparseValueMap;
- m_sparseValueMap = 0;
-}
-
-void JSArray::enterDictionaryMode(JSGlobalData& globalData)
-{
- ArrayStorage* storage = m_storage;
- SparseArrayValueMap* map = m_sparseValueMap;
-
- if (!map) {
- allocateSparseMap(globalData);
- map = m_sparseValueMap;
- }
-
- if (map->sparseMode())
- return;
-
- map->setSparseMode();
-
- unsigned usedVectorLength = min(storage->m_length, m_vectorLength);
- for (unsigned i = 0; i < usedVectorLength; ++i) {
- JSValue value = storage->m_vector[i].get();
- // This will always be a new entry in the map, so no need to check we can write,
- // and attributes are default so no need to set them.
- if (value)
- map->add(this, i).iterator->second.set(globalData, this, value);
- }
-
- void* newRawStorage = 0;
- if (!globalData.heap.tryAllocateStorage(storageSize(0), &newRawStorage))
- CRASH();
-
- ArrayStorage* newStorage = static_cast<ArrayStorage*>(newRawStorage);
- memcpy(newStorage, m_storage, storageSize(0));
- newStorage->m_allocBase = newStorage;
- m_storage = newStorage;
- m_indexBias = 0;
- m_vectorLength = 0;
-}
-
-void JSArray::putDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)