diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-03-12 14:11:15 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-03-12 14:11:15 +0100 |
commit | dd91e772430dc294e3bf478c119ef8d43c0a3358 (patch) | |
tree | 6f33ce4d5872a5691e0291eb45bf6ab373a5f567 /Source/JavaScriptCore/runtime/JSArray.cpp | |
parent | ad0d549d4cc13433f77c1ac8f0ab379c83d93f28 (diff) | |
download | qtwebkit-dd91e772430dc294e3bf478c119ef8d43c0a3358.tar.gz |
Imported WebKit commit 3db4eb1820ac8fb03065d7ea73a4d9db1e8fea1a (http://svn.webkit.org/repository/webkit/trunk@110422)
This includes build fixes for the latest qtbase/qtdeclarative as well as the final QML2 API.
Diffstat (limited to 'Source/JavaScriptCore/runtime/JSArray.cpp')
-rw-r--r-- | Source/JavaScriptCore/runtime/JSArray.cpp | 220 |
1 files changed, 136 insertions, 84 deletions
diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp index 71d520018..4244bc31c 100644 --- a/Source/JavaScriptCore/runtime/JSArray.cpp +++ b/Source/JavaScriptCore/runtime/JSArray.cpp @@ -41,6 +41,7 @@ using namespace WTF; namespace JSC { + ASSERT_CLASS_FITS_IN_CELL(JSArray); ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray); @@ -104,23 +105,16 @@ const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, 0, CRE // This value is capped by the constant FIRST_VECTOR_GROW defined above. static unsigned lastArraySize = 0; -static inline size_t storageSize(unsigned vectorLength) +static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues) { - 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(WriteBarrier<Unknown>)) + (vectorLength * sizeof(WriteBarrier<Unknown>)); - // 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(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)))); - - return size; + return length <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues; } -static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues) +static bool reject(ExecState* exec, bool throwException, const char* message) { - return length <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues; + if (throwException) + throwTypeError(exec, message); + return false; } #if !CHECK_ARRAY_CONSISTENCY @@ -213,7 +207,7 @@ inline std::pair<SparseArrayValueMap::iterator, bool> SparseArrayValueMap::add(J return result; } -inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i, JSValue value) +inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i, JSValue value, bool shouldThrow) { std::pair<SparseArrayValueMap::iterator, bool> result = add(array, i); SparseArrayEntry& entry = result.first->second; @@ -223,14 +217,15 @@ inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i // extensible, this is not the right thing to have done - so remove again. if (result.second && !array->isExtensible()) { remove(result.first); - // FIXME: should throw in strict mode. + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; } if (!(entry.attributes & Accessor)) { if (entry.attributes & ReadOnly) { - // FIXME: should throw if being called from strict mode. - // throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; } @@ -243,8 +238,8 @@ inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i JSObject* setter = asGetterSetter(accessor)->setter(); if (!setter) { - // FIXME: should throw if being called from strict mode. - // throwTypeError(exec, "setting a property that has only a getter"); + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; } @@ -255,6 +250,24 @@ inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i call(exec, setter, callType, callData, array, args); } +inline bool SparseArrayValueMap::putDirect(ExecState* exec, JSArray* array, unsigned i, JSValue value, bool shouldThrow) +{ + std::pair<SparseArrayValueMap::iterator, bool> result = add(array, i); + SparseArrayEntry& entry = result.first->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.second && !array->isExtensible()) { + remove(result.first); + 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(); @@ -395,13 +408,6 @@ void JSArray::putDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, Prope entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor); } -static bool reject(ExecState* exec, bool throwException, const char* message) -{ - if (throwException) - throwTypeError(exec, message); - return false; -} - // Defined in ES5.1 8.12.9 bool JSArray::defineOwnNumericProperty(ExecState* exec, unsigned index, PropertyDescriptor& descriptor, bool throwException) { @@ -414,8 +420,7 @@ bool JSArray::defineOwnNumericProperty(ExecState* exec, unsigned index, Property // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode'). if (!descriptor.attributes()) { ASSERT(!descriptor.isAccessorDescriptor()); - putByIndex(this, exec, index, descriptor.value()); - return true; + return putDirectIndex(exec, index, descriptor.value(), throwException); } enterDictionaryMode(exec->globalData()); @@ -721,7 +726,7 @@ void JSArray::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, bool isArrayIndex; unsigned i = propertyName.toArrayIndex(isArrayIndex); if (isArrayIndex) { - putByIndex(thisObject, exec, i, value); + putByIndex(thisObject, exec, i, value, slot.isStrictMode()); return; } @@ -738,7 +743,7 @@ void JSArray::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSObject::put(thisObject, exec, propertyName, value, slot); } -void JSArray::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value) +void JSArray::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value, bool shouldThrow) { JSArray* thisObject = jsCast<JSArray*>(cell); thisObject->checkConsistency(); @@ -765,17 +770,17 @@ void JSArray::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue valu // Handle 2^32-1 - this is not an array index (see ES5.1 15.4), and is treated as a regular property. if (UNLIKELY(i > MAX_ARRAY_INDEX)) { - PutPropertySlot slot; + PutPropertySlot slot(shouldThrow); thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, i), value, slot); return; } // For all other cases, call putByIndexBeyondVectorLength. - thisObject->putByIndexBeyondVectorLength(exec, i, value); + thisObject->putByIndexBeyondVectorLength(exec, i, value, shouldThrow); thisObject->checkConsistency(); } -NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value) +void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow) { JSGlobalData& globalData = exec->globalData(); @@ -806,7 +811,7 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigne // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value. allocateSparseMap(exec->globalData()); map = m_sparseValueMap; - map->put(exec, this, i, value); + map->put(exec, this, i, value, shouldThrow); return; } @@ -815,7 +820,8 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigne if (i >= length) { // Prohibit growing the array if length is not writable. if (map->lengthIsReadOnly() || !isExtensible()) { - // FIXME: should throw in strict mode. + if (shouldThrow) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; } length = i + 1; @@ -826,7 +832,7 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigne // 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(exec->globalData(), length)) { - map->put(exec, this, i, value); + map->put(exec, this, i, value, shouldThrow); return; } @@ -848,6 +854,77 @@ NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigne valueSlot.set(globalData, this, value); } +bool JSArray::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow) +{ + JSGlobalData& globalData = exec->globalData(); + + // i should be a valid array index that is outside of the current vector. + ASSERT(i >= m_vectorLength); + ASSERT(i <= MAX_ARRAY_INDEX); + + ArrayStorage* storage = m_storage; + SparseArrayValueMap* map = m_sparseValueMap; + + // First, handle cases where we don't currently have a sparse map. + if (LIKELY(!map)) { + // If the array is not extensible, we should have entered dictionary mode, and created the spare map. + ASSERT(isExtensible()); + + // Update m_length if necessary. + if (i >= storage->m_length) + 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(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); + ++storage->m_numValuesInVector; + return true; + } + // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value. + allocateSparseMap(exec->globalData()); + map = m_sparseValueMap; + return map->putDirect(exec, this, i, value, shouldThrow); + } + + // Update m_length if necessary. + unsigned length = storage->m_length; + if (i >= length) { + // Prohibit growing the array if length is not writable. + if (map->lengthIsReadOnly()) + return reject(exec, shouldThrow, StrictModeReadonlyPropertyWriteError); + if (!isExtensible()) + return reject(exec, shouldThrow, "Attempting to define property on object that is not extensible."); + length = i + 1; + storage->m_length = length; + } + + // 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(exec->globalData(), length)) + return map->putDirect(exec, this, i, value, shouldThrow); + + // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector. + storage = m_storage; + storage->m_numValuesInVector = numValuesInArray; + + // Copy all values from the map into the vector, and delete the map. + WriteBarrier<Unknown>* vector = storage->m_vector; + 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()); + deallocateSparseMap(); + + // Store the new property into the vector. + WriteBarrier<Unknown>& valueSlot = vector[i]; + if (!valueSlot) + ++storage->m_numValuesInVector; + valueSlot.set(globalData, this, value); + return true; +} + bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName) { JSArray* thisObject = jsCast<JSArray*>(cell); @@ -1220,18 +1297,19 @@ void JSArray::push(ExecState* exec, JSValue value) // Pushing to an array of length 2^32-1 stores the property, but throws a range error. if (UNLIKELY(storage->m_length == 0xFFFFFFFFu)) { - methodTable()->putByIndex(this, exec, storage->m_length, value); + methodTable()->putByIndex(this, exec, storage->m_length, value, true); // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d. - throwError(exec, createRangeError(exec, "Invalid array length")); + if (!exec->hadException()) + throwError(exec, createRangeError(exec, "Invalid array length")); return; } // Handled the same as putIndex. - putByIndexBeyondVectorLength(exec, storage->m_length, value); + putByIndexBeyondVectorLength(exec, storage->m_length, value, true); checkConsistency(); } -void JSArray::shiftCount(ExecState* exec, unsigned count) +bool JSArray::shiftCount(ExecState*, unsigned count) { ASSERT(count > 0); @@ -1239,32 +1317,15 @@ void JSArray::shiftCount(ExecState* exec, unsigned count) unsigned oldLength = storage->m_length; - if (!oldLength) - return; - - if (oldLength != storage->m_numValuesInVector) { - // If m_length and m_numValuesInVector aren't the same, we have a sparse vector - // which means we need to go through each entry looking for the the "empty" - // slots and then fill them with possible properties. See ECMA spec. - // 15.4.4.9 steps 11 through 13. - for (unsigned i = count; i < oldLength; ++i) { - if ((i >= m_vectorLength) || (!m_storage->m_vector[i])) { - PropertySlot slot(this); - JSValue p = prototype(); - if ((!p.isNull()) && (asObject(p)->getPropertySlot(exec, i, slot))) - methodTable()->putByIndex(this, exec, i, slot.getValue(exec, i)); - } - } - - storage = m_storage; // The put() above could have grown the vector and realloc'ed storage. + // If the array contains holes or is otherwise in an abnormal state, + // use the generic algorithm in ArrayPrototype. + if (oldLength != storage->m_numValuesInVector || inSparseMode()) + return false; - // Need to decrement numValuesInvector based on number of real entries - for (unsigned i = 0; i < (unsigned)count; ++i) - if ((i < m_vectorLength) && (storage->m_vector[i])) - --storage->m_numValuesInVector; - } else - storage->m_numValuesInVector -= count; + if (!oldLength) + return true; + storage->m_numValuesInVector -= count; storage->m_length -= count; if (m_vectorLength) { @@ -1280,30 +1341,20 @@ void JSArray::shiftCount(ExecState* exec, unsigned count) m_indexBias += count; } } + return true; } - -void JSArray::unshiftCount(ExecState* exec, unsigned count) + +// Returns true if the unshift can be handled, false to fallback. +bool JSArray::unshiftCount(ExecState* exec, unsigned count) { ArrayStorage* storage = m_storage; unsigned length = storage->m_length; - if (length != storage->m_numValuesInVector) { - // If m_length and m_numValuesInVector aren't the same, we have a sparse vector - // which means we need to go through each entry looking for the the "empty" - // slots and then fill them with possible properties. See ECMA spec. - // 15.4.4.13 steps 8 through 10. - for (unsigned i = 0; i < length; ++i) { - if ((i >= m_vectorLength) || (!m_storage->m_vector[i])) { - PropertySlot slot(this); - JSValue p = prototype(); - if ((!p.isNull()) && (asObject(p)->getPropertySlot(exec, i, slot))) - methodTable()->putByIndex(this, exec, i, slot.getValue(exec, i)); - } - } - } - - storage = m_storage; // The put() above could have grown the vector and realloc'ed storage. - + // If the array contains holes or is otherwise in an abnormal state, + // use the generic algorithm in ArrayPrototype. + if (length != storage->m_numValuesInVector || inSparseMode()) + return false; + if (m_indexBias >= count) { m_indexBias -= count; char* newBaseStorage = reinterpret_cast<char*>(storage) - count * sizeof(WriteBarrier<Unknown>); @@ -1312,12 +1363,13 @@ void JSArray::unshiftCount(ExecState* exec, unsigned count) m_vectorLength += count; } else if (!unshiftCountSlowCase(exec->globalData(), count)) { throwOutOfMemoryError(exec); - return; + return true; } WriteBarrier<Unknown>* vector = m_storage->m_vector; for (unsigned i = 0; i < count; i++) vector[i].clear(); + return true; } void JSArray::visitChildren(JSCell* cell, SlotVisitor& visitor) |