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;
TokenStream::Modifier modifier = TokenStream::Operand;
for (; ; index++) {
if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
if (index == NativeObject::NELEMENTS_LIMIT) {
report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG);
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();
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()) {
visitNewArrayCallVM(lir);
@ -6946,7 +6946,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
key, &callStub);
// 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.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.
initLength = arg->constantValue().toInt32();
if (initLength > NativeObject::MAX_DENSE_ELEMENTS_COUNT)
if (initLength >= NativeObject::NELEMENTS_LIMIT)
return InliningStatus_NotInlined;
MOZ_ASSERT(initLength <= INT32_MAX);
// Make sure initLength matches the template object's length. This is
// 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();
}
MOZ_ASSERT(length() <= NativeObject::MAX_DENSE_ELEMENTS_COUNT);
MOZ_ASSERT(length() < NativeObject::NELEMENTS_LIMIT);
size_t arraySlots =
gc::GetGCKindSlots(templateObject()->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER;

View File

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

View File

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

View File

@ -6,8 +6,8 @@
#include "vm/NativeObject-inl.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Casting.h"
#include "mozilla/CheckedInt.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.
*/
NativeObject::slotsSizeMustNotOverflow();
MOZ_ASSERT(newCount <= MAX_SLOTS_COUNT);
MOZ_ASSERT(newCount < NELEMENTS_LIMIT);
if (!oldCount) {
MOZ_ASSERT(!slots_);
@ -520,7 +520,7 @@ NativeObject::willBeSparseElements(uint32_t requiredCapacity, uint32_t newElemen
uint32_t cap = getDenseCapacity();
MOZ_ASSERT(requiredCapacity >= cap);
if (requiredCapacity > MAX_DENSE_ELEMENTS_COUNT)
if (requiredCapacity >= NELEMENTS_LIMIT)
return true;
uint32_t minimalDenseCount = requiredCapacity / SPARSE_DENSITY_RATIO;
@ -594,7 +594,7 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO
if (numDenseElements * SPARSE_DENSITY_RATIO < newInitializedLength)
return DenseElementResult::Incomplete;
if (newInitializedLength > MAX_DENSE_ELEMENTS_COUNT)
if (newInitializedLength >= NELEMENTS_LIMIT)
return DenseElementResult::Incomplete;
/*
@ -672,7 +672,7 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO
// UnboxedArrayObject::chooseCapacityIndex. Changes to the allocation strategy
// in one should generally be matched by the other.
/* 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.
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) + ', ');
// 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
// |MAX_DENSE_ELEMENTS_ALLOCATION| is the biggest allowed length.
// Dense array elements can't exceed |NELEMENTS_LIMIT|, so
// |NELEMENTS_LIMIT - 1| is the biggest allowed length.
static const uint32_t BigBuckets[] = {
0x100000, 0x200000, 0x300000, 0x400000, 0x500000, 0x600000, 0x700000,
0x800000, 0x900000, 0xb00000, 0xd00000, 0xf00000, 0x1100000, 0x1400000,
0x1700000, 0x1a00000, 0x1e00000, 0x2200000, 0x2700000, 0x2c00000,
0x3200000, 0x3900000, 0x4100000, 0x4a00000, 0x5400000, 0x5f00000,
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|.
for (uint32_t b : BigBuckets) {
@ -733,17 +732,12 @@ NativeObject::goodElementsAllocationAmount(uint32_t reqAllocated, uint32_t lengt
}
// Otherwise, return the maximum bucket size.
return MAX_DENSE_ELEMENTS_ALLOCATION;
return NELEMENTS_LIMIT - 1;
}
bool
NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity)
{
if (reqCapacity > MAX_DENSE_ELEMENTS_COUNT) {
ReportOutOfMemory(cx);
return false;
}
MOZ_ASSERT(nonProxyIsExtensible());
MOZ_ASSERT(canHaveNonEmptyElements());
if (denseElementsAreCopyOnWrite())
@ -752,9 +746,17 @@ NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity)
uint32_t oldCapacity = getDenseCapacity();
MOZ_ASSERT(oldCapacity < reqCapacity);
uint32_t reqAllocated = reqCapacity + ObjectElements::VALUES_PER_HEADER;
uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER;
MOZ_ASSERT(oldAllocated <= MAX_DENSE_ELEMENTS_ALLOCATION);
using mozilla::CheckedInt;
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;
if (is<ArrayObject>() && !as<ArrayObject>().lengthIsWritable()) {
@ -764,15 +766,14 @@ NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity)
// enforces this requirement.
newAllocated = reqAllocated;
} else {
newAllocated = goodElementsAllocationAmount(reqAllocated, getElementsHeader()->length);
newAllocated = goodAllocated(reqAllocated, getElementsHeader()->length);
}
uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
MOZ_ASSERT(newCapacity > oldCapacity && newCapacity >= reqCapacity);
// If newCapacity exceeds MAX_DENSE_ELEMENTS_COUNT, the array should become
// sparse.
MOZ_ASSERT(newCapacity <= MAX_DENSE_ELEMENTS_COUNT);
if (newCapacity >= NELEMENTS_LIMIT)
return false;
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 reqAllocated = reqCapacity + ObjectElements::VALUES_PER_HEADER;
uint32_t newAllocated = goodElementsAllocationAmount(reqAllocated);
uint32_t newAllocated = goodAllocated(reqAllocated);
if (newAllocated == oldAllocated)
return; // Leave elements at its old size.
MOZ_ASSERT(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* newHeaderSlots = ReallocateObjectBuffer<HeapSlot>(cx, this, oldHeaderSlots,
@ -844,12 +845,12 @@ NativeObject::CopyElementsForWrite(ExclusiveContext* cx, NativeObject* obj)
uint32_t initlen = obj->getDenseInitializedLength();
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;
// COPY_ON_WRITE flags is set only if obj is a dense array.
MOZ_ASSERT(newCapacity <= MAX_DENSE_ELEMENTS_COUNT);
if (newCapacity >= NELEMENTS_LIMIT)
return false;
JSObject::writeBarrierPre(obj->getElementsHeader()->ownerObject());

View File

@ -556,13 +556,8 @@ class NativeObject : public JSObject
bool shadowingShapeChange(ExclusiveContext* cx, const Shape& shape);
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_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 "
"number (or slot count) count multiplied by "
"sizeof(Value) can't overflow uint32_t (and sometimes "
@ -887,19 +882,11 @@ class NativeObject : public JSObject
/* Elements accessors. */
// The maximum size, in sizeof(Value), of the allocation used for an
// object's dense elements. (This includes space used to store an
// 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;
/* Upper bound on the number of elements in an object. */
static const uint32_t NELEMENTS_LIMIT = JS_BIT(28);
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 "
"count multiplied by sizeof(Value) can't overflow "
"uint32_t (and sometimes int32_t ,too)");
@ -917,7 +904,7 @@ class NativeObject : public JSObject
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);
void shrinkElements(ExclusiveContext* cx, uint32_t cap);
void setDynamicElements(ObjectElements* header) {