diff options
Diffstat (limited to 'Source/JavaScriptCore/runtime/JSArray.cpp')
-rw-r--r-- | Source/JavaScriptCore/runtime/JSArray.cpp | 297 |
1 files changed, 149 insertions, 148 deletions
diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp index b3210083d..c84fb5b10 100644 --- a/Source/JavaScriptCore/runtime/JSArray.cpp +++ b/Source/JavaScriptCore/runtime/JSArray.cpp @@ -24,6 +24,8 @@ #include "JSArray.h" #include "ArrayPrototype.h" +#include "BumpSpace.h" +#include "BumpSpaceInlineMethods.h" #include "CachedCall.h" #include "Error.h" #include "Executable.h" @@ -130,7 +132,10 @@ inline void JSArray::checkConsistency(ConsistencyCheckType) JSArray::JSArray(JSGlobalData& globalData, Structure* structure) : JSNonFinalObject(globalData, structure) + , m_indexBias(0) , m_storage(0) + , m_sparseValueMap(0) + , m_subclassData(0) { } @@ -142,13 +147,14 @@ void JSArray::finishCreation(JSGlobalData& globalData, unsigned initialLength) unsigned initialVectorLength = BASE_VECTOR_LEN; unsigned initialStorageSize = storageSize(initialVectorLength); - m_storage = static_cast<ArrayStorage*>(fastMalloc(initialStorageSize)); + 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_indexBias = 0; m_vectorLength = initialVectorLength; - m_storage->m_sparseValueMap = 0; - m_storage->subclassData = 0; m_storage->m_numValuesInVector = 0; #if CHECK_ARRAY_CONSISTENCY m_storage->m_inCompactInitialization = false; @@ -159,8 +165,6 @@ void JSArray::finishCreation(JSGlobalData& globalData, unsigned initialLength) vector[i].clear(); checkConsistency(); - - Heap::heap(this)->reportExtraMemoryCost(initialStorageSize); } JSArray* JSArray::tryFinishCreationUninitialized(JSGlobalData& globalData, unsigned initialLength) @@ -175,14 +179,16 @@ JSArray* JSArray::tryFinishCreationUninitialized(JSGlobalData& globalData, unsig unsigned initialVectorLength = max(initialLength, BASE_VECTOR_LEN); unsigned initialStorageSize = storageSize(initialVectorLength); - m_storage = static_cast<ArrayStorage*>(fastMalloc(initialStorageSize)); + 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 = 0; - m_indexBias = 0; m_vectorLength = initialVectorLength; - m_storage->m_sparseValueMap = 0; - m_storage->subclassData = 0; m_storage->m_numValuesInVector = initialLength; + #if CHECK_ARRAY_CONSISTENCY m_storage->m_inCompactInitialization = true; #endif @@ -191,26 +197,15 @@ JSArray* JSArray::tryFinishCreationUninitialized(JSGlobalData& globalData, unsig for (size_t i = initialLength; i < initialVectorLength; ++i) vector[i].clear(); - Heap::heap(this)->reportExtraMemoryCost(initialStorageSize); return this; } -JSArray::~JSArray() -{ - ASSERT(jsCast<JSArray*>(this)); - - // If we are unable to allocate memory for m_storage then this may be null. - if (!m_storage) - return; - - checkConsistency(DestructorConsistencyCheck); - delete m_storage->m_sparseValueMap; - fastFree(m_storage->m_allocBase); -} - -void JSArray::destroy(JSCell* cell) +// This function can be called multiple times on the same object. +void JSArray::finalize(JSCell* cell) { - jsCast<JSArray*>(cell)->JSArray::~JSArray(); + JSArray* thisObject = jsCast<JSArray*>(cell); + thisObject->checkConsistency(DestructorConsistencyCheck); + thisObject->deallocateSparseMap(); } inline std::pair<SparseArrayValueMap::iterator, bool> SparseArrayValueMap::add(JSArray* array, unsigned i) @@ -229,7 +224,7 @@ inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i { SparseArrayEntry& entry = add(array, i).first->second; - if (!(entry.attributes & (Getter | Setter))) { + if (!(entry.attributes & Accessor)) { if (entry.attributes & ReadOnly) { // FIXME: should throw if being called from strict mode. // throwTypeError(exec, StrictModeReadonlyPropertyWriteError); @@ -310,13 +305,27 @@ inline void SparseArrayValueMap::visitChildren(SlotVisitor& visitor) visitor.append(&it->second); } -void JSArray::enterSparseMode(JSGlobalData& globalData) +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 = storage->m_sparseValueMap; + SparseArrayValueMap* map = m_sparseValueMap; - if (!map) - map = storage->m_sparseValueMap = new SparseArrayValueMap; + if (!map) { + allocateSparseMap(globalData); + map = m_sparseValueMap; + } if (map->sparseMode()) return; @@ -332,10 +341,13 @@ void JSArray::enterSparseMode(JSGlobalData& globalData) map->add(this, i).first->second.set(globalData, this, value); } - ArrayStorage* newStorage = static_cast<ArrayStorage*>(fastMalloc(storageSize(0))); + 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; - fastFree(m_storage); m_storage = newStorage; m_indexBias = 0; m_vectorLength = 0; @@ -348,25 +360,21 @@ void JSArray::putDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, Prope entryInMap->set(exec->globalData(), this, descriptor.value()); else if (oldDescriptor.isAccessorDescriptor()) entryInMap->set(exec->globalData(), this, jsUndefined()); - entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~(Getter | Setter); + entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor; return; } if (descriptor.isAccessorDescriptor()) { JSObject* getter = 0; - if (descriptor.getter() && descriptor.getter().isObject()) - getter = asObject(descriptor.getter()); - if (!getter && oldDescriptor.isAccessorDescriptor()) { - if (oldDescriptor.getter() && oldDescriptor.getter().isObject()) - getter = asObject(oldDescriptor.getter()); - } + if (descriptor.getterPresent()) + getter = descriptor.getterObject(); + else if (oldDescriptor.isAccessorDescriptor()) + getter = oldDescriptor.getterObject(); JSObject* setter = 0; - if (descriptor.setter() && descriptor.setter().isObject()) - setter = asObject(descriptor.setter()); - if (!setter && oldDescriptor.isAccessorDescriptor()) { - if (oldDescriptor.setter() && oldDescriptor.setter().isObject()) - setter = asObject(oldDescriptor.setter()); - } + if (descriptor.setterPresent()) + setter = descriptor.setterObject(); + else if (oldDescriptor.isAccessorDescriptor()) + setter = oldDescriptor.setterObject(); GetterSetter* accessor = GetterSetter::create(exec); if (getter) @@ -406,10 +414,10 @@ bool JSArray::defineOwnNumericProperty(ExecState* exec, unsigned index, Property return true; } - enterSparseMode(exec->globalData()); + enterDictionaryMode(exec->globalData()); } - SparseArrayValueMap* map = m_storage->m_sparseValueMap; + SparseArrayValueMap* map = m_sparseValueMap; ASSERT(map); // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P. @@ -491,6 +499,7 @@ bool JSArray::defineOwnNumericProperty(ExecState* exec, unsigned index, Property } // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable. } else { + ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent()); // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then if (!current.configurable()) { // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false. @@ -515,9 +524,9 @@ void JSArray::setLengthWritable(ExecState* exec, bool writable) if (!isLengthWritable() || writable) return; - enterSparseMode(exec->globalData()); + enterDictionaryMode(exec->globalData()); - SparseArrayValueMap* map = m_storage->m_sparseValueMap; + SparseArrayValueMap* map = m_sparseValueMap; ASSERT(map); map->setLengthIsReadOnly(); } @@ -531,10 +540,10 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, const Identif if (propertyName == exec->propertyNames().length) { // All paths through length definition call the default [[DefineOwnProperty]], hence: // from ES5.1 8.12.9 7.a. - if (descriptor.configurable()) + if (descriptor.configurablePresent() && descriptor.configurable()) return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property."); // from ES5.1 8.12.9 7.b. - if (descriptor.enumerable()) + if (descriptor.enumerablePresent() && descriptor.enumerable()) return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property."); // a. If the [[Value]] field of Desc is absent, then @@ -542,11 +551,12 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, const Identif if (descriptor.isAccessorDescriptor()) return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property."); // from ES5.1 8.12.9 10.a. - if (!array->isLengthWritable() && descriptor.writable()) + if (!array->isLengthWritable() && descriptor.writablePresent() && descriptor.writable()) return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property."); // This descriptor is either just making length read-only, or changing nothing! if (!descriptor.value()) { - array->setLengthWritable(exec, descriptor.writable()); + if (descriptor.writablePresent()) + array->setLengthWritable(exec, descriptor.writable()); return true; } @@ -561,7 +571,8 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, const Identif // Based on SameValue check in 8.12.9, this is always okay. if (newLen == array->length()) { - array->setLengthWritable(exec, descriptor.writable()); + if (descriptor.writablePresent()) + array->setLengthWritable(exec, descriptor.writable()); return true; } @@ -588,13 +599,17 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, const Identif // 2. If newWritable is false, set newLenDesc.[[Writable] to false. // 3. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and false as arguments. // 4. Reject. - array->setLengthWritable(exec, descriptor.writable()); + if (descriptor.writablePresent()) + array->setLengthWritable(exec, descriptor.writable()); return false; } // m. If newWritable is false, then - // i. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Property Descriptor{[[Writable]]: false}, and false as arguments. This call will always return true. - array->setLengthWritable(exec, descriptor.writable()); + // i. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", + // Property Descriptor{[[Writable]]: false}, and false as arguments. This call will always + // return true. + if (descriptor.writablePresent()) + array->setLengthWritable(exec, descriptor.writable()); // n. Return true. return true; } @@ -623,7 +638,7 @@ bool JSArray::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned { JSArray* thisObject = jsCast<JSArray*>(cell); ArrayStorage* storage = thisObject->m_storage; - + if (i >= storage->m_length) { if (i > MAX_ARRAY_INDEX) return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); @@ -636,7 +651,7 @@ bool JSArray::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned slot.setValue(value); return true; } - } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) { SparseArrayValueMap::iterator it = map->find(i); if (it != map->notFound()) { it->second.get(slot); @@ -684,7 +699,7 @@ bool JSArray::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const descriptor.setDescriptor(value.get(), 0); return true; } - } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) { SparseArrayValueMap::iterator it = map->find(i); if (it != map->notFound()) { it->second.get(descriptor); @@ -765,7 +780,7 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigne ASSERT(i <= MAX_ARRAY_INDEX); ArrayStorage* storage = m_storage; - SparseArrayValueMap* map = storage->m_sparseValueMap; + SparseArrayValueMap* map = m_sparseValueMap; // First, handle cases where we don't currently have a sparse map. if (LIKELY(!map)) { @@ -774,7 +789,7 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigne storage->m_length = i + 1; // Check that it is sensible to still be using a vector, and then try to grow the vector. - if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(i + 1))) { + if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(globalData, i + 1))) { // success! - reread m_storage since it has likely been reallocated, and store to the vector. storage = m_storage; storage->m_vector[i].set(globalData, this, value); @@ -782,8 +797,8 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigne return; } // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value. - map = new SparseArrayValueMap; - storage->m_sparseValueMap = map; + allocateSparseMap(exec->globalData()); + map = m_sparseValueMap; map->put(exec, this, i, value); return; } @@ -803,7 +818,7 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigne // We are currently using a map - check whether we still want to be doing so. // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails. unsigned numValuesInArray = storage->m_numValuesInVector + map->size(); - if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(length)) { + if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length)) { map->put(exec, this, i, value); return; } @@ -817,8 +832,7 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigne SparseArrayValueMap::const_iterator end = map->end(); for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) vector[it->first].set(globalData, this, it->second.getNonSparseMode()); - delete map; - storage->m_sparseValueMap = 0; + deallocateSparseMap(); // Store the new property into the vector. WriteBarrier<Unknown>& valueSlot = vector[i]; @@ -857,7 +871,7 @@ bool JSArray::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) valueSlot.clear(); --storage->m_numValuesInVector; } - } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) { SparseArrayValueMap::iterator it = map->find(i); if (it != map->notFound()) { if (it->second.attributes & DontDelete) @@ -892,7 +906,7 @@ void JSArray::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNam propertyNames.add(Identifier::from(exec, i)); } - if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) { Vector<unsigned> keys; keys.reserveCapacity(map->size()); @@ -940,7 +954,7 @@ ALWAYS_INLINE unsigned JSArray::getNewVectorLength(unsigned desiredLength) return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH); } -bool JSArray::increaseVectorLength(unsigned newLength) +bool JSArray::increaseVectorLength(JSGlobalData& globalData, unsigned newLength) { // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map // to the vector. Callers have to account for that, because they can do it more efficiently. @@ -952,15 +966,16 @@ bool JSArray::increaseVectorLength(unsigned newLength) unsigned vectorLength = m_vectorLength; ASSERT(newLength > vectorLength); unsigned newVectorLength = getNewVectorLength(newLength); - void* baseStorage = storage->m_allocBase; // Fast case - there is no precapacity. In these cases a realloc makes sense. if (LIKELY(!m_indexBias)) { - if (!tryFastRealloc(baseStorage, storageSize(newVectorLength)).getValue(baseStorage)) + void* newStorage = storage->m_allocBase; + if (!globalData.heap.tryReallocateStorage(&newStorage, storageSize(vectorLength), storageSize(newVectorLength))) return false; - storage = m_storage = reinterpret_cast_ptr<ArrayStorage*>(baseStorage); - m_storage->m_allocBase = baseStorage; + storage = m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(newStorage)); + m_storage->m_allocBase = newStorage; + ASSERT(m_storage->m_allocBase); WriteBarrier<Unknown>* vector = storage->m_vector; for (unsigned i = vectorLength; i < newVectorLength; ++i) @@ -968,7 +983,6 @@ bool JSArray::increaseVectorLength(unsigned newLength) m_vectorLength = newVectorLength; - Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength)); return true; } @@ -976,15 +990,11 @@ bool JSArray::increaseVectorLength(unsigned newLength) unsigned newIndexBias = min(m_indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength); // Calculate new stoarge capcity, allowing room for the pre-capacity. unsigned newStorageCapacity = newVectorLength + newIndexBias; - void* newAllocBase; - if (!tryFastMalloc(storageSize(newStorageCapacity)).getValue(newAllocBase)) + void* newAllocBase = 0; + if (!globalData.heap.tryAllocateStorage(storageSize(newStorageCapacity), &newAllocBase)) return false; // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH. ASSERT(m_vectorLength <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - m_vectorLength) >= m_indexBias); - unsigned currentCapacity = m_vectorLength + m_indexBias; - // Currently there is no way to report to the heap that the extra capacity is shrinking! - if (newStorageCapacity > currentCapacity) - Heap::heap(this)->reportExtraMemoryCost((newStorageCapacity - currentCapacity) * sizeof(WriteBarrier<Unknown>)); m_vectorLength = newVectorLength; m_indexBias = newIndexBias; @@ -996,14 +1006,13 @@ bool JSArray::increaseVectorLength(unsigned newLength) m_storage->m_vector[i].clear(); // Free the old allocation, update m_allocBase. - fastFree(m_storage->m_allocBase); m_storage->m_allocBase = newAllocBase; return true; } // This method makes room in the vector, but leaves the new space uncleared. -bool JSArray::unshiftCountSlowCase(unsigned count) +bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, unsigned count) { // If not, we should have handled this on the fast path. ASSERT(count > m_indexBias); @@ -1034,19 +1043,16 @@ bool JSArray::unshiftCountSlowCase(unsigned count) // Step 2: // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing on. - void* newAllocBase; + void* newAllocBase = 0; unsigned newStorageCapacity; // If the current storage array is sufficiently large (but not too large!) then just keep using it. if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) { newAllocBase = storage->m_allocBase; newStorageCapacity = currentCapacity; } else { - if (!tryFastMalloc(storageSize(desiredCapacity)).getValue(newAllocBase)) + if (!globalData.heap.tryAllocateStorage(storageSize(desiredCapacity), &newAllocBase)) return false; newStorageCapacity = desiredCapacity; - // Currently there is no way to report to the heap that the extra capacity is shrinking! - if (desiredCapacity > currentCapacity) - Heap::heap(this)->reportExtraMemoryCost((desiredCapacity - currentCapacity) * sizeof(WriteBarrier<Unknown>)); } // Step 3: @@ -1079,7 +1085,6 @@ bool JSArray::unshiftCountSlowCase(unsigned count) // Are we copying into a new allocation? if (newAllocBase != m_storage->m_allocBase) { // Free the old allocation, update m_allocBase. - fastFree(m_storage->m_allocBase); m_storage->m_allocBase = newAllocBase; // We need to clear any entries in the vector beyond length. We only need to @@ -1101,9 +1106,9 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException unsigned length = storage->m_length; // If the length is read only then we enter sparse mode, so should enter the following 'if'. - ASSERT(isLengthWritable() || storage->m_sparseValueMap); + ASSERT(isLengthWritable() || m_sparseValueMap); - if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + if (SparseArrayValueMap* map = m_sparseValueMap) { // Fail if the length is not writable. if (map->lengthIsReadOnly()) return reject(exec, throwException, StrictModeReadonlyPropertyWriteError); @@ -1138,10 +1143,8 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException } else { for (unsigned i = 0; i < keys.size(); ++i) map->remove(keys[i]); - if (map->isEmpty()) { - delete map; - storage->m_sparseValueMap = 0; - } + if (map->isEmpty()) + deallocateSparseMap(); } } } @@ -1190,7 +1193,7 @@ JSValue JSArray::pop(ExecState* exec) result = jsUndefined(); } else { result = jsUndefined(); - if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + if (SparseArrayValueMap* map = m_sparseValueMap) { SparseArrayValueMap::iterator it = map->find(length); if (it != map->notFound()) { unsigned attributes = it->second.attributes; @@ -1206,10 +1209,8 @@ JSValue JSArray::pop(ExecState* exec) } map->remove(it); - if (map->isEmpty() && !map->sparseMode()) { - delete map; - storage->m_sparseValueMap = 0; - } + if (map->isEmpty() && !map->sparseMode()) + deallocateSparseMap(); } } } @@ -1331,7 +1332,7 @@ void JSArray::unshiftCount(ExecState* exec, unsigned count) memmove(newBaseStorage, storage, storageSize(0)); m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage); m_vectorLength += count; - } else if (!unshiftCountSlowCase(count)) { + } else if (!unshiftCountSlowCase(exec->globalData(), count)) { throwOutOfMemoryError(exec); return; } @@ -1349,13 +1350,21 @@ void JSArray::visitChildren(JSCell* cell, SlotVisitor& visitor) ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); JSNonFinalObject::visitChildren(thisObject, visitor); - - ArrayStorage* storage = thisObject->m_storage; - unsigned usedVectorLength = std::min(storage->m_length, thisObject->m_vectorLength); - visitor.appendValues(storage->m_vector, usedVectorLength); + if (thisObject->m_storage) { + ArrayStorage* storage = thisObject->m_storage; + void* baseStorage = storage->m_allocBase; + + visitor.copyAndAppend(reinterpret_cast<void**>(&baseStorage), storageSize(thisObject->m_vectorLength + thisObject->m_indexBias), storage->m_vector->slot(), thisObject->m_vectorLength); + + if (baseStorage != thisObject->m_storage->m_allocBase) { + thisObject->m_storage = reinterpret_cast<ArrayStorage*>(static_cast<char*>(baseStorage) + sizeof(JSValue) * thisObject->m_indexBias); + thisObject->m_storage->m_allocBase = baseStorage; + ASSERT(thisObject->m_storage->m_allocBase); + } + } - if (SparseArrayValueMap* map = storage->m_sparseValueMap) + if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) map->visitChildren(visitor); } @@ -1379,8 +1388,8 @@ void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType cal ArrayStorage* storage = m_storage; - unsigned lengthNotIncludingUndefined = compactForSorting(); - if (storage->m_sparseValueMap) { + unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData()); + if (m_sparseValueMap) { throwOutOfMemoryError(exec); return; } @@ -1412,10 +1421,8 @@ void JSArray::sort(ExecState* exec) { ASSERT(!inSparseMode()); - ArrayStorage* storage = m_storage; - - unsigned lengthNotIncludingUndefined = compactForSorting(); - if (storage->m_sparseValueMap) { + unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData()); + if (m_sparseValueMap) { throwOutOfMemoryError(exec); return; } @@ -1437,7 +1444,7 @@ void JSArray::sort(ExecState* exec) Heap::heap(this)->pushTempSortVector(&values); for (size_t i = 0; i < lengthNotIncludingUndefined; i++) { - JSValue value = storage->m_vector[i].get(); + JSValue value = m_storage->m_vector[i].get(); ASSERT(!value.isUndefined()); values[i].first = value; } @@ -1446,7 +1453,7 @@ void JSArray::sort(ExecState* exec) // a toString call raises an exception. for (size_t i = 0; i < lengthNotIncludingUndefined; i++) - values[i].second = values[i].first.toString(exec); + values[i].second = values[i].first.toString(exec)->value(exec); if (exec->hadException()) { Heap::heap(this)->popTempSortVector(&values); @@ -1467,13 +1474,13 @@ void JSArray::sort(ExecState* exec) // If the toString function changed the length of the array or vector storage, // increase the length to handle the orignal number of actual values. if (m_vectorLength < lengthNotIncludingUndefined) - increaseVectorLength(lengthNotIncludingUndefined); - if (storage->m_length < lengthNotIncludingUndefined) - storage->m_length = lengthNotIncludingUndefined; + increaseVectorLength(exec->globalData(), lengthNotIncludingUndefined); + if (m_storage->m_length < lengthNotIncludingUndefined) + m_storage->m_length = lengthNotIncludingUndefined; JSGlobalData& globalData = exec->globalData(); for (size_t i = 0; i < lengthNotIncludingUndefined; i++) - storage->m_vector[i].set(globalData, this, values[i].first); + m_storage->m_vector[i].set(globalData, this, values[i].first); Heap::heap(this)->popTempSortVector(&values); @@ -1563,18 +1570,16 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, checkConsistency(); - ArrayStorage* storage = m_storage; - // FIXME: This ignores exceptions raised in the compare function or in toNumber. // The maximum tree depth is compiled in - but the caller is clearly up to no good // if a larger array is passed. - ASSERT(storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max())); - if (storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max())) + ASSERT(m_storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max())); + if (m_storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max())) return; - unsigned usedVectorLength = min(storage->m_length, m_vectorLength); - unsigned nodeCount = usedVectorLength + (storage->m_sparseValueMap ? storage->m_sparseValueMap->size() : 0); + unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength); + unsigned nodeCount = usedVectorLength + (m_sparseValueMap ? m_sparseValueMap->size() : 0); if (!nodeCount) return; @@ -1602,14 +1607,14 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree. for (; numDefined < usedVectorLength; ++numDefined) { - JSValue v = storage->m_vector[numDefined].get(); + JSValue v = m_storage->m_vector[numDefined].get(); if (!v || v.isUndefined()) break; tree.abstractor().m_nodes[numDefined].value = v; tree.insert(numDefined); } for (unsigned i = numDefined; i < usedVectorLength; ++i) { - JSValue v = storage->m_vector[i].get(); + JSValue v = m_storage->m_vector[i].get(); if (v) { if (v.isUndefined()) ++numUndefined; @@ -1623,17 +1628,15 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, unsigned newUsedVectorLength = numDefined + numUndefined; - if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + if (SparseArrayValueMap* map = m_sparseValueMap) { newUsedVectorLength += map->size(); if (newUsedVectorLength > m_vectorLength) { // Check that it is possible to allocate an array large enough to hold all the entries. - if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength)) { + if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(exec->globalData(), newUsedVectorLength)) { throwOutOfMemoryError(exec); return; } } - - storage = m_storage; SparseArrayValueMap::const_iterator end = map->end(); for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { @@ -1642,8 +1645,7 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, ++numDefined; } - delete map; - storage->m_sparseValueMap = 0; + deallocateSparseMap(); } ASSERT(tree.abstractor().m_nodes.size() >= numDefined); @@ -1656,19 +1658,19 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, iter.start_iter_least(tree); JSGlobalData& globalData = exec->globalData(); for (unsigned i = 0; i < numDefined; ++i) { - storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value); + m_storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value); ++iter; } // Put undefined values back in. for (unsigned i = numDefined; i < newUsedVectorLength; ++i) - storage->m_vector[i].setUndefined(); + m_storage->m_vector[i].setUndefined(); // Ensure that unused values in the vector are zeroed out. for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) - storage->m_vector[i].clear(); + m_storage->m_vector[i].clear(); - storage->m_numValuesInVector = newUsedVectorLength; + m_storage->m_numValuesInVector = newUsedVectorLength; checkConsistency(SortConsistencyCheck); } @@ -1709,7 +1711,7 @@ void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t le callFrame->setArgument(i, get(exec, i)); } -unsigned JSArray::compactForSorting() +unsigned JSArray::compactForSorting(JSGlobalData& globalData) { ASSERT(!inSparseMode()); @@ -1740,12 +1742,12 @@ unsigned JSArray::compactForSorting() unsigned newUsedVectorLength = numDefined + numUndefined; - if (SparseArrayValueMap* map = storage->m_sparseValueMap) { + if (SparseArrayValueMap* map = m_sparseValueMap) { newUsedVectorLength += map->size(); if (newUsedVectorLength > m_vectorLength) { // Check that it is possible to allocate an array large enough to hold all the entries - if not, // exception is thrown by caller. - if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength)) + if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(globalData, newUsedVectorLength)) return 0; storage = m_storage; @@ -1755,8 +1757,7 @@ unsigned JSArray::compactForSorting() for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) storage->m_vector[numDefined++].setWithoutWriteBarrier(it->second.getNonSparseMode()); - delete map; - storage->m_sparseValueMap = 0; + deallocateSparseMap(); } for (unsigned i = numDefined; i < newUsedVectorLength; ++i) @@ -1773,12 +1774,12 @@ unsigned JSArray::compactForSorting() void* JSArray::subclassData() const { - return m_storage->subclassData; + return m_subclassData; } void JSArray::setSubclassData(void* d) { - m_storage->subclassData = d; + m_subclassData = d; } #if CHECK_ARRAY_CONSISTENCY @@ -1791,7 +1792,7 @@ void JSArray::checkConsistency(ConsistencyCheckType type) ASSERT(storage); if (type == SortConsistencyCheck) - ASSERT(!storage->m_sparseValueMap); + ASSERT(!m_sparseValueMap); unsigned numValuesInVector = 0; for (unsigned i = 0; i < m_vectorLength; ++i) { @@ -1808,9 +1809,9 @@ void JSArray::checkConsistency(ConsistencyCheckType type) ASSERT(numValuesInVector == storage->m_numValuesInVector); ASSERT(numValuesInVector <= storage->m_length); - if (storage->m_sparseValueMap) { - SparseArrayValueMap::iterator end = storage->m_sparseValueMap->end(); - for (SparseArrayValueMap::iterator it = storage->m_sparseValueMap->begin(); it != end; ++it) { + if (m_sparseValueMap) { + SparseArrayValueMap::iterator end = m_sparseValueMap->end(); + for (SparseArrayValueMap::iterator it = m_sparseValueMap->begin(); it != end; ++it) { unsigned index = it->first; ASSERT(index < storage->m_length); ASSERT(index >= storage->m_vectorLength); |