summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime/JSArray.cpp
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-03-12 14:11:15 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2012-03-12 14:11:15 +0100
commitdd91e772430dc294e3bf478c119ef8d43c0a3358 (patch)
tree6f33ce4d5872a5691e0291eb45bf6ab373a5f567 /Source/JavaScriptCore/runtime/JSArray.cpp
parentad0d549d4cc13433f77c1ac8f0ab379c83d93f28 (diff)
downloadqtwebkit-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.cpp220
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)