mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 02:14:43 +00:00
Bug 1635185 - Part 3: Store dictionary object slot span in the slots header r=jandem
This stores the slot span in the object slots error for dictionary mode objects. Since it's possible to have dictionary mode objects without dynamic slots, this adds sentinel ObjectSlots instances for each possible dictionary slot span up to the maximum number of fixed slots. Differential Revision: https://phabricator.services.mozilla.com/D87888
This commit is contained in:
parent
d73df31279
commit
f544bc0287
@ -138,7 +138,7 @@ JSObject* GCRuntime::tryNewTenuredObject(JSContext* cx, AllocKind kind,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
slotsHeader = new (allocation) ObjectSlots(nDynamicSlots);
|
||||
slotsHeader = new (allocation) ObjectSlots(nDynamicSlots, 0);
|
||||
Debug_SetSlotRangeToCrashOnTouch(slotsHeader->slots(), nDynamicSlots);
|
||||
}
|
||||
|
||||
|
@ -3561,7 +3561,8 @@ size_t js::TenuringTracer::moveSlotsToTenured(NativeObject* dst,
|
||||
"Failed to allocate slots while tenuring.");
|
||||
}
|
||||
|
||||
ObjectSlots* slotsHeader = new (allocation) ObjectSlots(count);
|
||||
ObjectSlots* slotsHeader = new (allocation)
|
||||
ObjectSlots(count, src->getSlotsHeader()->dictionarySlotSpan());
|
||||
dst->slots_ = slotsHeader->slots();
|
||||
}
|
||||
|
||||
@ -3569,6 +3570,7 @@ size_t js::TenuringTracer::moveSlotsToTenured(NativeObject* dst,
|
||||
|
||||
PodCopy(dst->slots_, src->slots_, count);
|
||||
nursery().setSlotsForwardingPointer(src->slots_, dst->slots_, count);
|
||||
|
||||
return count * sizeof(HeapSlot);
|
||||
}
|
||||
|
||||
|
@ -452,7 +452,7 @@ JSObject* js::Nursery::allocateObject(JSContext* cx, size_t size,
|
||||
// do not visit unallocated things in the nursery.
|
||||
return nullptr;
|
||||
}
|
||||
slotsHeader = new (allocation) ObjectSlots(nDynamicSlots);
|
||||
slotsHeader = new (allocation) ObjectSlots(nDynamicSlots, 0);
|
||||
}
|
||||
|
||||
// Store slots pointer directly in new object. If no dynamic slots were
|
||||
|
@ -11751,10 +11751,10 @@ void CodeGenerator::visitAddAndStoreSlot(LAddAndStoreSlot* ins) {
|
||||
const ValueOperand value = ToValue(ins, LAddAndStoreSlot::Value);
|
||||
const Register maybeTemp = ToTempRegisterOrInvalid(ins->getTemp(0));
|
||||
|
||||
masm.storeObjShape(ins->mir()->shape(), obj,
|
||||
[](MacroAssembler& masm, const Address& addr) {
|
||||
EmitPreBarrier(masm, addr, MIRType::Shape);
|
||||
});
|
||||
Shape* shape = ins->mir()->shape();
|
||||
masm.storeObjShape(shape, obj, [](MacroAssembler& masm, const Address& addr) {
|
||||
EmitPreBarrier(masm, addr, MIRType::Shape);
|
||||
});
|
||||
|
||||
// Perform the store. No pre-barrier required since this is a new
|
||||
// initialization.
|
||||
|
@ -678,6 +678,9 @@ void MacroAssembler::nurseryAllocateObject(Register result, Register temp,
|
||||
if (nDynamicSlots) {
|
||||
store32(Imm32(nDynamicSlots),
|
||||
Address(result, thingSize + ObjectSlots::offsetOfCapacity()));
|
||||
store32(
|
||||
Imm32(0),
|
||||
Address(result, thingSize + ObjectSlots::offsetOfDictionarySlotSpan()));
|
||||
computeEffectiveAddress(
|
||||
Address(result, thingSize + ObjectSlots::offsetOfSlots()), temp);
|
||||
storePtr(temp, Address(result, NativeObject::offsetOfSlots()));
|
||||
@ -1211,7 +1214,8 @@ void MacroAssembler::initGCThing(Register obj, Register temp,
|
||||
// If the object has dynamic slots, the slots member has already been
|
||||
// filled in.
|
||||
if (!ntemplate.hasDynamicSlots()) {
|
||||
storePtr(ImmPtr(nullptr), Address(obj, NativeObject::offsetOfSlots()));
|
||||
storePtr(ImmPtr(emptyObjectSlots),
|
||||
Address(obj, NativeObject::offsetOfSlots()));
|
||||
}
|
||||
|
||||
if (ntemplate.denseElementsAreCopyOnWrite()) {
|
||||
|
@ -61,7 +61,7 @@ inline void ArrayObject::setLength(JSContext* cx, uint32_t length) {
|
||||
aobj->initShape(shape);
|
||||
// NOTE: Dynamic slots are created internally by Allocate<JSObject>.
|
||||
if (!nDynamicSlots) {
|
||||
aobj->initSlots(nullptr);
|
||||
aobj->initEmptyDynamicSlots();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
|
||||
|
@ -110,7 +110,7 @@ inline JSFunction* CloneFunctionObjectIfNotSingleton(
|
||||
nobj->initGroup(group);
|
||||
nobj->initShape(shape);
|
||||
|
||||
nobj->initSlots(nullptr);
|
||||
nobj->initEmptyDynamicSlots();
|
||||
nobj->setEmptyElements();
|
||||
|
||||
MOZ_ASSERT(!clasp->hasPrivate());
|
||||
|
@ -39,8 +39,10 @@ static inline gc::AllocKind NewObjectGCKind(const JSClass* clasp) {
|
||||
} // namespace js
|
||||
|
||||
MOZ_ALWAYS_INLINE uint32_t js::NativeObject::numDynamicSlots() const {
|
||||
uint32_t slots = hasDynamicSlots() ? getSlotsHeader()->capacity() : 0;
|
||||
uint32_t slots = getSlotsHeader()->capacity();
|
||||
MOZ_ASSERT(slots == calculateDynamicSlots());
|
||||
MOZ_ASSERT_IF(hasDynamicSlots(), slots != 0);
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
|
@ -1471,8 +1471,6 @@ bool NativeObject::fillInAfterSwap(JSContext* cx, HandleNativeObject obj,
|
||||
MOZ_ASSERT(obj->slotSpan() == values.length());
|
||||
MOZ_ASSERT(!IsInsideNursery(obj));
|
||||
|
||||
size_t oldSlotCount = obj->numDynamicSlots();
|
||||
|
||||
// Make sure the shape's numFixedSlots() is correct.
|
||||
size_t nfixed =
|
||||
gc::GetGCKindSlots(obj->asTenured().getAllocKind(), obj->getClass());
|
||||
@ -1490,29 +1488,26 @@ bool NativeObject::fillInAfterSwap(JSContext* cx, HandleNativeObject obj,
|
||||
}
|
||||
|
||||
Zone* zone = obj->zone();
|
||||
if (obj->slots_) {
|
||||
size_t size = ObjectSlots::allocSize(oldSlotCount);
|
||||
if (obj->hasDynamicSlots()) {
|
||||
ObjectSlots* slotsHeader = obj->getSlotsHeader();
|
||||
uint32_t oldDictionarySlotSpan = slotsHeader->dictionarySlotSpan();
|
||||
size_t size = ObjectSlots::allocSize(slotsHeader->capacity());
|
||||
zone->removeCellMemory(old, size, MemoryUse::ObjectSlots);
|
||||
js_free(obj->getSlotsHeader());
|
||||
obj->slots_ = nullptr;
|
||||
js_free(slotsHeader);
|
||||
obj->setEmptyDynamicSlots(oldDictionarySlotSpan);
|
||||
}
|
||||
|
||||
if (size_t ndynamic =
|
||||
calculateDynamicSlots(nfixed, values.length(), obj->getClass())) {
|
||||
HeapSlot* allocation =
|
||||
cx->pod_malloc<HeapSlot>(ObjectSlots::allocCount(ndynamic));
|
||||
if (!allocation) {
|
||||
size_t ndynamic =
|
||||
calculateDynamicSlots(nfixed, values.length(), obj->getClass());
|
||||
MOZ_ASSERT(ndynamic >= obj->numDynamicSlots());
|
||||
if (ndynamic > obj->numDynamicSlots()) {
|
||||
if (!obj->growSlots(cx, obj->numDynamicSlots(), ndynamic)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* slotsHeader = new (allocation) ObjectSlots(ndynamic);
|
||||
obj->slots_ = slotsHeader->slots();
|
||||
zone->addCellMemory(obj, ObjectSlots::allocSize(ndynamic),
|
||||
MemoryUse::ObjectSlots);
|
||||
Debug_SetSlotRangeToCrashOnTouch(obj->slots_, ndynamic);
|
||||
}
|
||||
|
||||
obj->initSlotRange(0, values.begin(), values.length());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -522,7 +522,7 @@ inline bool NativeObject::isInWholeCellBuffer() const {
|
||||
nobj->initShape(shape);
|
||||
// NOTE: Dynamic slots are created internally by Allocate<JSObject>.
|
||||
if (!nDynamicSlots) {
|
||||
nobj->initSlots(nullptr);
|
||||
nobj->initEmptyDynamicSlots();
|
||||
}
|
||||
nobj->setEmptyElements();
|
||||
|
||||
@ -550,11 +550,12 @@ MOZ_ALWAYS_INLINE bool NativeObject::updateSlotsForSpan(JSContext* cx,
|
||||
size_t newSpan) {
|
||||
MOZ_ASSERT(oldSpan != newSpan);
|
||||
|
||||
size_t oldCount = numDynamicSlots();
|
||||
size_t newCount = calculateDynamicSlots(numFixedSlots(), newSpan, getClass());
|
||||
size_t oldCapacity = numDynamicSlots();
|
||||
size_t newCapacity =
|
||||
calculateDynamicSlots(numFixedSlots(), newSpan, getClass());
|
||||
|
||||
if (oldSpan < newSpan) {
|
||||
if (oldCount < newCount && !growSlots(cx, oldCount, newCount)) {
|
||||
if (oldCapacity < newCapacity && !growSlots(cx, oldCapacity, newCapacity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -568,14 +569,38 @@ MOZ_ALWAYS_INLINE bool NativeObject::updateSlotsForSpan(JSContext* cx,
|
||||
prepareSlotRangeForOverwrite(newSpan, oldSpan);
|
||||
invalidateSlotRange(newSpan, oldSpan - newSpan);
|
||||
|
||||
if (oldCount > newCount) {
|
||||
shrinkSlots(cx, oldCount, newCount);
|
||||
if (oldCapacity > newCapacity) {
|
||||
shrinkSlots(cx, oldCapacity, newCapacity);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void NativeObject::initEmptyDynamicSlots() {
|
||||
setEmptyDynamicSlots(0);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void NativeObject::setDictionaryModeSlotSpan(uint32_t span) {
|
||||
MOZ_ASSERT(inDictionaryMode());
|
||||
|
||||
if (!hasDynamicSlots()) {
|
||||
setEmptyDynamicSlots(span);
|
||||
return;
|
||||
}
|
||||
|
||||
getSlotsHeader()->setDictionarySlotSpan(span);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void NativeObject::setEmptyDynamicSlots(
|
||||
uint32_t dictionarySlotSpan) {
|
||||
MOZ_ASSERT_IF(!inDictionaryMode(), dictionarySlotSpan == 0);
|
||||
MOZ_ASSERT(dictionarySlotSpan <= MAX_FIXED_SLOTS);
|
||||
slots_ = emptyObjectSlotsForDictionaryObject[dictionarySlotSpan];
|
||||
MOZ_ASSERT(getSlotsHeader()->capacity() == 0);
|
||||
MOZ_ASSERT(getSlotsHeader()->dictionarySlotSpan() == dictionarySlotSpan);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool NativeObject::setLastProperty(JSContext* cx,
|
||||
Shape* shape) {
|
||||
MOZ_ASSERT(!inDictionaryMode());
|
||||
|
@ -72,6 +72,38 @@ static constexpr EmptyObjectElements emptyElementsHeaderShared(
|
||||
HeapSlot* const js::emptyObjectElementsShared = reinterpret_cast<HeapSlot*>(
|
||||
uintptr_t(&emptyElementsHeaderShared) + sizeof(ObjectElements));
|
||||
|
||||
struct EmptyObjectSlots : public ObjectSlots {
|
||||
explicit constexpr EmptyObjectSlots(size_t dictionarySlotSpan)
|
||||
: ObjectSlots(0, dictionarySlotSpan) {}
|
||||
};
|
||||
|
||||
static constexpr EmptyObjectSlots emptyObjectSlotsHeaders[17] = {
|
||||
EmptyObjectSlots(0), EmptyObjectSlots(1), EmptyObjectSlots(2),
|
||||
EmptyObjectSlots(3), EmptyObjectSlots(4), EmptyObjectSlots(5),
|
||||
EmptyObjectSlots(6), EmptyObjectSlots(7), EmptyObjectSlots(8),
|
||||
EmptyObjectSlots(9), EmptyObjectSlots(10), EmptyObjectSlots(11),
|
||||
EmptyObjectSlots(12), EmptyObjectSlots(13), EmptyObjectSlots(14),
|
||||
EmptyObjectSlots(15), EmptyObjectSlots(16)};
|
||||
|
||||
static_assert(ArrayLength(emptyObjectSlotsHeaders) ==
|
||||
NativeObject::MAX_FIXED_SLOTS + 1);
|
||||
|
||||
HeapSlot* const js::emptyObjectSlotsForDictionaryObject[17] = {
|
||||
emptyObjectSlotsHeaders[0].slots(), emptyObjectSlotsHeaders[1].slots(),
|
||||
emptyObjectSlotsHeaders[2].slots(), emptyObjectSlotsHeaders[3].slots(),
|
||||
emptyObjectSlotsHeaders[4].slots(), emptyObjectSlotsHeaders[5].slots(),
|
||||
emptyObjectSlotsHeaders[6].slots(), emptyObjectSlotsHeaders[7].slots(),
|
||||
emptyObjectSlotsHeaders[8].slots(), emptyObjectSlotsHeaders[9].slots(),
|
||||
emptyObjectSlotsHeaders[10].slots(), emptyObjectSlotsHeaders[11].slots(),
|
||||
emptyObjectSlotsHeaders[12].slots(), emptyObjectSlotsHeaders[13].slots(),
|
||||
emptyObjectSlotsHeaders[14].slots(), emptyObjectSlotsHeaders[15].slots(),
|
||||
emptyObjectSlotsHeaders[16].slots()};
|
||||
|
||||
static_assert(ArrayLength(emptyObjectSlotsForDictionaryObject) ==
|
||||
NativeObject::MAX_FIXED_SLOTS + 1);
|
||||
|
||||
HeapSlot* const js::emptyObjectSlots = emptyObjectSlotsForDictionaryObject[0];
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
bool NativeObject::canHaveNonEmptyElements() {
|
||||
@ -289,11 +321,13 @@ void js::NativeObject::initSlotRange(uint32_t start, const Value* vector,
|
||||
HeapSlot* slotsStart;
|
||||
HeapSlot* slotsEnd;
|
||||
getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
|
||||
|
||||
uint32_t offset = start;
|
||||
for (HeapSlot* sp = fixedStart; sp < fixedEnd; sp++) {
|
||||
sp->init(this, HeapSlot::Slot, start++, *vector++);
|
||||
sp->init(this, HeapSlot::Slot, offset++, *vector++);
|
||||
}
|
||||
for (HeapSlot* sp = slotsStart; sp < slotsEnd; sp++) {
|
||||
sp->init(this, HeapSlot::Slot, start++, *vector++);
|
||||
sp->init(this, HeapSlot::Slot, offset++, *vector++);
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,10 +388,11 @@ void NativeObject::setLastPropertyShrinkFixedSlots(Shape* shape) {
|
||||
setShape(shape);
|
||||
}
|
||||
|
||||
bool NativeObject::setSlotSpan(JSContext* cx, uint32_t span) {
|
||||
bool NativeObject::ensureSlotsForDictionaryObject(JSContext* cx,
|
||||
uint32_t span) {
|
||||
MOZ_ASSERT(inDictionaryMode());
|
||||
|
||||
size_t oldSpan = lastProperty()->base()->slotSpan();
|
||||
size_t oldSpan = dictionaryModeSlotSpan();
|
||||
if (oldSpan == span) {
|
||||
return true;
|
||||
}
|
||||
@ -367,6 +402,7 @@ bool NativeObject::setSlotSpan(JSContext* cx, uint32_t span) {
|
||||
}
|
||||
|
||||
lastProperty()->base()->setSlotSpan(span);
|
||||
setDictionaryModeSlotSpan(span);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -383,26 +419,13 @@ bool NativeObject::growSlots(JSContext* cx, uint32_t oldCapacity,
|
||||
NativeObject::slotsSizeMustNotOverflow();
|
||||
MOZ_ASSERT(newCapacity <= MAX_SLOTS_COUNT);
|
||||
|
||||
if (!hasDynamicSlots()) {
|
||||
return allocateSlots(cx, newCapacity);
|
||||
}
|
||||
|
||||
uint32_t newAllocated = ObjectSlots::allocCount(newCapacity);
|
||||
|
||||
if (!oldCapacity) {
|
||||
MOZ_ASSERT(!slots_);
|
||||
HeapSlot* allocation =
|
||||
AllocateObjectBuffer<HeapSlot>(cx, this, newAllocated);
|
||||
if (!allocation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* newHeaderSlots = new (allocation) ObjectSlots(newCapacity);
|
||||
slots_ = newHeaderSlots->slots();
|
||||
|
||||
Debug_SetSlotRangeToCrashOnTouch(slots_, newCapacity);
|
||||
|
||||
AddCellMemory(this, ObjectSlots::allocSize(newCapacity),
|
||||
MemoryUse::ObjectSlots);
|
||||
|
||||
return true;
|
||||
}
|
||||
uint32_t dictionarySpan = getSlotsHeader()->dictionarySlotSpan();
|
||||
|
||||
uint32_t oldAllocated = ObjectSlots::allocCount(oldCapacity);
|
||||
|
||||
@ -416,7 +439,8 @@ bool NativeObject::growSlots(JSContext* cx, uint32_t oldCapacity,
|
||||
return false; /* Leave slots at its old size. */
|
||||
}
|
||||
|
||||
auto* newHeaderSlots = new (allocation) ObjectSlots(newCapacity);
|
||||
auto* newHeaderSlots =
|
||||
new (allocation) ObjectSlots(newCapacity, dictionarySpan);
|
||||
slots_ = newHeaderSlots->slots();
|
||||
|
||||
Debug_SetSlotRangeToCrashOnTouch(slots_ + oldCapacity,
|
||||
@ -427,6 +451,32 @@ bool NativeObject::growSlots(JSContext* cx, uint32_t oldCapacity,
|
||||
AddCellMemory(this, ObjectSlots::allocSize(newCapacity),
|
||||
MemoryUse::ObjectSlots);
|
||||
|
||||
MOZ_ASSERT(hasDynamicSlots());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeObject::allocateSlots(JSContext* cx, uint32_t newCapacity) {
|
||||
MOZ_ASSERT(!hasDynamicSlots());
|
||||
|
||||
uint32_t newAllocated = ObjectSlots::allocCount(newCapacity);
|
||||
|
||||
uint32_t dictionarySpan = getSlotsHeader()->dictionarySlotSpan();
|
||||
|
||||
HeapSlot* allocation = AllocateObjectBuffer<HeapSlot>(cx, this, newAllocated);
|
||||
if (!allocation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* newHeaderSlots =
|
||||
new (allocation) ObjectSlots(newCapacity, dictionarySpan);
|
||||
slots_ = newHeaderSlots->slots();
|
||||
|
||||
Debug_SetSlotRangeToCrashOnTouch(slots_, newCapacity);
|
||||
|
||||
AddCellMemory(this, ObjectSlots::allocSize(newCapacity),
|
||||
MemoryUse::ObjectSlots);
|
||||
|
||||
MOZ_ASSERT(hasDynamicSlots());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -440,6 +490,7 @@ bool NativeObject::growSlotsPure(JSContext* cx, NativeObject* obj,
|
||||
cx->recoverFromOutOfMemory();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -484,7 +535,9 @@ static inline void FreeSlots(JSContext* cx, NativeObject* obj,
|
||||
void NativeObject::shrinkSlots(JSContext* cx, uint32_t oldCapacity,
|
||||
uint32_t newCapacity) {
|
||||
MOZ_ASSERT(newCapacity < oldCapacity);
|
||||
MOZ_ASSERT(oldCapacity == numDynamicSlots());
|
||||
MOZ_ASSERT(oldCapacity == getSlotsHeader()->capacity());
|
||||
|
||||
uint32_t dictionarySpan = getSlotsHeader()->dictionarySlotSpan();
|
||||
|
||||
ObjectSlots* oldHeaderSlots = ObjectSlots::fromSlots(slots_);
|
||||
MOZ_ASSERT(oldHeaderSlots->capacity() == oldCapacity);
|
||||
@ -495,7 +548,7 @@ void NativeObject::shrinkSlots(JSContext* cx, uint32_t oldCapacity,
|
||||
size_t nbytes = ObjectSlots::allocSize(oldCapacity);
|
||||
RemoveCellMemory(this, nbytes, MemoryUse::ObjectSlots);
|
||||
FreeSlots(cx, this, oldHeaderSlots, nbytes);
|
||||
slots_ = nullptr;
|
||||
setEmptyDynamicSlots(dictionarySpan);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -520,7 +573,8 @@ void NativeObject::shrinkSlots(JSContext* cx, uint32_t oldCapacity,
|
||||
AddCellMemory(this, ObjectSlots::allocSize(newCapacity),
|
||||
MemoryUse::ObjectSlots);
|
||||
|
||||
auto* newHeaderSlots = new (allocation) ObjectSlots(newCapacity);
|
||||
auto* newHeaderSlots =
|
||||
new (allocation) ObjectSlots(newCapacity, dictionarySpan);
|
||||
slots_ = newHeaderSlots->slots();
|
||||
}
|
||||
|
||||
@ -1190,7 +1244,7 @@ bool NativeObject::allocDictionarySlot(JSContext* cx, HandleNativeObject obj,
|
||||
|
||||
*slotp = slot;
|
||||
|
||||
return obj->setSlotSpan(cx, slot + 1);
|
||||
return obj->ensureSlotsForDictionaryObject(cx, slot + 1);
|
||||
}
|
||||
|
||||
void NativeObject::freeSlot(JSContext* cx, uint32_t slot) {
|
||||
|
@ -452,6 +452,7 @@ static_assert(ObjectElements::VALUES_PER_HEADER * sizeof(HeapSlot) ==
|
||||
*/
|
||||
class alignas(HeapSlot) ObjectSlots {
|
||||
uint32_t capacity_;
|
||||
uint32_t dictionarySlotSpan_;
|
||||
|
||||
public:
|
||||
static constexpr size_t VALUES_PER_HEADER = 1;
|
||||
@ -475,13 +476,23 @@ class alignas(HeapSlot) ObjectSlots {
|
||||
static constexpr size_t offsetOfCapacity() {
|
||||
return offsetof(ObjectSlots, capacity_);
|
||||
}
|
||||
static constexpr size_t offsetOfDictionarySlotSpan() {
|
||||
return offsetof(ObjectSlots, dictionarySlotSpan_);
|
||||
}
|
||||
static constexpr size_t offsetOfSlots() { return sizeof(ObjectSlots); }
|
||||
static constexpr int32_t offsetOfDictionarySlotSpanFromSlots() {
|
||||
return int32_t(offsetOfDictionarySlotSpan()) - int32_t(offsetOfSlots());
|
||||
}
|
||||
|
||||
explicit ObjectSlots(uint32_t capacity) : capacity_(capacity) {}
|
||||
constexpr explicit ObjectSlots(uint32_t capacity, uint32_t dictionarySlotSpan)
|
||||
: capacity_(capacity), dictionarySlotSpan_(dictionarySlotSpan) {}
|
||||
|
||||
uint32_t capacity() const { return capacity_; }
|
||||
uint32_t dictionarySlotSpan() const { return dictionarySlotSpan_; }
|
||||
|
||||
HeapSlot* slots() {
|
||||
void setDictionarySlotSpan(uint32_t span) { dictionarySlotSpan_ = span; }
|
||||
|
||||
HeapSlot* slots() const {
|
||||
return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(ObjectSlots));
|
||||
}
|
||||
};
|
||||
@ -494,6 +505,12 @@ class alignas(HeapSlot) ObjectSlots {
|
||||
extern HeapSlot* const emptyObjectElements;
|
||||
extern HeapSlot* const emptyObjectElementsShared;
|
||||
|
||||
/*
|
||||
* Shared singletons for objects with no dynamic slots.
|
||||
*/
|
||||
extern HeapSlot* const emptyObjectSlots;
|
||||
extern HeapSlot* const emptyObjectSlotsForDictionaryObject[];
|
||||
|
||||
class AutoCheckShapeConsistency;
|
||||
class GCMarker;
|
||||
class Shape;
|
||||
@ -663,12 +680,16 @@ class NativeObject : public JSObject {
|
||||
* Update the slot span directly for a dictionary object, and allocate
|
||||
* slots to cover the new span if necessary.
|
||||
*/
|
||||
bool setSlotSpan(JSContext* cx, uint32_t span);
|
||||
bool ensureSlotsForDictionaryObject(JSContext* cx, uint32_t span);
|
||||
|
||||
static MOZ_MUST_USE bool toDictionaryMode(JSContext* cx,
|
||||
HandleNativeObject obj);
|
||||
|
||||
private:
|
||||
inline void setEmptyDynamicSlots(uint32_t dictonarySlotSpan);
|
||||
|
||||
inline void setDictionaryModeSlotSpan(uint32_t span);
|
||||
|
||||
friend class TenuringTracer;
|
||||
|
||||
/*
|
||||
@ -767,7 +788,11 @@ class NativeObject : public JSObject {
|
||||
|
||||
public:
|
||||
/* Object allocation may directly initialize slots so this is public. */
|
||||
void initSlots(HeapSlot* slots) { slots_ = slots; }
|
||||
void initSlots(HeapSlot* slots) {
|
||||
MOZ_ASSERT(slots);
|
||||
slots_ = slots;
|
||||
}
|
||||
inline void initEmptyDynamicSlots();
|
||||
|
||||
static MOZ_MUST_USE bool generateOwnShape(JSContext* cx,
|
||||
HandleNativeObject obj,
|
||||
@ -818,11 +843,20 @@ class NativeObject : public JSObject {
|
||||
|
||||
uint32_t slotSpan() const {
|
||||
if (inDictionaryMode()) {
|
||||
return lastProperty()->base()->slotSpan();
|
||||
return dictionaryModeSlotSpan();
|
||||
} else {
|
||||
MOZ_ASSERT(getSlotsHeader()->dictionarySlotSpan() == 0);
|
||||
// Get the class from the object group rather than the base shape to avoid
|
||||
// a race between Shape::ensureOwnBaseShape and background sweeping.
|
||||
return lastProperty()->slotSpan(getClass());
|
||||
}
|
||||
// Get the class from the object group rather than the base shape to avoid a
|
||||
// race between Shape::ensureOwnBaseShape and background sweeping.
|
||||
return lastProperty()->slotSpan(getClass());
|
||||
}
|
||||
|
||||
uint32_t dictionaryModeSlotSpan() const {
|
||||
MOZ_ASSERT(inDictionaryMode());
|
||||
uint32_t span = getSlotsHeader()->dictionarySlotSpan();
|
||||
MOZ_ASSERT(span == lastProperty()->base()->slotSpan());
|
||||
return span;
|
||||
}
|
||||
|
||||
/* Whether a slot is at a fixed offset from this object. */
|
||||
@ -880,6 +914,8 @@ class NativeObject : public JSObject {
|
||||
bool growSlots(JSContext* cx, uint32_t oldCapacity, uint32_t newCapacity);
|
||||
void shrinkSlots(JSContext* cx, uint32_t oldCapacity, uint32_t newCapacity);
|
||||
|
||||
bool allocateSlots(JSContext* cx, uint32_t newCapacity);
|
||||
|
||||
/*
|
||||
* This method is static because it's called from JIT code. On OOM, returns
|
||||
* false without leaving a pending exception on the context.
|
||||
@ -894,7 +930,7 @@ class NativeObject : public JSObject {
|
||||
*/
|
||||
static bool addDenseElementPure(JSContext* cx, NativeObject* obj);
|
||||
|
||||
bool hasDynamicSlots() const { return !!slots_; }
|
||||
bool hasDynamicSlots() const { return getSlotsHeader()->capacity(); }
|
||||
|
||||
/* Compute the number of dynamic slots required for this object. */
|
||||
MOZ_ALWAYS_INLINE uint32_t calculateDynamicSlots() const;
|
||||
|
@ -425,7 +425,7 @@ Shape* Shape::replaceLastProperty(JSContext* cx, StackBaseShape& base,
|
||||
return nullptr;
|
||||
}
|
||||
if (child.slot() >= obj->lastProperty()->base()->slotSpan()) {
|
||||
if (!obj->setSlotSpan(cx, child.slot() + 1)) {
|
||||
if (!obj->ensureSlotsForDictionaryObject(cx, child.slot() + 1)) {
|
||||
new (shape) Shape(obj->lastProperty()->base()->unowned(), 0);
|
||||
return nullptr;
|
||||
}
|
||||
@ -540,6 +540,7 @@ bool js::NativeObject::toDictionaryMode(JSContext* cx, HandleNativeObject obj) {
|
||||
obj->setShape(root);
|
||||
|
||||
MOZ_ASSERT(obj->inDictionaryMode());
|
||||
obj->setDictionaryModeSlotSpan(span);
|
||||
root->base()->setSlotSpan(span);
|
||||
|
||||
return true;
|
||||
@ -867,7 +868,7 @@ Shape* NativeObject::addEnumerableDataProperty(JSContext* cx,
|
||||
return nullptr;
|
||||
}
|
||||
if (slot >= obj->lastProperty()->base()->slotSpan()) {
|
||||
if (MOZ_UNLIKELY(!obj->setSlotSpan(cx, slot + 1))) {
|
||||
if (MOZ_UNLIKELY(!obj->ensureSlotsForDictionaryObject(cx, slot + 1))) {
|
||||
new (shape) Shape(obj->lastProperty()->base()->unowned(), 0);
|
||||
return nullptr;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user