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); }
void JSArray::setLength(unsigned newLength) { checkConsistency(); ArrayStorage* storage = m_storage; unsigned length = m_storage->m_length; if (newLength < length) { unsigned usedVectorLength = min(length, m_vectorLength); for (unsigned i = newLength; i < usedVectorLength; ++i) { JSValue& valueSlot = storage->m_vector[i]; bool hadValue = valueSlot; valueSlot = JSValue(); storage->m_numValuesInVector -= hadValue; } if (SparseArrayValueMap* map = storage->m_sparseValueMap) { SparseArrayValueMap copy = *map; SparseArrayValueMap::iterator end = copy.end(); for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) { if (it->first >= newLength) map->remove(it->first); } if (map->isEmpty()) { delete map; storage->m_sparseValueMap = 0; } } } m_storage->m_length = newLength; checkConsistency(); }
void JSArray::setLengthWritable(ExecState* exec, bool writable) { ASSERT(isLengthWritable() || !writable); if (!isLengthWritable() || writable) return; enterDictionaryIndexingMode(exec->vm()); SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get(); ASSERT(map); map->setLengthIsReadOnly(); }
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)); }
SparseArrayValueMap* SparseArrayValueMap::create(VM& vm) { SparseArrayValueMap* result = new (NotNull, allocateCell<SparseArrayValueMap>(vm.heap)) SparseArrayValueMap(vm); result->finishCreation(vm); return result; }
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(); }