Commit cd37404e authored by barraclough@apple.com's avatar barraclough@apple.com

Split JSArray's [[Put]] & [[DefineOwnProperty]] traps.

https://bugs.webkit.org/show_bug.cgi?id=80217

Reviewed by Filip Pizlo.

putByIndex() provides similar behavior to put(), but for indexed property names.
Many places in ArrayPrototype call putByIndex() where they really mean to call
[[DefineOwnProperty]]. This is only okay due to a bug – putByIndex should be
calling numeric accessors (& respecting numeric read only properties) on the
prototype chain, but isn't. Add a new putDirectIndex (matching JSObject's
putDirect* methods), to correctly provide a fast [[DefineOwnProperty]] interface.

* runtime/ArrayPrototype.cpp:
(JSC::arrayProtoFuncConcat):
(JSC::arrayProtoFuncSlice):
(JSC::arrayProtoFuncFilter):
(JSC::arrayProtoFuncMap):
* runtime/JSArray.cpp:
(JSC):
(JSC::reject):
(JSC::SparseArrayValueMap::putDirect):
(JSC::JSArray::defineOwnNumericProperty):
(JSC::JSArray::putByIndexBeyondVectorLength):
(JSC::JSArray::putDirectIndexBeyondVectorLength):
* runtime/JSArray.h:
(SparseArrayValueMap):
(JSArray):
(JSC::JSArray::putDirectIndex):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@109673 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 65a6a829
2012-03-03 Gavin Barraclough <barraclough@apple.com>
Split JSArray's [[Put]] & [[DefineOwnProperty]] traps.
https://bugs.webkit.org/show_bug.cgi?id=80217
Reviewed by Filip Pizlo.
putByIndex() provides similar behavior to put(), but for indexed property names.
Many places in ArrayPrototype call putByIndex() where they really mean to call
[[DefineOwnProperty]]. This is only okay due to a bug – putByIndex should be
calling numeric accessors (& respecting numeric read only properties) on the
prototype chain, but isn't. Add a new putDirectIndex (matching JSObject's
putDirect* methods), to correctly provide a fast [[DefineOwnProperty]] interface.
* runtime/ArrayPrototype.cpp:
(JSC::arrayProtoFuncConcat):
(JSC::arrayProtoFuncSlice):
(JSC::arrayProtoFuncFilter):
(JSC::arrayProtoFuncMap):
* runtime/JSArray.cpp:
(JSC):
(JSC::reject):
(JSC::SparseArrayValueMap::putDirect):
(JSC::JSArray::defineOwnNumericProperty):
(JSC::JSArray::putByIndexBeyondVectorLength):
(JSC::JSArray::putDirectIndexBeyondVectorLength):
* runtime/JSArray.h:
(SparseArrayValueMap):
(JSArray):
(JSC::JSArray::putDirectIndex):
2012-03-03 Benjamin Poulain <benjamin@webkit.org>
Implement the basis of KURLWTFURL
......@@ -368,11 +368,11 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
if (exec->hadException())
return JSValue::encode(jsUndefined());
if (v)
arr->methodTable()->putByIndex(arr, exec, n, v);
arr->putDirectIndex(exec, n, v);
n++;
}
} else {
arr->methodTable()->putByIndex(arr, exec, n, curArg);
arr->putDirectIndex(exec, n, curArg);
n++;
}
if (i == argCount)
......@@ -521,7 +521,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
if (exec->hadException())
return JSValue::encode(jsUndefined());
if (v)
resObj->methodTable()->putByIndex(resObj, exec, n, v);
resObj->putDirectIndex(exec, n, v);
}
resObj->setLength(exec, n);
return JSValue::encode(result);
......@@ -732,7 +732,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec)
JSValue result = cachedCall.call();
if (result.toBoolean(exec))
resultArray->methodTable()->putByIndex(resultArray, exec, filterIndex++, v);
resultArray->putDirectIndex(exec, filterIndex++, v);
}
if (k == length)
return JSValue::encode(resultArray);
......@@ -753,7 +753,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec)
JSValue result = call(exec, function, callType, callData, applyThis, eachArguments);
if (result.toBoolean(exec))
resultArray->methodTable()->putByIndex(resultArray, exec, filterIndex++, v);
resultArray->putDirectIndex(exec, filterIndex++, v);
}
return JSValue::encode(resultArray);
}
......@@ -788,7 +788,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec)
cachedCall.setArgument(1, jsNumber(k));
cachedCall.setArgument(2, thisObj);
JSArray::putByIndex(resultArray, exec, k, cachedCall.call());
resultArray->putDirectIndex(exec, k, cachedCall.call());
}
}
for (; k < length && !exec->hadException(); ++k) {
......@@ -809,7 +809,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec)
return JSValue::encode(jsUndefined());
JSValue result = call(exec, function, callType, callData, applyThis, eachArguments);
resultArray->methodTable()->putByIndex(resultArray, exec, k, result);
resultArray->putDirectIndex(exec, k, result);
}
return JSValue::encode(resultArray);
......
......@@ -41,6 +41,7 @@ using namespace WTF;
namespace JSC {
ASSERT_CLASS_FITS_IN_CELL(JSArray);
ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray);
......@@ -109,6 +110,13 @@ static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
return length <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues;
}
static bool reject(ExecState* exec, bool throwException, const char* message)
{
if (throwException)
throwTypeError(exec, message);
return false;
}
#if !CHECK_ARRAY_CONSISTENCY
inline void JSArray::checkConsistency(ConsistencyCheckType)
......@@ -241,6 +249,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();
......@@ -381,13 +407,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)
{
......@@ -400,8 +419,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());
......@@ -761,7 +779,7 @@ void JSArray::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue valu
thisObject->checkConsistency();
}
NEVER_INLINE void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value)
void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value)
{
JSGlobalData& globalData = exec->globalData();
......@@ -834,6 +852,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);
......
......@@ -86,6 +86,7 @@ namespace JSC {
// These methods may mutate the contents of the map
void put(ExecState*, JSArray*, unsigned, JSValue);
bool putDirect(ExecState*, JSArray*, unsigned, JSValue, bool shouldThrow);
std::pair<iterator, bool> add(JSArray*, unsigned);
iterator find(unsigned i) { return m_map.find(i); }
// This should ASSERT the remove is valid (check the result of the find).
......@@ -160,6 +161,18 @@ namespace JSC {
JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&);
static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue);
// This is similar to the JSObject::putDirect* methods:
// - the prototype chain is not consulted
// - accessors are not called.
// This method creates a property with attributes writable, enumerable and configurable all set to true.
bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow = true)
{
if (canSetIndex(propertyName)) {
setIndex(exec->globalData(), propertyName, value);
return true;
}
return putDirectIndexBeyondVectorLength(exec, propertyName, value, shouldThrow);
}
static JS_EXPORTDATA const ClassInfo s_info;
......@@ -283,6 +296,7 @@ namespace JSC {
bool getOwnPropertySlotSlowCase(ExecState*, unsigned propertyName, PropertySlot&);
void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue);
bool putDirectIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
unsigned getNewVectorLength(unsigned desiredLength);
bool increaseVectorLength(JSGlobalData&, unsigned newLength);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment