Backed out changeset 84e1d41336a5 (bug 1201869) for windows SM(p) OOM

This commit is contained in:
Tooru Fujisawa 2015-09-23 16:41:45 +09:00
parent aac7f056fb
commit 3f7a4937a8
10 changed files with 50 additions and 105 deletions

View File

@ -8767,7 +8767,7 @@ Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling)
uint32_t index = 0; uint32_t index = 0;
TokenStream::Modifier modifier = TokenStream::Operand; TokenStream::Modifier modifier = TokenStream::Operand;
for (; ; index++) { for (; ; index++) {
if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) { if (index == NativeObject::NELEMENTS_LIMIT) {
report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG); report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG);
return null(); return null();
} }

View File

@ -1,39 +0,0 @@
// Appending elements to a dense array should make the array sparse when the
// length exceeds the limit, instead of throwing OOM error.
function test() {
const MAX_DENSE_ELEMENTS_ALLOCATION = (1 << 28) - 1;
const VALUES_PER_HEADER = 2;
const MAX_DENSE_ELEMENTS_COUNT = MAX_DENSE_ELEMENTS_ALLOCATION - VALUES_PER_HEADER;
const SPARSE_DENSITY_RATIO = 8;
const MIN_DENSE = MAX_DENSE_ELEMENTS_COUNT / SPARSE_DENSITY_RATIO;
const MARGIN = 16;
let a = [];
// Fill the beginning of array to make it keep dense until length exceeds
// MAX_DENSE_ELEMENTS_COUNT.
for (let i = 0; i < MIN_DENSE; i++)
a[i] = i;
// Skip from MIN_DENSE to MAX_DENSE_ELEMENTS_COUNT - MARGIN, to reduce the
// time taken by test.
// Fill the ending of array to make it sparse at MAX_DENSE_ELEMENTS_COUNT.
for (let i = MAX_DENSE_ELEMENTS_COUNT - MARGIN; i < MAX_DENSE_ELEMENTS_COUNT + MARGIN; i++)
a[i] = i;
// Make sure the last element is defined.
assertEq(a.length, MAX_DENSE_ELEMENTS_COUNT + MARGIN);
assertEq(a[a.length - 1], MAX_DENSE_ELEMENTS_COUNT + MARGIN - 1);
// Make sure elements around MAX_DENSE_ELEMENTS_COUNT are also defined.
assertEq(a[MAX_DENSE_ELEMENTS_COUNT - 1], MAX_DENSE_ELEMENTS_COUNT - 1);
assertEq(a[MAX_DENSE_ELEMENTS_COUNT], MAX_DENSE_ELEMENTS_COUNT);
assertEq(a[MAX_DENSE_ELEMENTS_COUNT + 1], MAX_DENSE_ELEMENTS_COUNT + 1);
}
var config = getBuildConfiguration();
// Takes too long time on debug build.
if (!config.debug) {
test();
}

View File

@ -4303,7 +4303,7 @@ CodeGenerator::visitNewArray(LNewArray* lir)
JSObject* templateObject = lir->mir()->templateObject(); JSObject* templateObject = lir->mir()->templateObject();
DebugOnly<uint32_t> length = lir->mir()->length(); DebugOnly<uint32_t> length = lir->mir()->length();
MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT); MOZ_ASSERT(length < NativeObject::NELEMENTS_LIMIT);
if (lir->mir()->shouldUseVM()) { if (lir->mir()->shouldUseVM()) {
visitNewArrayCallVM(lir); visitNewArrayCallVM(lir);
@ -6946,7 +6946,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
key, &callStub); key, &callStub);
// Update initialized length. The capacity guard above ensures this won't overflow, // Update initialized length. The capacity guard above ensures this won't overflow,
// due to MAX_DENSE_ELEMENTS_COUNT. // due to NELEMENTS_LIMIT.
masm.bumpKey(&key, 1); masm.bumpKey(&key, 1);
masm.storeKey(key, Address(elements, ObjectElements::offsetOfInitializedLength())); masm.storeKey(key, Address(elements, ObjectElements::offsetOfInitializedLength()));

View File

@ -442,9 +442,8 @@ IonBuilder::inlineArray(CallInfo& callInfo)
// Negative lengths generate a RangeError, unhandled by the inline path. // Negative lengths generate a RangeError, unhandled by the inline path.
initLength = arg->constantValue().toInt32(); initLength = arg->constantValue().toInt32();
if (initLength > NativeObject::MAX_DENSE_ELEMENTS_COUNT) if (initLength >= NativeObject::NELEMENTS_LIMIT)
return InliningStatus_NotInlined; return InliningStatus_NotInlined;
MOZ_ASSERT(initLength <= INT32_MAX);
// Make sure initLength matches the template object's length. This is // Make sure initLength matches the template object's length. This is
// not guaranteed to be the case, for instance if we're inlining the // not guaranteed to be the case, for instance if we're inlining the

View File

@ -4116,7 +4116,7 @@ MNewArray::shouldUseVM() const
return !templateObject()->as<UnboxedArrayObject>().hasInlineElements(); return !templateObject()->as<UnboxedArrayObject>().hasInlineElements();
} }
MOZ_ASSERT(length() <= NativeObject::MAX_DENSE_ELEMENTS_COUNT); MOZ_ASSERT(length() < NativeObject::NELEMENTS_LIMIT);
size_t arraySlots = size_t arraySlots =
gc::GetGCKindSlots(templateObject()->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER; gc::GetGCKindSlots(templateObject()->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER;

View File

@ -1734,7 +1734,7 @@ MArrayLength::computeRange(TempAllocator& alloc)
void void
MInitializedLength::computeRange(TempAllocator& alloc) MInitializedLength::computeRange(TempAllocator& alloc)
{ {
setRange(Range::NewUInt32Range(alloc, 0, NativeObject::MAX_DENSE_ELEMENTS_COUNT)); setRange(Range::NewUInt32Range(alloc, 0, NativeObject::NELEMENTS_LIMIT));
} }
void void

View File

@ -172,7 +172,7 @@ GetGCObjectKind(size_t numSlots)
/* As for GetGCObjectKind, but for dense array allocation. */ /* As for GetGCObjectKind, but for dense array allocation. */
static inline AllocKind static inline AllocKind
GetGCArrayKind(size_t numElements) GetGCArrayKind(size_t numSlots)
{ {
/* /*
* Dense arrays can use their fixed slots to hold their elements array * Dense arrays can use their fixed slots to hold their elements array
@ -181,12 +181,9 @@ GetGCArrayKind(size_t numElements)
* unused. * unused.
*/ */
JS_STATIC_ASSERT(ObjectElements::VALUES_PER_HEADER == 2); JS_STATIC_ASSERT(ObjectElements::VALUES_PER_HEADER == 2);
if (numElements > NativeObject::MAX_DENSE_ELEMENTS_COUNT || if (numSlots > NativeObject::NELEMENTS_LIMIT || numSlots + 2 >= SLOTS_TO_THING_KIND_LIMIT)
numElements + ObjectElements::VALUES_PER_HEADER >= SLOTS_TO_THING_KIND_LIMIT)
{
return AllocKind::OBJECT2; return AllocKind::OBJECT2;
} return slotsToThingKind[numSlots + 2];
return slotsToThingKind[numElements + ObjectElements::VALUES_PER_HEADER];
} }
static inline AllocKind static inline AllocKind

View File

@ -791,23 +791,23 @@ NewObjectWithGroup(ExclusiveContext* cx, HandleObjectGroup group,
} }
/* /*
* As for gc::GetGCObjectKind, where numElements is a guess at the final size of * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of
* the object, zero if the final size is unknown. This should only be used for * the object, zero if the final size is unknown. This should only be used for
* objects that do not require any fixed slots. * objects that do not require any fixed slots.
*/ */
static inline gc::AllocKind static inline gc::AllocKind
GuessObjectGCKind(size_t numElements) GuessObjectGCKind(size_t numSlots)
{ {
if (numElements) if (numSlots)
return gc::GetGCObjectKind(numElements); return gc::GetGCObjectKind(numSlots);
return gc::AllocKind::OBJECT4; return gc::AllocKind::OBJECT4;
} }
static inline gc::AllocKind static inline gc::AllocKind
GuessArrayGCKind(size_t numElements) GuessArrayGCKind(size_t numSlots)
{ {
if (numElements) if (numSlots)
return gc::GetGCArrayKind(numElements); return gc::GetGCArrayKind(numSlots);
return gc::AllocKind::OBJECT8; return gc::AllocKind::OBJECT8;
} }

View File

@ -6,8 +6,8 @@
#include "vm/NativeObject-inl.h" #include "vm/NativeObject-inl.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Casting.h" #include "mozilla/Casting.h"
#include "mozilla/CheckedInt.h"
#include "jswatchpoint.h" #include "jswatchpoint.h"
@ -398,7 +398,7 @@ NativeObject::growSlots(ExclusiveContext* cx, uint32_t oldCount, uint32_t newCou
* throttled well before the slot capacity can overflow. * throttled well before the slot capacity can overflow.
*/ */
NativeObject::slotsSizeMustNotOverflow(); NativeObject::slotsSizeMustNotOverflow();
MOZ_ASSERT(newCount <= MAX_SLOTS_COUNT); MOZ_ASSERT(newCount < NELEMENTS_LIMIT);
if (!oldCount) { if (!oldCount) {
MOZ_ASSERT(!slots_); MOZ_ASSERT(!slots_);
@ -520,7 +520,7 @@ NativeObject::willBeSparseElements(uint32_t requiredCapacity, uint32_t newElemen
uint32_t cap = getDenseCapacity(); uint32_t cap = getDenseCapacity();
MOZ_ASSERT(requiredCapacity >= cap); MOZ_ASSERT(requiredCapacity >= cap);
if (requiredCapacity > MAX_DENSE_ELEMENTS_COUNT) if (requiredCapacity >= NELEMENTS_LIMIT)
return true; return true;
uint32_t minimalDenseCount = requiredCapacity / SPARSE_DENSITY_RATIO; uint32_t minimalDenseCount = requiredCapacity / SPARSE_DENSITY_RATIO;
@ -594,7 +594,7 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO
if (numDenseElements * SPARSE_DENSITY_RATIO < newInitializedLength) if (numDenseElements * SPARSE_DENSITY_RATIO < newInitializedLength)
return DenseElementResult::Incomplete; return DenseElementResult::Incomplete;
if (newInitializedLength > MAX_DENSE_ELEMENTS_COUNT) if (newInitializedLength >= NELEMENTS_LIMIT)
return DenseElementResult::Incomplete; return DenseElementResult::Incomplete;
/* /*
@ -672,7 +672,7 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO
// UnboxedArrayObject::chooseCapacityIndex. Changes to the allocation strategy // UnboxedArrayObject::chooseCapacityIndex. Changes to the allocation strategy
// in one should generally be matched by the other. // in one should generally be matched by the other.
/* static */ uint32_t /* static */ uint32_t
NativeObject::goodElementsAllocationAmount(uint32_t reqAllocated, uint32_t length = 0) NativeObject::goodAllocated(uint32_t reqAllocated, uint32_t length = 0)
{ {
// Handle "small" requests primarily by doubling. // Handle "small" requests primarily by doubling.
const uint32_t Mebi = 1 << 20; const uint32_t Mebi = 1 << 20;
@ -712,19 +712,18 @@ NativeObject::goodElementsAllocationAmount(uint32_t reqAllocated, uint32_t lengt
// print('0x' + (n * (1 << 20)).toString(16) + ', '); // print('0x' + (n * (1 << 20)).toString(16) + ', ');
// n = Math.ceil(n * 1.125); // n = Math.ceil(n * 1.125);
// } // }
// print('MAX_DENSE_ELEMENTS_ALLOCATION'); // print('NELEMENTS_LIMIT - 1');
// //
// Dense array elements can't exceed |MAX_DENSE_ELEMENTS_ALLOCATION|, so // Dense array elements can't exceed |NELEMENTS_LIMIT|, so
// |MAX_DENSE_ELEMENTS_ALLOCATION| is the biggest allowed length. // |NELEMENTS_LIMIT - 1| is the biggest allowed length.
static const uint32_t BigBuckets[] = { static const uint32_t BigBuckets[] = {
0x100000, 0x200000, 0x300000, 0x400000, 0x500000, 0x600000, 0x700000, 0x100000, 0x200000, 0x300000, 0x400000, 0x500000, 0x600000, 0x700000,
0x800000, 0x900000, 0xb00000, 0xd00000, 0xf00000, 0x1100000, 0x1400000, 0x800000, 0x900000, 0xb00000, 0xd00000, 0xf00000, 0x1100000, 0x1400000,
0x1700000, 0x1a00000, 0x1e00000, 0x2200000, 0x2700000, 0x2c00000, 0x1700000, 0x1a00000, 0x1e00000, 0x2200000, 0x2700000, 0x2c00000,
0x3200000, 0x3900000, 0x4100000, 0x4a00000, 0x5400000, 0x5f00000, 0x3200000, 0x3900000, 0x4100000, 0x4a00000, 0x5400000, 0x5f00000,
0x6b00000, 0x7900000, 0x8900000, 0x9b00000, 0xaf00000, 0xc500000, 0x6b00000, 0x7900000, 0x8900000, 0x9b00000, 0xaf00000, 0xc500000,
0xde00000, 0xfa00000, MAX_DENSE_ELEMENTS_ALLOCATION 0xde00000, 0xfa00000, NELEMENTS_LIMIT - 1
}; };
MOZ_ASSERT(BigBuckets[ArrayLength(BigBuckets) - 2] <= MAX_DENSE_ELEMENTS_ALLOCATION);
// Pick the first bucket that'll fit |reqAllocated|. // Pick the first bucket that'll fit |reqAllocated|.
for (uint32_t b : BigBuckets) { for (uint32_t b : BigBuckets) {
@ -733,17 +732,12 @@ NativeObject::goodElementsAllocationAmount(uint32_t reqAllocated, uint32_t lengt
} }
// Otherwise, return the maximum bucket size. // Otherwise, return the maximum bucket size.
return MAX_DENSE_ELEMENTS_ALLOCATION; return NELEMENTS_LIMIT - 1;
} }
bool bool
NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity) NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity)
{ {
if (reqCapacity > MAX_DENSE_ELEMENTS_COUNT) {
ReportOutOfMemory(cx);
return false;
}
MOZ_ASSERT(nonProxyIsExtensible()); MOZ_ASSERT(nonProxyIsExtensible());
MOZ_ASSERT(canHaveNonEmptyElements()); MOZ_ASSERT(canHaveNonEmptyElements());
if (denseElementsAreCopyOnWrite()) if (denseElementsAreCopyOnWrite())
@ -752,9 +746,17 @@ NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity)
uint32_t oldCapacity = getDenseCapacity(); uint32_t oldCapacity = getDenseCapacity();
MOZ_ASSERT(oldCapacity < reqCapacity); MOZ_ASSERT(oldCapacity < reqCapacity);
uint32_t reqAllocated = reqCapacity + ObjectElements::VALUES_PER_HEADER; using mozilla::CheckedInt;
uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER;
MOZ_ASSERT(oldAllocated <= MAX_DENSE_ELEMENTS_ALLOCATION); CheckedInt<uint32_t> checkedOldAllocated =
CheckedInt<uint32_t>(oldCapacity) + ObjectElements::VALUES_PER_HEADER;
CheckedInt<uint32_t> checkedReqAllocated =
CheckedInt<uint32_t>(reqCapacity) + ObjectElements::VALUES_PER_HEADER;
if (!checkedOldAllocated.isValid() || !checkedReqAllocated.isValid())
return false;
uint32_t reqAllocated = checkedReqAllocated.value();
uint32_t oldAllocated = checkedOldAllocated.value();
uint32_t newAllocated; uint32_t newAllocated;
if (is<ArrayObject>() && !as<ArrayObject>().lengthIsWritable()) { if (is<ArrayObject>() && !as<ArrayObject>().lengthIsWritable()) {
@ -764,15 +766,14 @@ NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity)
// enforces this requirement. // enforces this requirement.
newAllocated = reqAllocated; newAllocated = reqAllocated;
} else { } else {
newAllocated = goodElementsAllocationAmount(reqAllocated, getElementsHeader()->length); newAllocated = goodAllocated(reqAllocated, getElementsHeader()->length);
} }
uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER; uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
MOZ_ASSERT(newCapacity > oldCapacity && newCapacity >= reqCapacity); MOZ_ASSERT(newCapacity > oldCapacity && newCapacity >= reqCapacity);
// If newCapacity exceeds MAX_DENSE_ELEMENTS_COUNT, the array should become if (newCapacity >= NELEMENTS_LIMIT)
// sparse. return false;
MOZ_ASSERT(newCapacity <= MAX_DENSE_ELEMENTS_COUNT);
uint32_t initlen = getDenseInitializedLength(); uint32_t initlen = getDenseInitializedLength();
@ -813,13 +814,13 @@ NativeObject::shrinkElements(ExclusiveContext* cx, uint32_t reqCapacity)
uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER; uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER;
uint32_t reqAllocated = reqCapacity + ObjectElements::VALUES_PER_HEADER; uint32_t reqAllocated = reqCapacity + ObjectElements::VALUES_PER_HEADER;
uint32_t newAllocated = goodElementsAllocationAmount(reqAllocated); uint32_t newAllocated = goodAllocated(reqAllocated);
if (newAllocated == oldAllocated) if (newAllocated == oldAllocated)
return; // Leave elements at its old size. return; // Leave elements at its old size.
MOZ_ASSERT(newAllocated > ObjectElements::VALUES_PER_HEADER); MOZ_ASSERT(newAllocated > ObjectElements::VALUES_PER_HEADER);
uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER; uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
MOZ_ASSERT(newCapacity <= MAX_DENSE_ELEMENTS_COUNT); MOZ_ASSERT(newCapacity < NELEMENTS_LIMIT);
HeapSlot* oldHeaderSlots = reinterpret_cast<HeapSlot*>(getElementsHeader()); HeapSlot* oldHeaderSlots = reinterpret_cast<HeapSlot*>(getElementsHeader());
HeapSlot* newHeaderSlots = ReallocateObjectBuffer<HeapSlot>(cx, this, oldHeaderSlots, HeapSlot* newHeaderSlots = ReallocateObjectBuffer<HeapSlot>(cx, this, oldHeaderSlots,
@ -844,12 +845,12 @@ NativeObject::CopyElementsForWrite(ExclusiveContext* cx, NativeObject* obj)
uint32_t initlen = obj->getDenseInitializedLength(); uint32_t initlen = obj->getDenseInitializedLength();
uint32_t allocated = initlen + ObjectElements::VALUES_PER_HEADER; uint32_t allocated = initlen + ObjectElements::VALUES_PER_HEADER;
uint32_t newAllocated = goodElementsAllocationAmount(allocated); uint32_t newAllocated = goodAllocated(allocated);
uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER; uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
// COPY_ON_WRITE flags is set only if obj is a dense array. if (newCapacity >= NELEMENTS_LIMIT)
MOZ_ASSERT(newCapacity <= MAX_DENSE_ELEMENTS_COUNT); return false;
JSObject::writeBarrierPre(obj->getElementsHeader()->ownerObject()); JSObject::writeBarrierPre(obj->getElementsHeader()->ownerObject());

View File

@ -556,13 +556,8 @@ class NativeObject : public JSObject
bool shadowingShapeChange(ExclusiveContext* cx, const Shape& shape); bool shadowingShapeChange(ExclusiveContext* cx, const Shape& shape);
bool clearFlag(ExclusiveContext* cx, BaseShape::Flag flag); bool clearFlag(ExclusiveContext* cx, BaseShape::Flag flag);
// The maximum number of slots in an object.
// |MAX_SLOTS_COUNT * sizeof(JS::Value)| shouldn't overflow
// int32_t (see slotsSizeMustNotOverflow).
static const uint32_t MAX_SLOTS_COUNT = (1 << 28) - 1;
static void slotsSizeMustNotOverflow() { static void slotsSizeMustNotOverflow() {
static_assert(NativeObject::MAX_SLOTS_COUNT <= INT32_MAX / sizeof(JS::Value), static_assert((NativeObject::NELEMENTS_LIMIT - 1) <= INT32_MAX / sizeof(JS::Value),
"every caller of this method requires that a slot " "every caller of this method requires that a slot "
"number (or slot count) count multiplied by " "number (or slot count) count multiplied by "
"sizeof(Value) can't overflow uint32_t (and sometimes " "sizeof(Value) can't overflow uint32_t (and sometimes "
@ -887,19 +882,11 @@ class NativeObject : public JSObject
/* Elements accessors. */ /* Elements accessors. */
// The maximum size, in sizeof(Value), of the allocation used for an /* Upper bound on the number of elements in an object. */
// object's dense elements. (This includes space used to store an static const uint32_t NELEMENTS_LIMIT = JS_BIT(28);
// ObjectElements instance.)
// |MAX_DENSE_ELEMENTS_ALLOCATION * sizeof(JS::Value)| shouldn't overflow
// int32_t (see elementsSizeMustNotOverflow).
static const uint32_t MAX_DENSE_ELEMENTS_ALLOCATION = (1 << 28) - 1;
// The maximum number of usable dense elements in an object.
static const uint32_t MAX_DENSE_ELEMENTS_COUNT =
MAX_DENSE_ELEMENTS_ALLOCATION - ObjectElements::VALUES_PER_HEADER;
static void elementsSizeMustNotOverflow() { static void elementsSizeMustNotOverflow() {
static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT <= INT32_MAX / sizeof(JS::Value), static_assert((NativeObject::NELEMENTS_LIMIT - 1) <= INT32_MAX / sizeof(JS::Value),
"every caller of this method require that an element " "every caller of this method require that an element "
"count multiplied by sizeof(Value) can't overflow " "count multiplied by sizeof(Value) can't overflow "
"uint32_t (and sometimes int32_t ,too)"); "uint32_t (and sometimes int32_t ,too)");
@ -917,7 +904,7 @@ class NativeObject : public JSObject
return true; return true;
} }
static uint32_t goodElementsAllocationAmount(uint32_t n, uint32_t length); static uint32_t goodAllocated(uint32_t n, uint32_t length);
bool growElements(ExclusiveContext* cx, uint32_t newcap); bool growElements(ExclusiveContext* cx, uint32_t newcap);
void shrinkElements(ExclusiveContext* cx, uint32_t cap); void shrinkElements(ExclusiveContext* cx, uint32_t cap);
void setDynamicElements(ObjectElements* header) { void setDynamicElements(ObjectElements* header) {