void JSArray::push(ExecState* exec, JSValue value) { checkConsistency(); if (m_storage->m_length < m_vectorLength) { m_storage->m_vector[m_storage->m_length] = value; ++m_storage->m_numValuesInVector; ++m_storage->m_length; checkConsistency(); return; } if (m_storage->m_length < MIN_SPARSE_ARRAY_INDEX) { SparseArrayValueMap* map = m_storage->m_sparseValueMap; if (!map || map->isEmpty()) { if (increaseVectorLength(m_storage->m_length + 1)) { m_storage->m_vector[m_storage->m_length] = value; ++m_storage->m_numValuesInVector; ++m_storage->m_length; checkConsistency(); return; } checkConsistency(); throwOutOfMemoryError(exec); return; } } putSlowCase(exec, m_storage->m_length++, value); }
NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue value) { ArrayStorage* storage = m_storage; SparseArrayValueMap* map = storage->m_sparseValueMap; if (i >= MIN_SPARSE_ARRAY_INDEX) { if (i > MAX_ARRAY_INDEX) { PutPropertySlot slot; put(exec, Identifier::from(exec, i), value, slot); return; } // We miss some cases where we could compact the storage, such as a large array that is being filled from the end // (which will only be compacted as we reach indices that are less than MIN_SPARSE_ARRAY_INDEX) - but this makes the check much faster. if ((i > MAX_STORAGE_VECTOR_INDEX) || !isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) { if (!map) { map = new SparseArrayValueMap; storage->m_sparseValueMap = map; } pair<SparseArrayValueMap::iterator, bool> result = map->add(i, value); if (!result.second) { // pre-existing entry result.first->second = value; return; } size_t capacity = map->capacity(); if (capacity != storage->reportedMapCapacity) { Heap::heap(this)->reportExtraMemoryCost((capacity - storage->reportedMapCapacity) * (sizeof(unsigned) + sizeof(JSValue))); storage->reportedMapCapacity = capacity; } return; } } // We have decided that we'll put the new item into the vector. // Fast case is when there is no sparse map, so we can increase the vector size without moving values from it. if (!map || map->isEmpty()) { if (increaseVectorLength(i + 1)) { storage = m_storage; storage->m_vector[i] = value; ++storage->m_numValuesInVector; checkConsistency(); } else throwOutOfMemoryError(exec); return; } // Decide how many values it would be best to move from the map. unsigned newNumValuesInVector = storage->m_numValuesInVector + 1; unsigned newVectorLength = increasedVectorLength(i + 1); for (unsigned j = max(m_vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j) newNumValuesInVector += map->contains(j); if (i >= MIN_SPARSE_ARRAY_INDEX) newNumValuesInVector -= map->contains(i); if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) { unsigned proposedNewNumValuesInVector = newNumValuesInVector; // If newVectorLength is already the maximum - MAX_STORAGE_VECTOR_LENGTH - then do not attempt to grow any further. while (newVectorLength < MAX_STORAGE_VECTOR_LENGTH) { unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1); for (unsigned j = max(newVectorLength, MIN_SPARSE_ARRAY_INDEX); j < proposedNewVectorLength; ++j) proposedNewNumValuesInVector += map->contains(j); if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector)) break; newVectorLength = proposedNewVectorLength; newNumValuesInVector = proposedNewNumValuesInVector; } } storage = static_cast<ArrayStorage*>(tryFastRealloc(storage, storageSize(newVectorLength))); if (!storage) { throwOutOfMemoryError(exec); return; } unsigned vectorLength = m_vectorLength; if (newNumValuesInVector == storage->m_numValuesInVector + 1) { for (unsigned j = vectorLength; j < newVectorLength; ++j) storage->m_vector[j] = JSValue(); if (i > MIN_SPARSE_ARRAY_INDEX) map->remove(i); } else { for (unsigned j = vectorLength; j < max(vectorLength, MIN_SPARSE_ARRAY_INDEX); ++j) storage->m_vector[j] = JSValue(); for (unsigned j = max(vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j) storage->m_vector[j] = map->take(j); } storage->m_vector[i] = value; m_vectorLength = newVectorLength; storage->m_numValuesInVector = newNumValuesInVector; m_storage = storage; checkConsistency(); Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength)); }
NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue* value) { ArrayStorage* storage = m_storage; SparseArrayValueMap* map = storage->m_sparseValueMap; if (i >= sparseArrayCutoff) { if (i > maxArrayIndex) { put(exec, Identifier::from(exec, i), value); return; } // We miss some cases where we could compact the storage, such as a large array that is being filled from the end // (which will only be compacted as we reach indices that are less than cutoff) - but this makes the check much faster. if (!isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) { if (!map) { map = new SparseArrayValueMap; storage->m_sparseValueMap = map; } map->set(i, value); return; } } // We have decided that we'll put the new item into the vector. // Fast case is when there is no sparse map, so we can increase the vector size without moving values from it. if (!map || map->isEmpty()) { increaseVectorLength(i + 1); storage = m_storage; ++storage->m_numValuesInVector; storage->m_vector[i] = value; checkConsistency(); return; } // Decide how many values it would be best to move from the map. unsigned newNumValuesInVector = storage->m_numValuesInVector + 1; unsigned newVectorLength = increasedVectorLength(i + 1); for (unsigned j = max(storage->m_vectorLength, sparseArrayCutoff); j < newVectorLength; ++j) newNumValuesInVector += map->contains(j); if (i >= sparseArrayCutoff) newNumValuesInVector -= map->contains(i); if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) { unsigned proposedNewNumValuesInVector = newNumValuesInVector; while (true) { unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1); for (unsigned j = max(newVectorLength, sparseArrayCutoff); j < proposedNewVectorLength; ++j) proposedNewNumValuesInVector += map->contains(j); if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector)) break; newVectorLength = proposedNewVectorLength; newNumValuesInVector = proposedNewNumValuesInVector; } } storage = static_cast<ArrayStorage*>(fastRealloc(storage, storageSize(newVectorLength))); unsigned vectorLength = storage->m_vectorLength; if (newNumValuesInVector == storage->m_numValuesInVector + 1) { for (unsigned j = vectorLength; j < newVectorLength; ++j) storage->m_vector[j] = 0; if (i > sparseArrayCutoff) map->remove(i); } else { for (unsigned j = vectorLength; j < max(vectorLength, sparseArrayCutoff); ++j) storage->m_vector[j] = 0; for (unsigned j = max(vectorLength, sparseArrayCutoff); j < newVectorLength; ++j) storage->m_vector[j] = map->take(j); } storage->m_vector[i] = value; storage->m_vectorLength = newVectorLength; storage->m_numValuesInVector = newNumValuesInVector; m_storage = storage; checkConsistency(); }