Bug 1350864 - Fix SetIntegrityLevel to set non-writable length correctly when freezing arrays. r=Waldo

This commit is contained in:
Jan de Mooij 2017-05-22 13:14:35 +02:00
Родитель 3c3ff310ce
Коммит d7c81386bd
5 изменённых файлов: 45 добавлений и 27 удалений

Просмотреть файл

@ -0,0 +1,19 @@
function maybeFreeze(arr, b) {
with(this) {}; // Don't inline this.
if (b) {
Object.freeze(arr);
}
}
function test() {
var arr = [];
for (var i = 0; i < 1800; i++) {
maybeFreeze(arr, i > 1500);
try {
arr.push(2);
assertEq(i <= 1500, true);
} catch(e) {
assertEq(e instanceof TypeError, true);
}
}
}
test();

Просмотреть файл

@ -890,25 +890,8 @@ js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
ObjectElements* header = arr->getElementsHeader();
header->initializedLength = Min(header->initializedLength, newLen);
if (attrs & JSPROP_READONLY) {
if (header->numShiftedElements() > 0) {
arr->unshiftElements();
header = arr->getElementsHeader();
}
header->setNonwritableArrayLength();
// When an array's length becomes non-writable, writes to indexes
// greater than or equal to the length don't change the array. We
// handle this with a check for non-writable length in most places.
// But in JIT code every check counts -- so we piggyback the check on
// the already-required range check for |index < capacity| by making
// capacity of arrays with non-writable length never exceed the length.
if (arr->getDenseCapacity() > newLen) {
arr->shrinkElements(cx, newLen);
arr->getElementsHeader()->capacity = newLen;
}
}
if (attrs & JSPROP_READONLY)
arr->setNonWritableLength(cx);
if (!succeeded)
return result.fail(JSMSG_CANT_TRUNCATE_ARRAY);

Просмотреть файл

@ -534,17 +534,10 @@ js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level)
// Ordinarily ArraySetLength handles this, but we're going behind its back
// right now, so we must do this manually.
//
// ArraySetLength also implements the capacity <= length invariant for
// arrays with non-writable length. We don't need to do anything special
// for that, because capacity was zeroed out by preventExtensions. (See
// the assertion about getDenseCapacity above.)
if (level == IntegrityLevel::Frozen && obj->is<ArrayObject>()) {
if (!obj->as<ArrayObject>().maybeCopyElementsForWrite(cx))
return false;
if (nobj->getElementsHeader()->numShiftedElements() > 0)
nobj->unshiftElements();
obj->as<ArrayObject>().getElementsHeader()->setNonwritableArrayLength();
obj->as<ArrayObject>().setNonWritableLength(cx);
}
} else {
RootedId id(cx);

Просмотреть файл

@ -31,6 +31,26 @@ class ArrayObject : public NativeObject
return getElementsHeader()->length;
}
void setNonWritableLength(JSContext* cx) {
if (getElementsHeader()->numShiftedElements() > 0)
unshiftElements();
// When an array's length becomes non-writable, writes to indexes
// greater than or equal to the length don't change the array. We
// handle this with a check for non-writable length in most places.
// But in JIT code every check counts -- so we piggyback the check on
// the already-required range check for |index < capacity| by making
// capacity of arrays with non-writable length never exceed the length.
ObjectElements* header = getElementsHeader();
uint32_t len = header->initializedLength;
if (header->capacity > len) {
shrinkElements(cx, len);
header = getElementsHeader();
header->capacity = len;
}
header->setNonwritableArrayLength();
}
inline void setLength(JSContext* cx, uint32_t length);
// Variant of setLength for use on arrays where the length cannot overflow int32_t.

Просмотреть файл

@ -259,6 +259,9 @@ class ObjectElements
return flags & NONWRITABLE_ARRAY_LENGTH;
}
void setNonwritableArrayLength() {
// See ArrayObject::setNonWritableLength.
MOZ_ASSERT(capacity == initializedLength);
MOZ_ASSERT(numShiftedElements() == 0);
MOZ_ASSERT(!isCopyOnWrite());
flags |= NONWRITABLE_ARRAY_LENGTH;
}