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

Source/JavaScriptCore: Do not allow Array length to be set if it is non-configurable

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

Reviewed by Sam Weinig.

Do not allow Array length to be set if it is non-configurable, and if the new
length is less than the old length then intervening properties should removed
in reverse order. Removal of properties should cease if an intervening indexed
property being removed is non-configurable.

* JavaScriptCore.exp:
    - Removed export for setLength.
* runtime/ArrayPrototype.cpp:
(JSC::arrayProtoFuncConcat):
    - JSArray::setLength now takes an ExecState*
(JSC::arrayProtoFuncSlice):
    - JSArray::setLength now takes an ExecState*
* runtime/JSArray.cpp:
(JSC::JSArray::defineOwnProperty):
    - JSArray::setLength now takes an ExecState*
(JSC::JSArray::put):
    - JSArray::setLength now takes an ExecState*
(JSC::compareKeysForQSort):
    - Keys extracted from the map can be stored as unsigneds.
(JSC::JSArray::getOwnPropertyNames):
    - Keys extracted from the map can be stored as unsigneds.
(JSC::JSArray::setLength):
    - Check lengthIsReadOnly(), rather than copying the entire map to iterate
      over to determine which keys to remove, instead just copy the keys from
      the map to a Vector. When inSparseMode sort the keys in the Vector so
      that we can remove properties in reverse order.
* runtime/JSArray.h:
    - JSArray::setLength now takes an ExecState*

Source/WebCore: Do not allow Array length to be set if it is non-configurable
https://bugs.webkit.org/show_bug.cgi?id=75935

Reviewed by Sam Weinig.

* bindings/js/SerializedScriptValue.cpp:
(WebCore::CloneDeserializer::deserialize):
    - remove unnecessary call to JSArray::setLength.

LayoutTests: rebaselining some canvas images
https://bugs.webkit.org/show_bug.cgi?id=75552

Patch by Elliot Poger <epoger@google.com> on 2012-01-10
Reviewed by Ryosuke Niwa.

* platform/chromium-gpu-linux/fast/canvas/canvas-text-baseline-expected.png:
* platform/chromium-gpu-linux/fast/canvas/quadraticCurveTo-expected.png:
* platform/chromium-gpu-mac/fast/canvas/canvas-text-baseline-expected.png: Added.
* platform/chromium-gpu-mac/fast/canvas/quadraticCurveTo-expected.png: Added.
* platform/chromium-gpu-win/fast/canvas/canvas-text-baseline-expected.png:
* platform/chromium-gpu-win/fast/canvas/quadraticCurveTo-expected.png:
* platform/chromium-mac-leopard/fast/canvas/quadraticCurveTo-expected.png: Added.
* platform/chromium-mac-snowleopard/fast/canvas/canvas-lineWidth-expected.txt: Added.
* platform/chromium-mac-snowleopard/fast/canvas/canvas-text-baseline-expected.png: Added.
* platform/chromium-mac-snowleopard/fast/canvas/quadraticCurveTo-expected.png: Added.
* platform/chromium/test_expectations.txt:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@104604 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent e7935eec
......@@ -17,6 +17,18 @@
* platform/chromium-mac-snowleopard/fast/canvas/quadraticCurveTo-expected.png: Added.
* platform/chromium/test_expectations.txt:
2012-01-10 Gavin Barraclough <barraclough@apple.com>
Do not allow Array length to be set if it is non-configurable
https://bugs.webkit.org/show_bug.cgi?id=75935
Reviewed by Sam Weinig.
* fast/js/array-defineOwnProperty-expected.txt:
* fast/js/mozilla/strict/15.4.5.1-expected.txt:
* fast/js/script-tests/array-defineOwnProperty.js:
- Added one more test & some missing comments, updated results.
2012-01-10 Gavin Barraclough <barraclough@apple.com>
Use SameValue to compare property descriptor values
......@@ -6,6 +6,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
PASS Object.defineProperty([], 'x', { get:function(){return true;} }).x is true
PASS Object.defineProperty([], 'length', { value: 1 }).length is 1
PASS var a = Object.defineProperty([], 'length', { writable: false }); a[1] = 1; a.length is 0
PASS var a = Object.defineProperty([], 'length', { writable: false }); a.length = 1; a.length is 0
PASS Object.defineProperty([], 'length', { get:function(){return true;} }) threw exception TypeError: Attempting to change access mechanism for an unconfigurable property..
PASS Object.defineProperty([], 'length', { enumerable: true }) threw exception TypeError: Attempting to change enumerable attribute of unconfigurable property..
PASS Object.defineProperty([], 'length', { configurable: true }) threw exception TypeError: Attempting to change configurable attribute of unconfigurable property..
......@@ -13,7 +14,7 @@ PASS Object.defineProperty(Object.defineProperty([], 'length', { writable: false
PASS var a = Object.defineProperty([], '0', { value: 42 }); a[0] is 42
PASS var a = Object.defineProperty([42], '0', { writable: false }); a[0] = 1; a[0] is 42
PASS var a = Object.defineProperty([42], '0', { enumerable: false }); a[0] + Object.keys(a).length is 42
FAIL var a = Object.defineProperty([42], '0', { configurable: false }); a.length = 0; a[0] should be 42 (of type number). Was undefined (of type undefined).
PASS var a = Object.defineProperty([42], '0', { configurable: false }); a.length = 0; a[0] is 42
PASS var foo = 0; Object.defineProperty([], '0', { set:function(x){foo = x;} })[0] = 42; foo is 42
PASS Object.defineProperty([], '0', { get:function(){return true;} })[0] is true
PASS Object.defineProperty(Object.defineProperty([true], '0', { configurable:true, writable: false }), '0', { writable: true })[0] is true
......
FAIL 'use strict'; var a = arr(); a.length = 2; a should throw an instance of TypeError
FAIL assertEq failed: false(of type boolean) !== true(of type boolean)
FAIL 'use strict'; var a = addx(arr()); a.length = 2; a should throw an instance of TypeError
FAIL assertEq failed: false(of type boolean) !== true(of type boolean)
PASS 'use strict'; var a = arr(); a.length = 2; a threw exception of type TypeError.
PASS true === true
PASS 'use strict'; var a = addx(arr()); a.length = 2; a threw exception of type TypeError.
PASS true === true
PASSED!
PASS successfullyParsed is true
......
......@@ -6,6 +6,7 @@ shouldBeTrue("Object.defineProperty([], 'x', { get:function(){return true;} }).x
// The length property can be set, and can be made read-only.
shouldBe("Object.defineProperty([], 'length', { value: 1 }).length", '1');
shouldBe("var a = Object.defineProperty([], 'length', { writable: false }); a[1] = 1; a.length", '0');
shouldBe("var a = Object.defineProperty([], 'length', { writable: false }); a.length = 1; a.length", '0');
// The length property can be replaced with an accessor, or made either enumerable or configurable.
shouldThrow("Object.defineProperty([], 'length', { get:function(){return true;} })");
......@@ -13,21 +14,17 @@ shouldThrow("Object.defineProperty([], 'length', { enumerable: true })");
shouldThrow("Object.defineProperty([], 'length', { configurable: true })");
shouldThrow("Object.defineProperty(Object.defineProperty([], 'length', { writable: false }), 'length', { writable: true })");
//
// The value of an indexed property can be set.
shouldBe("var a = Object.defineProperty([], '0', { value: 42 }); a[0]", '42');
//
// An indexed property can be made non-writable/enumerable/configurable.
shouldBe("var a = Object.defineProperty([42], '0', { writable: false }); a[0] = 1; a[0]", '42');
//
shouldBe("var a = Object.defineProperty([42], '0', { enumerable: false }); a[0] + Object.keys(a).length", '42');
// FIXME: this doesn't yet work. Reducing the length should not work if it contains non-configurable properties.
shouldBe("var a = Object.defineProperty([42], '0', { configurable: false }); a.length = 0; a[0]", '42');
//
// An indexed property can be defined as an accessor.
shouldBe("var foo = 0; Object.defineProperty([], '0', { set:function(x){foo = x;} })[0] = 42; foo", '42');
//
shouldBeTrue("Object.defineProperty([], '0', { get:function(){return true;} })[0]")
//
// A configurable, non-writable property can be made writable, but a non-configurable one cannot.
shouldBeTrue("Object.defineProperty(Object.defineProperty([true], '0', { configurable:true, writable: false }), '0', { writable: true })[0]");
//
shouldThrow("Object.defineProperty(Object.defineProperty([true], '0', { configurable:false, writable: false }), '0', { writable: true })[0]");
// Reassigning the value is okay if the property is writable.
......
2012-01-10 Gavin Barraclough <barraclough@apple.com>
Do not allow Array length to be set if it is non-configurable
https://bugs.webkit.org/show_bug.cgi?id=75935
Reviewed by Sam Weinig.
Do not allow Array length to be set if it is non-configurable, and if the new
length is less than the old length then intervening properties should removed
in reverse order. Removal of properties should cease if an intervening indexed
property being removed is non-configurable.
* JavaScriptCore.exp:
- Removed export for setLength.
* runtime/ArrayPrototype.cpp:
(JSC::arrayProtoFuncConcat):
- JSArray::setLength now takes an ExecState*
(JSC::arrayProtoFuncSlice):
- JSArray::setLength now takes an ExecState*
* runtime/JSArray.cpp:
(JSC::JSArray::defineOwnProperty):
- JSArray::setLength now takes an ExecState*
(JSC::JSArray::put):
- JSArray::setLength now takes an ExecState*
(JSC::compareKeysForQSort):
- Keys extracted from the map can be stored as unsigneds.
(JSC::JSArray::getOwnPropertyNames):
- Keys extracted from the map can be stored as unsigneds.
(JSC::JSArray::setLength):
- Check lengthIsReadOnly(), rather than copying the entire map to iterate
over to determine which keys to remove, instead just copy the keys from
the map to a Vector. When inSparseMode sort the keys in the Vector so
that we can remove properties in reverse order.
* runtime/JSArray.h:
- JSArray::setLength now takes an ExecState*
2012-01-10 Gavin Barraclough <barraclough@apple.com>
Use SameValue to compare property descriptor values
......@@ -286,7 +286,6 @@ __ZN3JSC7JSArray25getOwnPropertySlotByIndexEPNS_6JSCellEPNS_9ExecStateEjRNS_12Pr
__ZN3JSC7JSArray30tryFinishCreationUninitializedERNS_12JSGlobalDataEj
__ZN3JSC7JSArray6s_infoE
__ZN3JSC7JSArray7destroyEPNS_6JSCellE
__ZN3JSC7JSArray9setLengthEjb
__ZN3JSC7JSArrayC1ERNS_12JSGlobalDataEPNS_9StructureE
__ZN3JSC7JSArrayC2ERNS_12JSGlobalDataEPNS_9StructureE
__ZN3JSC7JSArrayD2Ev
......
......@@ -380,7 +380,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
curArg = (exec->argument(i));
++i;
}
arr->setLength(n);
arr->setLength(exec, n);
return JSValue::encode(arr);
}
......@@ -523,7 +523,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
if (v)
resObj->methodTable()->putByIndex(resObj, exec, n, v);
}
resObj->setLength(n);
resObj->setLength(exec, n);
return JSValue::encode(result);
}
......
......@@ -346,6 +346,8 @@ void JSArray::putDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, Prope
if (descriptor.isDataDescriptor()) {
if (descriptor.value())
entryInMap->set(exec->globalData(), this, descriptor.value());
else if (oldDescriptor.isAccessorDescriptor())
entryInMap->set(exec->globalData(), this, jsUndefined());
entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~(Getter | Setter);
return;
}
......@@ -581,7 +583,7 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, const Identif
// l.i. Set oldLen to oldLen – 1.
// l.ii. Let deleteSucceeded be the result of calling the [[Delete]] internal method of A passing ToString(oldLen) and false as arguments.
// l.iii. If deleteSucceeded is false, then
if (!array->setLength(newLen, throwException)) {
if (!array->setLength(exec, newLen, throwException)) {
// 1. Set newLenDesc.[[Value] to oldLen+1.
// 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.
......@@ -710,7 +712,7 @@ void JSArray::put(JSCell* cell, ExecState* exec, const Identifier& propertyName,
throwError(exec, createRangeError(exec, "Invalid array length"));
return;
}
thisObject->setLength(newLength, slot.isStrictMode());
thisObject->setLength(exec, newLength, slot.isStrictMode());
return;
}
......@@ -870,8 +872,8 @@ bool JSArray::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
static int compareKeysForQSort(const void* a, const void* b)
{
uint64_t da = *static_cast<const uint64_t*>(a);
uint64_t db = *static_cast<const uint64_t*>(b);
unsigned da = *static_cast<const unsigned*>(a);
unsigned db = *static_cast<const unsigned*>(b);
return (da > db) - (da < db);
}
......@@ -891,7 +893,7 @@ void JSArray::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNam
}
if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
Vector<uint64_t> keys;
Vector<unsigned> keys;
keys.reserveCapacity(map->size());
SparseArrayValueMap::const_iterator end = map->end();
......@@ -900,9 +902,9 @@ void JSArray::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNam
keys.append(it->first);
}
qsort(keys.begin(), keys.size(), sizeof(uint64_t), compareKeysForQSort);
qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort);
for (unsigned i = 0; i < keys.size(); ++i)
propertyNames.add(Identifier::from(exec, static_cast<unsigned>(keys[i])));
propertyNames.add(Identifier::from(exec, keys[i]));
}
if (mode == IncludeDontEnumProperties)
......@@ -1091,16 +1093,61 @@ bool JSArray::unshiftCountSlowCase(unsigned count)
return true;
}
bool JSArray::setLength(unsigned newLength, bool throwException)
bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
{
UNUSED_PARAM(throwException);
checkConsistency();
ArrayStorage* storage = m_storage;
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);
if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
// Fail if the length is not writable.
if (map->lengthIsReadOnly())
return reject(exec, throwException, StrictModeReadonlyPropertyWriteError);
if (newLength < length) {
// Copy any keys we might be interested in into a vector.
Vector<unsigned> keys;
keys.reserveCapacity(min(map->size(), static_cast<size_t>(length - newLength)));
SparseArrayValueMap::const_iterator end = map->end();
for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
unsigned index = it->first;
if (index < length && index >= newLength)
keys.append(index);
}
// Check if the array is in sparse mode. If so there may be non-configurable
// properties, so we have to perform deletion with caution, if not we can
// delete values in any order.
if (map->sparseMode()) {
qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort);
unsigned i = keys.size();
while (i) {
unsigned index = keys[--i];
SparseArrayValueMap::iterator it = map->find(index);
ASSERT(it != map->notFound());
if (it->second.attributes & DontDelete) {
storage->m_length = index + 1;
return reject(exec, throwException, "Unable to delete property.");
}
map->remove(it);
}
} else {
for (unsigned i = 0; i < keys.size(); ++i)
map->remove(keys[i]);
if (map->isEmpty()) {
delete map;
storage->m_sparseValueMap = 0;
}
}
}
}
if (newLength < length) {
// Delete properties from the vector.
unsigned usedVectorLength = min(length, m_vectorLength);
for (unsigned i = newLength; i < usedVectorLength; ++i) {
WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
......@@ -1108,22 +1155,6 @@ bool JSArray::setLength(unsigned newLength, bool throwException)
valueSlot.clear();
storage->m_numValuesInVector -= hadValue;
}
// FIXME: properties should be removed in reverse numeric order, if any cannot be
// removed this method should reject (possibly throw / return false), and length
// should be set the after the last property that could not be removed.
if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
SparseArrayValueMap copy = *map;
SparseArrayValueMap::const_iterator end = copy.end();
for (SparseArrayValueMap::const_iterator it = copy.begin(); it != end; ++it) {
if (it->first >= newLength)
map->remove(it->first);
}
if (map->isEmpty() && !map->sparseMode()) {
delete map;
storage->m_sparseValueMap = 0;
}
}
}
storage->m_length = newLength;
......
......@@ -166,7 +166,8 @@ namespace JSC {
static JS_EXPORTDATA const ClassInfo s_info;
unsigned length() const { return m_storage->m_length; }
bool setLength(unsigned, bool throwException = false); // OK to use on new arrays, but not if it might be a RegExpMatchArray.
// OK to use on new arrays, but not if it might be a RegExpMatchArray.
bool setLength(ExecState*, unsigned, bool throwException = false);
void sort(ExecState*);
void sort(ExecState*, JSValue compareFunction, CallType, const CallData&);
......
2012-01-10 Gavin Barraclough <barraclough@apple.com>
Do not allow Array length to be set if it is non-configurable
https://bugs.webkit.org/show_bug.cgi?id=75935
Reviewed by Sam Weinig.
* bindings/js/SerializedScriptValue.cpp:
(WebCore::CloneDeserializer::deserialize):
- remove unnecessary call to JSArray::setLength.
2012-01-10 Adrienne Walker <enne@google.com>
[chromium] Draw debug tile borders on composited layers
......@@ -1261,8 +1261,7 @@ DeserializationResult CloneDeserializer::deserialize()
fail();
goto error;
}
JSArray* outArray = constructEmptyArray(m_exec, m_globalObject);
outArray->setLength(length);
JSArray* outArray = constructEmptyArray(m_exec, m_globalObject, length);
m_gcBuffer.append(outArray);
outputArrayStack.append(outArray);
// fallthrough
......
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