Title: [109673] trunk/Source/_javascript_Core
Revision
109673
Author
[email protected]
Date
2012-03-03 21:56:24 -0800 (Sat, 03 Mar 2012)

Log Message

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):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (109672 => 109673)


--- trunk/Source/_javascript_Core/ChangeLog	2012-03-04 05:04:16 UTC (rev 109672)
+++ trunk/Source/_javascript_Core/ChangeLog	2012-03-04 05:56:24 UTC (rev 109673)
@@ -1,3 +1,34 @@
+2012-03-03  Gavin Barraclough  <[email protected]>
+
+        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  <[email protected]>
 
         Implement the basis of KURLWTFURL

Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp (109672 => 109673)


--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp	2012-03-04 05:04:16 UTC (rev 109672)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp	2012-03-04 05:56:24 UTC (rev 109673)
@@ -368,11 +368,11 @@
                 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 @@
         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 @@
             
             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 @@
 
         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 @@
             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 @@
             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);

Modified: trunk/Source/_javascript_Core/runtime/JSArray.cpp (109672 => 109673)


--- trunk/Source/_javascript_Core/runtime/JSArray.cpp	2012-03-04 05:04:16 UTC (rev 109672)
+++ trunk/Source/_javascript_Core/runtime/JSArray.cpp	2012-03-04 05:56:24 UTC (rev 109673)
@@ -41,6 +41,7 @@
 
 namespace JSC {
 
+
 ASSERT_CLASS_FITS_IN_CELL(JSArray);
 ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray);
 
@@ -109,6 +110,13 @@
     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 @@
     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 @@
     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 @@
         // 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 @@
     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 @@
     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);

Modified: trunk/Source/_javascript_Core/runtime/JSArray.h (109672 => 109673)


--- trunk/Source/_javascript_Core/runtime/JSArray.h	2012-03-04 05:04:16 UTC (rev 109672)
+++ trunk/Source/_javascript_Core/runtime/JSArray.h	2012-03-04 05:56:24 UTC (rev 109673)
@@ -86,6 +86,7 @@
 
         // 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 @@
         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 @@
 
         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);
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to