mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-16 14:29:46 +00:00
#509: M1348772 M1364345 (push and splice)
This commit is contained in:
parent
80c343933f
commit
c7075f4527
|
@ -38,18 +38,25 @@ FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TYPES)
|
||||||
bool
|
bool
|
||||||
HeapSlot::preconditionForSet(NativeObject* owner, Kind kind, uint32_t slot)
|
HeapSlot::preconditionForSet(NativeObject* owner, Kind kind, uint32_t slot)
|
||||||
{
|
{
|
||||||
return kind == Slot
|
if (kind == Slot)
|
||||||
? &owner->getSlotRef(slot) == this
|
return &owner->getSlotRef(slot) == this;
|
||||||
: &owner->getDenseElement(slot) == (const Value*)this;
|
|
||||||
|
uint32_t numShifted = owner->getElementsHeader()->numShiftedElements();
|
||||||
|
MOZ_ASSERT(slot >= numShifted);
|
||||||
|
return &owner->getDenseElement(slot - numShifted) == (const Value*)this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
HeapSlot::preconditionForWriteBarrierPost(NativeObject* obj, Kind kind, uint32_t slot,
|
HeapSlot::preconditionForWriteBarrierPost(NativeObject* obj, Kind kind, uint32_t slot,
|
||||||
Value target) const
|
Value target) const
|
||||||
{
|
{
|
||||||
return kind == Slot
|
if (kind == Slot) {
|
||||||
? obj->getSlotAddressUnchecked(slot)->get() == target
|
return (obj->getSlotAddressUnchecked(slot)->get() == target);
|
||||||
: static_cast<HeapSlot*>(obj->getDenseElements() + slot)->get() == target;
|
} else {
|
||||||
|
uint32_t numShifted = obj->getElementsHeader()->numShiftedElements();
|
||||||
|
MOZ_ASSERT(slot >= numShifted);
|
||||||
|
return (static_cast<HeapSlot*>(obj->getDenseElements() + (slot - numShifted))->get() == target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -1543,7 +1543,10 @@ GCMarker::saveValueRanges()
|
||||||
HeapSlot* vp = obj->getDenseElementsAllowCopyOnWrite();
|
HeapSlot* vp = obj->getDenseElementsAllowCopyOnWrite();
|
||||||
if (arr->end == vp + obj->getDenseInitializedLength()) {
|
if (arr->end == vp + obj->getDenseInitializedLength()) {
|
||||||
MOZ_ASSERT(arr->start >= vp);
|
MOZ_ASSERT(arr->start >= vp);
|
||||||
arr->index = arr->start - vp;
|
// Add the number of shifted elements here (and subtract in
|
||||||
|
// restoreValueArray) to ensure shift() calls on the array
|
||||||
|
// are handled correctly.
|
||||||
|
arr->index = obj->unshiftedIndex(arr->start - vp);
|
||||||
arr->kind = HeapSlot::Element;
|
arr->kind = HeapSlot::Element;
|
||||||
} else {
|
} else {
|
||||||
HeapSlot* vp = obj->fixedSlots();
|
HeapSlot* vp = obj->fixedSlots();
|
||||||
|
@ -1582,6 +1585,11 @@ GCMarker::restoreValueArray(JSObject* objArg, void** vpp, void** endp)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint32_t initlen = obj->getDenseInitializedLength();
|
uint32_t initlen = obj->getDenseInitializedLength();
|
||||||
|
|
||||||
|
// Account for shifted elements.
|
||||||
|
uint32_t numShifted = obj->getElementsHeader()->numShiftedElements();
|
||||||
|
start = (numShifted < start) ? start - numShifted : 0;
|
||||||
|
|
||||||
HeapSlot* vp = obj->getDenseElementsAllowCopyOnWrite();
|
HeapSlot* vp = obj->getDenseElementsAllowCopyOnWrite();
|
||||||
if (start < initlen) {
|
if (start < initlen) {
|
||||||
*vpp = vp + start;
|
*vpp = vp + start;
|
||||||
|
@ -1992,8 +2000,11 @@ js::gc::StoreBuffer::SlotsEdge::trace(TenuringTracer& mover) const
|
||||||
|
|
||||||
if (kind() == ElementKind) {
|
if (kind() == ElementKind) {
|
||||||
int32_t initLen = obj->getDenseInitializedLength();
|
int32_t initLen = obj->getDenseInitializedLength();
|
||||||
int32_t clampedStart = Min(start_, initLen);
|
int32_t numShifted = obj->getElementsHeader()->numShiftedElements();
|
||||||
int32_t clampedEnd = Min(start_ + count_, initLen);
|
int32_t clampedStart = Min(Max(0, start_ - numShifted), initLen);
|
||||||
|
int32_t clampedEnd = Min(Max(0, start_ + count_ - numShifted), initLen);
|
||||||
|
MOZ_ASSERT(clampedStart >= 0);
|
||||||
|
MOZ_ASSERT(clampedStart <= clampedEnd);
|
||||||
mover.traceSlots(static_cast<HeapSlot*>(obj->getDenseElements() + clampedStart)
|
mover.traceSlots(static_cast<HeapSlot*>(obj->getDenseElements() + clampedStart)
|
||||||
->unsafeUnbarrieredForTracing(), clampedEnd - clampedStart);
|
->unsafeUnbarrieredForTracing(), clampedEnd - clampedStart);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2249,30 +2260,35 @@ js::TenuringTracer::moveElementsToTenured(NativeObject* dst, NativeObject* src,
|
||||||
if (src->hasEmptyElements() || src->denseElementsAreCopyOnWrite())
|
if (src->hasEmptyElements() || src->denseElementsAreCopyOnWrite())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Zone* zone = src->zone();
|
void* srcAllocatedHeader = src->getUnshiftedElementsHeader();
|
||||||
ObjectElements* srcHeader = src->getElementsHeader();
|
|
||||||
ObjectElements* dstHeader;
|
|
||||||
|
|
||||||
/* TODO Bug 874151: Prefer to put element data inline if we have space. */
|
/* TODO Bug 874151: Prefer to put element data inline if we have space. */
|
||||||
if (!nursery().isInside(srcHeader)) {
|
if (!nursery().isInside(srcAllocatedHeader)) {
|
||||||
MOZ_ASSERT(src->elements_ == dst->elements_);
|
MOZ_ASSERT(src->elements_ == dst->elements_);
|
||||||
nursery().removeMallocedBuffer(srcHeader);
|
nursery().removeMallocedBuffer(srcAllocatedHeader);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nslots = ObjectElements::VALUES_PER_HEADER + srcHeader->capacity;
|
ObjectElements* srcHeader = src->getElementsHeader();
|
||||||
|
|
||||||
|
// Shifted elements are copied too.
|
||||||
|
uint32_t numShifted = srcHeader->numShiftedElements();
|
||||||
|
size_t nslots = srcHeader->numAllocatedElements();
|
||||||
|
|
||||||
/* Unlike other objects, Arrays can have fixed elements. */
|
/* Unlike other objects, Arrays can have fixed elements. */
|
||||||
if (src->is<ArrayObject>() && nslots <= GetGCKindSlots(dstKind)) {
|
if (src->is<ArrayObject>() && nslots <= GetGCKindSlots(dstKind)) {
|
||||||
dst->as<ArrayObject>().setFixedElements();
|
dst->as<ArrayObject>().setFixedElements();
|
||||||
dstHeader = dst->as<ArrayObject>().getElementsHeader();
|
js_memcpy(dst->getElementsHeader(), srcAllocatedHeader, nslots * sizeof(HeapSlot));
|
||||||
js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot));
|
dst->elements_ += numShifted;
|
||||||
nursery().setElementsForwardingPointer(srcHeader, dstHeader, nslots);
|
nursery().setElementsForwardingPointer(srcHeader, dst->getElementsHeader(),
|
||||||
|
srcHeader->capacity);
|
||||||
return nslots * sizeof(HeapSlot);
|
return nslots * sizeof(HeapSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(nslots >= 2);
|
MOZ_ASSERT(nslots >= 2);
|
||||||
|
|
||||||
|
Zone* zone = src->zone();
|
||||||
|
ObjectElements* dstHeader;
|
||||||
{
|
{
|
||||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||||
dstHeader = reinterpret_cast<ObjectElements*>(zone->pod_malloc<HeapSlot>(nslots));
|
dstHeader = reinterpret_cast<ObjectElements*>(zone->pod_malloc<HeapSlot>(nslots));
|
||||||
|
@ -2280,9 +2296,10 @@ js::TenuringTracer::moveElementsToTenured(NativeObject* dst, NativeObject* src,
|
||||||
oomUnsafe.crash("Failed to allocate elements while tenuring.");
|
oomUnsafe.crash("Failed to allocate elements while tenuring.");
|
||||||
}
|
}
|
||||||
|
|
||||||
js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot));
|
js_memcpy(dstHeader, srcAllocatedHeader, nslots * sizeof(HeapSlot));
|
||||||
nursery().setElementsForwardingPointer(srcHeader, dstHeader, nslots);
|
dst->elements_ = dstHeader->elements() + numShifted;
|
||||||
dst->elements_ = dstHeader->elements();
|
nursery().setElementsForwardingPointer(srcHeader, dst->getElementsHeader(),
|
||||||
|
srcHeader->capacity);
|
||||||
return nslots * sizeof(HeapSlot);
|
return nslots * sizeof(HeapSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -359,11 +359,11 @@ Nursery::setSlotsForwardingPointer(HeapSlot* oldSlots, HeapSlot* newSlots, uint3
|
||||||
|
|
||||||
void
|
void
|
||||||
Nursery::setElementsForwardingPointer(ObjectElements* oldHeader, ObjectElements* newHeader,
|
Nursery::setElementsForwardingPointer(ObjectElements* oldHeader, ObjectElements* newHeader,
|
||||||
uint32_t nelems)
|
uint32_t capacity)
|
||||||
{
|
{
|
||||||
// Only use a direct forwarding pointer if there is enough space for one.
|
// Only use a direct forwarding pointer if there is enough space for one.
|
||||||
setForwardingPointer(oldHeader->elements(), newHeader->elements(),
|
setForwardingPointer(oldHeader->elements(), newHeader->elements(),
|
||||||
nelems > ObjectElements::VALUES_PER_HEADER);
|
capacity > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
|
@ -374,7 +374,7 @@ class Nursery
|
||||||
|
|
||||||
void setSlotsForwardingPointer(HeapSlot* oldSlots, HeapSlot* newSlots, uint32_t nslots);
|
void setSlotsForwardingPointer(HeapSlot* oldSlots, HeapSlot* newSlots, uint32_t nslots);
|
||||||
void setElementsForwardingPointer(ObjectElements* oldHeader, ObjectElements* newHeader,
|
void setElementsForwardingPointer(ObjectElements* oldHeader, ObjectElements* newHeader,
|
||||||
uint32_t nelems);
|
uint32_t capacity);
|
||||||
|
|
||||||
/* Free malloced pointers owned by freed things in the nursery. */
|
/* Free malloced pointers owned by freed things in the nursery. */
|
||||||
void freeMallocedBuffers();
|
void freeMallocedBuffers();
|
||||||
|
|
|
@ -7899,7 +7899,26 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
|
||||||
Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
|
Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
|
||||||
Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
|
Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
|
||||||
masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
|
masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mir->mode() == MArrayPopShift::Shift) {
|
||||||
|
// Don't save the elementsTemp register.
|
||||||
|
LiveRegisterSet temps;
|
||||||
|
temps.add(elementsTemp);
|
||||||
|
|
||||||
|
saveVolatile(temps);
|
||||||
|
masm.setupUnalignedABICall(elementsTemp);
|
||||||
|
masm.passABIArg(obj);
|
||||||
|
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::ArrayShiftMoveElements));
|
||||||
|
restoreVolatile(temps);
|
||||||
|
|
||||||
|
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||||
|
// Reload elementsTemp as ArrayShiftMoveElements may have moved it.
|
||||||
|
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||||
// Now adjust length and initializedLength.
|
// Now adjust length and initializedLength.
|
||||||
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength()));
|
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength()));
|
||||||
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
|
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
|
||||||
|
@ -7910,19 +7929,6 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
|
||||||
masm.add32(Imm32(-1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
|
masm.add32(Imm32(-1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mir->mode() == MArrayPopShift::Shift) {
|
|
||||||
// Don't save the temp registers.
|
|
||||||
LiveRegisterSet temps;
|
|
||||||
temps.add(elementsTemp);
|
|
||||||
temps.add(lengthTemp);
|
|
||||||
|
|
||||||
saveVolatile(temps);
|
|
||||||
masm.setupUnalignedABICall(lengthTemp);
|
|
||||||
masm.passABIArg(obj);
|
|
||||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::ArrayShiftMoveElements));
|
|
||||||
restoreVolatile(temps);
|
|
||||||
}
|
|
||||||
|
|
||||||
masm.bind(&done);
|
masm.bind(&done);
|
||||||
masm.bind(ool->rejoin());
|
masm.bind(ool->rejoin());
|
||||||
}
|
}
|
||||||
|
|
|
@ -768,6 +768,11 @@ js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
|
||||||
header->initializedLength = Min(header->initializedLength, newLen);
|
header->initializedLength = Min(header->initializedLength, newLen);
|
||||||
|
|
||||||
if (attrs & JSPROP_READONLY) {
|
if (attrs & JSPROP_READONLY) {
|
||||||
|
if (header->numShiftedElements() > 0) {
|
||||||
|
arr->unshiftElements();
|
||||||
|
header = arr->getElementsHeader();
|
||||||
|
}
|
||||||
|
|
||||||
header->setNonwritableArrayLength();
|
header->setNonwritableArrayLength();
|
||||||
|
|
||||||
// When an array's length becomes non-writable, writes to indexes
|
// When an array's length becomes non-writable, writes to indexes
|
||||||
|
@ -2101,18 +2106,16 @@ ShiftMoveBoxedOrUnboxedDenseElements(JSObject* obj)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
|
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
|
||||||
|
|
||||||
/*
|
|
||||||
* At this point the length and initialized length have already been
|
|
||||||
* decremented and the result fetched, so just shift the array elements
|
|
||||||
* themselves.
|
|
||||||
*/
|
|
||||||
size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
|
size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
|
||||||
|
MOZ_ASSERT(initlen > 0);
|
||||||
|
|
||||||
if (Type == JSVAL_TYPE_MAGIC) {
|
if (Type == JSVAL_TYPE_MAGIC) {
|
||||||
obj->as<NativeObject>().moveDenseElementsNoPreBarrier(0, 1, initlen);
|
if (!obj->as<NativeObject>().tryShiftDenseElements(1))
|
||||||
|
obj->as<NativeObject>().moveDenseElementsNoPreBarrier(0, 1, initlen - 1);
|
||||||
} else {
|
} else {
|
||||||
uint8_t* data = obj->as<UnboxedArrayObject>().elements();
|
uint8_t* data = obj->as<UnboxedArrayObject>().elements();
|
||||||
size_t elementSize = UnboxedTypeSize(Type);
|
size_t elementSize = UnboxedTypeSize(Type);
|
||||||
memmove(data, data + elementSize, initlen * elementSize);
|
memmove(data, data + elementSize, (initlen - 1) * elementSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DenseElementResult::Success;
|
return DenseElementResult::Success;
|
||||||
|
@ -2151,6 +2154,11 @@ ArrayShiftDenseKernel(JSContext* cx, HandleObject obj, MutableHandleValue rval)
|
||||||
if (rval.isMagic(JS_ELEMENTS_HOLE))
|
if (rval.isMagic(JS_ELEMENTS_HOLE))
|
||||||
rval.setUndefined();
|
rval.setUndefined();
|
||||||
|
|
||||||
|
if (Type == JSVAL_TYPE_MAGIC) {
|
||||||
|
if (obj->as<NativeObject>().tryShiftDenseElements(1))
|
||||||
|
return DenseElementResult::Success;
|
||||||
|
}
|
||||||
|
|
||||||
DenseElementResult result = MoveBoxedOrUnboxedDenseElements<Type>(cx, obj, 0, 1, initlen - 1);
|
DenseElementResult result = MoveBoxedOrUnboxedDenseElements<Type>(cx, obj, 0, 1, initlen - 1);
|
||||||
MOZ_ASSERT(result != DenseElementResult::Incomplete);
|
MOZ_ASSERT(result != DenseElementResult::Incomplete);
|
||||||
if (result == DenseElementResult::Failure)
|
if (result == DenseElementResult::Failure)
|
||||||
|
@ -2445,12 +2453,17 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
|
||||||
|
|
||||||
if (CanOptimizeForDenseStorage(obj, 0, len, cx)) {
|
if (CanOptimizeForDenseStorage(obj, 0, len, cx)) {
|
||||||
/* Steps 12(a)-(b). */
|
/* Steps 12(a)-(b). */
|
||||||
DenseElementResult result =
|
if (targetIndex != 0 ||
|
||||||
MoveAnyBoxedOrUnboxedDenseElements(cx, obj, targetIndex, sourceIndex,
|
!obj->is<NativeObject>() ||
|
||||||
len - sourceIndex);
|
!obj->as<NativeObject>().tryShiftDenseElements(sourceIndex))
|
||||||
MOZ_ASSERT(result != DenseElementResult::Incomplete);
|
{
|
||||||
if (result == DenseElementResult::Failure)
|
DenseElementResult result =
|
||||||
return false;
|
MoveAnyBoxedOrUnboxedDenseElements(cx, obj, targetIndex, sourceIndex,
|
||||||
|
len - sourceIndex);
|
||||||
|
MOZ_ASSERT(result != DenseElementResult::Incomplete);
|
||||||
|
if (result == DenseElementResult::Failure)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Steps 12(c)-(d). */
|
/* Steps 12(c)-(d). */
|
||||||
SetAnyBoxedOrUnboxedInitializedLength(cx, obj, finalLength);
|
SetAnyBoxedOrUnboxedInitializedLength(cx, obj, finalLength);
|
||||||
|
|
|
@ -2128,8 +2128,10 @@ RelocateCell(Zone* zone, TenuredCell* src, AllocKind thingKind, size_t thingSize
|
||||||
NativeObject* dstNative = &dstObj->as<NativeObject>();
|
NativeObject* dstNative = &dstObj->as<NativeObject>();
|
||||||
|
|
||||||
// Fixup the pointer to inline object elements if necessary.
|
// Fixup the pointer to inline object elements if necessary.
|
||||||
if (srcNative->hasFixedElements())
|
if (srcNative->hasFixedElements()) {
|
||||||
dstNative->setFixedElements();
|
uint32_t numShifted = srcNative->getElementsHeader()->numShiftedElements();
|
||||||
|
dstNative->setFixedElements(numShifted);
|
||||||
|
}
|
||||||
|
|
||||||
// For copy-on-write objects that own their elements, fix up the
|
// For copy-on-write objects that own their elements, fix up the
|
||||||
// owner pointer to point to the relocated object.
|
// owner pointer to point to the relocated object.
|
||||||
|
|
|
@ -536,6 +536,8 @@ js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level)
|
||||||
if (level == IntegrityLevel::Frozen && obj->is<ArrayObject>()) {
|
if (level == IntegrityLevel::Frozen && obj->is<ArrayObject>()) {
|
||||||
if (!obj->as<ArrayObject>().maybeCopyElementsForWrite(cx))
|
if (!obj->as<ArrayObject>().maybeCopyElementsForWrite(cx))
|
||||||
return false;
|
return false;
|
||||||
|
if (nobj->getElementsHeader()->numShiftedElements() > 0)
|
||||||
|
nobj->unshiftElements();
|
||||||
obj->as<ArrayObject>().getElementsHeader()->setNonwritableArrayLength();
|
obj->as<ArrayObject>().getElementsHeader()->setNonwritableArrayLength();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -3653,8 +3655,10 @@ JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassIn
|
||||||
|
|
||||||
if (is<NativeObject>() && as<NativeObject>().hasDynamicElements()) {
|
if (is<NativeObject>() && as<NativeObject>().hasDynamicElements()) {
|
||||||
js::ObjectElements* elements = as<NativeObject>().getElementsHeader();
|
js::ObjectElements* elements = as<NativeObject>().getElementsHeader();
|
||||||
if (!elements->isCopyOnWrite() || elements->ownerObject() == this)
|
if (!elements->isCopyOnWrite() || elements->ownerObject() == this) {
|
||||||
info->objectsMallocHeapElementsNonAsmJS += mallocSizeOf(elements);
|
void* allocatedElements = as<NativeObject>().getUnshiftedElementsHeader();
|
||||||
|
info->objectsMallocHeapElementsNonAsmJS += mallocSizeOf(allocatedElements);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other things may be measured in the future if DMD indicates it is worthwhile.
|
// Other things may be measured in the future if DMD indicates it is worthwhile.
|
||||||
|
@ -3722,7 +3726,7 @@ JSObject::sizeOfIncludingThisInNursery() const
|
||||||
if (native.hasDynamicElements()) {
|
if (native.hasDynamicElements()) {
|
||||||
js::ObjectElements& elements = *native.getElementsHeader();
|
js::ObjectElements& elements = *native.getElementsHeader();
|
||||||
if (!elements.isCopyOnWrite() || elements.ownerObject() == this)
|
if (!elements.isCopyOnWrite() || elements.ownerObject() == this)
|
||||||
size += elements.capacity * sizeof(HeapSlot);
|
size += (elements.capacity + elements.numShiftedElements()) * sizeof(HeapSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is<ArgumentsObject>())
|
if (is<ArgumentsObject>())
|
||||||
|
|
|
@ -96,10 +96,11 @@ JSObject::finalize(js::FreeOp* fop)
|
||||||
// Don't free the elements until object finalization finishes,
|
// Don't free the elements until object finalization finishes,
|
||||||
// so that other objects can access these elements while they
|
// so that other objects can access these elements while they
|
||||||
// are themselves finalized.
|
// are themselves finalized.
|
||||||
|
MOZ_ASSERT(elements->numShiftedElements() == 0);
|
||||||
fop->freeLater(elements);
|
fop->freeLater(elements);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fop->free_(elements);
|
fop->free_(nobj->getUnshiftedElementsHeader());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,12 +130,13 @@ NativeObject::ensureDenseInitializedLengthNoPackedCheck(ExclusiveContext* cx, ui
|
||||||
uint32_t& initlen = getElementsHeader()->initializedLength;
|
uint32_t& initlen = getElementsHeader()->initializedLength;
|
||||||
|
|
||||||
if (initlen < index + extra) {
|
if (initlen < index + extra) {
|
||||||
|
uint32_t numShifted = getElementsHeader()->numShiftedElements();
|
||||||
size_t offset = initlen;
|
size_t offset = initlen;
|
||||||
for (HeapSlot* sp = elements_ + initlen;
|
for (HeapSlot* sp = elements_ + initlen;
|
||||||
sp != elements_ + (index + extra);
|
sp != elements_ + (index + extra);
|
||||||
sp++, offset++)
|
sp++, offset++)
|
||||||
{
|
{
|
||||||
sp->init(this, HeapSlot::Element, offset, MagicValue(JS_ELEMENTS_HOLE));
|
sp->init(this, HeapSlot::Element, offset + numShifted, MagicValue(JS_ELEMENTS_HOLE));
|
||||||
}
|
}
|
||||||
initlen = index + extra;
|
initlen = index + extra;
|
||||||
}
|
}
|
||||||
|
@ -286,6 +287,36 @@ NativeObject::setSlotWithType(ExclusiveContext* cx, Shape* shape,
|
||||||
AddTypePropertyId(cx, this, shape->propid(), value);
|
AddTypePropertyId(cx, this, shape->propid(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
NativeObject::tryShiftDenseElements(uint32_t count)
|
||||||
|
{
|
||||||
|
ObjectElements* header = getElementsHeader();
|
||||||
|
if (header->initializedLength == count ||
|
||||||
|
count > ObjectElements::MaxShiftedElements ||
|
||||||
|
header->isCopyOnWrite() ||
|
||||||
|
// header->isFrozen() || // NYI
|
||||||
|
header->hasNonwritableArrayLength())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(count > 0);
|
||||||
|
MOZ_ASSERT(count < header->initializedLength);
|
||||||
|
|
||||||
|
if (MOZ_UNLIKELY(header->numShiftedElements() + count > ObjectElements::MaxShiftedElements)) {
|
||||||
|
unshiftElements();
|
||||||
|
header = getElementsHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareElementRangeForOverwrite(0, count);
|
||||||
|
header->addShiftedElements(count);
|
||||||
|
|
||||||
|
elements_ += count;
|
||||||
|
ObjectElements* newHeader = getElementsHeader();
|
||||||
|
memmove(newHeader, header, sizeof(ObjectElements));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* Make an object with pregenerated shape from a NEWOBJECT bytecode. */
|
/* Make an object with pregenerated shape from a NEWOBJECT bytecode. */
|
||||||
static inline PlainObject*
|
static inline PlainObject*
|
||||||
CopyInitializerObject(JSContext* cx, HandlePlainObject baseobj, NewObjectKind newKind = GenericObject)
|
CopyInitializerObject(JSContext* cx, HandlePlainObject baseobj, NewObjectKind newKind = GenericObject)
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
#include "mozilla/Casting.h"
|
#include "mozilla/Casting.h"
|
||||||
|
#include "mozilla/CheckedInt.h"
|
||||||
|
|
||||||
#include "jswatchpoint.h"
|
#include "jswatchpoint.h"
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ using namespace js;
|
||||||
|
|
||||||
using JS::GenericNaN;
|
using JS::GenericNaN;
|
||||||
using mozilla::ArrayLength;
|
using mozilla::ArrayLength;
|
||||||
|
using mozilla::CheckedInt;
|
||||||
using mozilla::DebugOnly;
|
using mozilla::DebugOnly;
|
||||||
using mozilla::PodCopy;
|
using mozilla::PodCopy;
|
||||||
using mozilla::RoundUpPow2;
|
using mozilla::RoundUpPow2;
|
||||||
|
@ -343,7 +345,7 @@ NativeObject::setLastPropertyMakeNonNative(Shape* shape)
|
||||||
MOZ_ASSERT(shape->numFixedSlots() == 0);
|
MOZ_ASSERT(shape->numFixedSlots() == 0);
|
||||||
|
|
||||||
if (hasDynamicElements())
|
if (hasDynamicElements())
|
||||||
js_free(getElementsHeader());
|
js_free(getUnshiftedElementsHeader());
|
||||||
if (hasDynamicSlots()) {
|
if (hasDynamicSlots()) {
|
||||||
js_free(slots_);
|
js_free(slots_);
|
||||||
slots_ = nullptr;
|
slots_ = nullptr;
|
||||||
|
@ -757,6 +759,49 @@ NativeObject::goodElementsAllocationAmount(ExclusiveContext* cx, uint32_t reqCap
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NativeObject::unshiftElements()
|
||||||
|
{
|
||||||
|
ObjectElements* header = getElementsHeader();
|
||||||
|
uint32_t numShifted = header->numShiftedElements();
|
||||||
|
MOZ_ASSERT(numShifted > 0);
|
||||||
|
|
||||||
|
uint32_t initLength = header->initializedLength;
|
||||||
|
|
||||||
|
ObjectElements* newHeader = static_cast<ObjectElements*>(getUnshiftedElementsHeader());
|
||||||
|
memmove(newHeader, header, sizeof(ObjectElements));
|
||||||
|
|
||||||
|
newHeader->clearShiftedElements();
|
||||||
|
newHeader->capacity += numShifted;
|
||||||
|
elements_ = newHeader->elements();
|
||||||
|
|
||||||
|
// To move the elements, temporarily update initializedLength to include
|
||||||
|
// both shifted and unshifted elements.
|
||||||
|
newHeader->initializedLength += numShifted;
|
||||||
|
|
||||||
|
// Move the elements. Initialize to |undefined| to ensure pre-barriers
|
||||||
|
// don't see garbage.
|
||||||
|
for (size_t i = 0; i < numShifted; i++)
|
||||||
|
initDenseElement(i, UndefinedValue());
|
||||||
|
moveDenseElements(0, numShifted, initLength);
|
||||||
|
|
||||||
|
// Restore the initialized length. We use setDenseInitializedLength to
|
||||||
|
// make sure prepareElementRangeForOverwrite is called on the shifted
|
||||||
|
// elements.
|
||||||
|
setDenseInitializedLength(initLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NativeObject::maybeUnshiftElements()
|
||||||
|
{
|
||||||
|
ObjectElements* header = getElementsHeader();
|
||||||
|
MOZ_ASSERT(header->numShiftedElements() > 0);
|
||||||
|
|
||||||
|
// Unshift if less than a third of the allocated space is in use.
|
||||||
|
if (header->capacity < header->numAllocatedElements() / 3)
|
||||||
|
unshiftElements();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity)
|
NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity)
|
||||||
{
|
{
|
||||||
|
@ -765,6 +810,26 @@ NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity)
|
||||||
if (denseElementsAreCopyOnWrite())
|
if (denseElementsAreCopyOnWrite())
|
||||||
MOZ_CRASH();
|
MOZ_CRASH();
|
||||||
|
|
||||||
|
// If there are shifted elements, consider unshifting them first. If we
|
||||||
|
// don't unshift here, the code below will include the shifted elements in
|
||||||
|
// the resize.
|
||||||
|
uint32_t numShifted = getElementsHeader()->numShiftedElements();
|
||||||
|
if (numShifted > 0) {
|
||||||
|
maybeUnshiftElements();
|
||||||
|
if (getDenseCapacity() >= reqCapacity)
|
||||||
|
return true;
|
||||||
|
numShifted = getElementsHeader()->numShiftedElements();
|
||||||
|
|
||||||
|
// Ensure |reqCapacity + numShifted| below won't overflow by forcing an
|
||||||
|
// unshift in that case.
|
||||||
|
CheckedInt<uint32_t> checkedReqCapacity(reqCapacity);
|
||||||
|
checkedReqCapacity += numShifted;
|
||||||
|
if (MOZ_UNLIKELY(!checkedReqCapacity.isValid())) {
|
||||||
|
unshiftElements();
|
||||||
|
numShifted = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t oldCapacity = getDenseCapacity();
|
uint32_t oldCapacity = getDenseCapacity();
|
||||||
MOZ_ASSERT(oldCapacity < reqCapacity);
|
MOZ_ASSERT(oldCapacity < reqCapacity);
|
||||||
|
|
||||||
|
@ -775,13 +840,17 @@ NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity)
|
||||||
// Preserve the |capacity <= length| invariant for arrays with
|
// Preserve the |capacity <= length| invariant for arrays with
|
||||||
// non-writable length. See also js::ArraySetLength which initially
|
// non-writable length. See also js::ArraySetLength which initially
|
||||||
// enforces this requirement.
|
// enforces this requirement.
|
||||||
newAllocated = reqCapacity + ObjectElements::VALUES_PER_HEADER;
|
newAllocated = reqCapacity + numShifted + ObjectElements::VALUES_PER_HEADER;
|
||||||
} else {
|
} else {
|
||||||
if (!goodElementsAllocationAmount(cx, reqCapacity, getElementsHeader()->length, &newAllocated))
|
if (!goodElementsAllocationAmount(cx, reqCapacity + numShifted,
|
||||||
|
getElementsHeader()->length,
|
||||||
|
&newAllocated))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
|
uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER - numShifted;
|
||||||
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 exceeds MAX_DENSE_ELEMENTS_COUNT, the array should become
|
||||||
|
@ -790,11 +859,11 @@ NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity)
|
||||||
|
|
||||||
uint32_t initlen = getDenseInitializedLength();
|
uint32_t initlen = getDenseInitializedLength();
|
||||||
|
|
||||||
HeapSlot* oldHeaderSlots = reinterpret_cast<HeapSlot*>(getElementsHeader());
|
HeapSlot* oldHeaderSlots = reinterpret_cast<HeapSlot*>(getUnshiftedElementsHeader());
|
||||||
HeapSlot* newHeaderSlots;
|
HeapSlot* newHeaderSlots;
|
||||||
if (hasDynamicElements()) {
|
if (hasDynamicElements()) {
|
||||||
MOZ_ASSERT(oldCapacity <= MAX_DENSE_ELEMENTS_COUNT);
|
MOZ_ASSERT(oldCapacity <= MAX_DENSE_ELEMENTS_COUNT);
|
||||||
uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER;
|
uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER + numShifted;
|
||||||
|
|
||||||
newHeaderSlots = ReallocateObjectBuffer<HeapSlot>(cx, this, oldHeaderSlots, oldAllocated, newAllocated);
|
newHeaderSlots = ReallocateObjectBuffer<HeapSlot>(cx, this, oldHeaderSlots, oldAllocated, newAllocated);
|
||||||
if (!newHeaderSlots)
|
if (!newHeaderSlots)
|
||||||
|
@ -803,12 +872,13 @@ NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity)
|
||||||
newHeaderSlots = AllocateObjectBuffer<HeapSlot>(cx, this, newAllocated);
|
newHeaderSlots = AllocateObjectBuffer<HeapSlot>(cx, this, newAllocated);
|
||||||
if (!newHeaderSlots)
|
if (!newHeaderSlots)
|
||||||
return false; // Leave elements at its old size.
|
return false; // Leave elements at its old size.
|
||||||
PodCopy(newHeaderSlots, oldHeaderSlots, ObjectElements::VALUES_PER_HEADER + initlen);
|
PodCopy(newHeaderSlots, oldHeaderSlots,
|
||||||
|
ObjectElements::VALUES_PER_HEADER + initlen + numShifted);
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectElements* newheader = reinterpret_cast<ObjectElements*>(newHeaderSlots);
|
ObjectElements* newheader = reinterpret_cast<ObjectElements*>(newHeaderSlots);
|
||||||
newheader->capacity = newCapacity;
|
elements_ = newheader->elements() + numShifted;
|
||||||
elements_ = newheader->elements();
|
getElementsHeader()->capacity = newCapacity;
|
||||||
|
|
||||||
Debug_SetSlotRangeToCrashOnTouch(elements_ + initlen, newCapacity - initlen);
|
Debug_SetSlotRangeToCrashOnTouch(elements_ + initlen, newCapacity - initlen);
|
||||||
|
|
||||||
|
@ -818,9 +888,6 @@ NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity)
|
||||||
void
|
void
|
||||||
NativeObject::shrinkElements(ExclusiveContext* cx, uint32_t reqCapacity)
|
NativeObject::shrinkElements(ExclusiveContext* cx, uint32_t reqCapacity)
|
||||||
{
|
{
|
||||||
uint32_t oldCapacity = getDenseCapacity();
|
|
||||||
MOZ_ASSERT(reqCapacity < oldCapacity);
|
|
||||||
|
|
||||||
MOZ_ASSERT(canHaveNonEmptyElements());
|
MOZ_ASSERT(canHaveNonEmptyElements());
|
||||||
if (denseElementsAreCopyOnWrite())
|
if (denseElementsAreCopyOnWrite())
|
||||||
MOZ_CRASH();
|
MOZ_CRASH();
|
||||||
|
@ -828,18 +895,29 @@ NativeObject::shrinkElements(ExclusiveContext* cx, uint32_t reqCapacity)
|
||||||
if (!hasDynamicElements())
|
if (!hasDynamicElements())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// If we have shifted elements, consider unshifting them.
|
||||||
|
uint32_t numShifted = getElementsHeader()->numShiftedElements();
|
||||||
|
if (numShifted > 0) {
|
||||||
|
maybeUnshiftElements();
|
||||||
|
numShifted = getElementsHeader()->numShiftedElements();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t oldCapacity = getDenseCapacity();
|
||||||
|
MOZ_ASSERT(reqCapacity < oldCapacity);
|
||||||
|
|
||||||
uint32_t newAllocated = 0;
|
uint32_t newAllocated = 0;
|
||||||
MOZ_ALWAYS_TRUE(goodElementsAllocationAmount(cx, reqCapacity, 0, &newAllocated));
|
MOZ_ALWAYS_TRUE(goodElementsAllocationAmount(cx, reqCapacity + numShifted, 0, &newAllocated));
|
||||||
MOZ_ASSERT(oldCapacity <= MAX_DENSE_ELEMENTS_COUNT);
|
MOZ_ASSERT(oldCapacity <= MAX_DENSE_ELEMENTS_COUNT);
|
||||||
uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER;
|
|
||||||
|
uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER + numShifted;
|
||||||
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 - numShifted;
|
||||||
MOZ_ASSERT(newCapacity <= MAX_DENSE_ELEMENTS_COUNT);
|
MOZ_ASSERT(newCapacity <= MAX_DENSE_ELEMENTS_COUNT);
|
||||||
|
|
||||||
HeapSlot* oldHeaderSlots = reinterpret_cast<HeapSlot*>(getElementsHeader());
|
HeapSlot* oldHeaderSlots = reinterpret_cast<HeapSlot*>(getUnshiftedElementsHeader());
|
||||||
HeapSlot* newHeaderSlots = ReallocateObjectBuffer<HeapSlot>(cx, this, oldHeaderSlots,
|
HeapSlot* newHeaderSlots = ReallocateObjectBuffer<HeapSlot>(cx, this, oldHeaderSlots,
|
||||||
oldAllocated, newAllocated);
|
oldAllocated, newAllocated);
|
||||||
if (!newHeaderSlots) {
|
if (!newHeaderSlots) {
|
||||||
|
@ -848,8 +926,8 @@ NativeObject::shrinkElements(ExclusiveContext* cx, uint32_t reqCapacity)
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectElements* newheader = reinterpret_cast<ObjectElements*>(newHeaderSlots);
|
ObjectElements* newheader = reinterpret_cast<ObjectElements*>(newHeaderSlots);
|
||||||
newheader->capacity = newCapacity;
|
elements_ = newheader->elements() + numShifted;
|
||||||
elements_ = newheader->elements();
|
getElementsHeader()->capacity = newCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ bool
|
/* static */ bool
|
||||||
|
|
|
@ -156,11 +156,28 @@ ArraySetLength(JSContext* cx, Handle<ArrayObject*> obj, HandleId id,
|
||||||
* Elements do not track property creation order, so enumerating the elements
|
* Elements do not track property creation order, so enumerating the elements
|
||||||
* of an object does not necessarily visit indexes in the order they were
|
* of an object does not necessarily visit indexes in the order they were
|
||||||
* created.
|
* created.
|
||||||
|
*
|
||||||
|
* Shifted elements
|
||||||
|
* ----------------
|
||||||
|
* It's pretty common to use an array as a queue, like this:
|
||||||
|
*
|
||||||
|
* while (arr.length > 0)
|
||||||
|
* foo(arr.shift());
|
||||||
|
*
|
||||||
|
* To ensure we don't get quadratic behavior on this, elements can be 'shifted'
|
||||||
|
* in memory. tryShiftDenseElements does this by incrementing elements_ to point
|
||||||
|
* to the next element and moving the ObjectElements header in memory (so it's
|
||||||
|
* stored where the shifted Value used to be).
|
||||||
|
*
|
||||||
|
* Shifted elements can be unshifted when we grow the array, when the array is
|
||||||
|
* frozen (for simplicity, shifted elements are not supported on objects that
|
||||||
|
* are frozen, have copy-on-write elements, or on arrays with non-writable
|
||||||
|
* length).
|
||||||
*/
|
*/
|
||||||
class ObjectElements
|
class ObjectElements
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum Flags {
|
enum Flags: uint16_t {
|
||||||
// Integers written to these elements must be converted to doubles.
|
// Integers written to these elements must be converted to doubles.
|
||||||
CONVERT_DOUBLE_ELEMENTS = 0x1,
|
CONVERT_DOUBLE_ELEMENTS = 0x1,
|
||||||
|
|
||||||
|
@ -180,9 +197,22 @@ class ObjectElements
|
||||||
// For TypedArrays only: this TypedArray's storage is mapping shared
|
// For TypedArrays only: this TypedArray's storage is mapping shared
|
||||||
// memory. This is a static property of the TypedArray, set when it
|
// memory. This is a static property of the TypedArray, set when it
|
||||||
// is created and never changed.
|
// is created and never changed.
|
||||||
SHARED_MEMORY = 0x8
|
SHARED_MEMORY = 0x8,
|
||||||
|
|
||||||
|
// These elements are set to integrity level "frozen".
|
||||||
|
// Not currently implemented.
|
||||||
|
FROZEN = 0x10,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The flags word stores both the flags and the number of shifted elements.
|
||||||
|
// Allow shifting 2047 elements before unshifting.
|
||||||
|
static const size_t NumShiftedElementsBits = 11;
|
||||||
|
static const size_t MaxShiftedElements = (1 << NumShiftedElementsBits) - 1;
|
||||||
|
static const size_t NumShiftedElementsShift = 32 - NumShiftedElementsBits;
|
||||||
|
static const size_t FlagsMask = (1 << NumShiftedElementsShift) - 1;
|
||||||
|
static_assert(MaxShiftedElements == 2047,
|
||||||
|
"MaxShiftedElements should match the comment");
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class ::JSObject;
|
friend class ::JSObject;
|
||||||
friend class ArrayObject;
|
friend class ArrayObject;
|
||||||
|
@ -195,7 +225,9 @@ class ObjectElements
|
||||||
ArraySetLength(JSContext* cx, Handle<ArrayObject*> obj, HandleId id,
|
ArraySetLength(JSContext* cx, Handle<ArrayObject*> obj, HandleId id,
|
||||||
unsigned attrs, HandleValue value, ObjectOpResult& result);
|
unsigned attrs, HandleValue value, ObjectOpResult& result);
|
||||||
|
|
||||||
/* See Flags enum above. */
|
// The NumShiftedElementsBits high bits of this are used to store the
|
||||||
|
// number of shifted elements, the other bits are available for the flags.
|
||||||
|
// See Flags enum above.
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -238,6 +270,21 @@ class ObjectElements
|
||||||
flags &= ~COPY_ON_WRITE;
|
flags &= ~COPY_ON_WRITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addShiftedElements(uint32_t count) {
|
||||||
|
MOZ_ASSERT(count < capacity);
|
||||||
|
MOZ_ASSERT(count < initializedLength);
|
||||||
|
MOZ_ASSERT(!(flags & (NONWRITABLE_ARRAY_LENGTH | FROZEN | COPY_ON_WRITE)));
|
||||||
|
uint32_t numShifted = numShiftedElements() + count;
|
||||||
|
MOZ_ASSERT(numShifted <= MaxShiftedElements);
|
||||||
|
flags = (numShifted << NumShiftedElementsShift) | (flags & FlagsMask);
|
||||||
|
capacity -= count;
|
||||||
|
initializedLength -= count;
|
||||||
|
}
|
||||||
|
void clearShiftedElements() {
|
||||||
|
flags &= FlagsMask;
|
||||||
|
MOZ_ASSERT(numShiftedElements() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MOZ_CONSTEXPR ObjectElements(uint32_t capacity, uint32_t length)
|
MOZ_CONSTEXPR ObjectElements(uint32_t capacity, uint32_t length)
|
||||||
: flags(0), initializedLength(0), capacity(capacity), length(length)
|
: flags(0), initializedLength(0), capacity(capacity), length(length)
|
||||||
|
@ -257,7 +304,7 @@ class ObjectElements
|
||||||
const HeapSlot* elements() const {
|
const HeapSlot* elements() const {
|
||||||
return reinterpret_cast<const HeapSlot*>(uintptr_t(this) + sizeof(ObjectElements));
|
return reinterpret_cast<const HeapSlot*>(uintptr_t(this) + sizeof(ObjectElements));
|
||||||
}
|
}
|
||||||
static ObjectElements * fromElements(HeapSlot* elems) {
|
static ObjectElements* fromElements(HeapSlot* elems) {
|
||||||
return reinterpret_cast<ObjectElements*>(uintptr_t(elems) - sizeof(ObjectElements));
|
return reinterpret_cast<ObjectElements*>(uintptr_t(elems) - sizeof(ObjectElements));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,6 +333,17 @@ class ObjectElements
|
||||||
static bool ConvertElementsToDoubles(JSContext* cx, uintptr_t elements);
|
static bool ConvertElementsToDoubles(JSContext* cx, uintptr_t elements);
|
||||||
static bool MakeElementsCopyOnWrite(ExclusiveContext* cx, NativeObject* obj);
|
static bool MakeElementsCopyOnWrite(ExclusiveContext* cx, NativeObject* obj);
|
||||||
|
|
||||||
|
uint32_t numShiftedElements() const {
|
||||||
|
uint32_t numShifted = flags >> NumShiftedElementsShift;
|
||||||
|
MOZ_ASSERT_IF(numShifted > 0,
|
||||||
|
!(flags & (NONWRITABLE_ARRAY_LENGTH | FROZEN | COPY_ON_WRITE)));
|
||||||
|
return numShifted;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t numAllocatedElements() const {
|
||||||
|
return VALUES_PER_HEADER + capacity + numShiftedElements();
|
||||||
|
}
|
||||||
|
|
||||||
// This is enough slots to store an object of this class. See the static
|
// This is enough slots to store an object of this class. See the static
|
||||||
// assertion below.
|
// assertion below.
|
||||||
static const size_t VALUES_PER_HEADER = 2;
|
static const size_t VALUES_PER_HEADER = 2;
|
||||||
|
@ -943,10 +1001,26 @@ class NativeObject : public JSObject
|
||||||
"uint32_t (and sometimes int32_t ,too)");
|
"uint32_t (and sometimes int32_t ,too)");
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectElements * getElementsHeader() const {
|
ObjectElements* getElementsHeader() const {
|
||||||
return ObjectElements::fromElements(elements_);
|
return ObjectElements::fromElements(elements_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a pointer to the first element, including shifted elements.
|
||||||
|
inline HeapSlot* unshiftedElements() const {
|
||||||
|
return elements_ - getElementsHeader()->numShiftedElements();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like getElementsHeader, but returns a pointer to the unshifted header.
|
||||||
|
// This is mainly useful for free()ing dynamic elements: the pointer
|
||||||
|
// returned here is the one we got from malloc.
|
||||||
|
void* getUnshiftedElementsHeader() const {
|
||||||
|
return ObjectElements::fromElements(unshiftedElements());
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t unshiftedIndex(uint32_t index) const {
|
||||||
|
return index + getElementsHeader()->numShiftedElements();
|
||||||
|
}
|
||||||
|
|
||||||
/* Accessors for elements. */
|
/* Accessors for elements. */
|
||||||
bool ensureElements(ExclusiveContext* cx, uint32_t capacity) {
|
bool ensureElements(ExclusiveContext* cx, uint32_t capacity) {
|
||||||
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
|
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
|
||||||
|
@ -955,6 +1029,15 @@ class NativeObject : public JSObject
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to shift |count| dense elements, see the "Shifted elements" comment.
|
||||||
|
inline bool tryShiftDenseElements(uint32_t count);
|
||||||
|
|
||||||
|
// Unshift all shifted elements so that numShiftedElements is 0.
|
||||||
|
void unshiftElements();
|
||||||
|
|
||||||
|
// If this object has many shifted elements, unshift them.
|
||||||
|
void maybeUnshiftElements();
|
||||||
|
|
||||||
static bool goodElementsAllocationAmount(ExclusiveContext* cx, uint32_t reqAllocated,
|
static bool goodElementsAllocationAmount(ExclusiveContext* cx, uint32_t reqAllocated,
|
||||||
uint32_t length, uint32_t* goodAmount);
|
uint32_t length, uint32_t* goodAmount);
|
||||||
bool growElements(ExclusiveContext* cx, uint32_t newcap);
|
bool growElements(ExclusiveContext* cx, uint32_t newcap);
|
||||||
|
@ -985,7 +1068,8 @@ class NativeObject : public JSObject
|
||||||
if (v.isObject() && IsInsideNursery(&v.toObject())) {
|
if (v.isObject() && IsInsideNursery(&v.toObject())) {
|
||||||
JS::shadow::Runtime* shadowRuntime = shadowRuntimeFromMainThread();
|
JS::shadow::Runtime* shadowRuntime = shadowRuntimeFromMainThread();
|
||||||
shadowRuntime->gcStoreBufferPtr()->putSlot(this, HeapSlot::Element,
|
shadowRuntime->gcStoreBufferPtr()->putSlot(this, HeapSlot::Element,
|
||||||
start + i, count - i);
|
unshiftedIndex(start + i),
|
||||||
|
count - i);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1004,13 +1088,13 @@ class NativeObject : public JSObject
|
||||||
void setDenseElement(uint32_t index, const Value& val) {
|
void setDenseElement(uint32_t index, const Value& val) {
|
||||||
MOZ_ASSERT(index < getDenseInitializedLength());
|
MOZ_ASSERT(index < getDenseInitializedLength());
|
||||||
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
|
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
|
||||||
elements_[index].set(this, HeapSlot::Element, index, val);
|
elements_[index].set(this, HeapSlot::Element, unshiftedIndex(index), val);
|
||||||
}
|
}
|
||||||
|
|
||||||
void initDenseElement(uint32_t index, const Value& val) {
|
void initDenseElement(uint32_t index, const Value& val) {
|
||||||
MOZ_ASSERT(index < getDenseInitializedLength());
|
MOZ_ASSERT(index < getDenseInitializedLength());
|
||||||
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
|
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
|
||||||
elements_[index].init(this, HeapSlot::Element, index, val);
|
elements_[index].init(this, HeapSlot::Element, unshiftedIndex(index), val);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDenseElementMaybeConvertDouble(uint32_t index, const Value& val) {
|
void setDenseElementMaybeConvertDouble(uint32_t index, const Value& val) {
|
||||||
|
@ -1034,8 +1118,12 @@ class NativeObject : public JSObject
|
||||||
MOZ_ASSERT(dstStart + count <= getDenseCapacity());
|
MOZ_ASSERT(dstStart + count <= getDenseCapacity());
|
||||||
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
|
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
|
||||||
if (JS::shadow::Zone::asShadowZone(zone())->needsIncrementalBarrier()) {
|
if (JS::shadow::Zone::asShadowZone(zone())->needsIncrementalBarrier()) {
|
||||||
for (uint32_t i = 0; i < count; ++i)
|
uint32_t numShifted = getElementsHeader()->numShiftedElements();
|
||||||
elements_[dstStart + i].set(this, HeapSlot::Element, dstStart + i, src[i]);
|
for (uint32_t i = 0; i < count; ++i) {
|
||||||
|
elements_[dstStart + i].set(this, HeapSlot::Element,
|
||||||
|
dstStart + i + numShifted,
|
||||||
|
src[i]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
memcpy(&elements_[dstStart], src, count * sizeof(HeapSlot));
|
memcpy(&elements_[dstStart], src, count * sizeof(HeapSlot));
|
||||||
elementsRangeWriteBarrierPost(dstStart, count);
|
elementsRangeWriteBarrierPost(dstStart, count);
|
||||||
|
@ -1067,16 +1155,17 @@ class NativeObject : public JSObject
|
||||||
* the array before and after the move.
|
* the array before and after the move.
|
||||||
*/
|
*/
|
||||||
if (JS::shadow::Zone::asShadowZone(zone())->needsIncrementalBarrier()) {
|
if (JS::shadow::Zone::asShadowZone(zone())->needsIncrementalBarrier()) {
|
||||||
|
uint32_t numShifted = getElementsHeader()->numShiftedElements();
|
||||||
if (dstStart < srcStart) {
|
if (dstStart < srcStart) {
|
||||||
HeapSlot* dst = elements_ + dstStart;
|
HeapSlot* dst = elements_ + dstStart;
|
||||||
HeapSlot* src = elements_ + srcStart;
|
HeapSlot* src = elements_ + srcStart;
|
||||||
for (uint32_t i = 0; i < count; i++, dst++, src++)
|
for (uint32_t i = 0; i < count; i++, dst++, src++)
|
||||||
dst->set(this, HeapSlot::Element, dst - elements_, *src);
|
dst->set(this, HeapSlot::Element, dst - elements_ + numShifted, *src);
|
||||||
} else {
|
} else {
|
||||||
HeapSlot* dst = elements_ + dstStart + count - 1;
|
HeapSlot* dst = elements_ + dstStart + count - 1;
|
||||||
HeapSlot* src = elements_ + srcStart + count - 1;
|
HeapSlot* src = elements_ + srcStart + count - 1;
|
||||||
for (uint32_t i = 0; i < count; i++, dst--, src--)
|
for (uint32_t i = 0; i < count; i++, dst--, src--)
|
||||||
dst->set(this, HeapSlot::Element, dst - elements_, *src);
|
dst->set(this, HeapSlot::Element, dst - elements_ + numShifted, *src);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
memmove(elements_ + dstStart, elements_ + srcStart, count * sizeof(HeapSlot));
|
memmove(elements_ + dstStart, elements_ + srcStart, count * sizeof(HeapSlot));
|
||||||
|
@ -1161,9 +1250,9 @@ class NativeObject : public JSObject
|
||||||
bool canHaveNonEmptyElements();
|
bool canHaveNonEmptyElements();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void setFixedElements() {
|
void setFixedElements(uint32_t numShifted = 0) {
|
||||||
MOZ_ASSERT(canHaveNonEmptyElements());
|
MOZ_ASSERT(canHaveNonEmptyElements());
|
||||||
elements_ = fixedElements();
|
elements_ = fixedElements() + numShifted;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool hasDynamicElements() const {
|
inline bool hasDynamicElements() const {
|
||||||
|
@ -1174,11 +1263,11 @@ class NativeObject : public JSObject
|
||||||
* immediately afterwards. Such cases cannot occur for dense arrays
|
* immediately afterwards. Such cases cannot occur for dense arrays
|
||||||
* (which have at least two fixed slots) and can only result in a leak.
|
* (which have at least two fixed slots) and can only result in a leak.
|
||||||
*/
|
*/
|
||||||
return !hasEmptyElements() && elements_ != fixedElements();
|
return !hasEmptyElements() && !hasFixedElements();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool hasFixedElements() const {
|
inline bool hasFixedElements() const {
|
||||||
return elements_ == fixedElements();
|
return unshiftedElements() == fixedElements();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool hasEmptyElements() const {
|
inline bool hasEmptyElements() const {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user