📄 jsarray.cpp
字号:
/* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) * Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2003 Peter Kelly (pmk@post.com) * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */#include "config.h"#include "JSArray.h"#include "ArrayPrototype.h"#include "PropertyNameArray.h"#include <wtf/AVLTree.h>#include <wtf/Assertions.h>#include <Operations.h>#define CHECK_ARRAY_CONSISTENCY 0using namespace std;using namespace WTF;namespace JSC {ASSERT_CLASS_FITS_IN_CELL(JSArray);// Overview of JSArray//// Properties of JSArray objects may be stored in one of three locations:// * The regular JSObject property map.// * A storage vector.// * A sparse map of array entries.//// Properties with non-numeric identifiers, with identifiers that are not representable// as an unsigned integer, or where the value is greater than MAX_ARRAY_INDEX// (specifically, this is only one property - the value 0xFFFFFFFFU as an unsigned 32-bit// integer) are not considered array indices and will be stored in the JSObject property map.//// All properties with a numeric identifer, representable as an unsigned integer i,// where (i <= MAX_ARRAY_INDEX), are an array index and will be stored in either the// storage vector or the sparse map. An array index i will be handled in the following// fashion://// * Where (i < MIN_SPARSE_ARRAY_INDEX) the value will be stored in the storage vector.// * Where (MIN_SPARSE_ARRAY_INDEX <= i <= MAX_STORAGE_VECTOR_INDEX) the value will either// be stored in the storage vector or in the sparse array, depending on the density of// data that would be stored in the vector (a vector being used where at least// (1 / minDensityMultiplier) of the entries would be populated).// * Where (MAX_STORAGE_VECTOR_INDEX < i <= MAX_ARRAY_INDEX) the value will always be stored// in the sparse array.// The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize// function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage// size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(JSValuePtr)) +// (vectorLength * sizeof(JSValuePtr)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).#define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(JSValuePtr))) / sizeof(JSValuePtr))// These values have to be macros to be used in max() and min() without introducing// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.#define MIN_SPARSE_ARRAY_INDEX 10000U#define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)// 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer.#define MAX_ARRAY_INDEX 0xFFFFFFFEU// Our policy for when to use a vector and when to use a sparse map.// For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector.// When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector// as long as it is 1/8 full. If more sparse than that, we use a map.static const unsigned minDensityMultiplier = 8;const ClassInfo JSArray::info = {"Array", 0, 0, 0};static inline size_t storageSize(unsigned vectorLength){ ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH); // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH) // - as asserted above - the following calculation cannot overflow. size_t size = (sizeof(ArrayStorage) - sizeof(JSValuePtr)) + (vectorLength * sizeof(JSValuePtr)); // Assertion to detect integer overflow in previous calculation (should not be possible, provided that // MAX_STORAGE_VECTOR_LENGTH is correctly defined). ASSERT(((size - (sizeof(ArrayStorage) - sizeof(JSValuePtr))) / sizeof(JSValuePtr) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(JSValuePtr)))); return size;}static inline unsigned increasedVectorLength(unsigned newLength){ ASSERT(newLength <= MAX_STORAGE_VECTOR_LENGTH); // Mathematically equivalent to: // increasedLength = (newLength * 3 + 1) / 2; // or: // increasedLength = (unsigned)ceil(newLength * 1.5)); // This form is not prone to internal overflow. unsigned increasedLength = newLength + (newLength >> 1) + (newLength & 1); ASSERT(increasedLength >= newLength); return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH);}static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues){ return length / minDensityMultiplier <= numValues;}#if !CHECK_ARRAY_CONSISTENCYinline void JSArray::checkConsistency(ConsistencyCheckType){}#endifJSArray::JSArray(PassRefPtr<Structure> structure) : JSObject(structure){ unsigned initialCapacity = 0; m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity))); m_fastAccessCutoff = 0; m_storage->m_vectorLength = initialCapacity; m_storage->m_length = 0; checkConsistency();}JSArray::JSArray(PassRefPtr<Structure> structure, unsigned initialLength) : JSObject(structure){ unsigned initialCapacity = min(initialLength, MIN_SPARSE_ARRAY_INDEX); m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity))); m_fastAccessCutoff = 0; m_storage->m_vectorLength = initialCapacity; m_storage->m_length = initialLength; Heap::heap(this)->reportExtraMemoryCost(initialCapacity * sizeof(JSValuePtr)); checkConsistency();}JSArray::JSArray(ExecState* exec, PassRefPtr<Structure> structure, const ArgList& list) : JSObject(structure){ unsigned length = list.size(); m_fastAccessCutoff = length; ArrayStorage* storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(length))); storage->m_vectorLength = length; storage->m_numValuesInVector = length; storage->m_sparseValueMap = 0; storage->m_length = length; size_t i = 0; ArgList::const_iterator end = list.end(); for (ArgList::const_iterator it = list.begin(); it != end; ++it, ++i) storage->m_vector[i] = (*it).jsValue(exec); m_storage = storage; Heap::heap(this)->reportExtraMemoryCost(storageSize(length)); checkConsistency();}JSArray::~JSArray(){ checkConsistency(DestructorConsistencyCheck); delete m_storage->m_sparseValueMap; fastFree(m_storage);}bool JSArray::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot){ ArrayStorage* storage = m_storage; if (i >= storage->m_length) { if (i > MAX_ARRAY_INDEX) return getOwnPropertySlot(exec, Identifier::from(exec, i), slot); return false; } if (i < storage->m_vectorLength) { JSValuePtr& valueSlot = storage->m_vector[i]; if (valueSlot) { slot.setValueSlot(&valueSlot); return true; } } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) { if (i >= MIN_SPARSE_ARRAY_INDEX) { SparseArrayValueMap::iterator it = map->find(i); if (it != map->end()) { slot.setValueSlot(&it->second); return true; } } } return false;}bool JSArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot){ if (propertyName == exec->propertyNames().length) { slot.setValue(jsNumber(exec, length())); return true; } bool isArrayIndex; unsigned i = propertyName.toArrayIndex(&isArrayIndex); if (isArrayIndex) return JSArray::getOwnPropertySlot(exec, i, slot); return JSObject::getOwnPropertySlot(exec, propertyName, slot);}// ECMA 15.4.5.1void JSArray::put(ExecState* exec, const Identifier& propertyName, JSValuePtr value, PutPropertySlot& slot){ bool isArrayIndex; unsigned i = propertyName.toArrayIndex(&isArrayIndex); if (isArrayIndex) { put(exec, i, value); return; } if (propertyName == exec->propertyNames().length) { unsigned newLength = value.toUInt32(exec); if (value.toNumber(exec) != static_cast<double>(newLength)) { throwError(exec, RangeError, "Invalid array length."); return; } setLength(newLength); return; } JSObject::put(exec, propertyName, value, slot);}void JSArray::put(ExecState* exec, unsigned i, JSValuePtr value){ checkConsistency(); unsigned length = m_storage->m_length; if (i >= length && i <= MAX_ARRAY_INDEX) { length = i + 1; m_storage->m_length = length; } if (i < m_storage->m_vectorLength) { JSValuePtr& valueSlot = m_storage->m_vector[i]; if (valueSlot) { valueSlot = value; checkConsistency(); return; } valueSlot = value; if (++m_storage->m_numValuesInVector == m_storage->m_length) m_fastAccessCutoff = m_storage->m_length; checkConsistency(); return; } putSlowCase(exec, i, value);}NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValuePtr 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 cutoff) - 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; } 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()) { if (increaseVectorLength(i + 1)) { storage = m_storage; storage->m_vector[i] = value; if (++storage->m_numValuesInVector == storage->m_length) m_fastAccessCutoff = storage->m_length; 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(storage->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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -