mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1165392, Bug 1165463 - Various unboxed array fixes and optimizations, r=jandem.
This commit is contained in:
parent
3ce99ceded
commit
c6e41638c6
@ -41,7 +41,8 @@ AllocateObjectBuffer(ExclusiveContext* cx, uint32_t count)
|
||||
{
|
||||
if (cx->isJSContext()) {
|
||||
Nursery& nursery = cx->asJSContext()->runtime()->gc.nursery;
|
||||
return static_cast<T*>(nursery.allocateBuffer(cx->zone(), count * sizeof(T)));
|
||||
size_t nbytes = JS_ROUNDUP(count * sizeof(T), sizeof(Value));
|
||||
return static_cast<T*>(nursery.allocateBuffer(cx->zone(), nbytes));
|
||||
}
|
||||
return cx->zone()->pod_malloc<T>(count);
|
||||
}
|
||||
@ -52,7 +53,8 @@ AllocateObjectBuffer(ExclusiveContext* cx, JSObject* obj, uint32_t count)
|
||||
{
|
||||
if (cx->isJSContext()) {
|
||||
Nursery& nursery = cx->asJSContext()->runtime()->gc.nursery;
|
||||
return static_cast<T*>(nursery.allocateBuffer(obj, count * sizeof(T)));
|
||||
size_t nbytes = JS_ROUNDUP(count * sizeof(T), sizeof(Value));
|
||||
return static_cast<T*>(nursery.allocateBuffer(obj, nbytes));
|
||||
}
|
||||
return obj->zone()->pod_malloc<T>(count);
|
||||
}
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "vm/Interpreter-inl.h"
|
||||
#include "vm/ScopeObject-inl.h"
|
||||
#include "vm/StringObject-inl.h"
|
||||
#include "vm/UnboxedObject-inl.h"
|
||||
|
||||
using mozilla::BitwiseCast;
|
||||
using mozilla::DebugOnly;
|
||||
@ -5154,29 +5155,13 @@ RemoveExistingTypedArraySetElemStub(JSContext* cx, ICSetElem_Fallback* stub, Han
|
||||
return false;
|
||||
}
|
||||
|
||||
static size_t
|
||||
SetElemObjectInitializedLength(JSObject *obj)
|
||||
{
|
||||
if (obj->isNative())
|
||||
return obj->as<NativeObject>().getDenseInitializedLength();
|
||||
return obj->as<UnboxedArrayObject>().initializedLength();
|
||||
}
|
||||
|
||||
static size_t
|
||||
SetElemObjectCapacity(JSObject *obj)
|
||||
{
|
||||
if (obj->isNative())
|
||||
return obj->as<NativeObject>().getDenseCapacity();
|
||||
return obj->as<UnboxedArrayObject>().capacity();
|
||||
}
|
||||
|
||||
static bool
|
||||
CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index,
|
||||
Shape* oldShape, uint32_t oldCapacity, uint32_t oldInitLength,
|
||||
bool* isAddingCaseOut, size_t* protoDepthOut)
|
||||
{
|
||||
uint32_t initLength = SetElemObjectInitializedLength(obj);
|
||||
uint32_t capacity = SetElemObjectCapacity(obj);
|
||||
uint32_t initLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
|
||||
uint32_t capacity = GetAnyBoxedOrUnboxedCapacity(obj);
|
||||
|
||||
*isAddingCaseOut = false;
|
||||
*protoDepthOut = 0;
|
||||
@ -5263,9 +5248,9 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
|
||||
// Check the old capacity
|
||||
uint32_t oldCapacity = 0;
|
||||
uint32_t oldInitLength = 0;
|
||||
if (obj->isNative() && index.isInt32() && index.toInt32() >= 0) {
|
||||
oldCapacity = obj->as<NativeObject>().getDenseCapacity();
|
||||
oldInitLength = obj->as<NativeObject>().getDenseInitializedLength();
|
||||
if (index.isInt32() && index.toInt32() >= 0) {
|
||||
oldCapacity = GetAnyBoxedOrUnboxedCapacity(obj);
|
||||
oldInitLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
|
||||
}
|
||||
|
||||
if (op == JSOP_INITELEM) {
|
||||
@ -5533,8 +5518,6 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
// Unbox key.
|
||||
Register key = masm.extractInt32(R1, ExtractTemp1);
|
||||
|
||||
Address valueAddr(BaselineStackReg, ICStackValueOffset);
|
||||
|
||||
if (unboxedType_ == JSVAL_TYPE_MAGIC) {
|
||||
// Set element on a native object.
|
||||
|
||||
@ -5569,6 +5552,8 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
regs.takeUnchecked(obj);
|
||||
regs.takeUnchecked(key);
|
||||
|
||||
Address valueAddr(BaselineStackReg, ICStackValueOffset);
|
||||
|
||||
// We need to convert int32 values being stored into doubles. In this case
|
||||
// the heap typeset is guaranteed to contain both int32 and double, so it's
|
||||
// okay to store a double. Note that double arrays are only created by
|
||||
@ -5598,10 +5583,11 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
|
||||
|
||||
// Compute the address being written to.
|
||||
BaseIndex address(obj, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
|
||||
BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
|
||||
|
||||
EmitUnboxedPreBarrierForBaseline(masm, address, unboxedType_);
|
||||
|
||||
Address valueAddr(BaselineStackReg, ICStackValueOffset + sizeof(Value));
|
||||
masm.Push(R0);
|
||||
masm.loadValue(valueAddr, R0);
|
||||
masm.storeUnboxedProperty(address, unboxedType_,
|
||||
@ -5748,8 +5734,6 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm)
|
||||
// Unbox key.
|
||||
Register key = masm.extractInt32(R1, ExtractTemp1);
|
||||
|
||||
Address valueAddr(BaselineStackReg, ICStackValueOffset);
|
||||
|
||||
if (unboxedType_ == JSVAL_TYPE_MAGIC) {
|
||||
// Adding element to a native object.
|
||||
|
||||
@ -5793,6 +5777,9 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm)
|
||||
masm.branchTest32(Assembler::Zero, elementsFlags,
|
||||
Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS),
|
||||
&dontConvertDoubles);
|
||||
|
||||
Address valueAddr(BaselineStackReg, ICStackValueOffset);
|
||||
|
||||
// Note that double arrays are only created by IonMonkey, so if we have no
|
||||
// floating-point support Ion is disabled and there should be no double arrays.
|
||||
if (cx->runtime()->jitSupportsFloatingPoint)
|
||||
@ -5815,31 +5802,31 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm)
|
||||
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg);
|
||||
masm.branch32(Assembler::NotEqual, scratchReg, key, &failure);
|
||||
|
||||
Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
|
||||
|
||||
// Capacity check.
|
||||
masm.checkUnboxedArrayCapacity(obj, Int32Key(key), scratchReg, &failure);
|
||||
|
||||
// Increment initLength before write.
|
||||
masm.add32(Imm32(1), initLengthAddr);
|
||||
|
||||
// If length is now <= key, increment length before write.
|
||||
Label skipIncrementLength;
|
||||
masm.branch32(Assembler::Above, lengthAddr, key, &skipIncrementLength);
|
||||
masm.add32(Imm32(1), lengthAddr);
|
||||
masm.bind(&skipIncrementLength);
|
||||
|
||||
// Load obj->elements.
|
||||
masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
|
||||
|
||||
// Write the value first, since this can fail. No need for pre-barrier
|
||||
// since we're not overwriting an old value.
|
||||
masm.Push(R0);
|
||||
Address valueAddr(BaselineStackReg, ICStackValueOffset + sizeof(Value));
|
||||
masm.loadValue(valueAddr, R0);
|
||||
|
||||
// Write the value. No need for pre-barrier since we're not overwriting an old value.
|
||||
BaseIndex address(obj, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
|
||||
BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
|
||||
masm.storeUnboxedProperty(address, unboxedType_,
|
||||
ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0);
|
||||
masm.Pop(R0);
|
||||
|
||||
// Increment initialized length.
|
||||
masm.add32(Imm32(1), initLengthAddr);
|
||||
|
||||
// If length is now <= key, increment length.
|
||||
Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
|
||||
Label skipIncrementLength;
|
||||
masm.branch32(Assembler::Above, lengthAddr, key, &skipIncrementLength);
|
||||
masm.add32(Imm32(1), lengthAddr);
|
||||
masm.bind(&skipIncrementLength);
|
||||
}
|
||||
|
||||
EmitReturnFromIC(masm);
|
||||
@ -9956,18 +9943,11 @@ GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc,
|
||||
}
|
||||
|
||||
if (native == js::array_concat) {
|
||||
if (args.thisv().isObject() &&
|
||||
args.thisv().toObject().is<ArrayObject>() &&
|
||||
!args.thisv().toObject().isSingleton() &&
|
||||
!args.thisv().toObject().group()->hasUnanalyzedPreliminaryObjects())
|
||||
{
|
||||
RootedObject proto(cx, args.thisv().toObject().getProto());
|
||||
res.set(NewDenseEmptyArray(cx, proto, TenuredObject));
|
||||
if (args.thisv().isObject() && !args.thisv().toObject().isSingleton()) {
|
||||
res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0,
|
||||
TenuredObject, /* forceAnalyze = */ true));
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
res->setGroup(args.thisv().toObject().group());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2492,6 +2492,12 @@ CodeGenerator::visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite* lir)
|
||||
OutOfLineCode* ool = oolCallVM(CopyElementsForWriteInfo, lir,
|
||||
(ArgList(), object), StoreNothing());
|
||||
|
||||
if (lir->mir()->checkNative()) {
|
||||
masm.loadObjClass(object, temp);
|
||||
masm.branchTest32(Assembler::NonZero, Address(temp, Class::offsetOfFlags()),
|
||||
Imm32(Class::NON_NATIVE), ool->rejoin());
|
||||
}
|
||||
|
||||
masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp);
|
||||
masm.branchTest32(Assembler::NonZero,
|
||||
Address(temp, ObjectElements::offsetOfFlags()),
|
||||
@ -6808,9 +6814,11 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
||||
|
||||
template <typename T>
|
||||
static void
|
||||
StoreUnboxedPointer(MacroAssembler& masm, T address, MIRType type, const LAllocation* value)
|
||||
StoreUnboxedPointer(MacroAssembler& masm, T address, MIRType type, const LAllocation* value,
|
||||
bool preBarrier)
|
||||
{
|
||||
masm.patchableCallPreBarrier(address, type);
|
||||
if (preBarrier)
|
||||
masm.patchableCallPreBarrier(address, type);
|
||||
if (value->isConstant()) {
|
||||
Value v = *value->toConstant();
|
||||
if (v.isMarkable()) {
|
||||
@ -6829,12 +6837,15 @@ CodeGenerator::visitStoreUnboxedPointer(LStoreUnboxedPointer* lir)
|
||||
{
|
||||
MIRType type;
|
||||
int32_t offsetAdjustment;
|
||||
bool preBarrier;
|
||||
if (lir->mir()->isStoreUnboxedObjectOrNull()) {
|
||||
type = MIRType_Object;
|
||||
offsetAdjustment = lir->mir()->toStoreUnboxedObjectOrNull()->offsetAdjustment();
|
||||
preBarrier = lir->mir()->toStoreUnboxedObjectOrNull()->preBarrier();
|
||||
} else if (lir->mir()->isStoreUnboxedString()) {
|
||||
type = MIRType_String;
|
||||
offsetAdjustment = lir->mir()->toStoreUnboxedString()->offsetAdjustment();
|
||||
preBarrier = lir->mir()->toStoreUnboxedString()->preBarrier();
|
||||
} else {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
@ -6845,10 +6856,10 @@ CodeGenerator::visitStoreUnboxedPointer(LStoreUnboxedPointer* lir)
|
||||
|
||||
if (index->isConstant()) {
|
||||
Address address(elements, ToInt32(index) * sizeof(uintptr_t) + offsetAdjustment);
|
||||
StoreUnboxedPointer(masm, address, type, value);
|
||||
StoreUnboxedPointer(masm, address, type, value, preBarrier);
|
||||
} else {
|
||||
BaseIndex address(elements, ToRegister(index), ScalePointer, offsetAdjustment);
|
||||
StoreUnboxedPointer(masm, address, type, value);
|
||||
StoreUnboxedPointer(masm, address, type, value, preBarrier);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6889,14 +6900,22 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
|
||||
// VM call if a write barrier is necessary.
|
||||
masm.branchTestNeedsIncrementalBarrier(Assembler::NonZero, ool->entry());
|
||||
|
||||
// Load elements and length.
|
||||
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
|
||||
masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp);
|
||||
|
||||
// VM call if length != initializedLength.
|
||||
// Load elements and length, and VM call if length != initializedLength.
|
||||
Int32Key key = Int32Key(lengthTemp);
|
||||
Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
|
||||
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
|
||||
masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp);
|
||||
|
||||
Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
|
||||
} else {
|
||||
masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), elementsTemp);
|
||||
masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), lengthTemp);
|
||||
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), lengthTemp);
|
||||
|
||||
Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
|
||||
masm.branchKey(Assembler::NotEqual, lengthAddr, key, ool->entry());
|
||||
}
|
||||
|
||||
// Test for length != 0. On zero length either take a VM call or generate
|
||||
// an undefined value, depending on whether the call is known to produce
|
||||
@ -6915,25 +6934,41 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
|
||||
masm.bumpKey(&key, -1);
|
||||
|
||||
if (mir->mode() == MArrayPopShift::Pop) {
|
||||
masm.loadElementTypedOrValue(BaseIndex(elementsTemp, lengthTemp, TimesEight), out,
|
||||
mir->needsHoleCheck(), ool->entry());
|
||||
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
BaseIndex addr(elementsTemp, lengthTemp, TimesEight);
|
||||
masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry());
|
||||
} else {
|
||||
size_t elemSize = UnboxedTypeSize(mir->unboxedType());
|
||||
BaseIndex addr(elementsTemp, lengthTemp, ScaleFromElemWidth(elemSize));
|
||||
masm.loadUnboxedProperty(addr, mir->unboxedType(), out);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(mir->mode() == MArrayPopShift::Shift);
|
||||
masm.loadElementTypedOrValue(Address(elementsTemp, 0), out, mir->needsHoleCheck(),
|
||||
ool->entry());
|
||||
Address addr(elementsTemp, 0);
|
||||
if (mir->unboxedType() == JSVAL_TYPE_MAGIC)
|
||||
masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry());
|
||||
else
|
||||
masm.loadUnboxedProperty(addr, mir->unboxedType(), out);
|
||||
}
|
||||
|
||||
// Handle the failure case when the array length is non-writable in the
|
||||
// OOL path. (Unlike in the adding-an-element cases, we can't rely on the
|
||||
// capacity <= length invariant for such arrays to avoid an explicit
|
||||
// check.)
|
||||
Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
|
||||
Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
|
||||
masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
|
||||
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
// Handle the failure case when the array length is non-writable in the
|
||||
// OOL path. (Unlike in the adding-an-element cases, we can't rely on the
|
||||
// capacity <= length invariant for such arrays to avoid an explicit
|
||||
// check.)
|
||||
Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
|
||||
Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
|
||||
masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
|
||||
|
||||
// Now adjust length and initializedLength.
|
||||
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength()));
|
||||
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
|
||||
// Now adjust length and initializedLength.
|
||||
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength()));
|
||||
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
|
||||
} else {
|
||||
// Unboxed arrays always have writable lengths. Adjust length and
|
||||
// initializedLength.
|
||||
masm.store32(lengthTemp, Address(obj, UnboxedArrayObject::offsetOfLength()));
|
||||
masm.add32(Imm32(-1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
|
||||
}
|
||||
|
||||
if (mir->mode() == MArrayPopShift::Shift) {
|
||||
// Don't save the temp registers.
|
||||
@ -6972,7 +7007,7 @@ CodeGenerator::visitArrayPopShiftT(LArrayPopShiftT* lir)
|
||||
emitArrayPopShift(lir, lir->mir(), obj, elements, length, out);
|
||||
}
|
||||
|
||||
typedef bool (*ArrayPushDenseFn)(JSContext*, HandleArrayObject, HandleValue, uint32_t*);
|
||||
typedef bool (*ArrayPushDenseFn)(JSContext*, HandleObject, HandleValue, uint32_t*);
|
||||
static const VMFunction ArrayPushDenseInfo =
|
||||
FunctionInfo<ArrayPushDenseFn>(jit::ArrayPushDense);
|
||||
|
||||
@ -6982,25 +7017,51 @@ CodeGenerator::emitArrayPush(LInstruction* lir, const MArrayPush* mir, Register
|
||||
{
|
||||
OutOfLineCode* ool = oolCallVM(ArrayPushDenseInfo, lir, (ArgList(), obj, value), StoreRegisterTo(length));
|
||||
|
||||
// Load elements and length.
|
||||
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
|
||||
masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
|
||||
|
||||
Int32Key key = Int32Key(length);
|
||||
Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
|
||||
Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
|
||||
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
// Load elements and length.
|
||||
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
|
||||
masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
|
||||
|
||||
// Guard length == initializedLength.
|
||||
masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
|
||||
// Guard length == initializedLength.
|
||||
Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
|
||||
|
||||
// Guard length < capacity.
|
||||
masm.branchKey(Assembler::BelowOrEqual, capacity, key, ool->entry());
|
||||
// Guard length < capacity.
|
||||
Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
|
||||
masm.branchKey(Assembler::BelowOrEqual, capacity, key, ool->entry());
|
||||
|
||||
masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight));
|
||||
// Do the store.
|
||||
masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight));
|
||||
} else {
|
||||
// Load initialized length.
|
||||
masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), length);
|
||||
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), length);
|
||||
|
||||
// Guard length == initializedLength.
|
||||
Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
|
||||
masm.branchKey(Assembler::NotEqual, lengthAddr, key, ool->entry());
|
||||
|
||||
// Guard length < capacity.
|
||||
masm.checkUnboxedArrayCapacity(obj, key, elementsTemp, ool->entry());
|
||||
|
||||
// Load elements and do the store.
|
||||
masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), elementsTemp);
|
||||
size_t elemSize = UnboxedTypeSize(mir->unboxedType());
|
||||
BaseIndex addr(elementsTemp, length, ScaleFromElemWidth(elemSize));
|
||||
masm.storeUnboxedProperty(addr, mir->unboxedType(), value, nullptr);
|
||||
}
|
||||
|
||||
masm.bumpKey(&key, 1);
|
||||
masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
|
||||
masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
|
||||
|
||||
// Update length and initialized length.
|
||||
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
|
||||
masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
|
||||
} else {
|
||||
masm.store32(length, Address(obj, UnboxedArrayObject::offsetOfLength()));
|
||||
masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
|
||||
}
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
@ -7044,13 +7105,23 @@ CodeGenerator::visitArrayConcat(LArrayConcat* lir)
|
||||
// inline and pass it to the stub. Else, we just pass nullptr and the stub falls
|
||||
// back to a slow path.
|
||||
Label fail, call;
|
||||
masm.loadPtr(Address(lhs, NativeObject::offsetOfElements()), temp1);
|
||||
masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
|
||||
masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
|
||||
if (lir->mir()->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
masm.loadPtr(Address(lhs, NativeObject::offsetOfElements()), temp1);
|
||||
masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
|
||||
masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
|
||||
|
||||
masm.loadPtr(Address(rhs, NativeObject::offsetOfElements()), temp1);
|
||||
masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
|
||||
masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
|
||||
masm.loadPtr(Address(rhs, NativeObject::offsetOfElements()), temp1);
|
||||
masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
|
||||
masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
|
||||
} else {
|
||||
masm.load32(Address(lhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1);
|
||||
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1);
|
||||
masm.branch32(Assembler::NotEqual, Address(lhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail);
|
||||
|
||||
masm.load32(Address(rhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1);
|
||||
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1);
|
||||
masm.branch32(Assembler::NotEqual, Address(rhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail);
|
||||
}
|
||||
|
||||
// Try to allocate an object.
|
||||
masm.createGCObject(temp1, temp2, lir->mir()->templateObj(), lir->mir()->initialHeap(), &fail);
|
||||
|
@ -6619,7 +6619,7 @@ IonBuilder::jsop_initelem_array()
|
||||
|
||||
if (unboxedType != JSVAL_TYPE_MAGIC) {
|
||||
// Note: storeUnboxedValue takes care of any post barriers on the value.
|
||||
storeUnboxedValue(obj, elements, 0, id, unboxedType, value);
|
||||
storeUnboxedValue(obj, elements, 0, id, unboxedType, value, /* preBarrier = */ false);
|
||||
|
||||
MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj);
|
||||
current->add(increment);
|
||||
@ -7101,6 +7101,7 @@ static bool
|
||||
ClassHasEffectlessLookup(const Class* clasp)
|
||||
{
|
||||
return (clasp == &UnboxedPlainObject::class_) ||
|
||||
(clasp == &UnboxedArrayObject::class_) ||
|
||||
IsTypedObjectClass(clasp) ||
|
||||
(clasp->isNative() && !clasp->ops.lookupProperty);
|
||||
}
|
||||
@ -9109,7 +9110,9 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object,
|
||||
bool guardHoles = ElementAccessHasExtraIndexedProperty(constraints(), object);
|
||||
|
||||
// Make sure the object being written to doesn't have copy on write elements.
|
||||
object = addMaybeCopyElementsForWrite(object);
|
||||
const Class* clasp = object->resultTypeSet() ? object->resultTypeSet()->getKnownClass(constraints()) : nullptr;
|
||||
bool checkNative = !clasp || !clasp->isNative();
|
||||
object = addMaybeCopyElementsForWrite(object, checkNative);
|
||||
|
||||
if (NeedsPostBarrier(info(), value))
|
||||
current->add(MPostWriteBarrier::New(alloc(), object, value));
|
||||
@ -9152,7 +9155,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
||||
id = idInt32;
|
||||
|
||||
// Copy the elements vector if necessary.
|
||||
obj = addMaybeCopyElementsForWrite(obj);
|
||||
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
|
||||
|
||||
// Get the elements vector.
|
||||
MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
|
||||
@ -11503,7 +11506,7 @@ IonBuilder::storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType un
|
||||
MInstruction*
|
||||
IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t elementsOffset,
|
||||
MDefinition* scaledOffset, JSValueType unboxedType,
|
||||
MDefinition* value)
|
||||
MDefinition* value, bool preBarrier /* = true */)
|
||||
{
|
||||
MInstruction* store;
|
||||
switch (unboxedType) {
|
||||
@ -11523,12 +11526,13 @@ IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t e
|
||||
break;
|
||||
|
||||
case JSVAL_TYPE_STRING:
|
||||
store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, elementsOffset);
|
||||
store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value,
|
||||
elementsOffset, preBarrier);
|
||||
break;
|
||||
|
||||
case JSVAL_TYPE_OBJECT:
|
||||
store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, obj,
|
||||
elementsOffset);
|
||||
elementsOffset, preBarrier);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -12533,11 +12537,11 @@ IonBuilder::addConvertElementsToDoubles(MDefinition* elements)
|
||||
}
|
||||
|
||||
MDefinition*
|
||||
IonBuilder::addMaybeCopyElementsForWrite(MDefinition* object)
|
||||
IonBuilder::addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative)
|
||||
{
|
||||
if (!ElementAccessMightBeCopyOnWrite(constraints(), object))
|
||||
return object;
|
||||
MInstruction* copy = MMaybeCopyElementsForWrite::New(alloc(), object);
|
||||
MInstruction* copy = MMaybeCopyElementsForWrite::New(alloc(), object, checkNative);
|
||||
current->add(copy);
|
||||
return copy;
|
||||
}
|
||||
|
@ -395,7 +395,7 @@ class IonBuilder
|
||||
MDefinition* walkScopeChain(unsigned hops);
|
||||
|
||||
MInstruction* addConvertElementsToDoubles(MDefinition* elements);
|
||||
MDefinition* addMaybeCopyElementsForWrite(MDefinition* object);
|
||||
MDefinition* addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative);
|
||||
MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);
|
||||
MInstruction* addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind);
|
||||
MInstruction* addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind);
|
||||
@ -959,7 +959,7 @@ class IonBuilder
|
||||
MInstruction* storeUnboxedValue(MDefinition* obj,
|
||||
MDefinition* elements, int32_t elementsOffset,
|
||||
MDefinition* scaledOffset, JSValueType unboxedType,
|
||||
MDefinition* value);
|
||||
MDefinition* value, bool preBarrier = true);
|
||||
bool checkPreliminaryGroups(MDefinition *obj);
|
||||
bool freezePropTypeSets(TemporaryTypeSet* types,
|
||||
JSObject* foundProto, PropertyName* name);
|
||||
|
@ -628,6 +628,9 @@ TestMatchingReceiver(MacroAssembler& masm, IonCache::StubAttacher& attacher,
|
||||
} else {
|
||||
masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure);
|
||||
}
|
||||
} else if (obj->is<UnboxedArrayObject>()) {
|
||||
MOZ_ASSERT(failure);
|
||||
masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
|
||||
} else if (obj->is<TypedObject>()) {
|
||||
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
|
||||
Address(object, JSObject::offsetOfGroup()),
|
||||
@ -1154,6 +1157,40 @@ GenerateArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher&
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateUnboxedArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
|
||||
JSObject* array, Register object, TypedOrValueRegister output)
|
||||
{
|
||||
Label failures;
|
||||
|
||||
Register outReg;
|
||||
if (output.hasValue()) {
|
||||
outReg = output.valueReg().scratchReg();
|
||||
} else {
|
||||
MOZ_ASSERT(output.type() == MIRType_Int32);
|
||||
outReg = output.typedReg().gpr();
|
||||
}
|
||||
MOZ_ASSERT(object != outReg);
|
||||
|
||||
TestMatchingReceiver(masm, attacher, object, array, &failures);
|
||||
|
||||
// Load length.
|
||||
masm.load32(Address(object, UnboxedArrayObject::offsetOfLength()), outReg);
|
||||
|
||||
// Check for a length that fits in an int32.
|
||||
masm.branchTest32(Assembler::Signed, outReg, outReg, &failures);
|
||||
|
||||
if (output.hasValue())
|
||||
masm.tagValue(JSVAL_TYPE_INT32, outReg, output.valueReg());
|
||||
|
||||
// Success.
|
||||
attacher.jumpRejoin(masm);
|
||||
|
||||
// Failure.
|
||||
masm.bind(&failures);
|
||||
attacher.jumpNextStub(masm);
|
||||
}
|
||||
|
||||
// In this case, the code for TypedArray and SharedTypedArray is not the same,
|
||||
// because the code embeds pointers to the respective class arrays. Code that
|
||||
// caches the stub code must distinguish between the two cases.
|
||||
@ -1244,7 +1281,7 @@ CanAttachNativeGetProp(JSContext* cx, const GetPropCache& cache,
|
||||
// |length| is a non-configurable getter property on ArrayObjects. Any time this
|
||||
// check would have passed, we can install a getter stub instead. Allow people to
|
||||
// make that decision themselves with skipArrayLen
|
||||
if (!skipArrayLen && cx->names().length == name && cache.allowArrayLength(cx, obj) &&
|
||||
if (!skipArrayLen && cx->names().length == name && cache.allowArrayLength(cx) &&
|
||||
IsCacheableArrayLength(cx, obj, name, cache.output()))
|
||||
{
|
||||
// The array length property is non-configurable, which means both that
|
||||
@ -1277,7 +1314,7 @@ CanAttachNativeGetProp(JSContext* cx, const GetPropCache& cache,
|
||||
}
|
||||
|
||||
bool
|
||||
GetPropertyIC::allowArrayLength(JSContext* cx, HandleObject obj) const
|
||||
GetPropertyIC::allowArrayLength(JSContext* cx) const
|
||||
{
|
||||
if (!idempotent())
|
||||
return true;
|
||||
@ -1402,6 +1439,33 @@ GetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript,
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed expando");
|
||||
}
|
||||
|
||||
bool
|
||||
GetPropertyIC::tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandlePropertyName name,
|
||||
void* returnAddr, bool* emitted)
|
||||
{
|
||||
MOZ_ASSERT(canAttachStub());
|
||||
MOZ_ASSERT(!*emitted);
|
||||
MOZ_ASSERT(outerScript->ionScript() == ion);
|
||||
|
||||
if (!obj->is<UnboxedArrayObject>())
|
||||
return true;
|
||||
|
||||
if (cx->names().length != name)
|
||||
return true;
|
||||
|
||||
if (obj->as<UnboxedArrayObject>().length() > INT32_MAX)
|
||||
return true;
|
||||
|
||||
*emitted = true;
|
||||
|
||||
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
|
||||
|
||||
StubAttacher attacher(*this);
|
||||
GenerateUnboxedArrayLength(cx, masm, attacher, obj, object(), output());
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "unboxed array length");
|
||||
}
|
||||
|
||||
bool
|
||||
GetPropertyIC::tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandlePropertyName name, bool* emitted)
|
||||
@ -1841,6 +1905,9 @@ GetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript*
|
||||
if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, name, returnAddr, emitted))
|
||||
return false;
|
||||
|
||||
if (!*emitted && !tryAttachUnboxedArrayLength(cx, outerScript, ion, obj, name, returnAddr, emitted))
|
||||
return false;
|
||||
|
||||
if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, name, emitted))
|
||||
return false;
|
||||
|
||||
@ -3570,16 +3637,15 @@ GetElementIC::attachDenseElementHole(JSContext* cx, HandleScript outerScript, Io
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
GetElementIC::canAttachTypedArrayElement(JSObject* obj, const Value& idval,
|
||||
TypedOrValueRegister output)
|
||||
GetElementIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval,
|
||||
TypedOrValueRegister output)
|
||||
{
|
||||
if (!IsAnyTypedArray(obj))
|
||||
if (!IsAnyTypedArray(obj) && !obj->is<UnboxedArrayObject>())
|
||||
return false;
|
||||
|
||||
if (!idval.isInt32() && !idval.isString())
|
||||
return false;
|
||||
|
||||
|
||||
// Don't emit a stub if the access is out of bounds. We make to make
|
||||
// certain that we monitor the type coming out of the typed array when
|
||||
// we generate the stub. Out of bounds accesses will hit the fallback
|
||||
@ -3592,34 +3658,42 @@ GetElementIC::canAttachTypedArrayElement(JSObject* obj, const Value& idval,
|
||||
if (index == UINT32_MAX)
|
||||
return false;
|
||||
}
|
||||
if (index >= AnyTypedArrayLength(obj))
|
||||
|
||||
if (IsAnyTypedArray(obj)) {
|
||||
if (index >= AnyTypedArrayLength(obj))
|
||||
return false;
|
||||
|
||||
// The output register is not yet specialized as a float register, the only
|
||||
// way to accept float typed arrays for now is to return a Value type.
|
||||
uint32_t arrayType = AnyTypedArrayType(obj);
|
||||
if (arrayType == Scalar::Float32 || arrayType == Scalar::Float64)
|
||||
return output.hasValue();
|
||||
|
||||
return output.hasValue() || !output.typedReg().isFloat();
|
||||
}
|
||||
|
||||
if (index >= obj->as<UnboxedArrayObject>().initializedLength())
|
||||
return false;
|
||||
|
||||
// The output register is not yet specialized as a float register, the only
|
||||
// way to accept float typed arrays for now is to return a Value type.
|
||||
uint32_t arrayType = AnyTypedArrayType(obj);
|
||||
if (arrayType == Scalar::Float32 || arrayType == Scalar::Float64)
|
||||
JSValueType elementType = obj->as<UnboxedArrayObject>().elementType();
|
||||
if (elementType == JSVAL_TYPE_DOUBLE)
|
||||
return output.hasValue();
|
||||
|
||||
return output.hasValue() || !output.typedReg().isFloat();
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateGetTypedArrayElement(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
|
||||
HandleObject tarr, const Value& idval, Register object,
|
||||
ConstantOrRegister index, TypedOrValueRegister output,
|
||||
bool allowDoubleResult)
|
||||
GenerateGetTypedOrUnboxedArrayElement(JSContext* cx, MacroAssembler& masm,
|
||||
IonCache::StubAttacher& attacher,
|
||||
HandleObject array, const Value& idval, Register object,
|
||||
ConstantOrRegister index, TypedOrValueRegister output,
|
||||
bool allowDoubleResult)
|
||||
{
|
||||
MOZ_ASSERT(GetElementIC::canAttachTypedArrayElement(tarr, idval, output));
|
||||
MOZ_ASSERT(GetElementIC::canAttachTypedOrUnboxedArrayElement(array, idval, output));
|
||||
|
||||
Label failures;
|
||||
|
||||
// The array type is the object within the table of typed array classes.
|
||||
Scalar::Type arrayType = AnyTypedArrayType(tarr);
|
||||
|
||||
// Guard on the shape.
|
||||
Shape* shape = AnyTypedArrayShape(tarr);
|
||||
masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
|
||||
TestMatchingReceiver(masm, attacher, object, array, &failures);
|
||||
|
||||
// Decide to what type index the stub should be optimized
|
||||
Register tmpReg = output.scratchReg().gpr();
|
||||
@ -3675,34 +3749,55 @@ GenerateGetTypedArrayElement(JSContext* cx, MacroAssembler& masm, IonCache::Stub
|
||||
}
|
||||
}
|
||||
|
||||
// Guard on the initialized length.
|
||||
Address length(object, TypedArrayLayout::lengthOffset());
|
||||
masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
|
||||
Label popObjectAndFail;
|
||||
|
||||
// Save the object register on the stack in case of failure.
|
||||
Label popAndFail;
|
||||
Register elementReg = object;
|
||||
masm.push(object);
|
||||
if (IsAnyTypedArray(array)) {
|
||||
// Guard on the initialized length.
|
||||
Address length(object, TypedArrayLayout::lengthOffset());
|
||||
masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
|
||||
|
||||
// Load elements vector.
|
||||
masm.loadPtr(Address(object, TypedArrayLayout::dataOffset()), elementReg);
|
||||
// Save the object register on the stack in case of failure.
|
||||
Register elementReg = object;
|
||||
masm.push(object);
|
||||
|
||||
// Load the value. We use an invalid register because the destination
|
||||
// register is necessary a non double register.
|
||||
int width = Scalar::byteSize(arrayType);
|
||||
BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width));
|
||||
if (output.hasValue()) {
|
||||
masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult,
|
||||
elementReg, &popAndFail);
|
||||
// Load elements vector.
|
||||
masm.loadPtr(Address(object, TypedArrayLayout::dataOffset()), elementReg);
|
||||
|
||||
// Load the value. We use an invalid register because the destination
|
||||
// register is necessary a non double register.
|
||||
Scalar::Type arrayType = AnyTypedArrayType(array);
|
||||
int width = Scalar::byteSize(arrayType);
|
||||
BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width));
|
||||
if (output.hasValue()) {
|
||||
masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult,
|
||||
elementReg, &popObjectAndFail);
|
||||
} else {
|
||||
masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popObjectAndFail);
|
||||
}
|
||||
} else {
|
||||
masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popAndFail);
|
||||
// Save the object register on the stack in case of failure.
|
||||
masm.push(object);
|
||||
|
||||
// Guard on the initialized length.
|
||||
masm.load32(Address(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), object);
|
||||
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), object);
|
||||
masm.branch32(Assembler::BelowOrEqual, object, indexReg, &popObjectAndFail);
|
||||
|
||||
// Load elements vector.
|
||||
Register elementReg = object;
|
||||
masm.loadPtr(Address(masm.getStackPointer(), 0), object);
|
||||
masm.loadPtr(Address(object, UnboxedArrayObject::offsetOfElements()), elementReg);
|
||||
|
||||
JSValueType elementType = array->as<UnboxedArrayObject>().elementType();
|
||||
BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(UnboxedTypeSize(elementType)));
|
||||
masm.loadUnboxedProperty(source, elementType, output);
|
||||
}
|
||||
|
||||
masm.pop(object);
|
||||
attacher.jumpRejoin(masm);
|
||||
|
||||
// Restore the object before continuing to the next stub.
|
||||
masm.bind(&popAndFail);
|
||||
masm.bind(&popObjectAndFail);
|
||||
masm.pop(object);
|
||||
masm.bind(&failures);
|
||||
|
||||
@ -3710,13 +3805,14 @@ GenerateGetTypedArrayElement(JSContext* cx, MacroAssembler& masm, IonCache::Stub
|
||||
}
|
||||
|
||||
bool
|
||||
GetElementIC::attachTypedArrayElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject tarr, const Value& idval)
|
||||
GetElementIC::attachTypedOrUnboxedArrayElement(JSContext* cx, HandleScript outerScript,
|
||||
IonScript* ion, HandleObject tarr,
|
||||
const Value& idval)
|
||||
{
|
||||
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
|
||||
StubAttacher attacher(*this);
|
||||
GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output(),
|
||||
allowDoubleResult());
|
||||
GenerateGetTypedOrUnboxedArrayElement(cx, masm, attacher, tarr, idval, object(), index(),
|
||||
output(), allowDoubleResult());
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
|
||||
}
|
||||
|
||||
@ -3887,8 +3983,8 @@ GetElementIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex,
|
||||
return false;
|
||||
attachedStub = true;
|
||||
}
|
||||
if (!attachedStub && canAttachTypedArrayElement(obj, idval, cache.output())) {
|
||||
if (!cache.attachTypedArrayElement(cx, outerScript, ion, obj, idval))
|
||||
if (!attachedStub && canAttachTypedOrUnboxedArrayElement(obj, idval, cache.output())) {
|
||||
if (!cache.attachTypedOrUnboxedArrayElement(cx, outerScript, ion, obj, idval))
|
||||
return false;
|
||||
attachedStub = true;
|
||||
}
|
||||
|
@ -467,7 +467,7 @@ class GetPropertyIC : public IonCache
|
||||
};
|
||||
|
||||
// Helpers for CanAttachNativeGetProp
|
||||
bool allowArrayLength(JSContext* cx, HandleObject obj) const;
|
||||
bool allowArrayLength(JSContext* cx) const;
|
||||
bool allowGetters() const {
|
||||
return monitoredResult() && !idempotent();
|
||||
}
|
||||
@ -503,6 +503,10 @@ class GetPropertyIC : public IonCache
|
||||
HandleObject obj, HandlePropertyName name,
|
||||
void* returnAddr, bool* emitted);
|
||||
|
||||
bool tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandlePropertyName name,
|
||||
void* returnAddr, bool* emitted);
|
||||
|
||||
bool tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandlePropertyName name, bool* emitted);
|
||||
|
||||
@ -668,7 +672,7 @@ class GetElementIC : public IonCache
|
||||
// Helpers for CanAttachNativeGetProp
|
||||
typedef JSContext * Context;
|
||||
bool allowGetters() const { MOZ_ASSERT(!idempotent()); return true; }
|
||||
bool allowArrayLength(Context, HandleObject) const { return false; }
|
||||
bool allowArrayLength(Context) const { return false; }
|
||||
bool canMonitorSingletonUndefinedSlot(HandleObject holder, HandleShape shape) const {
|
||||
return monitoredResult();
|
||||
}
|
||||
@ -677,8 +681,8 @@ class GetElementIC : public IonCache
|
||||
static bool canAttachDenseElement(JSObject* obj, const Value& idval);
|
||||
static bool canAttachDenseElementHole(JSObject* obj, const Value& idval,
|
||||
TypedOrValueRegister output);
|
||||
static bool canAttachTypedArrayElement(JSObject* obj, const Value& idval,
|
||||
TypedOrValueRegister output);
|
||||
static bool canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval,
|
||||
TypedOrValueRegister output);
|
||||
|
||||
bool attachGetProp(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, const Value& idval, HandlePropertyName name);
|
||||
@ -689,8 +693,8 @@ class GetElementIC : public IonCache
|
||||
bool attachDenseElementHole(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, const Value& idval);
|
||||
|
||||
bool attachTypedArrayElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject tarr, const Value& idval);
|
||||
bool attachTypedOrUnboxedArrayElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject tarr, const Value& idval);
|
||||
|
||||
bool attachArgumentsElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj);
|
||||
|
@ -4142,6 +4142,10 @@ class LMaybeCopyElementsForWrite : public LInstructionHelper<0, 1, 1>
|
||||
const LDefinition* temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
|
||||
const MMaybeCopyElementsForWrite* mir() const {
|
||||
return mir_->toMaybeCopyElementsForWrite();
|
||||
}
|
||||
};
|
||||
|
||||
// Load the initialized length from an elements header.
|
||||
|
@ -667,7 +667,10 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
|
||||
|
||||
MDefinition* obj = callInfo.thisArg();
|
||||
TemporaryTypeSet* thisTypes = obj->resultTypeSet();
|
||||
if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
|
||||
if (!thisTypes)
|
||||
return InliningStatus_NotInlined;
|
||||
const Class* clasp = thisTypes->getKnownClass(constraints());
|
||||
if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
|
||||
return InliningStatus_NotInlined;
|
||||
if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
|
||||
@ -679,9 +682,17 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
|
||||
if (clasp == &UnboxedArrayObject::class_) {
|
||||
unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
obj = addMaybeCopyElementsForWrite(obj);
|
||||
if (clasp == &ArrayObject::class_)
|
||||
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
|
||||
|
||||
TemporaryTypeSet* returnTypes = getInlineReturnTypeSet();
|
||||
bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED);
|
||||
@ -692,7 +703,8 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
|
||||
if (barrier != BarrierKind::NoBarrier)
|
||||
returnType = MIRType_Value;
|
||||
|
||||
MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode, needsHoleCheck, maybeUndefined);
|
||||
MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode,
|
||||
unboxedType, needsHoleCheck, maybeUndefined);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
ins->setResultType(returnType);
|
||||
@ -795,7 +807,10 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
|
||||
if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
|
||||
if (!thisTypes)
|
||||
return InliningStatus_NotInlined;
|
||||
const Class* clasp = thisTypes->getKnownClass(constraints());
|
||||
if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
|
||||
return InliningStatus_NotInlined;
|
||||
if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
|
||||
OBJECT_FLAG_LENGTH_OVERFLOW))
|
||||
@ -816,6 +831,13 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
|
||||
if (clasp == &UnboxedArrayObject::class_) {
|
||||
unboxedType = UnboxedArrayElementType(constraints(), callInfo.thisArg(), nullptr);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
value = callInfo.getArg(0);
|
||||
|
||||
@ -827,12 +849,13 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
|
||||
value = valueDouble;
|
||||
}
|
||||
|
||||
obj = addMaybeCopyElementsForWrite(obj);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC)
|
||||
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
|
||||
|
||||
if (NeedsPostBarrier(info(), value))
|
||||
current->add(MPostWriteBarrier::New(alloc(), obj, value));
|
||||
|
||||
MArrayPush* ins = MArrayPush::New(alloc(), obj, value);
|
||||
MArrayPush* ins = MArrayPush::New(alloc(), obj, value, unboxedType);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
@ -863,7 +886,8 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
|
||||
if (!thisTypes || !argTypes)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
|
||||
const Class* clasp = thisTypes->getKnownClass(constraints());
|
||||
if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
|
||||
return InliningStatus_NotInlined;
|
||||
if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
|
||||
OBJECT_FLAG_LENGTH_OVERFLOW))
|
||||
@ -872,7 +896,7 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (argTypes->getKnownClass(constraints()) != &ArrayObject::class_)
|
||||
if (argTypes->getKnownClass(constraints()) != clasp)
|
||||
return InliningStatus_NotInlined;
|
||||
if (argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
|
||||
OBJECT_FLAG_LENGTH_OVERFLOW))
|
||||
@ -881,6 +905,15 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
|
||||
if (clasp == &UnboxedArrayObject::class_) {
|
||||
unboxedType = UnboxedArrayElementType(constraints(), callInfo.thisArg(), nullptr);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC)
|
||||
return InliningStatus_NotInlined;
|
||||
if (unboxedType != UnboxedArrayElementType(constraints(), callInfo.getArg(0), nullptr))
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// Watch out for indexed properties on the prototype.
|
||||
if (ArrayPrototypeHasIndexedProperty(constraints(), script())) {
|
||||
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
|
||||
@ -934,13 +967,14 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
|
||||
JSObject* templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
|
||||
if (!templateObj || templateObj->group() != thisGroup)
|
||||
return InliningStatus_NotInlined;
|
||||
MOZ_ASSERT(templateObj->is<ArrayObject>());
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(), callInfo.thisArg(), callInfo.getArg(0),
|
||||
&templateObj->as<ArrayObject>(),
|
||||
templateObj->group()->initialHeap(constraints()));
|
||||
MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(),
|
||||
callInfo.thisArg(), callInfo.getArg(0),
|
||||
templateObj,
|
||||
templateObj->group()->initialHeap(constraints()),
|
||||
unboxedType);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
|
112
js/src/jit/MIR.h
112
js/src/jit/MIR.h
@ -360,6 +360,9 @@ class AliasSet {
|
||||
MOZ_ASSERT(flags && !(flags & Store_));
|
||||
return AliasSet(flags | Store_);
|
||||
}
|
||||
static uint32_t BoxedOrUnboxedElements(JSValueType type) {
|
||||
return (type == JSVAL_TYPE_MAGIC) ? Element : UnboxedElement;
|
||||
}
|
||||
};
|
||||
|
||||
// An MDefinition is an SSA name.
|
||||
@ -7758,8 +7761,10 @@ class MMaybeCopyElementsForWrite
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
explicit MMaybeCopyElementsForWrite(MDefinition* object)
|
||||
: MUnaryInstruction(object)
|
||||
bool checkNative_;
|
||||
|
||||
explicit MMaybeCopyElementsForWrite(MDefinition* object, bool checkNative)
|
||||
: MUnaryInstruction(object), checkNative_(checkNative)
|
||||
{
|
||||
setGuard();
|
||||
setMovable();
|
||||
@ -7770,15 +7775,19 @@ class MMaybeCopyElementsForWrite
|
||||
public:
|
||||
INSTRUCTION_HEADER(MaybeCopyElementsForWrite)
|
||||
|
||||
static MMaybeCopyElementsForWrite* New(TempAllocator& alloc, MDefinition* object) {
|
||||
return new(alloc) MMaybeCopyElementsForWrite(object);
|
||||
static MMaybeCopyElementsForWrite* New(TempAllocator& alloc, MDefinition* object, bool checkNative) {
|
||||
return new(alloc) MMaybeCopyElementsForWrite(object, checkNative);
|
||||
}
|
||||
|
||||
MDefinition* object() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
bool checkNative() const {
|
||||
return checkNative_;
|
||||
}
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
return congruentIfOperandsEqual(ins) &&
|
||||
checkNative() == ins->toMaybeCopyElementsForWrite()->checkNative();
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Store(AliasSet::ObjectFields);
|
||||
@ -8542,9 +8551,7 @@ class MLoadElementHole
|
||||
return congruentIfOperandsEqual(other);
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Load(unboxedType() == JSVAL_TYPE_MAGIC
|
||||
? AliasSet::Element
|
||||
: AliasSet::UnboxedElement);
|
||||
return AliasSet::Load(AliasSet::BoxedOrUnboxedElements(unboxedType()));
|
||||
}
|
||||
void collectRangeInfoPreTrunc() override;
|
||||
|
||||
@ -8804,9 +8811,7 @@ class MStoreElementHole
|
||||
// StoreElementHole can update the initialized length, the array length
|
||||
// or reallocate obj->elements.
|
||||
return AliasSet::Store(AliasSet::ObjectFields |
|
||||
((unboxedType() == JSVAL_TYPE_MAGIC)
|
||||
? AliasSet::Element
|
||||
: AliasSet::UnboxedElement));
|
||||
AliasSet::BoxedOrUnboxedElements(unboxedType()));
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreElementHole)
|
||||
@ -8818,11 +8823,12 @@ class MStoreUnboxedObjectOrNull
|
||||
public StoreUnboxedObjectOrNullPolicy::Data
|
||||
{
|
||||
int32_t offsetAdjustment_;
|
||||
bool preBarrier_;
|
||||
|
||||
MStoreUnboxedObjectOrNull(MDefinition* elements, MDefinition* index,
|
||||
MDefinition* value, MDefinition* typedObj,
|
||||
int32_t offsetAdjustment)
|
||||
: offsetAdjustment_(offsetAdjustment)
|
||||
int32_t offsetAdjustment, bool preBarrier)
|
||||
: offsetAdjustment_(offsetAdjustment), preBarrier_(preBarrier)
|
||||
{
|
||||
initOperand(0, elements);
|
||||
initOperand(1, index);
|
||||
@ -8839,9 +8845,10 @@ class MStoreUnboxedObjectOrNull
|
||||
static MStoreUnboxedObjectOrNull* New(TempAllocator& alloc,
|
||||
MDefinition* elements, MDefinition* index,
|
||||
MDefinition* value, MDefinition* typedObj,
|
||||
int32_t offsetAdjustment = 0) {
|
||||
int32_t offsetAdjustment = 0,
|
||||
bool preBarrier = true) {
|
||||
return new(alloc) MStoreUnboxedObjectOrNull(elements, index, value, typedObj,
|
||||
offsetAdjustment);
|
||||
offsetAdjustment, preBarrier);
|
||||
}
|
||||
MDefinition* elements() const {
|
||||
return getOperand(0);
|
||||
@ -8858,6 +8865,9 @@ class MStoreUnboxedObjectOrNull
|
||||
int32_t offsetAdjustment() const {
|
||||
return offsetAdjustment_;
|
||||
}
|
||||
bool preBarrier() const {
|
||||
return preBarrier_;
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Store(AliasSet::UnboxedElement);
|
||||
}
|
||||
@ -8876,10 +8886,11 @@ class MStoreUnboxedString
|
||||
public MixPolicy<SingleObjectPolicy, ConvertToStringPolicy<2> >::Data
|
||||
{
|
||||
int32_t offsetAdjustment_;
|
||||
bool preBarrier_;
|
||||
|
||||
MStoreUnboxedString(MDefinition* elements, MDefinition* index, MDefinition* value,
|
||||
int32_t offsetAdjustment)
|
||||
: offsetAdjustment_(offsetAdjustment)
|
||||
int32_t offsetAdjustment, bool preBarrier)
|
||||
: offsetAdjustment_(offsetAdjustment), preBarrier_(preBarrier)
|
||||
{
|
||||
initOperand(0, elements);
|
||||
initOperand(1, index);
|
||||
@ -8893,8 +8904,10 @@ class MStoreUnboxedString
|
||||
|
||||
static MStoreUnboxedString* New(TempAllocator& alloc,
|
||||
MDefinition* elements, MDefinition* index,
|
||||
MDefinition* value, int32_t offsetAdjustment = 0) {
|
||||
return new(alloc) MStoreUnboxedString(elements, index, value, offsetAdjustment);
|
||||
MDefinition* value, int32_t offsetAdjustment = 0,
|
||||
bool preBarrier = true) {
|
||||
return new(alloc) MStoreUnboxedString(elements, index, value,
|
||||
offsetAdjustment, preBarrier);
|
||||
}
|
||||
MDefinition* elements() const {
|
||||
return getOperand(0);
|
||||
@ -8908,6 +8921,9 @@ class MStoreUnboxedString
|
||||
int32_t offsetAdjustment() const {
|
||||
return offsetAdjustment_;
|
||||
}
|
||||
bool preBarrier() const {
|
||||
return preBarrier_;
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Store(AliasSet::UnboxedElement);
|
||||
}
|
||||
@ -8982,21 +8998,23 @@ class MArrayPopShift
|
||||
|
||||
private:
|
||||
Mode mode_;
|
||||
JSValueType unboxedType_;
|
||||
bool needsHoleCheck_;
|
||||
bool maybeUndefined_;
|
||||
|
||||
MArrayPopShift(MDefinition* object, Mode mode, bool needsHoleCheck, bool maybeUndefined)
|
||||
: MUnaryInstruction(object), mode_(mode), needsHoleCheck_(needsHoleCheck),
|
||||
maybeUndefined_(maybeUndefined)
|
||||
MArrayPopShift(MDefinition* object, Mode mode, JSValueType unboxedType,
|
||||
bool needsHoleCheck, bool maybeUndefined)
|
||||
: MUnaryInstruction(object), mode_(mode), unboxedType_(unboxedType),
|
||||
needsHoleCheck_(needsHoleCheck), maybeUndefined_(maybeUndefined)
|
||||
{ }
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(ArrayPopShift)
|
||||
|
||||
static MArrayPopShift* New(TempAllocator& alloc, MDefinition* object, Mode mode,
|
||||
bool needsHoleCheck, bool maybeUndefined)
|
||||
JSValueType unboxedType, bool needsHoleCheck, bool maybeUndefined)
|
||||
{
|
||||
return new(alloc) MArrayPopShift(object, mode, needsHoleCheck, maybeUndefined);
|
||||
return new(alloc) MArrayPopShift(object, mode, unboxedType, needsHoleCheck, maybeUndefined);
|
||||
}
|
||||
|
||||
MDefinition* object() const {
|
||||
@ -9011,8 +9029,12 @@ class MArrayPopShift
|
||||
bool mode() const {
|
||||
return mode_;
|
||||
}
|
||||
JSValueType unboxedType() const {
|
||||
return unboxedType_;
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
|
||||
return AliasSet::Store(AliasSet::ObjectFields |
|
||||
AliasSet::BoxedOrUnboxedElements(unboxedType()));
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MArrayPopShift)
|
||||
@ -9023,8 +9045,10 @@ class MArrayPush
|
||||
: public MBinaryInstruction,
|
||||
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data
|
||||
{
|
||||
MArrayPush(MDefinition* object, MDefinition* value)
|
||||
: MBinaryInstruction(object, value)
|
||||
JSValueType unboxedType_;
|
||||
|
||||
MArrayPush(MDefinition* object, MDefinition* value, JSValueType unboxedType)
|
||||
: MBinaryInstruction(object, value), unboxedType_(unboxedType)
|
||||
{
|
||||
setResultType(MIRType_Int32);
|
||||
}
|
||||
@ -9032,8 +9056,9 @@ class MArrayPush
|
||||
public:
|
||||
INSTRUCTION_HEADER(ArrayPush)
|
||||
|
||||
static MArrayPush* New(TempAllocator& alloc, MDefinition* object, MDefinition* value) {
|
||||
return new(alloc) MArrayPush(object, value);
|
||||
static MArrayPush* New(TempAllocator& alloc, MDefinition* object, MDefinition* value,
|
||||
JSValueType unboxedType) {
|
||||
return new(alloc) MArrayPush(object, value, unboxedType);
|
||||
}
|
||||
|
||||
MDefinition* object() const {
|
||||
@ -9042,8 +9067,12 @@ class MArrayPush
|
||||
MDefinition* value() const {
|
||||
return getOperand(1);
|
||||
}
|
||||
JSValueType unboxedType() const {
|
||||
return unboxedType_;
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
|
||||
return AliasSet::Store(AliasSet::ObjectFields |
|
||||
AliasSet::BoxedOrUnboxedElements(unboxedType()));
|
||||
}
|
||||
void computeRange(TempAllocator& alloc) override;
|
||||
|
||||
@ -9055,14 +9084,16 @@ class MArrayConcat
|
||||
: public MBinaryInstruction,
|
||||
public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
|
||||
{
|
||||
AlwaysTenured<ArrayObject*> templateObj_;
|
||||
AlwaysTenuredObject templateObj_;
|
||||
gc::InitialHeap initialHeap_;
|
||||
JSValueType unboxedType_;
|
||||
|
||||
MArrayConcat(CompilerConstraintList* constraints, MDefinition* lhs, MDefinition* rhs,
|
||||
ArrayObject* templateObj, gc::InitialHeap initialHeap)
|
||||
JSObject* templateObj, gc::InitialHeap initialHeap, JSValueType unboxedType)
|
||||
: MBinaryInstruction(lhs, rhs),
|
||||
templateObj_(templateObj),
|
||||
initialHeap_(initialHeap)
|
||||
initialHeap_(initialHeap),
|
||||
unboxedType_(unboxedType)
|
||||
{
|
||||
setResultType(MIRType_Object);
|
||||
setResultTypeSet(MakeSingletonTypeSet(constraints, templateObj));
|
||||
@ -9073,12 +9104,14 @@ class MArrayConcat
|
||||
|
||||
static MArrayConcat* New(TempAllocator& alloc, CompilerConstraintList* constraints,
|
||||
MDefinition* lhs, MDefinition* rhs,
|
||||
ArrayObject* templateObj, gc::InitialHeap initialHeap)
|
||||
JSObject* templateObj, gc::InitialHeap initialHeap,
|
||||
JSValueType unboxedType)
|
||||
{
|
||||
return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj, initialHeap);
|
||||
return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj,
|
||||
initialHeap, unboxedType);
|
||||
}
|
||||
|
||||
ArrayObject* templateObj() const {
|
||||
JSObject* templateObj() const {
|
||||
return templateObj_;
|
||||
}
|
||||
|
||||
@ -9086,8 +9119,13 @@ class MArrayConcat
|
||||
return initialHeap_;
|
||||
}
|
||||
|
||||
JSValueType unboxedType() const {
|
||||
return unboxedType_;
|
||||
}
|
||||
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
|
||||
return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedType()) |
|
||||
AliasSet::ObjectFields);
|
||||
}
|
||||
bool possiblyCalls() const override {
|
||||
return true;
|
||||
|
@ -269,7 +269,7 @@ ArraySpliceDense(JSContext* cx, HandleObject obj, uint32_t start, uint32_t delet
|
||||
bool
|
||||
ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
|
||||
{
|
||||
MOZ_ASSERT(obj->is<ArrayObject>());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
|
||||
AutoDetectInvalidation adi(cx, rval);
|
||||
|
||||
@ -288,21 +288,14 @@ ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayPushDense(JSContext* cx, HandleArrayObject obj, HandleValue v, uint32_t* length)
|
||||
ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length)
|
||||
{
|
||||
if (MOZ_LIKELY(obj->lengthIsWritable())) {
|
||||
uint32_t idx = obj->length();
|
||||
DenseElementResult result = obj->ensureDenseElements(cx, idx, 1);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
|
||||
if (result == DenseElementResult::Success) {
|
||||
obj->setDenseElement(idx, v);
|
||||
MOZ_ASSERT(idx < INT32_MAX);
|
||||
*length = idx + 1;
|
||||
obj->setLengthInt32(*length);
|
||||
return true;
|
||||
}
|
||||
*length = GetAnyBoxedOrUnboxedArrayLength(obj);
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, *length, v.address(), 1, DontUpdateTypes);
|
||||
if (result != DenseElementResult::Incomplete) {
|
||||
(*length)++;
|
||||
return result == DenseElementResult::Success;
|
||||
}
|
||||
|
||||
JS::AutoValueArray<3> argv(cx);
|
||||
@ -319,7 +312,7 @@ ArrayPushDense(JSContext* cx, HandleArrayObject obj, HandleValue v, uint32_t* le
|
||||
bool
|
||||
ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
|
||||
{
|
||||
MOZ_ASSERT(obj->is<ArrayObject>());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
|
||||
AutoDetectInvalidation adi(cx, rval);
|
||||
|
||||
@ -340,21 +333,17 @@ ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
|
||||
JSObject*
|
||||
ArrayConcatDense(JSContext* cx, HandleObject obj1, HandleObject obj2, HandleObject objRes)
|
||||
{
|
||||
Rooted<ArrayObject*> arr1(cx, &obj1->as<ArrayObject>());
|
||||
Rooted<ArrayObject*> arr2(cx, &obj2->as<ArrayObject>());
|
||||
Rooted<ArrayObject*> arrRes(cx, objRes ? &objRes->as<ArrayObject>() : nullptr);
|
||||
|
||||
if (arrRes) {
|
||||
if (objRes) {
|
||||
// Fast path if we managed to allocate an object inline.
|
||||
if (!js::array_concat_dense(cx, arr1, arr2, arrRes))
|
||||
if (!js::array_concat_dense(cx, obj1, obj2, objRes))
|
||||
return nullptr;
|
||||
return arrRes;
|
||||
return objRes;
|
||||
}
|
||||
|
||||
JS::AutoValueArray<3> argv(cx);
|
||||
argv[0].setUndefined();
|
||||
argv[1].setObject(*arr1);
|
||||
argv[2].setObject(*arr2);
|
||||
argv[1].setObject(*obj1);
|
||||
argv[2].setObject(*obj2);
|
||||
if (!js::array_concat(cx, 1, argv.begin()))
|
||||
return nullptr;
|
||||
return &argv[0].toObject();
|
||||
|
@ -658,7 +658,7 @@ template<bool Equal>
|
||||
bool StringsEqual(JSContext* cx, HandleString left, HandleString right, bool* res);
|
||||
|
||||
bool ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval);
|
||||
bool ArrayPushDense(JSContext* cx, HandleArrayObject obj, HandleValue v, uint32_t* length);
|
||||
bool ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length);
|
||||
bool ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval);
|
||||
JSObject* ArrayConcatDense(JSContext* cx, HandleObject obj1, HandleObject obj2, HandleObject res);
|
||||
JSString* ArrayJoin(JSContext* cx, HandleObject array, HandleString sep);
|
||||
|
@ -2069,18 +2069,38 @@ js::array_pop(JSContext* cx, unsigned argc, Value* vp)
|
||||
return SetLengthProperty(cx, obj, index);
|
||||
}
|
||||
|
||||
void
|
||||
js::ArrayShiftMoveElements(ArrayObject* obj)
|
||||
template <JSValueType Type>
|
||||
static inline DenseElementResult
|
||||
ShiftMoveBoxedOrUnboxedDenseElements(JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(obj->lengthIsWritable());
|
||||
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.
|
||||
*/
|
||||
uint32_t initlen = obj->getDenseInitializedLength();
|
||||
obj->moveDenseElementsNoPreBarrier(0, 1, initlen);
|
||||
size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
obj->as<NativeObject>().moveDenseElementsNoPreBarrier(0, 1, initlen);
|
||||
} else {
|
||||
uint8_t* data = obj->as<UnboxedArrayObject>().elements();
|
||||
size_t elementSize = UnboxedTypeSize(Type);
|
||||
memmove(data, data + elementSize, initlen * elementSize);
|
||||
}
|
||||
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor1(ShiftMoveBoxedOrUnboxedDenseElements, JSObject*);
|
||||
|
||||
void
|
||||
js::ArrayShiftMoveElements(JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable());
|
||||
|
||||
ShiftMoveBoxedOrUnboxedDenseElementsFunctor functor(obj);
|
||||
JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
@ -2263,23 +2283,23 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
|
||||
// object. The resulting array will have the same boxed/unboxed elements
|
||||
// representation as the input object, and will either reuse the input
|
||||
// object's group or will have unknown property types.
|
||||
static inline JSObject*
|
||||
NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length)
|
||||
JSObject*
|
||||
js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
|
||||
NewObjectKind newKind, bool forceAnalyze)
|
||||
{
|
||||
if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
|
||||
return NewDenseFullyAllocatedArray(cx, length);
|
||||
return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
|
||||
|
||||
if (obj->getProto() != cx->global()->maybeGetArrayPrototype())
|
||||
return NewDenseFullyAllocatedArray(cx, length);
|
||||
return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
|
||||
|
||||
RootedObjectGroup group(cx, obj->getGroup(cx));
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
if (group->maybePreliminaryObjects())
|
||||
group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
|
||||
group->maybePreliminaryObjects()->maybeAnalyze(cx, group, forceAnalyze);
|
||||
|
||||
NewObjectKind newKind = GenericObject;
|
||||
if (group->shouldPreTenure() || group->maybePreliminaryObjects())
|
||||
newKind = TenuredObject;
|
||||
|
||||
@ -2442,7 +2462,7 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
|
||||
if (!arr)
|
||||
return false;
|
||||
DebugOnly<DenseElementResult> result =
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, actualStart, actualDeleteCount);
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, 0, actualStart, actualDeleteCount);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
}
|
||||
} else {
|
||||
@ -2607,29 +2627,49 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::array_concat_dense(JSContext* cx, Handle<ArrayObject*> arr1, Handle<ArrayObject*> arr2,
|
||||
Handle<ArrayObject*> result)
|
||||
template <JSValueType Type>
|
||||
DenseElementResult
|
||||
ArrayConcatDenseKernel(JSContext* cx, JSObject* obj1, JSObject* obj2, JSObject* result)
|
||||
{
|
||||
uint32_t initlen1 = arr1->getDenseInitializedLength();
|
||||
MOZ_ASSERT(initlen1 == arr1->length());
|
||||
uint32_t initlen1 = GetBoxedOrUnboxedInitializedLength<Type>(obj1);
|
||||
MOZ_ASSERT(initlen1 == GetAnyBoxedOrUnboxedArrayLength(obj1));
|
||||
|
||||
uint32_t initlen2 = arr2->getDenseInitializedLength();
|
||||
MOZ_ASSERT(initlen2 == arr2->length());
|
||||
uint32_t initlen2 = GetBoxedOrUnboxedInitializedLength<Type>(obj2);
|
||||
MOZ_ASSERT(initlen2 == GetAnyBoxedOrUnboxedArrayLength(obj2));
|
||||
|
||||
/* No overflow here due to nelements limit. */
|
||||
uint32_t len = initlen1 + initlen2;
|
||||
|
||||
if (!result->ensureElements(cx, len))
|
||||
return false;
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<Type>(result) == 0);
|
||||
|
||||
MOZ_ASSERT(!result->getDenseInitializedLength());
|
||||
result->setDenseInitializedLength(len);
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
if (!result->as<ArrayObject>().ensureElements(cx, len))
|
||||
return DenseElementResult::Failure;
|
||||
} else {
|
||||
if (result->as<UnboxedArrayObject>().capacity() < len) {
|
||||
if (!result->as<UnboxedArrayObject>().growElements(cx, len))
|
||||
return DenseElementResult::Failure;
|
||||
}
|
||||
}
|
||||
|
||||
result->initDenseElements(0, arr1->getDenseElements(), initlen1);
|
||||
result->initDenseElements(initlen1, arr2->getDenseElements(), initlen2);
|
||||
result->setLengthInt32(len);
|
||||
return true;
|
||||
CopyBoxedOrUnboxedDenseElements<Type>(cx, result, obj1, 0, 0, initlen1);
|
||||
CopyBoxedOrUnboxedDenseElements<Type>(cx, result, obj2, initlen1, 0, initlen2);
|
||||
|
||||
SetAnyBoxedOrUnboxedArrayLength(cx, result, len);
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor4(ArrayConcatDenseKernel,
|
||||
JSContext*, JSObject*, JSObject*, JSObject*);
|
||||
|
||||
bool
|
||||
js::array_concat_dense(JSContext* cx, HandleObject obj1, HandleObject obj2,
|
||||
HandleObject result)
|
||||
{
|
||||
ArrayConcatDenseKernelFunctor functor(cx, obj1, obj2, result);
|
||||
DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result);
|
||||
MOZ_ASSERT(rv != DenseElementResult::Incomplete);
|
||||
return rv == DenseElementResult::Success;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2661,7 +2701,7 @@ js::array_concat(JSContext* cx, unsigned argc, Value* vp)
|
||||
SetAnyBoxedOrUnboxedArrayLength(cx, narr, length);
|
||||
|
||||
DebugOnly<DenseElementResult> result =
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, narr, aobj, 0, initlen);
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, narr, aobj, 0, 0, initlen);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
|
||||
args.rval().setObject(*narr);
|
||||
@ -2917,7 +2957,7 @@ js::array_slice(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
||||
if (count) {
|
||||
DebugOnly<DenseElementResult> result =
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, begin, count);
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, count);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
}
|
||||
args.rval().setObject(*narr);
|
||||
|
@ -95,6 +95,12 @@ NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSObject
|
||||
extern JSObject*
|
||||
NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap);
|
||||
|
||||
/* Create a dense or unboxed array, using the same group as |obj| if possible. */
|
||||
extern JSObject*
|
||||
NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
|
||||
NewObjectKind newKind = GenericObject,
|
||||
bool forceAnalyze = false);
|
||||
|
||||
/*
|
||||
* Determines whether a write to the given element on |obj| should fail because
|
||||
* |obj| is an Array with a non-writable length, and writing that element would
|
||||
@ -153,8 +159,8 @@ JSString*
|
||||
ArrayJoin(JSContext* cx, HandleObject obj, HandleLinearString sepstr, uint32_t length);
|
||||
|
||||
extern bool
|
||||
array_concat_dense(JSContext* cx, Handle<ArrayObject*> arr1, Handle<ArrayObject*> arr2,
|
||||
Handle<ArrayObject*> result);
|
||||
array_concat_dense(JSContext* cx, HandleObject arr1, HandleObject arr2,
|
||||
HandleObject result);
|
||||
|
||||
bool
|
||||
array_join(JSContext* cx, unsigned argc, js::Value* vp);
|
||||
@ -163,7 +169,7 @@ extern JSString*
|
||||
array_join_impl(JSContext* cx, HandleValue array, HandleString sep);
|
||||
|
||||
extern void
|
||||
ArrayShiftMoveElements(ArrayObject* obj);
|
||||
ArrayShiftMoveElements(JSObject* obj);
|
||||
|
||||
extern bool
|
||||
array_shift(JSContext* cx, unsigned argc, js::Value* vp);
|
||||
|
@ -2810,6 +2810,12 @@ js::LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject**
|
||||
MarkNonNativePropertyFound<NoGC>(propp);
|
||||
return true;
|
||||
}
|
||||
} else if (obj->is<UnboxedArrayObject>()) {
|
||||
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
|
||||
*objp = obj;
|
||||
MarkNonNativePropertyFound<NoGC>(propp);
|
||||
return true;
|
||||
}
|
||||
} else if (obj->is<TypedObject>()) {
|
||||
if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
|
||||
*objp = obj;
|
||||
|
@ -282,6 +282,16 @@ GetAnyBoxedOrUnboxedInitializedLength(JSObject* obj)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
GetAnyBoxedOrUnboxedCapacity(JSObject* obj)
|
||||
{
|
||||
if (obj->isNative())
|
||||
return obj->as<NativeObject>().getDenseCapacity();
|
||||
if (obj->is<UnboxedArrayObject>())
|
||||
return obj->as<UnboxedArrayObject>().capacity();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline Value
|
||||
GetAnyBoxedOrUnboxedDenseElement(JSObject* obj, size_t index)
|
||||
{
|
||||
@ -290,6 +300,14 @@ GetAnyBoxedOrUnboxedDenseElement(JSObject* obj, size_t index)
|
||||
return obj->as<UnboxedArrayObject>().getElement(index);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
GetAnyBoxedOrUnboxedArrayLength(JSObject* obj)
|
||||
{
|
||||
if (obj->is<ArrayObject>())
|
||||
return obj->as<ArrayObject>().length();
|
||||
return obj->as<UnboxedArrayObject>().length();
|
||||
}
|
||||
|
||||
static inline void
|
||||
SetAnyBoxedOrUnboxedArrayLength(JSContext* cx, JSObject* obj, size_t length)
|
||||
{
|
||||
@ -493,24 +511,26 @@ MoveBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, uint32_t dstStart,
|
||||
template <JSValueType Type>
|
||||
static inline DenseElementResult
|
||||
CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
|
||||
uint32_t srcStart, uint32_t length)
|
||||
uint32_t dstStart, uint32_t srcStart, uint32_t length)
|
||||
{
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(src));
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(dst));
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<Type>(dst) == 0);
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<Type>(dst) == dstStart);
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedCapacity<Type>(dst) >= length);
|
||||
|
||||
SetBoxedOrUnboxedInitializedLength<Type>(cx, dst, length);
|
||||
SetBoxedOrUnboxedInitializedLength<Type>(cx, dst, dstStart + length);
|
||||
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
const Value* vp = src->as<NativeObject>().getDenseElements() + srcStart;
|
||||
dst->as<NativeObject>().initDenseElements(0, vp, length);
|
||||
dst->as<NativeObject>().initDenseElements(dstStart, vp, length);
|
||||
} else {
|
||||
uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
|
||||
uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
|
||||
size_t elementSize = UnboxedTypeSize(Type);
|
||||
|
||||
memcpy(dstData, srcData + srcStart * elementSize, length * elementSize);
|
||||
memcpy(dstData + dstStart * elementSize,
|
||||
srcData + srcStart * elementSize,
|
||||
length * elementSize);
|
||||
|
||||
// Add a post barrier if we might have copied a nursery pointer to dst.
|
||||
if (UnboxedTypeNeedsPostBarrier(Type) && !IsInsideNursery(dst))
|
||||
@ -554,6 +574,18 @@ CallBoxedOrUnboxedSpecialization(F f, JSObject* obj)
|
||||
|
||||
#undef DEPENDENT_TEMPLATE_HINT
|
||||
|
||||
#define DefineBoxedOrUnboxedFunctor1(Signature, A) \
|
||||
struct Signature ## Functor { \
|
||||
A a; \
|
||||
explicit Signature ## Functor(A a) \
|
||||
: a(a) \
|
||||
{} \
|
||||
template <JSValueType Type> \
|
||||
DenseElementResult operator()() { \
|
||||
return Signature<Type>(a); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define DefineBoxedOrUnboxedFunctor3(Signature, A, B, C) \
|
||||
struct Signature ## Functor { \
|
||||
A a; B b; C c; \
|
||||
@ -613,7 +645,7 @@ MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
|
||||
|
||||
DenseElementResult
|
||||
CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
|
||||
uint32_t srcStart, uint32_t length);
|
||||
uint32_t dstStart, uint32_t srcStart, uint32_t length);
|
||||
|
||||
void
|
||||
SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen);
|
||||
|
@ -1281,7 +1281,7 @@ UnboxedArrayObject::shrinkElements(ExclusiveContext* cx, size_t cap)
|
||||
}
|
||||
|
||||
bool
|
||||
UnboxedArrayObject::containsProperty(JSContext* cx, jsid id)
|
||||
UnboxedArrayObject::containsProperty(ExclusiveContext* cx, jsid id)
|
||||
{
|
||||
if (JSID_IS_INT(id) && uint32_t(JSID_TO_INT(id)) < initializedLength())
|
||||
return true;
|
||||
@ -1396,11 +1396,11 @@ UnboxedArrayObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id
|
||||
if (!CanonicalizeArrayLengthValue(cx, v, &len))
|
||||
return false;
|
||||
UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
|
||||
nobj->setLength(cx, len);
|
||||
if (len < nobj->initializedLength()) {
|
||||
nobj->setInitializedLength(len);
|
||||
nobj->shrinkElements(cx, len);
|
||||
}
|
||||
nobj->setLength(cx, len);
|
||||
return result.succeed();
|
||||
}
|
||||
|
||||
@ -1439,6 +1439,15 @@ UnboxedArrayObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj
|
||||
UnboxedArrayObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
ObjectOpResult& result)
|
||||
{
|
||||
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
|
||||
size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
|
||||
if (JSID_IS_INT(id) && JSID_TO_INT(id) == int32_t(initlen - 1)) {
|
||||
obj->as<UnboxedArrayObject>().setInitializedLength(initlen - 1);
|
||||
obj->as<UnboxedArrayObject>().shrinkElements(cx, initlen - 1);
|
||||
return result.succeed();
|
||||
}
|
||||
}
|
||||
|
||||
if (!convertToNative(cx, obj))
|
||||
return false;
|
||||
return DeleteProperty(cx, obj, id, result);
|
||||
@ -1959,14 +1968,14 @@ js::MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
|
||||
return CallBoxedOrUnboxedSpecialization(functor, obj);
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor5(CopyBoxedOrUnboxedDenseElements,
|
||||
JSContext*, JSObject*, JSObject*, uint32_t, uint32_t);
|
||||
DefineBoxedOrUnboxedFunctor6(CopyBoxedOrUnboxedDenseElements,
|
||||
JSContext*, JSObject*, JSObject*, uint32_t, uint32_t, uint32_t);
|
||||
|
||||
DenseElementResult
|
||||
js::CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
|
||||
uint32_t srcStart, uint32_t length)
|
||||
uint32_t dstStart, uint32_t srcStart, uint32_t length)
|
||||
{
|
||||
CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, srcStart, length);
|
||||
CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, dstStart, srcStart, length);
|
||||
return CallBoxedOrUnboxedSpecialization(functor, dst);
|
||||
}
|
||||
|
||||
|
@ -435,7 +435,7 @@ class UnboxedArrayObject : public JSObject
|
||||
return computeCapacity(capacityIndex(), length());
|
||||
}
|
||||
|
||||
bool containsProperty(JSContext* cx, jsid id);
|
||||
bool containsProperty(ExclusiveContext* cx, jsid id);
|
||||
|
||||
bool setElement(ExclusiveContext* cx, size_t index, const Value& v);
|
||||
bool initElement(ExclusiveContext* cx, size_t index, const Value& v);
|
||||
|
Loading…
Reference in New Issue
Block a user