Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f23478616c | ||
|
|
adbec0c69b | ||
|
|
b791b67121 | ||
|
|
c15bc7d3dd | ||
|
|
33b7d3e638 |
No files matched your search
@@ -927,7 +927,7 @@ ModuleObject::evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValu
|
||||
ModuleObject::createNamespace(JSContext* cx, HandleModuleObject self, HandleObject exports)
|
||||
{
|
||||
MOZ_ASSERT(!self->namespace_());
|
||||
MOZ_ASSERT(exports->is<ArrayObject>() || exports->is<UnboxedArrayObject>());
|
||||
MOZ_ASSERT(exports->is<ArrayObject>());
|
||||
|
||||
RootedModuleNamespaceObject ns(cx, ModuleNamespaceObject::create(cx, self));
|
||||
if (!ns)
|
||||
|
||||
@@ -2548,8 +2548,6 @@ js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind
|
||||
InlineTypedObject::objectMovedDuringMinorGC(this, dst, src);
|
||||
} else if (src->is<TypedArrayObject>()) {
|
||||
tenuredSize += TypedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
|
||||
} else if (src->is<UnboxedArrayObject>()) {
|
||||
tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
|
||||
} else if (src->is<ArgumentsObject>()) {
|
||||
tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src);
|
||||
} else if (src->is<ProxyObject>()) {
|
||||
|
||||
@@ -787,8 +787,8 @@ BaselineCacheIRCompiler::emitGuardClass()
|
||||
case GuardClassKind::Array:
|
||||
clasp = &ArrayObject::class_;
|
||||
break;
|
||||
case GuardClassKind::UnboxedArray:
|
||||
clasp = &UnboxedArrayObject::class_;
|
||||
case GuardClassKind::UnboxedArray: // XXX Remove
|
||||
clasp = &ArrayObject::class_;
|
||||
break;
|
||||
case GuardClassKind::MappedArguments:
|
||||
clasp = &MappedArgumentsObject::class_;
|
||||
@@ -1006,6 +1006,8 @@ BaselineCacheIRCompiler::emitLoadInt32ArrayLengthResult()
|
||||
bool
|
||||
BaselineCacheIRCompiler::emitLoadUnboxedArrayLengthResult()
|
||||
{
|
||||
MOZ_CRASH("Not implemented");
|
||||
/*
|
||||
Register obj = allocator.useRegister(masm, reader.objOperandId());
|
||||
masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), R0.scratchReg());
|
||||
masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0);
|
||||
@@ -1014,6 +1016,7 @@ BaselineCacheIRCompiler::emitLoadUnboxedArrayLengthResult()
|
||||
// just return.
|
||||
emitReturnFromIC();
|
||||
return true;
|
||||
*/
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
+15
-152
@@ -1375,7 +1375,7 @@ IsNativeDenseElementAccess(HandleObject obj, HandleValue key)
|
||||
static bool
|
||||
IsNativeOrUnboxedDenseElementAccess(HandleObject obj, HandleValue key)
|
||||
{
|
||||
if (!obj->isNative() && !obj->is<UnboxedArrayObject>())
|
||||
if (!obj->isNative())
|
||||
return false;
|
||||
if (key.isInt32() && key.toInt32() >= 0 && !obj->is<TypedArrayObject>())
|
||||
return true;
|
||||
@@ -1479,20 +1479,6 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_
|
||||
script = rootedScript;
|
||||
}
|
||||
|
||||
// Check for UnboxedArray[int] accesses.
|
||||
if (obj->is<UnboxedArrayObject>() && rhs.isInt32() && rhs.toInt32() >= 0) {
|
||||
JitSpew(JitSpew_BaselineIC, " Generating GetElem(UnboxedArray[Int32]) stub");
|
||||
ICGetElem_UnboxedArray::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
obj->group());
|
||||
ICStub* unboxedStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!unboxedStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(unboxedStub);
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for TypedArray[int] => Number and TypedObject[int] => Number accesses.
|
||||
if ((obj->is<TypedArrayObject>() || IsPrimitiveArrayTypedObject(obj)) &&
|
||||
rhs.isNumber() &&
|
||||
@@ -2098,56 +2084,6 @@ ICGetElem_Dense::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// GetElem_UnboxedArray
|
||||
//
|
||||
|
||||
bool
|
||||
ICGetElem_UnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
{
|
||||
MOZ_ASSERT(engine_ == Engine::Baseline);
|
||||
|
||||
Label failure;
|
||||
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
|
||||
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
|
||||
|
||||
AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
|
||||
Register scratchReg = regs.takeAny();
|
||||
|
||||
// Unbox R0 and group guard.
|
||||
Register obj = masm.extractObject(R0, ExtractTemp0);
|
||||
masm.loadPtr(Address(ICStubReg, ICGetElem_UnboxedArray::offsetOfGroup()), scratchReg);
|
||||
masm.branchTestObjGroup(Assembler::NotEqual, obj, scratchReg, &failure);
|
||||
|
||||
// Unbox key.
|
||||
Register key = masm.extractInt32(R1, ExtractTemp1);
|
||||
|
||||
// Bounds check.
|
||||
masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()),
|
||||
scratchReg);
|
||||
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg);
|
||||
masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure);
|
||||
|
||||
// Load obj->elements.
|
||||
masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
|
||||
|
||||
// Load value.
|
||||
size_t width = UnboxedTypeSize(elementType_);
|
||||
BaseIndex addr(scratchReg, key, ScaleFromElemWidth(width));
|
||||
masm.loadUnboxedProperty(addr, elementType_, R0);
|
||||
|
||||
// Only monitor the result if its type might change.
|
||||
if (elementType_ == JSVAL_TYPE_OBJECT)
|
||||
EmitEnterTypeMonitorIC(masm);
|
||||
else
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
// Failure case - jump to next stub
|
||||
masm.bind(&failure);
|
||||
EmitStubGuardFailure(masm);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// GetElem_TypedArray
|
||||
//
|
||||
@@ -2452,8 +2388,12 @@ CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index,
|
||||
Shape* oldShape, uint32_t oldCapacity, uint32_t oldInitLength,
|
||||
bool* isAddingCaseOut, size_t* protoDepthOut)
|
||||
{
|
||||
uint32_t initLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
|
||||
uint32_t capacity = GetAnyBoxedOrUnboxedCapacity(obj);
|
||||
uint32_t initLength = obj->isNative() ?
|
||||
obj->as<NativeObject>().getDenseInitializedLength() :
|
||||
0;
|
||||
uint32_t capacity = obj->isNative() ?
|
||||
obj->as<NativeObject>().getDenseCapacity() :
|
||||
0;
|
||||
|
||||
*isAddingCaseOut = false;
|
||||
*protoDepthOut = 0;
|
||||
@@ -2462,10 +2402,6 @@ CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index,
|
||||
if (initLength < oldInitLength || capacity < oldCapacity)
|
||||
return false;
|
||||
|
||||
// Unboxed arrays need to be able to emit floating point code.
|
||||
if (obj->is<UnboxedArrayObject>() && !obj->runtimeFromMainThread()->jitSupportsFloatingPoint)
|
||||
return false;
|
||||
|
||||
Shape* shape = obj->maybeShape();
|
||||
|
||||
// Cannot optimize if the shape changed.
|
||||
@@ -2546,9 +2482,9 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
|
||||
// Check the old capacity
|
||||
uint32_t oldCapacity = 0;
|
||||
uint32_t oldInitLength = 0;
|
||||
if (index.isInt32() && index.toInt32() >= 0) {
|
||||
oldCapacity = GetAnyBoxedOrUnboxedCapacity(obj);
|
||||
oldInitLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
|
||||
if (index.isInt32() && index.toInt32() >= 0 && obj->isNative()) {
|
||||
oldCapacity = obj->as<NativeObject>().getDenseCapacity();
|
||||
oldInitLength = obj->as<NativeObject>().getDenseInitializedLength();
|
||||
}
|
||||
|
||||
if (op == JSOP_INITELEM || op == JSOP_INITHIDDENELEM) {
|
||||
@@ -2886,29 +2822,6 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
masm.loadValue(valueAddr, tmpVal);
|
||||
EmitPreBarrier(masm, element, MIRType::Value);
|
||||
masm.storeValue(tmpVal, element);
|
||||
} else {
|
||||
// Set element on an unboxed array.
|
||||
|
||||
// Bounds check.
|
||||
Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
|
||||
masm.load32(initLength, scratchReg);
|
||||
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg);
|
||||
masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure);
|
||||
|
||||
// Load obj->elements.
|
||||
masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
|
||||
|
||||
// Compute the address being written to.
|
||||
BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
|
||||
|
||||
EmitUnboxedPreBarrierForBaseline(masm, address, unboxedType_);
|
||||
|
||||
Address valueAddr(masm.getStackPointer(), ICStackValueOffset + sizeof(Value));
|
||||
masm.Push(R0);
|
||||
masm.loadValue(valueAddr, R0);
|
||||
masm.storeUnboxedProperty(address, unboxedType_,
|
||||
ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0);
|
||||
masm.Pop(R0);
|
||||
}
|
||||
|
||||
EmitReturnFromIC(masm);
|
||||
@@ -3102,40 +3015,6 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm)
|
||||
BaseIndex element(scratchReg, key, TimesEight);
|
||||
masm.loadValue(valueAddr, tmpVal);
|
||||
masm.storeValue(tmpVal, element);
|
||||
} else {
|
||||
// Adding element to an unboxed array.
|
||||
|
||||
// Bounds check (key == initLength)
|
||||
Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
|
||||
masm.load32(initLengthAddr, scratchReg);
|
||||
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg);
|
||||
masm.branch32(Assembler::NotEqual, scratchReg, key, &failure);
|
||||
|
||||
// Capacity check.
|
||||
masm.checkUnboxedArrayCapacity(obj, RegisterOrInt32Constant(key), scratchReg, &failure);
|
||||
|
||||
// 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(masm.getStackPointer(), ICStackValueOffset + sizeof(Value));
|
||||
masm.loadValue(valueAddr, R0);
|
||||
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);
|
||||
@@ -5903,12 +5782,12 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
|
||||
static bool
|
||||
CopyArray(JSContext* cx, HandleObject obj, MutableHandleValue result)
|
||||
{
|
||||
uint32_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
|
||||
uint32_t length = obj->as<ArrayObject>().length();
|
||||
JSObject* nobj = NewFullyAllocatedArrayTryReuseGroup(cx, obj, length, TenuredObject);
|
||||
if (!nobj)
|
||||
return false;
|
||||
EnsureArrayGroupAnalyzed(cx, nobj);
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, nobj, obj, 0, 0, length);
|
||||
CopyBoxedOrUnboxedDenseElements(cx, nobj, obj, 0, 0, length);
|
||||
|
||||
result.setObject(*nobj);
|
||||
return true;
|
||||
@@ -5945,16 +5824,13 @@ TryAttachStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
|
||||
|
||||
// Atomize all elements of the array.
|
||||
RootedObject arrObj(cx, &arr.toObject());
|
||||
uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(arrObj);
|
||||
uint32_t initLength = arrObj->as<ArrayObject>().length();
|
||||
for (uint32_t i = 0; i < initLength; i++) {
|
||||
JSAtom* str = js::AtomizeString(cx, GetAnyBoxedOrUnboxedDenseElement(arrObj, i).toString());
|
||||
JSAtom* str = js::AtomizeString(cx, arrObj->as<NativeObject>().getDenseElement(i).toString());
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
if (!SetAnyBoxedOrUnboxedDenseElement(cx, arrObj, i, StringValue(str))) {
|
||||
// The value could not be stored to an unboxed dense element.
|
||||
return true;
|
||||
}
|
||||
arrObj->as<NativeObject>().setDenseElementWithType(cx, i, StringValue(str));
|
||||
}
|
||||
|
||||
ICCall_StringSplit::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
@@ -8324,19 +8200,6 @@ ICGetElem_Dense::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorSt
|
||||
return New<ICGetElem_Dense>(cx, space, other.jitCode(), firstMonitorStub, other.shape_);
|
||||
}
|
||||
|
||||
ICGetElem_UnboxedArray::ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub,
|
||||
ObjectGroup *group)
|
||||
: ICMonitoredStub(GetElem_UnboxedArray, stubCode, firstMonitorStub),
|
||||
group_(group)
|
||||
{ }
|
||||
|
||||
/* static */ ICGetElem_UnboxedArray*
|
||||
ICGetElem_UnboxedArray::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
|
||||
ICGetElem_UnboxedArray& other)
|
||||
{
|
||||
return New<ICGetElem_UnboxedArray>(cx, space, other.jitCode(), firstMonitorStub, other.group_);
|
||||
}
|
||||
|
||||
ICGetElem_TypedArray::ICGetElem_TypedArray(JitCode* stubCode, Shape* shape, Scalar::Type type)
|
||||
: ICStub(GetElem_TypedArray, stubCode),
|
||||
shape_(shape)
|
||||
|
||||
+1
-51
@@ -892,54 +892,6 @@ class ICGetElem_Dense : public ICMonitoredStub
|
||||
};
|
||||
};
|
||||
|
||||
class ICGetElem_UnboxedArray : public ICMonitoredStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
GCPtrObjectGroup group_;
|
||||
|
||||
ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub, ObjectGroup* group);
|
||||
|
||||
public:
|
||||
static ICGetElem_UnboxedArray* Clone(JSContext* cx, ICStubSpace* space,
|
||||
ICStub* firstMonitorStub, ICGetElem_UnboxedArray& other);
|
||||
|
||||
static size_t offsetOfGroup() {
|
||||
return offsetof(ICGetElem_UnboxedArray, group_);
|
||||
}
|
||||
|
||||
GCPtrObjectGroup& group() {
|
||||
return group_;
|
||||
}
|
||||
|
||||
class Compiler : public ICStubCompiler {
|
||||
ICStub* firstMonitorStub_;
|
||||
RootedObjectGroup group_;
|
||||
JSValueType elementType_;
|
||||
|
||||
protected:
|
||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
||||
|
||||
virtual int32_t getKey() const {
|
||||
return static_cast<int32_t>(engine_) |
|
||||
(static_cast<int32_t>(kind) << 1) |
|
||||
(static_cast<int32_t>(elementType_) << 17);
|
||||
}
|
||||
|
||||
public:
|
||||
Compiler(JSContext* cx, ICStub* firstMonitorStub, ObjectGroup* group)
|
||||
: ICStubCompiler(cx, ICStub::GetElem_UnboxedArray, Engine::Baseline),
|
||||
firstMonitorStub_(firstMonitorStub),
|
||||
group_(cx, group),
|
||||
elementType_(group->unboxedLayoutDontCheckGeneration().elementType())
|
||||
{}
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
return newStub<ICGetElem_UnboxedArray>(space, getStubCode(), firstMonitorStub_, group_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Accesses scalar elements of a typed array or typed object.
|
||||
class ICGetElem_TypedArray : public ICStub
|
||||
{
|
||||
@@ -1225,9 +1177,7 @@ class ICSetElemDenseOrUnboxedArrayAddCompiler : public ICStubCompiler {
|
||||
: ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd, Engine::Baseline),
|
||||
obj_(cx, obj),
|
||||
protoChainDepth_(protoChainDepth),
|
||||
unboxedType_(obj->is<UnboxedArrayObject>()
|
||||
? obj->as<UnboxedArrayObject>().elementType()
|
||||
: JSVAL_TYPE_MAGIC)
|
||||
unboxedType_(JSVAL_TYPE_MAGIC)
|
||||
{}
|
||||
|
||||
template <size_t ProtoChainDepth>
|
||||
|
||||
@@ -60,7 +60,6 @@ namespace jit {
|
||||
_(GetElem_UnboxedPropertyName) \
|
||||
_(GetElem_String) \
|
||||
_(GetElem_Dense) \
|
||||
_(GetElem_UnboxedArray) \
|
||||
_(GetElem_TypedArray) \
|
||||
_(GetElem_Arguments) \
|
||||
\
|
||||
|
||||
@@ -864,7 +864,6 @@ BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc)
|
||||
case ICStub::GetElem_String:
|
||||
case ICStub::GetElem_Dense:
|
||||
case ICStub::GetElem_TypedArray:
|
||||
case ICStub::GetElem_UnboxedArray:
|
||||
stubType = MIRType::Object;
|
||||
break;
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, Shape* shape, ObjOper
|
||||
} else {
|
||||
writer.guardNoUnboxedExpando(objId);
|
||||
}
|
||||
} else if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) {
|
||||
} else if (obj->is<TypedObject>()) {
|
||||
writer.guardGroup(objId, obj->group());
|
||||
} else {
|
||||
Shape* shape = obj->maybeShape();
|
||||
@@ -368,13 +368,6 @@ GetPropIRGenerator::tryAttachObjectLength(CacheIRWriter& writer, HandleObject ob
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj->is<UnboxedArrayObject>()) {
|
||||
writer.guardClass(objId, GuardClassKind::UnboxedArray);
|
||||
writer.loadUnboxedArrayLengthResult(objId);
|
||||
emitted_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj->is<ArgumentsObject>() && !obj->as<ArgumentsObject>().hasOverriddenLength()) {
|
||||
if (obj->is<MappedArgumentsObject>()) {
|
||||
writer.guardClass(objId, GuardClassKind::MappedArguments);
|
||||
|
||||
+15
-125
@@ -3209,9 +3209,7 @@ CodeGenerator::visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins)
|
||||
void
|
||||
CodeGenerator::visitElements(LElements* lir)
|
||||
{
|
||||
Address elements(ToRegister(lir->object()),
|
||||
lir->mir()->unboxed() ? UnboxedArrayObject::offsetOfElements()
|
||||
: NativeObject::offsetOfElements());
|
||||
Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements());
|
||||
masm.loadPtr(elements, ToRegister(lir->output()));
|
||||
}
|
||||
|
||||
@@ -5395,13 +5393,7 @@ CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir)
|
||||
canInline = false;
|
||||
}
|
||||
} else {
|
||||
if (templateObject->as<UnboxedArrayObject>().hasInlineElements()) {
|
||||
size_t nbytes =
|
||||
templateObject->tenuredSizeOfThis() - UnboxedArrayObject::offsetOfInlineElements();
|
||||
inlineLength = nbytes / templateObject->as<UnboxedArrayObject>().elementSize();
|
||||
} else {
|
||||
canInline = false;
|
||||
}
|
||||
MOZ_CRASH("UnboxedArrayObject -- should not get here");
|
||||
}
|
||||
|
||||
if (canInline) {
|
||||
@@ -7860,44 +7852,25 @@ CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir)
|
||||
void
|
||||
CodeGenerator::visitUnboxedArrayLength(LUnboxedArrayLength* lir)
|
||||
{
|
||||
Register obj = ToRegister(lir->object());
|
||||
Register result = ToRegister(lir->output());
|
||||
masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), result);
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir)
|
||||
{
|
||||
Register obj = ToRegister(lir->object());
|
||||
Register result = ToRegister(lir->output());
|
||||
masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), result);
|
||||
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), result);
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir)
|
||||
{
|
||||
Register obj = ToRegister(lir->object());
|
||||
masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitSetUnboxedArrayInitializedLength(LSetUnboxedArrayInitializedLength* lir)
|
||||
{
|
||||
Register obj = ToRegister(lir->object());
|
||||
RegisterOrInt32Constant key = ToRegisterOrInt32Constant(lir->length());
|
||||
Register temp = ToRegister(lir->temp());
|
||||
|
||||
Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
|
||||
masm.load32(initLengthAddr, temp);
|
||||
masm.and32(Imm32(UnboxedArrayObject::CapacityMask), temp);
|
||||
|
||||
if (key.isRegister())
|
||||
masm.or32(key.reg(), temp);
|
||||
else
|
||||
masm.or32(Imm32(key.constant()), temp);
|
||||
|
||||
masm.store32(temp, initLengthAddr);
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -8213,28 +8186,7 @@ CodeGenerator::emitStoreElementHoleT(T* lir)
|
||||
emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(),
|
||||
elements, index, 0);
|
||||
} else {
|
||||
Register temp = ToRegister(lir->getTemp(0));
|
||||
Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
|
||||
masm.load32(initLength, temp);
|
||||
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp);
|
||||
masm.branch32(Assembler::BelowOrEqual, temp, key, ool->entry());
|
||||
|
||||
ConstantOrRegister v = ToConstantOrRegister(lir->value(), lir->mir()->value()->type());
|
||||
|
||||
if (index->isConstant()) {
|
||||
Address address(elements, ToInt32(index) * UnboxedTypeSize(unboxedType));
|
||||
EmitUnboxedPreBarrier(masm, address, unboxedType);
|
||||
|
||||
masm.bind(ool->rejoinStore());
|
||||
masm.storeUnboxedProperty(address, unboxedType, v, nullptr);
|
||||
} else {
|
||||
BaseIndex address(elements, ToRegister(index),
|
||||
ScaleFromElemWidth(UnboxedTypeSize(unboxedType)));
|
||||
EmitUnboxedPreBarrier(masm, address, unboxedType);
|
||||
|
||||
masm.bind(ool->rejoinStore());
|
||||
masm.storeUnboxedProperty(address, unboxedType, v, nullptr);
|
||||
}
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
@@ -8275,26 +8227,7 @@ CodeGenerator::emitStoreElementHoleV(T* lir)
|
||||
else
|
||||
masm.storeValue(value, BaseIndex(elements, ToRegister(index), TimesEight));
|
||||
} else {
|
||||
Register temp = ToRegister(lir->getTemp(0));
|
||||
Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
|
||||
masm.load32(initLength, temp);
|
||||
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp);
|
||||
masm.branch32(Assembler::BelowOrEqual, temp, key, ool->entry());
|
||||
|
||||
if (index->isConstant()) {
|
||||
Address address(elements, ToInt32(index) * UnboxedTypeSize(unboxedType));
|
||||
EmitUnboxedPreBarrier(masm, address, unboxedType);
|
||||
|
||||
masm.bind(ool->rejoinStore());
|
||||
masm.storeUnboxedProperty(address, unboxedType, ConstantOrRegister(value), nullptr);
|
||||
} else {
|
||||
BaseIndex address(elements, ToRegister(index),
|
||||
ScaleFromElemWidth(UnboxedTypeSize(unboxedType)));
|
||||
EmitUnboxedPreBarrier(masm, address, unboxedType);
|
||||
|
||||
masm.bind(ool->rejoinStore());
|
||||
masm.storeUnboxedProperty(address, unboxedType, ConstantOrRegister(value), nullptr);
|
||||
}
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
@@ -8439,10 +8372,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
||||
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branch32(Assembler::NotEqual, initLength, key, &callStub);
|
||||
} else {
|
||||
Address initLength(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
|
||||
masm.load32(initLength, ToRegister(temp));
|
||||
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), ToRegister(temp));
|
||||
masm.branch32(Assembler::NotEqual, ToRegister(temp), key, &callStub);
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
#else
|
||||
masm.j(Assembler::NotEqual, &callStub);
|
||||
@@ -8467,18 +8397,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
||||
|
||||
masm.dec32(&key);
|
||||
} else {
|
||||
// Check array capacity.
|
||||
masm.checkUnboxedArrayCapacity(object, key, ToRegister(temp), &callStub);
|
||||
|
||||
// Update initialized length.
|
||||
masm.add32(Imm32(1), Address(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
|
||||
|
||||
// Update length if length < initializedLength.
|
||||
Address lengthAddr(object, UnboxedArrayObject::offsetOfLength());
|
||||
Label dontUpdate;
|
||||
masm.branch32(Assembler::Above, lengthAddr, key, &dontUpdate);
|
||||
masm.add32(Imm32(1), lengthAddr);
|
||||
masm.bind(&dontUpdate);
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
|
||||
if ((ins->isStoreElementHoleT() || ins->isFallibleStoreElementT()) &&
|
||||
@@ -8572,18 +8491,13 @@ typedef bool (*ConvertUnboxedObjectToNativeFn)(JSContext*, JSObject*);
|
||||
static const VMFunction ConvertUnboxedPlainObjectToNativeInfo =
|
||||
FunctionInfo<ConvertUnboxedObjectToNativeFn>(UnboxedPlainObject::convertToNative,
|
||||
"UnboxedPlainObject::convertToNative");
|
||||
static const VMFunction ConvertUnboxedArrayObjectToNativeInfo =
|
||||
FunctionInfo<ConvertUnboxedObjectToNativeFn>(UnboxedArrayObject::convertToNative,
|
||||
"UnboxedArrayObject::convertToNative");
|
||||
|
||||
void
|
||||
CodeGenerator::visitConvertUnboxedObjectToNative(LConvertUnboxedObjectToNative* lir)
|
||||
{
|
||||
Register object = ToRegister(lir->getOperand(0));
|
||||
|
||||
OutOfLineCode* ool = oolCallVM(lir->mir()->group()->unboxedLayoutDontCheckGeneration().isArray()
|
||||
? ConvertUnboxedArrayObjectToNativeInfo
|
||||
: ConvertUnboxedPlainObjectToNativeInfo,
|
||||
OutOfLineCode* ool = oolCallVM(ConvertUnboxedPlainObjectToNativeInfo,
|
||||
lir, ArgList(object), StoreNothing());
|
||||
|
||||
masm.branchPtr(Assembler::Equal, Address(object, JSObject::offsetOfGroup()),
|
||||
@@ -8622,12 +8536,7 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
|
||||
Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branch32(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.branch32(Assembler::NotEqual, lengthAddr, key, ool->entry());
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
|
||||
// Test for length != 0. On zero length either take a VM call or generate
|
||||
@@ -8688,10 +8597,7 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
|
||||
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()));
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
|
||||
if (mir->mode() == MArrayPopShift::Shift) {
|
||||
@@ -8758,22 +8664,7 @@ CodeGenerator::emitArrayPush(LInstruction* lir, const MArrayPush* mir, Register
|
||||
// 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.branch32(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);
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
|
||||
masm.inc32(&key);
|
||||
@@ -8783,8 +8674,7 @@ CodeGenerator::emitArrayPush(LInstruction* lir, const MArrayPush* mir, Register
|
||||
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()));
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
|
||||
@@ -7354,12 +7354,6 @@ IonBuilder::newArrayTryTemplateObject(bool* emitted, JSObject* templateObject, u
|
||||
if (!templateObject)
|
||||
return true;
|
||||
|
||||
if (templateObject->is<UnboxedArrayObject>()) {
|
||||
MOZ_ASSERT(templateObject->as<UnboxedArrayObject>().capacity() >= length);
|
||||
if (!templateObject->as<UnboxedArrayObject>().hasInlineElements())
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT);
|
||||
|
||||
size_t arraySlots =
|
||||
@@ -7626,12 +7620,6 @@ IonBuilder::jsop_initelem_array()
|
||||
} else {
|
||||
MOZ_ASSERT(obj->resultTypeSet()->getObjectCount() == 1);
|
||||
TypeSet::ObjectKey* initializer = obj->resultTypeSet()->getObject(0);
|
||||
if (initializer->clasp() == &UnboxedArrayObject::class_) {
|
||||
if (initializer->group()->unboxedLayout().nativeGroup())
|
||||
needStub = true;
|
||||
else
|
||||
unboxedType = initializer->group()->unboxedLayout().elementType();
|
||||
}
|
||||
if (value->type() == MIRType::MagicHole) {
|
||||
if (!initializer->hasFlags(constraints(), OBJECT_FLAG_NON_PACKED))
|
||||
needStub = true;
|
||||
@@ -8196,7 +8184,6 @@ static bool
|
||||
ClassHasEffectlessLookup(const Class* clasp)
|
||||
{
|
||||
return (clasp == &UnboxedPlainObject::class_) ||
|
||||
(clasp == &UnboxedArrayObject::class_) ||
|
||||
IsTypedObjectClass(clasp) ||
|
||||
(clasp->isNative() && !clasp->getOpsLookupProperty());
|
||||
}
|
||||
|
||||
@@ -639,9 +639,6 @@ 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()),
|
||||
@@ -1193,31 +1190,7 @@ GenerateUnboxedArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAt
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1599,33 +1572,8 @@ GetPropertyIC::tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScri
|
||||
HandleObject obj, HandleId id, void* returnAddr,
|
||||
bool* emitted)
|
||||
{
|
||||
MOZ_ASSERT(canAttachStub());
|
||||
MOZ_ASSERT(!*emitted);
|
||||
MOZ_ASSERT(outerScript->ionScript() == ion);
|
||||
|
||||
if (!obj->is<UnboxedArrayObject>())
|
||||
return true;
|
||||
|
||||
if (!JSID_IS_ATOM(id, cx->names().length))
|
||||
return true;
|
||||
|
||||
if (obj->as<UnboxedArrayObject>().length() > INT32_MAX)
|
||||
return true;
|
||||
|
||||
if (!allowArrayLength(cx))
|
||||
return true;
|
||||
|
||||
*emitted = true;
|
||||
|
||||
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
|
||||
|
||||
Label failures;
|
||||
emitIdGuard(masm, id, &failures);
|
||||
|
||||
StubAttacher attacher(*this);
|
||||
GenerateUnboxedArrayLength(cx, masm, attacher, obj, object(), output(), &failures);
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "unboxed array length",
|
||||
JS::TrackedOutcome::ICGetPropStub_UnboxedArrayLength);
|
||||
// Stub UnboxedArrayObject
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -4026,7 +3974,7 @@ GetPropertyIC::tryAttachDenseElementHole(JSContext* cx, HandleScript outerScript
|
||||
GetPropertyIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval,
|
||||
TypedOrValueRegister output)
|
||||
{
|
||||
if (!obj->is<TypedArrayObject>() && !obj->is<UnboxedArrayObject>())
|
||||
if (!obj->is<TypedArrayObject>())
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(idval.isInt32() || idval.isString());
|
||||
@@ -4044,24 +3992,13 @@ GetPropertyIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& i
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj->is<TypedArrayObject>()) {
|
||||
if (index >= obj->as<TypedArrayObject>().length())
|
||||
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 = obj->as<TypedArrayObject>().type();
|
||||
if (arrayType == Scalar::Float32 || arrayType == Scalar::Float64)
|
||||
return output.hasValue();
|
||||
|
||||
return output.hasValue() || !output.typedReg().isFloat();
|
||||
}
|
||||
|
||||
if (index >= obj->as<UnboxedArrayObject>().initializedLength())
|
||||
if (index >= obj->as<TypedArrayObject>().length())
|
||||
return false;
|
||||
|
||||
JSValueType elementType = obj->as<UnboxedArrayObject>().elementType();
|
||||
if (elementType == JSVAL_TYPE_DOUBLE)
|
||||
// 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 = obj->as<TypedArrayObject>().type();
|
||||
if (arrayType == Scalar::Float32 || arrayType == Scalar::Float64)
|
||||
return output.hasValue();
|
||||
|
||||
return output.hasValue() || !output.typedReg().isFloat();
|
||||
@@ -4163,23 +4100,6 @@ GenerateGetTypedOrUnboxedArrayElement(JSContext* cx, MacroAssembler& masm,
|
||||
} else {
|
||||
masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popObjectAndFail);
|
||||
}
|
||||
} else {
|
||||
// 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);
|
||||
|
||||
@@ -471,11 +471,6 @@ IonBuilder::inlineArray(CallInfo& callInfo)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (templateObject->is<UnboxedArrayObject>()) {
|
||||
if (templateObject->group()->unboxedLayout().nativeGroup())
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// Multiple arguments imply array initialization, not just construction.
|
||||
if (callInfo.argc() >= 2) {
|
||||
initLength = callInfo.argc();
|
||||
@@ -523,7 +518,7 @@ IonBuilder::inlineArray(CallInfo& callInfo)
|
||||
// Make sure initLength matches the template object's length. This is
|
||||
// not guaranteed to be the case, for instance if we're inlining the
|
||||
// MConstant may come from an outer script.
|
||||
if (initLength != GetAnyBoxedOrUnboxedArrayLength(templateObject))
|
||||
if (initLength != templateObject->as<ArrayObject>().length())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
// Don't inline large allocations.
|
||||
@@ -538,7 +533,7 @@ IonBuilder::inlineArray(CallInfo& callInfo)
|
||||
|
||||
MDefinition* array = current->peek(-1);
|
||||
if (callInfo.argc() >= 2) {
|
||||
JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject);
|
||||
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
|
||||
for (uint32_t i = 0; i < initLength; i++) {
|
||||
if (!alloc().ensureBallast())
|
||||
return InliningStatus_Error;
|
||||
@@ -580,7 +575,7 @@ IonBuilder::inlineArrayIsArray(CallInfo& callInfo)
|
||||
if (!clasp || clasp->isProxy())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
isArray = (clasp == &ArrayObject::class_ || clasp == &UnboxedArrayObject::class_);
|
||||
isArray = (clasp == &ArrayObject::class_);
|
||||
}
|
||||
|
||||
pushConstant(BooleanValue(isArray));
|
||||
@@ -616,7 +611,7 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
|
||||
if (!thisTypes)
|
||||
return InliningStatus_NotInlined;
|
||||
const Class* clasp = thisTypes->getKnownClass(constraints());
|
||||
if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
|
||||
if (clasp != &ArrayObject::class_)
|
||||
return InliningStatus_NotInlined;
|
||||
if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
|
||||
@@ -630,11 +625,6 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
|
||||
}
|
||||
|
||||
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
|
||||
if (clasp == &UnboxedArrayObject::class_) {
|
||||
unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
@@ -734,11 +724,6 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
|
||||
}
|
||||
|
||||
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
|
||||
if (clasp == &UnboxedArrayObject::class_) {
|
||||
unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
@@ -797,15 +782,10 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
const Class* clasp = thisTypes->getKnownClass(constraints());
|
||||
if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
|
||||
if (clasp != &ArrayObject::class_)
|
||||
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;
|
||||
}
|
||||
|
||||
// Watch out for indexed properties on the object or its prototype.
|
||||
if (ElementAccessHasExtraIndexedProperty(this, obj)) {
|
||||
@@ -831,10 +811,7 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo)
|
||||
if (!templateObj->is<ArrayObject>())
|
||||
return InliningStatus_NotInlined;
|
||||
} else {
|
||||
if (!templateObj->is<UnboxedArrayObject>())
|
||||
return InliningStatus_NotInlined;
|
||||
if (templateObj->as<UnboxedArrayObject>().elementType() != unboxedType)
|
||||
return InliningStatus_NotInlined;
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
@@ -1408,13 +1385,16 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo)
|
||||
if (!key.maybeTypes()->hasType(TypeSet::StringType()))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(templateObject);
|
||||
if (GetAnyBoxedOrUnboxedInitializedLength(templateObject) != initLength)
|
||||
uint32_t arrayLength = templateObject->as<ArrayObject>().length();
|
||||
uint32_t initLength = templateObject->isNative() ?
|
||||
templateObject->as<NativeObject>().getDenseInitializedLength() :
|
||||
0;
|
||||
if (arrayLength != initLength)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
Vector<MConstant*, 0, SystemAllocPolicy> arrayValues;
|
||||
for (uint32_t i = 0; i < initLength; i++) {
|
||||
Value str = GetAnyBoxedOrUnboxedDenseElement(templateObject, i);
|
||||
Value str = templateObject->as<NativeObject>().getDenseElement(i);
|
||||
MOZ_ASSERT(str.toString()->isAtom());
|
||||
MConstant* value = MConstant::New(alloc().fallible(), str, constraints());
|
||||
if (!value)
|
||||
@@ -1445,7 +1425,7 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo)
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject);
|
||||
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
|
||||
|
||||
// Store all values, no need to initialize the length after each as
|
||||
// jsop_initelem_array is doing because we do not expect to bailout
|
||||
|
||||
+1
-41
@@ -5914,42 +5914,7 @@ JSValueType
|
||||
jit::UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* obj,
|
||||
MDefinition* id)
|
||||
{
|
||||
if (obj->mightBeType(MIRType::String))
|
||||
return JSVAL_TYPE_MAGIC;
|
||||
|
||||
if (id && id->type() != MIRType::Int32 && id->type() != MIRType::Double)
|
||||
return JSVAL_TYPE_MAGIC;
|
||||
|
||||
TemporaryTypeSet* types = obj->resultTypeSet();
|
||||
if (!types || types->unknownObject())
|
||||
return JSVAL_TYPE_MAGIC;
|
||||
|
||||
JSValueType elementType = JSVAL_TYPE_MAGIC;
|
||||
for (unsigned i = 0; i < types->getObjectCount(); i++) {
|
||||
TypeSet::ObjectKey* key = types->getObject(i);
|
||||
if (!key)
|
||||
continue;
|
||||
|
||||
if (key->unknownProperties() || !key->isGroup())
|
||||
return JSVAL_TYPE_MAGIC;
|
||||
|
||||
if (key->clasp() != &UnboxedArrayObject::class_)
|
||||
return JSVAL_TYPE_MAGIC;
|
||||
|
||||
const UnboxedLayout &layout = key->group()->unboxedLayout();
|
||||
|
||||
if (layout.nativeGroup())
|
||||
return JSVAL_TYPE_MAGIC;
|
||||
|
||||
if (elementType == layout.elementType() || elementType == JSVAL_TYPE_MAGIC)
|
||||
elementType = layout.elementType();
|
||||
else
|
||||
return JSVAL_TYPE_MAGIC;
|
||||
|
||||
key->watchStateChangeForUnboxedConvertedToNative(constraints);
|
||||
}
|
||||
|
||||
return elementType;
|
||||
return JSVAL_TYPE_MAGIC;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -6111,11 +6076,6 @@ ObjectSubsumes(TypeSet::ObjectKey* first, TypeSet::ObjectKey* second)
|
||||
firstElements.maybeTypes()->equals(secondElements.maybeTypes());
|
||||
}
|
||||
|
||||
if (first->clasp() == &UnboxedArrayObject::class_) {
|
||||
return first->group()->unboxedLayout().elementType() ==
|
||||
second->group()->unboxedLayout().elementType();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -709,25 +709,7 @@ void
|
||||
MacroAssembler::checkUnboxedArrayCapacity(Register obj, const RegisterOrInt32Constant& index,
|
||||
Register temp, Label* failure)
|
||||
{
|
||||
Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
|
||||
Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
|
||||
|
||||
Label capacityIsIndex, done;
|
||||
load32(initLengthAddr, temp);
|
||||
branchTest32(Assembler::NonZero, temp, Imm32(UnboxedArrayObject::CapacityMask), &capacityIsIndex);
|
||||
branch32(Assembler::BelowOrEqual, lengthAddr, index, failure);
|
||||
jump(&done);
|
||||
bind(&capacityIsIndex);
|
||||
|
||||
// Do a partial shift so that we can get an absolute offset from the base
|
||||
// of CapacityArray to use.
|
||||
JS_STATIC_ASSERT(sizeof(UnboxedArrayObject::CapacityArray[0]) == 4);
|
||||
rshiftPtr(Imm32(UnboxedArrayObject::CapacityShift - 2), temp);
|
||||
and32(Imm32(~0x3), temp);
|
||||
|
||||
addPtr(ImmPtr(&UnboxedArrayObject::CapacityArray), temp);
|
||||
branch32(Assembler::BelowOrEqual, Address(temp, 0), index, failure);
|
||||
bind(&done);
|
||||
// Stub
|
||||
}
|
||||
|
||||
// Inlined version of gc::CheckAllocatorState that checks the bare essentials
|
||||
@@ -1281,16 +1263,6 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj,
|
||||
storePtr(ImmWord(0), Address(obj, UnboxedPlainObject::offsetOfExpando()));
|
||||
if (initContents)
|
||||
initUnboxedObjectContents(obj, &templateObj->as<UnboxedPlainObject>());
|
||||
} else if (templateObj->is<UnboxedArrayObject>()) {
|
||||
MOZ_ASSERT(templateObj->as<UnboxedArrayObject>().hasInlineElements());
|
||||
int elementsOffset = UnboxedArrayObject::offsetOfInlineElements();
|
||||
computeEffectiveAddress(Address(obj, elementsOffset), temp);
|
||||
storePtr(temp, Address(obj, UnboxedArrayObject::offsetOfElements()));
|
||||
store32(Imm32(templateObj->as<UnboxedArrayObject>().length()),
|
||||
Address(obj, UnboxedArrayObject::offsetOfLength()));
|
||||
uint32_t capacityIndex = templateObj->as<UnboxedArrayObject>().capacityIndex();
|
||||
store32(Imm32(capacityIndex << UnboxedArrayObject::CapacityShift),
|
||||
Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
|
||||
} else {
|
||||
MOZ_CRASH("Unknown object");
|
||||
}
|
||||
|
||||
@@ -1626,7 +1626,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address& dest,
|
||||
unsigned numElems = 0);
|
||||
|
||||
// Load a property from an UnboxedPlainObject or UnboxedArrayObject.
|
||||
// Load a property from an Unboxed Object.
|
||||
template <typename T>
|
||||
void loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output);
|
||||
|
||||
|
||||
@@ -907,11 +907,6 @@ IsArrayEscaped(MInstruction* ins)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj->is<UnboxedArrayObject>()) {
|
||||
JitSpew(JitSpew_Escape, "Template object is an unboxed plain object.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (length >= 16) {
|
||||
JitSpew(JitSpew_Escape, "Array has too many elements");
|
||||
return true;
|
||||
|
||||
@@ -285,11 +285,6 @@ ICStub::trace(JSTracer* trc)
|
||||
TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-dense-shape");
|
||||
break;
|
||||
}
|
||||
case ICStub::GetElem_UnboxedArray: {
|
||||
ICGetElem_UnboxedArray* getElemStub = toGetElem_UnboxedArray();
|
||||
TraceEdge(trc, &getElemStub->group(), "baseline-getelem-unboxed-array-group");
|
||||
break;
|
||||
}
|
||||
case ICStub::GetElem_TypedArray: {
|
||||
ICGetElem_TypedArray* getElemStub = toGetElem_TypedArray();
|
||||
TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-typedarray-shape");
|
||||
@@ -2249,7 +2244,6 @@ IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy)
|
||||
if (obj == holder)
|
||||
return false;
|
||||
if (!obj->is<UnboxedPlainObject>() &&
|
||||
!obj->is<UnboxedArrayObject>() &&
|
||||
!obj->is<TypedObject>())
|
||||
{
|
||||
return false;
|
||||
@@ -2581,9 +2575,6 @@ CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, PropertyName* name,
|
||||
} else if (curObj->is<UnboxedPlainObject>()) {
|
||||
if (curObj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, NameToId(name)))
|
||||
return false;
|
||||
} else if (curObj->is<UnboxedArrayObject>()) {
|
||||
if (name == cx->names().length)
|
||||
return false;
|
||||
} else if (curObj->is<TypedObject>()) {
|
||||
if (curObj->as<TypedObject>().typeDescr().hasProperty(cx->names(), NameToId(name)))
|
||||
return false;
|
||||
|
||||
@@ -306,7 +306,7 @@ template bool StringsEqual<false>(JSContext* cx, HandleString lhs, HandleString
|
||||
bool
|
||||
ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
|
||||
{
|
||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>());
|
||||
|
||||
AutoDetectInvalidation adi(cx, rval);
|
||||
|
||||
@@ -327,10 +327,10 @@ ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
|
||||
bool
|
||||
ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length)
|
||||
{
|
||||
*length = GetAnyBoxedOrUnboxedArrayLength(obj);
|
||||
*length = obj->as<ArrayObject>().length();
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, *length, v.address(), 1,
|
||||
ShouldUpdateTypes::DontUpdate);
|
||||
SetOrExtendBoxedOrUnboxedDenseElements(cx, obj, *length, v.address(), 1,
|
||||
ShouldUpdateTypes::DontUpdate);
|
||||
if (result != DenseElementResult::Incomplete) {
|
||||
(*length)++;
|
||||
return result == DenseElementResult::Success;
|
||||
@@ -350,7 +350,7 @@ ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length)
|
||||
bool
|
||||
ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
|
||||
{
|
||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>());
|
||||
|
||||
AutoDetectInvalidation adi(cx, rval);
|
||||
|
||||
@@ -1139,8 +1139,8 @@ SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index,
|
||||
// no type changes are needed.
|
||||
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, index, value.address(), 1,
|
||||
ShouldUpdateTypes::DontUpdate);
|
||||
SetOrExtendBoxedOrUnboxedDenseElements(cx, obj, index, value.address(), 1,
|
||||
ShouldUpdateTypes::DontUpdate);
|
||||
if (result != DenseElementResult::Incomplete)
|
||||
return result == DenseElementResult::Success;
|
||||
|
||||
|
||||
@@ -1173,12 +1173,6 @@ class JS_PUBLIC_API(ContextOptions) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool unboxedArrays() const { return unboxedArrays_; }
|
||||
ContextOptions& setUnboxedArrays(bool flag) {
|
||||
unboxedArrays_ = flag;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool asyncStack() const { return asyncStack_; }
|
||||
ContextOptions& setAsyncStack(bool flag) {
|
||||
asyncStack_ = flag;
|
||||
|
||||
+71
-85
@@ -64,7 +64,7 @@ using JS::ToUint32;
|
||||
bool
|
||||
JS::IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer)
|
||||
{
|
||||
if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) {
|
||||
if (obj->is<ArrayObject>()) {
|
||||
*answer = IsArrayAnswer::Array;
|
||||
return true;
|
||||
}
|
||||
@@ -100,11 +100,6 @@ js::GetLengthProperty(JSContext* cx, HandleObject obj, uint32_t* lengthp)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj->is<UnboxedArrayObject>()) {
|
||||
*lengthp = obj->as<UnboxedArrayObject>().length();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj->is<ArgumentsObject>()) {
|
||||
ArgumentsObject& argsobj = obj->as<ArgumentsObject>();
|
||||
if (!argsobj.hasOverriddenLength()) {
|
||||
@@ -254,8 +249,11 @@ GetElement(JSContext* cx, HandleObject obj, HandleObject receiver,
|
||||
uint32_t index, bool* hole, MutableHandleValue vp)
|
||||
{
|
||||
AssertGreaterThanZero(index);
|
||||
if (index < GetAnyBoxedOrUnboxedInitializedLength(obj)) {
|
||||
vp.set(GetAnyBoxedOrUnboxedDenseElement(obj, uint32_t(index)));
|
||||
uint32_t initLength = obj->isNative() ?
|
||||
obj->as<NativeObject>().getDenseInitializedLength() :
|
||||
0;
|
||||
if (index < initLength) {
|
||||
vp.set(obj->as<NativeObject>().getDenseElement(index));
|
||||
if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
|
||||
*hole = false;
|
||||
return true;
|
||||
@@ -284,7 +282,7 @@ ElementAdder::append(JSContext* cx, HandleValue v)
|
||||
MOZ_ASSERT(index_ < length_);
|
||||
if (resObj_) {
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_, v.address(), 1);
|
||||
SetOrExtendBoxedOrUnboxedDenseElements(cx, resObj_, index_, v.address(), 1);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
if (result == DenseElementResult::Incomplete) {
|
||||
@@ -342,11 +340,11 @@ GetBoxedOrUnboxedDenseElements(JSObject* aobj, uint32_t length, Value* vp)
|
||||
{
|
||||
MOZ_ASSERT(!ObjectMayHaveExtraIndexedProperties(aobj));
|
||||
|
||||
if (length > GetBoxedOrUnboxedInitializedLength<Type>(aobj))
|
||||
if (length > GetBoxedOrUnboxedInitializedLength(aobj))
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
vp[i] = GetBoxedOrUnboxedDenseElement<Type>(aobj, i);
|
||||
vp[i] = GetBoxedOrUnboxedDenseElement(aobj, i);
|
||||
|
||||
// No other indexed properties so hole => undefined.
|
||||
if (vp[i].isMagic(JS_ELEMENTS_HOLE))
|
||||
@@ -398,9 +396,9 @@ SetArrayElement(JSContext* cx, HandleObject obj, double index, HandleValue v)
|
||||
{
|
||||
MOZ_ASSERT(index >= 0);
|
||||
|
||||
if ((obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) && !obj->isIndexed() && index <= UINT32_MAX) {
|
||||
if (obj->is<ArrayObject>() && !obj->isIndexed() && index <= UINT32_MAX) {
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(index), v.address(), 1);
|
||||
SetOrExtendBoxedOrUnboxedDenseElements(cx, obj, uint32_t(index), v.address(), 1);
|
||||
if (result != DenseElementResult::Incomplete)
|
||||
return result == DenseElementResult::Success;
|
||||
}
|
||||
@@ -823,7 +821,7 @@ array_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
|
||||
static inline bool
|
||||
ObjectMayHaveExtraIndexedOwnProperties(JSObject* obj)
|
||||
{
|
||||
return (!obj->isNative() && !obj->is<UnboxedArrayObject>()) ||
|
||||
return !obj->isNative() ||
|
||||
obj->isIndexed() ||
|
||||
obj->is<TypedArrayObject>() ||
|
||||
ClassMayResolveId(*obj->runtimeFromAnyThread()->commonNames,
|
||||
@@ -854,7 +852,10 @@ js::ObjectMayHaveExtraIndexedProperties(JSObject* obj)
|
||||
|
||||
if (ObjectMayHaveExtraIndexedOwnProperties(obj))
|
||||
return true;
|
||||
if (GetAnyBoxedOrUnboxedInitializedLength(obj) != 0)
|
||||
uint32_t initLength = obj->isNative() ?
|
||||
obj->as<NativeObject>().getDenseInitializedLength() :
|
||||
0;
|
||||
if (initLength != 0)
|
||||
return true;
|
||||
} while (true);
|
||||
}
|
||||
@@ -1073,7 +1074,7 @@ ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_
|
||||
// length > initLength we rely on the second loop to add the
|
||||
// other elements.
|
||||
MOZ_ASSERT(*numProcessed == 0);
|
||||
uint32_t initLength = Min<uint32_t>(GetBoxedOrUnboxedInitializedLength<Type>(obj),
|
||||
uint32_t initLength = Min<uint32_t>(GetBoxedOrUnboxedInitializedLength(obj),
|
||||
length);
|
||||
while (*numProcessed < initLength) {
|
||||
if (!CheckForInterrupt(cx))
|
||||
@@ -1213,8 +1214,11 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp)
|
||||
// An optimized version of a special case of steps 7-11: when length==1 and
|
||||
// the 0th element is a string, ToString() of that element is a no-op and
|
||||
// so it can be immediately returned as the result.
|
||||
if (length == 1 && GetAnyBoxedOrUnboxedInitializedLength(obj) == 1) {
|
||||
Value elem0 = GetAnyBoxedOrUnboxedDenseElement(obj, 0);
|
||||
uint32_t initLength = obj->isNative() ?
|
||||
obj->as<NativeObject>().getDenseInitializedLength() :
|
||||
0;
|
||||
if (length == 1 && initLength == 1) {
|
||||
Value elem0 = obj->as<NativeObject>().getDenseElement(0);
|
||||
if (elem0.isString()) {
|
||||
args.rval().set(elem0);
|
||||
return true;
|
||||
@@ -1289,10 +1293,6 @@ array_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
|
||||
args.rval().setString(cx->names().empty);
|
||||
return true;
|
||||
}
|
||||
if (obj->is<UnboxedArrayObject>() && obj->as<UnboxedArrayObject>().length() == 0) {
|
||||
args.rval().setString(cx->names().empty);
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoCycleDetector detector(cx, obj);
|
||||
if (!detector.init())
|
||||
@@ -1330,7 +1330,7 @@ InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start,
|
||||
|
||||
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, start, vector, count, updateTypes);
|
||||
SetOrExtendBoxedOrUnboxedDenseElements(cx, obj, start, vector, count, updateTypes);
|
||||
if (result != DenseElementResult::Incomplete)
|
||||
return result == DenseElementResult::Success;
|
||||
}
|
||||
@@ -1369,7 +1369,7 @@ DenseElementResult
|
||||
ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
|
||||
{
|
||||
/* An empty array or an array with no elements is already reversed. */
|
||||
if (length == 0 || GetBoxedOrUnboxedInitializedLength<Type>(obj) == 0)
|
||||
if (length == 0 || GetBoxedOrUnboxedInitializedLength(obj) == 0)
|
||||
return DenseElementResult::Success;
|
||||
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
@@ -1392,26 +1392,22 @@ ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
|
||||
/* Fill out the array's initialized length to its proper length. */
|
||||
obj->as<NativeObject>().ensureDenseInitializedLength(cx, length, 0);
|
||||
} else {
|
||||
// Unboxed arrays can only be reversed here if their initialized length
|
||||
// matches their actual length. Otherwise the reversal will place holes
|
||||
// at the beginning of the array, which we don't support.
|
||||
if (length != obj->as<UnboxedArrayObject>().initializedLength())
|
||||
return DenseElementResult::Incomplete;
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
|
||||
RootedValue origlo(cx), orighi(cx);
|
||||
|
||||
uint32_t lo = 0, hi = length - 1;
|
||||
for (; lo < hi; lo++, hi--) {
|
||||
origlo = GetBoxedOrUnboxedDenseElement<Type>(obj, lo);
|
||||
orighi = GetBoxedOrUnboxedDenseElement<Type>(obj, hi);
|
||||
SetBoxedOrUnboxedDenseElementNoTypeChange<Type>(obj, lo, orighi);
|
||||
origlo = GetBoxedOrUnboxedDenseElement(obj, lo);
|
||||
orighi = GetBoxedOrUnboxedDenseElement(obj, hi);
|
||||
SetBoxedOrUnboxedDenseElementNoTypeChange(obj, lo, orighi);
|
||||
if (orighi.isMagic(JS_ELEMENTS_HOLE) &&
|
||||
!SuppressDeletedProperty(cx, obj, INT_TO_JSID(lo)))
|
||||
{
|
||||
return DenseElementResult::Failure;
|
||||
}
|
||||
SetBoxedOrUnboxedDenseElementNoTypeChange<Type>(obj, hi, origlo);
|
||||
SetBoxedOrUnboxedDenseElementNoTypeChange(obj, hi, origlo);
|
||||
if (origlo.isMagic(JS_ELEMENTS_HOLE) &&
|
||||
!SuppressDeletedProperty(cx, obj, INT_TO_JSID(hi)))
|
||||
{
|
||||
@@ -2083,8 +2079,8 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
||||
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, length,
|
||||
args.array(), args.length());
|
||||
SetOrExtendBoxedOrUnboxedDenseElements(cx, obj, length,
|
||||
args.array(), args.length());
|
||||
if (result != DenseElementResult::Incomplete) {
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
@@ -2092,7 +2088,7 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp)
|
||||
uint32_t newlength = length + args.length();
|
||||
args.rval().setNumber(newlength);
|
||||
|
||||
// SetOrExtendAnyBoxedOrUnboxedDenseElements takes care of updating the
|
||||
// SetOrExtendBoxedOrUnboxedDenseElements takes care of updating the
|
||||
// length for boxed and unboxed arrays. Handle updates to the length of
|
||||
// non-arrays here.
|
||||
bool isArray;
|
||||
@@ -2159,20 +2155,18 @@ template <JSValueType Type>
|
||||
static inline DenseElementResult
|
||||
ShiftMoveBoxedOrUnboxedDenseElements(JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(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(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);
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
|
||||
return DenseElementResult::Success;
|
||||
@@ -2203,19 +2197,19 @@ ArrayShiftDenseKernel(JSContext* cx, HandleObject obj, MutableHandleValue rval)
|
||||
if (MOZ_UNLIKELY(group->hasAllFlags(OBJECT_FLAG_ITERATED)))
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
|
||||
size_t initlen = GetBoxedOrUnboxedInitializedLength(obj);
|
||||
if (initlen == 0)
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
rval.set(GetBoxedOrUnboxedDenseElement<Type>(obj, 0));
|
||||
rval.set(GetBoxedOrUnboxedDenseElement(obj, 0));
|
||||
if (rval.isMagic(JS_ELEMENTS_HOLE))
|
||||
rval.setUndefined();
|
||||
|
||||
DenseElementResult result = MoveBoxedOrUnboxedDenseElements<Type>(cx, obj, 0, 1, initlen - 1);
|
||||
DenseElementResult result = MoveBoxedOrUnboxedDenseElements(cx, obj, 0, 1, initlen - 1);
|
||||
if (result != DenseElementResult::Success)
|
||||
return result;
|
||||
|
||||
SetBoxedOrUnboxedInitializedLength<Type>(cx, obj, initlen - 1);
|
||||
SetBoxedOrUnboxedInitializedLength(cx, obj, initlen - 1);
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
@@ -2384,7 +2378,7 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co
|
||||
return false;
|
||||
|
||||
/* There's no optimizing possible if it's not an array. */
|
||||
if (!arr->is<ArrayObject>() && !arr->is<UnboxedArrayObject>())
|
||||
if (!arr->is<ArrayObject>())
|
||||
return false;
|
||||
|
||||
/* If it's a frozen array, always pick the slow path */
|
||||
@@ -2419,8 +2413,11 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co
|
||||
* other indexed properties on the object. (Note that non-writable length
|
||||
* is subsumed by the initializedLength comparison.)
|
||||
*/
|
||||
uint32_t initLength = arr->isNative() ?
|
||||
arr->as<NativeObject>().getDenseInitializedLength() :
|
||||
0;
|
||||
return !ObjectMayHaveExtraIndexedProperties(arr) &&
|
||||
startingIndex + count <= GetAnyBoxedOrUnboxedInitializedLength(arr);
|
||||
startingIndex + count <= initLength;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
@@ -2514,7 +2511,7 @@ array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUse
|
||||
|
||||
/* Steps 10-11. */
|
||||
DebugOnly<DenseElementResult> result =
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, 0, actualStart, actualDeleteCount);
|
||||
CopyBoxedOrUnboxedDenseElements(cx, arr, obj, 0, actualStart, actualDeleteCount);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
|
||||
/* Step 12 (implicit). */
|
||||
@@ -2551,14 +2548,14 @@ array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUse
|
||||
if (CanOptimizeForDenseStorage(obj, 0, len, cx)) {
|
||||
/* Steps 15.a-b. */
|
||||
DenseElementResult result =
|
||||
MoveAnyBoxedOrUnboxedDenseElements(cx, obj, targetIndex, sourceIndex,
|
||||
len - sourceIndex);
|
||||
MoveBoxedOrUnboxedDenseElements(cx, obj, targetIndex, sourceIndex,
|
||||
len - sourceIndex);
|
||||
MOZ_ASSERT(result != DenseElementResult::Incomplete);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
|
||||
/* Steps 15.c-d. */
|
||||
SetAnyBoxedOrUnboxedInitializedLength(cx, obj, finalLength);
|
||||
SetBoxedOrUnboxedInitializedLength(cx, obj, finalLength);
|
||||
} else {
|
||||
/*
|
||||
* This is all very slow if the length is very large. We don't yet
|
||||
@@ -2638,15 +2635,15 @@ array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUse
|
||||
|
||||
if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) {
|
||||
DenseElementResult result =
|
||||
MoveAnyBoxedOrUnboxedDenseElements(cx, obj, actualStart + itemCount,
|
||||
actualStart + actualDeleteCount,
|
||||
len - (actualStart + actualDeleteCount));
|
||||
MoveBoxedOrUnboxedDenseElements(cx, obj, actualStart + itemCount,
|
||||
actualStart + actualDeleteCount,
|
||||
len - (actualStart + actualDeleteCount));
|
||||
MOZ_ASSERT(result != DenseElementResult::Incomplete);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
|
||||
/* Steps 16.a-b. */
|
||||
SetAnyBoxedOrUnboxedInitializedLength(cx, obj, len + itemCount - actualDeleteCount);
|
||||
SetBoxedOrUnboxedInitializedLength(cx, obj, len + itemCount - actualDeleteCount);
|
||||
} else {
|
||||
RootedValue fromValue(cx);
|
||||
for (double k = len - actualDeleteCount; k > actualStart; k--) {
|
||||
@@ -2881,7 +2878,9 @@ ArraySliceOrdinary(JSContext* cx, HandleObject obj, uint32_t length, uint32_t be
|
||||
begin = end;
|
||||
|
||||
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
|
||||
size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
|
||||
size_t initlen = obj->isNative() ?
|
||||
obj->as<NativeObject>().getDenseInitializedLength() :
|
||||
0;
|
||||
size_t count = 0;
|
||||
if (initlen > begin)
|
||||
count = Min<size_t>(initlen - begin, end - begin);
|
||||
@@ -2889,11 +2888,12 @@ ArraySliceOrdinary(JSContext* cx, HandleObject obj, uint32_t length, uint32_t be
|
||||
RootedObject narr(cx, NewFullyAllocatedArrayTryReuseGroup(cx, obj, count));
|
||||
if (!narr)
|
||||
return false;
|
||||
SetAnyBoxedOrUnboxedArrayLength(cx, narr, end - begin);
|
||||
MOZ_ASSERT((end - begin) >= narr->as<ArrayObject>().length());
|
||||
narr->as<ArrayObject>().setLength(cx, end - begin);
|
||||
|
||||
if (count) {
|
||||
DebugOnly<DenseElementResult> result =
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, count);
|
||||
CopyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, count);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
}
|
||||
arr.set(narr);
|
||||
@@ -3021,7 +3021,7 @@ template <JSValueType Type>
|
||||
DenseElementResult
|
||||
ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t endArg, JSObject* result)
|
||||
{
|
||||
int32_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
|
||||
int32_t length = obj->as<ArrayObject>().length();
|
||||
|
||||
uint32_t begin = NormalizeSliceTerm(beginArg, length);
|
||||
uint32_t end = NormalizeSliceTerm(endArg, length);
|
||||
@@ -3029,18 +3029,19 @@ ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t en
|
||||
if (begin > end)
|
||||
begin = end;
|
||||
|
||||
size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
|
||||
size_t initlen = GetBoxedOrUnboxedInitializedLength(obj);
|
||||
if (initlen > begin) {
|
||||
size_t count = Min<size_t>(initlen - begin, end - begin);
|
||||
if (count) {
|
||||
DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements<Type>(cx, result, count);
|
||||
DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements(cx, result, count);
|
||||
if (rv != DenseElementResult::Success)
|
||||
return rv;
|
||||
CopyBoxedOrUnboxedDenseElements<Type, Type>(cx, result, obj, 0, begin, count);
|
||||
CopyBoxedOrUnboxedDenseElements(cx, result, obj, 0, begin, count);
|
||||
}
|
||||
}
|
||||
|
||||
SetAnyBoxedOrUnboxedArrayLength(cx, result, end - begin);
|
||||
MOZ_ASSERT((end - begin) >= result->as<ArrayObject>().length());
|
||||
result->as<ArrayObject>().setLength(cx, end - begin);
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
@@ -3612,11 +3613,6 @@ NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length
|
||||
newKind = TenuredObject;
|
||||
|
||||
RootedObject proto(cx, group->proto().toObject());
|
||||
if (group->maybeUnboxedLayout()) {
|
||||
if (length > UnboxedArrayObject::MaximumCapacity)
|
||||
return NewArray<maxLength>(cx, length, proto, newKind);
|
||||
return UnboxedArrayObject::create(cx, group, length, newKind, maxLength);
|
||||
}
|
||||
|
||||
ArrayObject* res = NewArray<maxLength>(cx, length, proto, newKind);
|
||||
if (!res)
|
||||
@@ -3653,14 +3649,13 @@ js::NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup g
|
||||
// object. The resulting array will either reuse the input object's group or
|
||||
// will have unknown property types. Additionally, the result will have the
|
||||
// same boxed/unboxed elements representation as the input object, unless
|
||||
// |length| is larger than the input object's initialized length (in which case
|
||||
// UnboxedArrayObject::MaximumCapacity might be exceeded).
|
||||
// |length| is larger than the input object's initialized length.
|
||||
template <uint32_t maxLength>
|
||||
static inline JSObject*
|
||||
NewArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length,
|
||||
NewObjectKind newKind = GenericObject)
|
||||
{
|
||||
if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
|
||||
if (!obj->is<ArrayObject>())
|
||||
return NewArray<maxLength>(cx, length, nullptr, newKind);
|
||||
|
||||
if (obj->staticPrototype() != cx->global()->maybeGetArrayPrototype())
|
||||
@@ -3724,8 +3719,8 @@ js::MaybeAnalyzeBeforeCreatingLargeArray(ExclusiveContext* cx, HandleObjectGroup
|
||||
if (!obj)
|
||||
return false;
|
||||
DebugOnly<DenseElementResult> result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, nlength,
|
||||
ShouldUpdateTypes::Update);
|
||||
SetOrExtendBoxedOrUnboxedDenseElements(cx, obj, 0, vp, nlength,
|
||||
ShouldUpdateTypes::Update);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
}
|
||||
objects->maybeAnalyze(cx, group, /* forceAnalyze = */ true);
|
||||
@@ -3747,23 +3742,14 @@ js::NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group,
|
||||
return nullptr;
|
||||
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, updateTypes);
|
||||
SetOrExtendBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, updateTypes);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return nullptr;
|
||||
if (result == DenseElementResult::Success)
|
||||
return obj;
|
||||
|
||||
MOZ_ASSERT(obj->is<UnboxedArrayObject>());
|
||||
if (!UnboxedArrayObject::convertToNative(cx->asJSContext(), obj))
|
||||
return nullptr;
|
||||
|
||||
result = SetOrExtendBoxedOrUnboxedDenseElements<JSVAL_TYPE_MAGIC>(cx, obj, 0, vp, length,
|
||||
updateTypes);
|
||||
MOZ_ASSERT(result != DenseElementResult::Incomplete);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return nullptr;
|
||||
|
||||
return obj;
|
||||
// if we get here it was unboxed
|
||||
MOZ_CRASH("UnboxedArrayObject -- Should not get here");
|
||||
}
|
||||
|
||||
JSObject*
|
||||
|
||||
@@ -270,7 +270,7 @@ js::GetBuiltinClass(JSContext* cx, HandleObject obj, ESClass* cls)
|
||||
|
||||
if (obj->is<PlainObject>() || obj->is<UnboxedPlainObject>())
|
||||
*cls = ESClass::Object;
|
||||
else if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>())
|
||||
else if (obj->is<ArrayObject>())
|
||||
*cls = ESClass::Array;
|
||||
else if (obj->is<NumberObject>())
|
||||
*cls = ESClass::Number;
|
||||
|
||||
+10
-24
@@ -1141,16 +1141,18 @@ static bool
|
||||
GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, MutableHandle<GCVector<Value>> values)
|
||||
{
|
||||
MOZ_ASSERT(!obj->isSingleton());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>());
|
||||
MOZ_ASSERT(!obj->isIndexed());
|
||||
|
||||
size_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
|
||||
size_t length = obj->as<ArrayObject>().length();
|
||||
if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), length))
|
||||
return false;
|
||||
|
||||
size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
|
||||
size_t initlen = obj->isNative() ?
|
||||
obj->as<NativeObject>().getDenseInitializedLength() :
|
||||
0;
|
||||
for (size_t i = 0; i < initlen; i++)
|
||||
values[i].set(GetAnyBoxedOrUnboxedDenseElement(obj, i));
|
||||
values[i].set(obj->as<NativeObject>().getDenseElement(i));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1221,10 +1223,10 @@ js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKin
|
||||
MOZ_ASSERT_IF(obj->isSingleton(),
|
||||
cx->compartment()->behaviors().getSingletonsAsTemplates());
|
||||
MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() ||
|
||||
obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
obj->is<ArrayObject>());
|
||||
MOZ_ASSERT(newKind != SingletonObject);
|
||||
|
||||
if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) {
|
||||
if (obj->is<ArrayObject>()) {
|
||||
Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
|
||||
if (!GetScriptArrayObjectElements(cx, obj, &values))
|
||||
return nullptr;
|
||||
@@ -1341,9 +1343,8 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
|
||||
if (mode == XDR_ENCODE) {
|
||||
MOZ_ASSERT(obj->is<PlainObject>() ||
|
||||
obj->is<UnboxedPlainObject>() ||
|
||||
obj->is<ArrayObject>() ||
|
||||
obj->is<UnboxedArrayObject>());
|
||||
isArray = (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) ? 1 : 0;
|
||||
obj->is<ArrayObject>());
|
||||
isArray = (obj->is<ArrayObject>()) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (!xdr->codeUint32(&isArray))
|
||||
@@ -2341,11 +2342,6 @@ js::LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Shape**
|
||||
MarkNonNativePropertyFound<NoGC>(propp);
|
||||
return true;
|
||||
}
|
||||
} else if (obj->is<UnboxedArrayObject>()) {
|
||||
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
|
||||
MarkNonNativePropertyFound<NoGC>(propp);
|
||||
return true;
|
||||
}
|
||||
} else if (obj->is<TypedObject>()) {
|
||||
if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
|
||||
MarkNonNativePropertyFound<NoGC>(propp);
|
||||
@@ -3651,16 +3647,6 @@ JSObject::allocKindForTenure(const js::Nursery& nursery) const
|
||||
return GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + nbytes);
|
||||
}
|
||||
|
||||
// Unboxed arrays use inline data if their size is small enough.
|
||||
if (is<UnboxedArrayObject>()) {
|
||||
const UnboxedArrayObject* nobj = &as<UnboxedArrayObject>();
|
||||
size_t nbytes = UnboxedArrayObject::offsetOfInlineElements() +
|
||||
nobj->capacity() * nobj->elementSize();
|
||||
if (nbytes <= JSObject::MAX_BYTE_SIZE)
|
||||
return GetGCObjectKindForBytes(nbytes);
|
||||
return AllocKind::OBJECT0;
|
||||
}
|
||||
|
||||
// Inlined typed objects are followed by their data, so make sure we copy
|
||||
// it all over to the new object.
|
||||
if (is<InlineTypedObject>()) {
|
||||
|
||||
@@ -40,8 +40,6 @@ MaybeConvertUnboxedObjectToNative(ExclusiveContext* cx, JSObject* obj)
|
||||
{
|
||||
if (obj->is<UnboxedPlainObject>())
|
||||
return UnboxedPlainObject::convertToNative(cx->asJSContext(), obj);
|
||||
if (obj->is<UnboxedArrayObject>())
|
||||
return UnboxedArrayObject::convertToNative(cx->asJSContext(), obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -317,7 +317,6 @@ static bool enableIon = false;
|
||||
static bool enableAsmJS = false;
|
||||
static bool enableWasm = false;
|
||||
static bool enableNativeRegExp = false;
|
||||
static bool enableUnboxedArrays = false;
|
||||
static bool enableSharedMemory = SHARED_MEMORY_DEFAULT;
|
||||
static bool enableWasmAlwaysBaseline = false;
|
||||
static bool enableArrayProtoValues = true;
|
||||
@@ -7263,7 +7262,6 @@ SetContextOptions(JSContext* cx, const OptionParser& op)
|
||||
enableAsmJS = !op.getBoolOption("no-asmjs");
|
||||
enableWasm = !op.getBoolOption("no-wasm");
|
||||
enableNativeRegExp = !op.getBoolOption("no-native-regexp");
|
||||
enableUnboxedArrays = op.getBoolOption("unboxed-arrays");
|
||||
enableWasmAlwaysBaseline = op.getBoolOption("wasm-always-baseline");
|
||||
enableArrayProtoValues = !op.getBoolOption("no-array-proto-values");
|
||||
|
||||
@@ -7273,7 +7271,6 @@ SetContextOptions(JSContext* cx, const OptionParser& op)
|
||||
.setWasm(enableWasm)
|
||||
.setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
|
||||
.setNativeRegExp(enableNativeRegExp)
|
||||
.setUnboxedArrays(enableUnboxedArrays)
|
||||
.setArrayProtoValues(enableArrayProtoValues);
|
||||
|
||||
if (op.getBoolOption("wasm-check-bce"))
|
||||
@@ -7545,7 +7542,6 @@ SetWorkerContextOptions(JSContext* cx)
|
||||
.setWasm(enableWasm)
|
||||
.setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
|
||||
.setNativeRegExp(enableNativeRegExp)
|
||||
.setUnboxedArrays(enableUnboxedArrays)
|
||||
.setArrayProtoValues(enableArrayProtoValues);
|
||||
cx->setOffthreadIonCompilationEnabled(offthreadCompilation);
|
||||
cx->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
|
||||
@@ -7716,7 +7712,6 @@ main(int argc, char** argv, char** envp)
|
||||
|| !op.addBoolOption('\0', "no-wasm", "Disable WebAssembly compilation")
|
||||
|| !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation")
|
||||
|| !op.addBoolOption('\0', "no-unboxed-objects", "Disable creating unboxed plain objects")
|
||||
|| !op.addBoolOption('\0', "unboxed-arrays", "Allow creating unboxed arrays")
|
||||
|| !op.addBoolOption('\0', "wasm-always-baseline", "Enable wasm baseline compiler when possible")
|
||||
|| !op.addBoolOption('\0', "wasm-check-bce", "Always generate wasm bounds check, even redundant ones.")
|
||||
|| !op.addBoolOption('\0', "no-array-proto-values", "Remove Array.prototype.values")
|
||||
|
||||
@@ -598,7 +598,7 @@ InitArrayElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, uint32_t
|
||||
JSOp op = JSOp(*pc);
|
||||
MOZ_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC);
|
||||
|
||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>());
|
||||
|
||||
if (op == JSOP_INITELEM_INC && index == INT32_MAX) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SPREAD_TOO_LARGE);
|
||||
|
||||
@@ -5052,9 +5052,6 @@ js::NewArrayOperation(JSContext* cx, HandleScript script, jsbytecode* pc, uint32
|
||||
|
||||
if (group->shouldPreTenure() || group->maybePreliminaryObjects())
|
||||
newKind = TenuredObject;
|
||||
|
||||
if (group->maybeUnboxedLayout())
|
||||
return UnboxedArrayObject::create(cx, group, length, newKind);
|
||||
}
|
||||
|
||||
ArrayObject* obj = NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
|
||||
@@ -5080,12 +5077,6 @@ js::NewArrayOperationWithTemplate(JSContext* cx, HandleObject templateObject)
|
||||
|
||||
NewObjectKind newKind = templateObject->group()->shouldPreTenure() ? TenuredObject : GenericObject;
|
||||
|
||||
if (templateObject->is<UnboxedArrayObject>()) {
|
||||
uint32_t length = templateObject->as<UnboxedArrayObject>().length();
|
||||
RootedObjectGroup group(cx, templateObject->group());
|
||||
return UnboxedArrayObject::create(cx, group, length, newKind);
|
||||
}
|
||||
|
||||
ArrayObject* obj = NewDenseFullyAllocatedArray(cx, templateObject->as<ArrayObject>().length(),
|
||||
nullptr, newKind);
|
||||
if (!obj)
|
||||
|
||||
@@ -734,10 +734,6 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO
|
||||
// * minimize the number of unused elements beyond an array's length, and
|
||||
// * provide at least SLOT_CAPACITY_MIN elements no matter what (so adding
|
||||
// the first several elements to small arrays only needs one allocation).
|
||||
//
|
||||
// Note: the structure and behavior of this method follow along with
|
||||
// UnboxedArrayObject::chooseCapacityIndex. Changes to the allocation strategy
|
||||
// in one should generally be matched by the other.
|
||||
/* static */ bool
|
||||
NativeObject::goodElementsAllocationAmount(ExclusiveContext* cx, uint32_t reqCapacity,
|
||||
uint32_t length, uint32_t* goodAmount)
|
||||
|
||||
@@ -900,7 +900,7 @@ GiveObjectGroup(ExclusiveContext* cx, JSObject* source, JSObject* target)
|
||||
{
|
||||
MOZ_ASSERT(source->group() != target->group());
|
||||
|
||||
if (!target->is<ArrayObject>() && !target->is<UnboxedArrayObject>())
|
||||
if (!target->is<ArrayObject>())
|
||||
return true;
|
||||
|
||||
if (target->group()->maybePreliminaryObjects()) {
|
||||
@@ -911,11 +911,7 @@ GiveObjectGroup(ExclusiveContext* cx, JSObject* source, JSObject* target)
|
||||
if (target->is<ArrayObject>()) {
|
||||
ObjectGroup* sourceGroup = source->group();
|
||||
|
||||
if (source->is<UnboxedArrayObject>()) {
|
||||
Shape* shape = target->as<ArrayObject>().lastProperty();
|
||||
if (!UnboxedArrayObject::convertToNativeWithGroup(cx, source, target->group(), shape))
|
||||
return false;
|
||||
} else if (source->is<ArrayObject>()) {
|
||||
if (source->is<ArrayObject>()) {
|
||||
source->setGroup(target->group());
|
||||
} else {
|
||||
return true;
|
||||
@@ -934,17 +930,6 @@ GiveObjectGroup(ExclusiveContext* cx, JSObject* source, JSObject* target)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target->is<UnboxedArrayObject>()) {
|
||||
if (!source->is<UnboxedArrayObject>())
|
||||
return true;
|
||||
if (source->as<UnboxedArrayObject>().elementType() != JSVAL_TYPE_INT32)
|
||||
return true;
|
||||
if (target->as<UnboxedArrayObject>().elementType() != JSVAL_TYPE_DOUBLE)
|
||||
return true;
|
||||
|
||||
return source->as<UnboxedArrayObject>().convertInt32ToDouble(cx, target->group());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1506,18 +1491,6 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode*
|
||||
}
|
||||
}
|
||||
|
||||
if (kind == JSProto_Array &&
|
||||
(JSOp(*pc) == JSOP_NEWARRAY || IsCallPC(pc)) &&
|
||||
cx->options().unboxedArrays())
|
||||
{
|
||||
PreliminaryObjectArrayWithTemplate* preliminaryObjects =
|
||||
cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr);
|
||||
if (preliminaryObjects)
|
||||
res->setPreliminaryObjects(preliminaryObjects);
|
||||
else
|
||||
cx->recoverFromOutOfMemory();
|
||||
}
|
||||
|
||||
if (!table->add(p, key, res)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
|
||||
@@ -505,7 +505,7 @@ class ObjectGroup : public gc::TenuredCell
|
||||
UnknownIndex // Make an array with an unknown element type.
|
||||
};
|
||||
|
||||
// Create an ArrayObject or UnboxedArrayObject with the specified elements
|
||||
// Create an ArrayObject with the specified elements
|
||||
// and a group specialized for the elements.
|
||||
static JSObject* newArrayObject(ExclusiveContext* cx, const Value* vp, size_t length,
|
||||
NewObjectKind newKind,
|
||||
|
||||
@@ -20,7 +20,7 @@ ReceiverGuard::ReceiverGuard(JSObject* obj)
|
||||
group = obj->group();
|
||||
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
|
||||
shape = expando->lastProperty();
|
||||
} else if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) {
|
||||
} else if (obj->is<TypedObject>()) {
|
||||
group = obj->group();
|
||||
} else {
|
||||
shape = obj->maybeShape();
|
||||
@@ -35,7 +35,7 @@ ReceiverGuard::ReceiverGuard(ObjectGroup* group, Shape* shape)
|
||||
const Class* clasp = group->clasp();
|
||||
if (clasp == &UnboxedPlainObject::class_) {
|
||||
// Keep both group and shape.
|
||||
} else if (clasp == &UnboxedArrayObject::class_ || IsTypedObjectClass(clasp)) {
|
||||
} else if (IsTypedObjectClass(clasp)) {
|
||||
this->shape = nullptr;
|
||||
} else {
|
||||
this->group = nullptr;
|
||||
@@ -50,8 +50,8 @@ HeapReceiverGuard::keyBits(JSObject* obj)
|
||||
// Both the group and shape need to be guarded for unboxed plain objects.
|
||||
return obj->as<UnboxedPlainObject>().maybeExpando() ? 0 : 1;
|
||||
}
|
||||
if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) {
|
||||
// Only the group needs to be guarded for unboxed arrays and typed objects.
|
||||
if (obj->is<TypedObject>()) {
|
||||
// Only the group needs to be guarded for typed objects.
|
||||
return 2;
|
||||
}
|
||||
// Other objects only need the shape to be guarded.
|
||||
|
||||
@@ -2520,7 +2520,7 @@ TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList* constraints, jsid
|
||||
bool
|
||||
js::ClassCanHaveExtraProperties(const Class* clasp)
|
||||
{
|
||||
if (clasp == &UnboxedPlainObject::class_ || clasp == &UnboxedArrayObject::class_)
|
||||
if (clasp == &UnboxedPlainObject::class_)
|
||||
return false;
|
||||
return clasp->getResolve()
|
||||
|| clasp->getOpsLookupProperty()
|
||||
|
||||
+48
-475
@@ -172,475 +172,129 @@ UnboxedPlainObject::layout() const
|
||||
return group()->unboxedLayout();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// UnboxedArrayObject
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline const UnboxedLayout&
|
||||
UnboxedArrayObject::layout() const
|
||||
{
|
||||
return group()->unboxedLayout();
|
||||
}
|
||||
|
||||
inline void
|
||||
UnboxedArrayObject::setLength(ExclusiveContext* cx, uint32_t length)
|
||||
{
|
||||
if (length > INT32_MAX) {
|
||||
// Track objects with overflowing lengths in type information.
|
||||
MarkObjectGroupFlags(cx, this, OBJECT_FLAG_LENGTH_OVERFLOW);
|
||||
}
|
||||
|
||||
length_ = length;
|
||||
}
|
||||
|
||||
inline void
|
||||
UnboxedArrayObject::setInitializedLength(uint32_t initlen)
|
||||
{
|
||||
if (initlen < initializedLength()) {
|
||||
switch (elementType()) {
|
||||
case JSVAL_TYPE_STRING:
|
||||
for (size_t i = initlen; i < initializedLength(); i++)
|
||||
triggerPreBarrier<JSVAL_TYPE_STRING>(i);
|
||||
break;
|
||||
case JSVAL_TYPE_OBJECT:
|
||||
for (size_t i = initlen; i < initializedLength(); i++)
|
||||
triggerPreBarrier<JSVAL_TYPE_OBJECT>(i);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(elementType()));
|
||||
}
|
||||
}
|
||||
setInitializedLengthNoBarrier(initlen);
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
inline bool
|
||||
UnboxedArrayObject::setElementSpecific(ExclusiveContext* cx, size_t index, const Value& v)
|
||||
{
|
||||
MOZ_ASSERT(index < initializedLength());
|
||||
MOZ_ASSERT(Type == elementType());
|
||||
uint8_t* p = elements() + index * UnboxedTypeSize(Type);
|
||||
return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ true);
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
inline void
|
||||
UnboxedArrayObject::setElementNoTypeChangeSpecific(size_t index, const Value& v)
|
||||
{
|
||||
MOZ_ASSERT(index < initializedLength());
|
||||
MOZ_ASSERT(Type == elementType());
|
||||
uint8_t* p = elements() + index * UnboxedTypeSize(Type);
|
||||
return SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ true);
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
inline bool
|
||||
UnboxedArrayObject::initElementSpecific(ExclusiveContext* cx, size_t index, const Value& v)
|
||||
{
|
||||
MOZ_ASSERT(index < initializedLength());
|
||||
MOZ_ASSERT(Type == elementType());
|
||||
uint8_t* p = elements() + index * UnboxedTypeSize(Type);
|
||||
return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ false);
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
inline void
|
||||
UnboxedArrayObject::initElementNoTypeChangeSpecific(size_t index, const Value& v)
|
||||
{
|
||||
MOZ_ASSERT(index < initializedLength());
|
||||
MOZ_ASSERT(Type == elementType());
|
||||
uint8_t* p = elements() + index * UnboxedTypeSize(Type);
|
||||
return SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ false);
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
inline Value
|
||||
UnboxedArrayObject::getElementSpecific(size_t index)
|
||||
{
|
||||
MOZ_ASSERT(index < initializedLength());
|
||||
MOZ_ASSERT(Type == elementType());
|
||||
uint8_t* p = elements() + index * UnboxedTypeSize(Type);
|
||||
return GetUnboxedValue(p, Type, /* maybeUninitialized = */ false);
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
inline void
|
||||
UnboxedArrayObject::triggerPreBarrier(size_t index)
|
||||
{
|
||||
MOZ_ASSERT(UnboxedTypeNeedsPreBarrier(Type));
|
||||
|
||||
uint8_t* p = elements() + index * UnboxedTypeSize(Type);
|
||||
|
||||
switch (Type) {
|
||||
case JSVAL_TYPE_STRING: {
|
||||
JSString** np = reinterpret_cast<JSString**>(p);
|
||||
JSString::writeBarrierPre(*np);
|
||||
break;
|
||||
}
|
||||
|
||||
case JSVAL_TYPE_OBJECT: {
|
||||
JSObject** np = reinterpret_cast<JSObject**>(p);
|
||||
JSObject::writeBarrierPre(*np);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Bad type");
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Combined methods for NativeObject and UnboxedArrayObject accesses.
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline bool
|
||||
HasAnyBoxedOrUnboxedDenseElements(JSObject* obj)
|
||||
{
|
||||
return obj->isNative() || obj->is<UnboxedArrayObject>();
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
GetAnyBoxedOrUnboxedInitializedLength(JSObject* obj)
|
||||
{
|
||||
if (obj->isNative())
|
||||
return obj->as<NativeObject>().getDenseInitializedLength();
|
||||
if (obj->is<UnboxedArrayObject>())
|
||||
return obj->as<UnboxedArrayObject>().initializedLength();
|
||||
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)
|
||||
{
|
||||
if (obj->isNative())
|
||||
return obj->as<NativeObject>().getDenseElement(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)
|
||||
{
|
||||
if (obj->is<ArrayObject>()) {
|
||||
MOZ_ASSERT(length >= obj->as<ArrayObject>().length());
|
||||
obj->as<ArrayObject>().setLength(cx, length);
|
||||
} else {
|
||||
MOZ_ASSERT(length >= obj->as<UnboxedArrayObject>().length());
|
||||
obj->as<UnboxedArrayObject>().setLength(cx, length);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
SetAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
|
||||
{
|
||||
if (obj->isNative()) {
|
||||
obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
|
||||
return true;
|
||||
}
|
||||
return obj->as<UnboxedArrayObject>().setElement(cx, index, value);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
InitAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
|
||||
{
|
||||
if (obj->isNative()) {
|
||||
obj->as<NativeObject>().initDenseElementWithType(cx, index, value);
|
||||
return true;
|
||||
}
|
||||
return obj->as<UnboxedArrayObject>().initElement(cx, index, value);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Template methods for NativeObject and UnboxedArrayObject accesses.
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline JSValueType
|
||||
GetBoxedOrUnboxedType(JSObject* obj)
|
||||
{
|
||||
if (obj->isNative())
|
||||
return JSVAL_TYPE_MAGIC;
|
||||
return obj->as<UnboxedArrayObject>().elementType();
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline bool
|
||||
HasBoxedOrUnboxedDenseElements(JSObject* obj)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC)
|
||||
return obj->isNative();
|
||||
return obj->is<UnboxedArrayObject>() && obj->as<UnboxedArrayObject>().elementType() == Type;
|
||||
return obj->isNative();
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline size_t
|
||||
GetBoxedOrUnboxedInitializedLength(JSObject* obj)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC)
|
||||
return obj->as<NativeObject>().getDenseInitializedLength();
|
||||
return obj->as<UnboxedArrayObject>().initializedLength();
|
||||
return obj->as<NativeObject>().getDenseInitializedLength();
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline DenseElementResult
|
||||
SetBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen)
|
||||
{
|
||||
size_t oldInitlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
obj->as<NativeObject>().setDenseInitializedLength(initlen);
|
||||
if (initlen < oldInitlen)
|
||||
obj->as<NativeObject>().shrinkElements(cx, initlen);
|
||||
} else {
|
||||
obj->as<UnboxedArrayObject>().setInitializedLength(initlen);
|
||||
if (initlen < oldInitlen)
|
||||
obj->as<UnboxedArrayObject>().shrinkElements(cx, initlen);
|
||||
}
|
||||
size_t oldInitlen = GetBoxedOrUnboxedInitializedLength(obj);
|
||||
obj->as<NativeObject>().setDenseInitializedLength(initlen);
|
||||
if (initlen < oldInitlen)
|
||||
obj->as<NativeObject>().shrinkElements(cx, initlen);
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline size_t
|
||||
GetBoxedOrUnboxedCapacity(JSObject* obj)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC)
|
||||
return obj->as<NativeObject>().getDenseCapacity();
|
||||
return obj->as<UnboxedArrayObject>().capacity();
|
||||
return obj->as<NativeObject>().getDenseCapacity();
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline Value
|
||||
GetBoxedOrUnboxedDenseElement(JSObject* obj, size_t index)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC)
|
||||
return obj->as<NativeObject>().getDenseElement(index);
|
||||
return obj->as<UnboxedArrayObject>().getElementSpecific<Type>(index);
|
||||
return obj->as<NativeObject>().getDenseElement(index);
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline void
|
||||
SetBoxedOrUnboxedDenseElementNoTypeChange(JSObject* obj, size_t index, const Value& value)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC)
|
||||
obj->as<NativeObject>().setDenseElement(index, value);
|
||||
else
|
||||
obj->as<UnboxedArrayObject>().setElementNoTypeChangeSpecific<Type>(index, value);
|
||||
obj->as<NativeObject>().setDenseElement(index, value);
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline bool
|
||||
SetBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
|
||||
return true;
|
||||
}
|
||||
return obj->as<UnboxedArrayObject>().setElementSpecific<Type>(cx, index, value);
|
||||
obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline DenseElementResult
|
||||
EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
if (!obj->as<ArrayObject>().ensureElements(cx, count))
|
||||
return DenseElementResult::Failure;
|
||||
} else {
|
||||
if (obj->as<UnboxedArrayObject>().capacity() < count) {
|
||||
if (!obj->as<UnboxedArrayObject>().growElements(cx, count))
|
||||
return DenseElementResult::Failure;
|
||||
}
|
||||
}
|
||||
if (!obj->as<ArrayObject>().ensureElements(cx, count))
|
||||
return DenseElementResult::Failure;
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline DenseElementResult
|
||||
SetOrExtendBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
|
||||
uint32_t start, const Value* vp, uint32_t count,
|
||||
ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
NativeObject* nobj = &obj->as<NativeObject>();
|
||||
NativeObject* nobj = &obj->as<NativeObject>();
|
||||
|
||||
if (nobj->denseElementsAreFrozen())
|
||||
return DenseElementResult::Incomplete;
|
||||
if (nobj->denseElementsAreFrozen())
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
if (obj->is<ArrayObject>() &&
|
||||
!obj->as<ArrayObject>().lengthIsWritable() &&
|
||||
start + count >= obj->as<ArrayObject>().length())
|
||||
{
|
||||
return DenseElementResult::Incomplete;
|
||||
}
|
||||
|
||||
DenseElementResult result = nobj->ensureDenseElements(cx, start, count);
|
||||
if (result != DenseElementResult::Success)
|
||||
return result;
|
||||
|
||||
if (obj->is<ArrayObject>() && start + count >= obj->as<ArrayObject>().length())
|
||||
obj->as<ArrayObject>().setLengthInt32(start + count);
|
||||
|
||||
if (updateTypes == ShouldUpdateTypes::DontUpdate && !nobj->shouldConvertDoubleElements()) {
|
||||
nobj->copyDenseElements(start, vp, count);
|
||||
} else {
|
||||
for (size_t i = 0; i < count; i++)
|
||||
nobj->setDenseElementWithType(cx, start + i, vp[i]);
|
||||
}
|
||||
|
||||
return DenseElementResult::Success;
|
||||
if (obj->is<ArrayObject>() &&
|
||||
!obj->as<ArrayObject>().lengthIsWritable() &&
|
||||
start + count >= obj->as<ArrayObject>().length())
|
||||
{
|
||||
return DenseElementResult::Incomplete;
|
||||
}
|
||||
|
||||
UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
|
||||
DenseElementResult result = nobj->ensureDenseElements(cx, start, count);
|
||||
if (result != DenseElementResult::Success)
|
||||
return result;
|
||||
|
||||
if (start > nobj->initializedLength())
|
||||
return DenseElementResult::Incomplete;
|
||||
if (obj->is<ArrayObject>() && start + count >= obj->as<ArrayObject>().length())
|
||||
obj->as<ArrayObject>().setLengthInt32(start + count);
|
||||
|
||||
if (start + count >= UnboxedArrayObject::MaximumCapacity)
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
if (start + count > nobj->capacity() && !nobj->growElements(cx, start + count))
|
||||
return DenseElementResult::Failure;
|
||||
|
||||
size_t oldInitlen = nobj->initializedLength();
|
||||
|
||||
// Overwrite any existing elements covered by the new range. If we fail
|
||||
// after this point due to some incompatible type being written to the
|
||||
// object's elements, afterwards the contents will be different from when
|
||||
// we started. The caller must retry the operation using a generic path,
|
||||
// which will overwrite the already-modified elements as well as the ones
|
||||
// that were left alone.
|
||||
size_t i = 0;
|
||||
if (updateTypes == ShouldUpdateTypes::DontUpdate) {
|
||||
for (size_t j = start; i < count && j < oldInitlen; i++, j++)
|
||||
nobj->setElementNoTypeChangeSpecific<Type>(j, vp[i]);
|
||||
if (updateTypes == ShouldUpdateTypes::DontUpdate && !nobj->shouldConvertDoubleElements()) {
|
||||
nobj->copyDenseElements(start, vp, count);
|
||||
} else {
|
||||
for (size_t j = start; i < count && j < oldInitlen; i++, j++) {
|
||||
if (!nobj->setElementSpecific<Type>(cx, j, vp[i]))
|
||||
return DenseElementResult::Incomplete;
|
||||
}
|
||||
for (size_t i = 0; i < count; i++)
|
||||
nobj->setDenseElementWithType(cx, start + i, vp[i]);
|
||||
}
|
||||
|
||||
if (i != count) {
|
||||
obj->as<UnboxedArrayObject>().setInitializedLength(start + count);
|
||||
if (updateTypes == ShouldUpdateTypes::DontUpdate) {
|
||||
for (; i < count; i++)
|
||||
nobj->initElementNoTypeChangeSpecific<Type>(start + i, vp[i]);
|
||||
} else {
|
||||
for (; i < count; i++) {
|
||||
if (!nobj->initElementSpecific<Type>(cx, start + i, vp[i])) {
|
||||
nobj->setInitializedLengthNoBarrier(oldInitlen);
|
||||
return DenseElementResult::Incomplete;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (start + count >= nobj->length())
|
||||
nobj->setLength(cx, start + count);
|
||||
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline DenseElementResult
|
||||
MoveBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, uint32_t dstStart, uint32_t srcStart,
|
||||
uint32_t length)
|
||||
{
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(obj));
|
||||
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
if (obj->as<NativeObject>().denseElementsAreFrozen())
|
||||
return DenseElementResult::Incomplete;
|
||||
if (obj->as<NativeObject>().denseElementsAreFrozen())
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
if (!obj->as<NativeObject>().maybeCopyElementsForWrite(cx))
|
||||
return DenseElementResult::Failure;
|
||||
obj->as<NativeObject>().moveDenseElements(dstStart, srcStart, length);
|
||||
} else {
|
||||
uint8_t* data = obj->as<UnboxedArrayObject>().elements();
|
||||
size_t elementSize = UnboxedTypeSize(Type);
|
||||
|
||||
if (UnboxedTypeNeedsPreBarrier(Type) &&
|
||||
JS::shadow::Zone::asShadowZone(obj->zone())->needsIncrementalBarrier())
|
||||
{
|
||||
// Trigger pre barriers on any elements we are overwriting. See
|
||||
// NativeObject::moveDenseElements. No post barrier is needed as
|
||||
// only whole cell post barriers are used with unboxed objects.
|
||||
for (size_t i = 0; i < length; i++)
|
||||
obj->as<UnboxedArrayObject>().triggerPreBarrier<Type>(dstStart + i);
|
||||
}
|
||||
|
||||
memmove(data + dstStart * elementSize,
|
||||
data + srcStart * elementSize,
|
||||
length * elementSize);
|
||||
}
|
||||
if (!obj->as<NativeObject>().maybeCopyElementsForWrite(cx))
|
||||
return DenseElementResult::Failure;
|
||||
obj->as<NativeObject>().moveDenseElements(dstStart, srcStart, length);
|
||||
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
template <JSValueType DstType, JSValueType SrcType>
|
||||
static inline DenseElementResult
|
||||
CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
|
||||
uint32_t dstStart, uint32_t srcStart, uint32_t length)
|
||||
{
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<SrcType>(src));
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<DstType>(dst));
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<DstType>(dst) == dstStart);
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<SrcType>(src) >= srcStart + length);
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedCapacity<DstType>(dst) >= dstStart + length);
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(src));
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(dst));
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength(dst) == dstStart);
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength(src) >= srcStart + length);
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedCapacity(dst) >= dstStart + length);
|
||||
|
||||
SetBoxedOrUnboxedInitializedLength<DstType>(cx, dst, dstStart + length);
|
||||
SetBoxedOrUnboxedInitializedLength(cx, dst, dstStart + length);
|
||||
|
||||
if (DstType == JSVAL_TYPE_MAGIC) {
|
||||
if (SrcType == JSVAL_TYPE_MAGIC) {
|
||||
const Value* vp = src->as<NativeObject>().getDenseElements() + srcStart;
|
||||
dst->as<NativeObject>().initDenseElements(dstStart, vp, length);
|
||||
} else {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
Value v = GetBoxedOrUnboxedDenseElement<SrcType>(src, srcStart + i);
|
||||
dst->as<NativeObject>().initDenseElement(dstStart + i, v);
|
||||
}
|
||||
}
|
||||
} else if (DstType == SrcType) {
|
||||
uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
|
||||
uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
|
||||
size_t elementSize = UnboxedTypeSize(DstType);
|
||||
|
||||
memcpy(dstData + dstStart * elementSize,
|
||||
srcData + srcStart * elementSize,
|
||||
length * elementSize);
|
||||
|
||||
// Add a store buffer entry if we might have copied a nursery pointer to dst.
|
||||
if (UnboxedTypeNeedsPostBarrier(DstType) && !IsInsideNursery(dst))
|
||||
dst->runtimeFromMainThread()->gc.storeBuffer.putWholeCell(dst);
|
||||
} else if (DstType == JSVAL_TYPE_DOUBLE && SrcType == JSVAL_TYPE_INT32) {
|
||||
uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
|
||||
uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
int32_t v = *reinterpret_cast<int32_t*>(srcData + (srcStart + i) * sizeof(int32_t));
|
||||
*reinterpret_cast<double*>(dstData + (dstStart + i) * sizeof(double)) = v;
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
Value v = GetBoxedOrUnboxedDenseElement<SrcType>(src, srcStart + i);
|
||||
dst->as<UnboxedArrayObject>().initElementNoTypeChangeSpecific<DstType>(dstStart + i, v);
|
||||
}
|
||||
}
|
||||
const Value* vp = src->as<NativeObject>().getDenseElements() + srcStart;
|
||||
dst->as<NativeObject>().initDenseElements(dstStart, vp, length);
|
||||
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
@@ -664,24 +318,9 @@ template <typename F>
|
||||
DenseElementResult
|
||||
CallBoxedOrUnboxedSpecialization(F f, JSObject* obj)
|
||||
{
|
||||
if (!HasAnyBoxedOrUnboxedDenseElements(obj))
|
||||
if (!obj->isNative())
|
||||
return DenseElementResult::Incomplete;
|
||||
switch (GetBoxedOrUnboxedType(obj)) {
|
||||
case JSVAL_TYPE_MAGIC:
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_MAGIC>();
|
||||
case JSVAL_TYPE_BOOLEAN:
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_BOOLEAN>();
|
||||
case JSVAL_TYPE_INT32:
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_INT32>();
|
||||
case JSVAL_TYPE_DOUBLE:
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_DOUBLE>();
|
||||
case JSVAL_TYPE_STRING:
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_STRING>();
|
||||
case JSVAL_TYPE_OBJECT:
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_OBJECT>();
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_MAGIC>();
|
||||
}
|
||||
|
||||
// As above, except the specialization can reflect the unboxed type of two objects.
|
||||
@@ -689,45 +328,10 @@ template <typename F>
|
||||
DenseElementResult
|
||||
CallBoxedOrUnboxedSpecialization(F f, JSObject* obj1, JSObject* obj2)
|
||||
{
|
||||
if (!HasAnyBoxedOrUnboxedDenseElements(obj1) || !HasAnyBoxedOrUnboxedDenseElements(obj2))
|
||||
if (!obj1->isNative() || !obj2->isNative())
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
#define SPECIALIZE_OBJ2(TYPE) \
|
||||
switch (GetBoxedOrUnboxedType(obj2)) { \
|
||||
case JSVAL_TYPE_MAGIC: \
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_MAGIC>(); \
|
||||
case JSVAL_TYPE_BOOLEAN: \
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_BOOLEAN>(); \
|
||||
case JSVAL_TYPE_INT32: \
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_INT32>(); \
|
||||
case JSVAL_TYPE_DOUBLE: \
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_DOUBLE>(); \
|
||||
case JSVAL_TYPE_STRING: \
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_STRING>(); \
|
||||
case JSVAL_TYPE_OBJECT: \
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_OBJECT>(); \
|
||||
default: \
|
||||
MOZ_CRASH(); \
|
||||
}
|
||||
|
||||
switch (GetBoxedOrUnboxedType(obj1)) {
|
||||
case JSVAL_TYPE_MAGIC:
|
||||
SPECIALIZE_OBJ2(JSVAL_TYPE_MAGIC)
|
||||
case JSVAL_TYPE_BOOLEAN:
|
||||
SPECIALIZE_OBJ2(JSVAL_TYPE_BOOLEAN)
|
||||
case JSVAL_TYPE_INT32:
|
||||
SPECIALIZE_OBJ2(JSVAL_TYPE_INT32)
|
||||
case JSVAL_TYPE_DOUBLE:
|
||||
SPECIALIZE_OBJ2(JSVAL_TYPE_DOUBLE)
|
||||
case JSVAL_TYPE_STRING:
|
||||
SPECIALIZE_OBJ2(JSVAL_TYPE_STRING)
|
||||
case JSVAL_TYPE_OBJECT:
|
||||
SPECIALIZE_OBJ2(JSVAL_TYPE_OBJECT)
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
#undef SPECIALIZE_OBJ2
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_MAGIC, JSVAL_TYPE_MAGIC>();
|
||||
}
|
||||
|
||||
#undef DEPENDENT_TEMPLATE_HINT
|
||||
@@ -792,18 +396,6 @@ struct Signature ## Functor { \
|
||||
} \
|
||||
}
|
||||
|
||||
#define DefineBoxedOrUnboxedFunctor6(Signature, A, B, C, D, E, F) \
|
||||
struct Signature ## Functor { \
|
||||
A a; B b; C c; D d; E e; F f; \
|
||||
Signature ## Functor(A a, B b, C c, D d, E e, F f) \
|
||||
: a(a), b(b), c(c), d(d), e(e), f(f) \
|
||||
{} \
|
||||
template <JSValueType Type> \
|
||||
DenseElementResult operator()() { \
|
||||
return Signature<Type>(a, b, c, d, e, f); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define DefineBoxedOrUnboxedFunctorPair6(Signature, A, B, C, D, E, F) \
|
||||
struct Signature ## Functor { \
|
||||
A a; B b; C c; D d; E e; F f; \
|
||||
@@ -816,25 +408,6 @@ struct Signature ## Functor { \
|
||||
} \
|
||||
}
|
||||
|
||||
DenseElementResult
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
|
||||
uint32_t start, const Value* vp, uint32_t count,
|
||||
ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update);
|
||||
|
||||
DenseElementResult
|
||||
MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
|
||||
uint32_t dstStart, uint32_t srcStart, uint32_t length);
|
||||
|
||||
DenseElementResult
|
||||
CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
|
||||
uint32_t dstStart, uint32_t srcStart, uint32_t length);
|
||||
|
||||
void
|
||||
SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen);
|
||||
|
||||
DenseElementResult
|
||||
EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // vm_UnboxedObject_inl_h
|
||||
+4
-780
@@ -945,692 +945,6 @@ const Class UnboxedPlainObject::class_ = {
|
||||
&UnboxedPlainObjectObjectOps
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// UnboxedArrayObject
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <JSValueType Type>
|
||||
DenseElementResult
|
||||
AppendUnboxedDenseElements(UnboxedArrayObject* obj, uint32_t initlen,
|
||||
MutableHandle<GCVector<Value>> values)
|
||||
{
|
||||
for (size_t i = 0; i < initlen; i++)
|
||||
values.infallibleAppend(obj->getElementSpecific<Type>(i));
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor3(AppendUnboxedDenseElements,
|
||||
UnboxedArrayObject*, uint32_t, MutableHandle<GCVector<Value>>);
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj,
|
||||
ObjectGroup* group, Shape* shape)
|
||||
{
|
||||
size_t length = obj->as<UnboxedArrayObject>().length();
|
||||
size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
|
||||
|
||||
Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
|
||||
if (!values.reserve(initlen))
|
||||
return false;
|
||||
|
||||
AppendUnboxedDenseElementsFunctor functor(&obj->as<UnboxedArrayObject>(), initlen, &values);
|
||||
DebugOnly<DenseElementResult> result = CallBoxedOrUnboxedSpecialization(functor, obj);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
|
||||
obj->setGroup(group);
|
||||
|
||||
ArrayObject* aobj = &obj->as<ArrayObject>();
|
||||
aobj->setLastPropertyMakeNative(cx, shape);
|
||||
|
||||
// Make sure there is at least one element, so that this array does not
|
||||
// use emptyObjectElements / emptyObjectElementsShared.
|
||||
if (!aobj->ensureElements(cx, Max<size_t>(initlen, 1)))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(!aobj->getDenseInitializedLength());
|
||||
aobj->setDenseInitializedLength(initlen);
|
||||
aobj->initDenseElements(0, values.begin(), initlen);
|
||||
aobj->setLengthInt32(length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
|
||||
{
|
||||
const UnboxedLayout& layout = obj->as<UnboxedArrayObject>().layout();
|
||||
|
||||
if (!layout.nativeGroup()) {
|
||||
if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return convertToNativeWithGroup(cx, obj, layout.nativeGroup(), layout.nativeShape());
|
||||
}
|
||||
|
||||
bool
|
||||
UnboxedArrayObject::convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group)
|
||||
{
|
||||
MOZ_ASSERT(elementType() == JSVAL_TYPE_INT32);
|
||||
MOZ_ASSERT(group->unboxedLayout().elementType() == JSVAL_TYPE_DOUBLE);
|
||||
|
||||
Vector<int32_t> values(cx);
|
||||
if (!values.reserve(initializedLength()))
|
||||
return false;
|
||||
for (size_t i = 0; i < initializedLength(); i++)
|
||||
values.infallibleAppend(getElementSpecific<JSVAL_TYPE_INT32>(i).toInt32());
|
||||
|
||||
uint8_t* newElements;
|
||||
if (hasInlineElements()) {
|
||||
newElements = AllocateObjectBuffer<uint8_t>(cx, this, capacity() * sizeof(double));
|
||||
} else {
|
||||
newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
|
||||
capacity() * sizeof(int32_t),
|
||||
capacity() * sizeof(double));
|
||||
}
|
||||
if (!newElements)
|
||||
return false;
|
||||
|
||||
setGroup(group);
|
||||
elements_ = newElements;
|
||||
|
||||
for (size_t i = 0; i < initializedLength(); i++)
|
||||
setElementNoTypeChangeSpecific<JSVAL_TYPE_DOUBLE>(i, DoubleValue(values[i]));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ UnboxedArrayObject*
|
||||
UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32_t length,
|
||||
NewObjectKind newKind, uint32_t maxLength)
|
||||
{
|
||||
MOZ_ASSERT(length <= MaximumCapacity);
|
||||
|
||||
MOZ_ASSERT(group->clasp() == &class_);
|
||||
uint32_t elementSize = UnboxedTypeSize(group->unboxedLayout().elementType());
|
||||
uint32_t capacity = Min(length, maxLength);
|
||||
uint32_t nbytes = offsetOfInlineElements() + elementSize * capacity;
|
||||
|
||||
UnboxedArrayObject* res;
|
||||
if (nbytes <= JSObject::MAX_BYTE_SIZE) {
|
||||
gc::AllocKind allocKind = gc::GetGCObjectKindForBytes(nbytes);
|
||||
|
||||
// If there was no provided length information, pick an allocation kind
|
||||
// to accommodate small arrays (as is done for normal native arrays).
|
||||
if (capacity == 0)
|
||||
allocKind = gc::AllocKind::OBJECT8;
|
||||
|
||||
res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, allocKind, newKind);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
res->setInitializedLengthNoBarrier(0);
|
||||
res->setInlineElements();
|
||||
|
||||
size_t actualCapacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize;
|
||||
MOZ_ASSERT(actualCapacity >= capacity);
|
||||
res->setCapacityIndex(exactCapacityIndex(actualCapacity));
|
||||
} else {
|
||||
res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, gc::AllocKind::OBJECT0, newKind);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
res->setInitializedLengthNoBarrier(0);
|
||||
|
||||
uint32_t capacityIndex = (capacity == length)
|
||||
? CapacityMatchesLengthIndex
|
||||
: chooseCapacityIndex(capacity, length);
|
||||
uint32_t actualCapacity = computeCapacity(capacityIndex, length);
|
||||
|
||||
res->elements_ = AllocateObjectBuffer<uint8_t>(cx, res, actualCapacity * elementSize);
|
||||
if (!res->elements_) {
|
||||
// Make the object safe for GC.
|
||||
res->setInlineElements();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
res->setCapacityIndex(capacityIndex);
|
||||
}
|
||||
|
||||
res->setLength(cx, length);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool
|
||||
UnboxedArrayObject::setElement(ExclusiveContext* cx, size_t index, const Value& v)
|
||||
{
|
||||
MOZ_ASSERT(index < initializedLength());
|
||||
uint8_t* p = elements() + index * elementSize();
|
||||
return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ true);
|
||||
}
|
||||
|
||||
bool
|
||||
UnboxedArrayObject::initElement(ExclusiveContext* cx, size_t index, const Value& v)
|
||||
{
|
||||
MOZ_ASSERT(index < initializedLength());
|
||||
uint8_t* p = elements() + index * elementSize();
|
||||
return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ false);
|
||||
}
|
||||
|
||||
void
|
||||
UnboxedArrayObject::initElementNoTypeChange(size_t index, const Value& v)
|
||||
{
|
||||
MOZ_ASSERT(index < initializedLength());
|
||||
uint8_t* p = elements() + index * elementSize();
|
||||
if (UnboxedTypeNeedsPreBarrier(elementType()))
|
||||
*reinterpret_cast<void**>(p) = nullptr;
|
||||
SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ false);
|
||||
}
|
||||
|
||||
Value
|
||||
UnboxedArrayObject::getElement(size_t index)
|
||||
{
|
||||
MOZ_ASSERT(index < initializedLength());
|
||||
uint8_t* p = elements() + index * elementSize();
|
||||
return GetUnboxedValue(p, elementType(), /* maybeUninitialized = */ false);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
UnboxedArrayObject::trace(JSTracer* trc, JSObject* obj)
|
||||
{
|
||||
JSValueType type = obj->as<UnboxedArrayObject>().elementType();
|
||||
if (!UnboxedTypeNeedsPreBarrier(type))
|
||||
return;
|
||||
|
||||
MOZ_ASSERT(obj->as<UnboxedArrayObject>().elementSize() == sizeof(uintptr_t));
|
||||
size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
|
||||
void** elements = reinterpret_cast<void**>(obj->as<UnboxedArrayObject>().elements());
|
||||
|
||||
switch (type) {
|
||||
case JSVAL_TYPE_OBJECT:
|
||||
for (size_t i = 0; i < initlen; i++) {
|
||||
GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(elements + i);
|
||||
TraceNullableEdge(trc, heap, "unboxed_object");
|
||||
}
|
||||
break;
|
||||
|
||||
case JSVAL_TYPE_STRING:
|
||||
for (size_t i = 0; i < initlen; i++) {
|
||||
GCPtrString* heap = reinterpret_cast<GCPtrString*>(elements + i);
|
||||
TraceEdge(trc, heap, "unboxed_string");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
UnboxedArrayObject::objectMoved(JSObject* obj, const JSObject* old)
|
||||
{
|
||||
UnboxedArrayObject& dst = obj->as<UnboxedArrayObject>();
|
||||
const UnboxedArrayObject& src = old->as<UnboxedArrayObject>();
|
||||
|
||||
// Fix up possible inline data pointer.
|
||||
if (src.hasInlineElements())
|
||||
dst.setInlineElements();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
UnboxedArrayObject::finalize(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(!IsInsideNursery(obj));
|
||||
if (!obj->as<UnboxedArrayObject>().hasInlineElements())
|
||||
js_free(obj->as<UnboxedArrayObject>().elements());
|
||||
}
|
||||
|
||||
/* static */ size_t
|
||||
UnboxedArrayObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src,
|
||||
gc::AllocKind allocKind)
|
||||
{
|
||||
UnboxedArrayObject* ndst = &dst->as<UnboxedArrayObject>();
|
||||
UnboxedArrayObject* nsrc = &src->as<UnboxedArrayObject>();
|
||||
MOZ_ASSERT(ndst->elements() == nsrc->elements());
|
||||
|
||||
Nursery& nursery = trc->runtime()->gc.nursery;
|
||||
|
||||
if (!nursery.isInside(nsrc->elements())) {
|
||||
nursery.removeMallocedBuffer(nsrc->elements());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Determine if we can use inline data for the target array. If this is
|
||||
// possible, the nursery will have picked an allocation size that is large
|
||||
// enough.
|
||||
size_t nbytes = nsrc->capacity() * nsrc->elementSize();
|
||||
if (offsetOfInlineElements() + nbytes <= GetGCKindBytes(allocKind)) {
|
||||
ndst->setInlineElements();
|
||||
} else {
|
||||
MOZ_ASSERT(allocKind == gc::AllocKind::OBJECT0);
|
||||
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
uint8_t* data = nsrc->zone()->pod_malloc<uint8_t>(nbytes);
|
||||
if (!data)
|
||||
oomUnsafe.crash("Failed to allocate unboxed array elements while tenuring.");
|
||||
ndst->elements_ = data;
|
||||
}
|
||||
|
||||
PodCopy(ndst->elements(), nsrc->elements(), nsrc->initializedLength() * nsrc->elementSize());
|
||||
|
||||
// Set a forwarding pointer for the element buffers in case they were
|
||||
// preserved on the stack by Ion.
|
||||
bool direct = nsrc->capacity() * nsrc->elementSize() >= sizeof(uintptr_t);
|
||||
nursery.maybeSetForwardingPointer(trc, nsrc->elements(), ndst->elements(), direct);
|
||||
|
||||
return ndst->hasInlineElements() ? 0 : nbytes;
|
||||
}
|
||||
|
||||
// Possible capacities for unboxed arrays. Some of these capacities might seem
|
||||
// a little weird, but were chosen to allow the inline data of objects of each
|
||||
// size to be fully utilized for arrays of the various types on both 32 bit and
|
||||
// 64 bit platforms.
|
||||
//
|
||||
// To find the possible inline capacities, the following script was used:
|
||||
//
|
||||
// var fixedSlotCapacities = [0, 2, 4, 8, 12, 16];
|
||||
// var dataSizes = [1, 4, 8];
|
||||
// var header32 = 4 * 2 + 4 * 2;
|
||||
// var header64 = 8 * 2 + 4 * 2;
|
||||
//
|
||||
// for (var i = 0; i < fixedSlotCapacities.length; i++) {
|
||||
// var nfixed = fixedSlotCapacities[i];
|
||||
// var size32 = 4 * 4 + 8 * nfixed - header32;
|
||||
// var size64 = 8 * 4 + 8 * nfixed - header64;
|
||||
// for (var j = 0; j < dataSizes.length; j++) {
|
||||
// print(size32 / dataSizes[j]);
|
||||
// print(size64 / dataSizes[j]);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
/* static */ const uint32_t
|
||||
UnboxedArrayObject::CapacityArray[] = {
|
||||
UINT32_MAX, // For CapacityMatchesLengthIndex.
|
||||
0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 13, 16, 17, 18, 24, 26, 32, 34, 40, 64, 72, 96, 104, 128, 136,
|
||||
256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288,
|
||||
1048576, 2097152, 3145728, 4194304, 5242880, 6291456, 7340032, 8388608, 9437184, 11534336,
|
||||
13631488, 15728640, 17825792, 20971520, 24117248, 27262976, 31457280, 35651584, 40894464,
|
||||
46137344, 52428800, 59768832, MaximumCapacity
|
||||
};
|
||||
|
||||
static const uint32_t
|
||||
Pow2CapacityIndexes[] = {
|
||||
2, // 1
|
||||
3, // 2
|
||||
5, // 4
|
||||
8, // 8
|
||||
13, // 16
|
||||
18, // 32
|
||||
21, // 64
|
||||
25, // 128
|
||||
27, // 256
|
||||
28, // 512
|
||||
29, // 1024
|
||||
30, // 2048
|
||||
31, // 4096
|
||||
32, // 8192
|
||||
33, // 16384
|
||||
34, // 32768
|
||||
35, // 65536
|
||||
36, // 131072
|
||||
37, // 262144
|
||||
38, // 524288
|
||||
39 // 1048576
|
||||
};
|
||||
|
||||
static const uint32_t MebiCapacityIndex = 39;
|
||||
|
||||
/* static */ uint32_t
|
||||
UnboxedArrayObject::chooseCapacityIndex(uint32_t capacity, uint32_t length)
|
||||
{
|
||||
// Note: the structure and behavior of this method follow along with
|
||||
// NativeObject::goodAllocated. Changes to the allocation strategy in one
|
||||
// should generally be matched by the other.
|
||||
|
||||
// Make sure we have enough space to store all possible values for the capacity index.
|
||||
// This ought to be a static_assert, but MSVC doesn't like that.
|
||||
MOZ_ASSERT(mozilla::ArrayLength(CapacityArray) - 1 <= (CapacityMask >> CapacityShift));
|
||||
|
||||
// The caller should have ensured the capacity is possible for an unboxed array.
|
||||
MOZ_ASSERT(capacity <= MaximumCapacity);
|
||||
|
||||
static const uint32_t Mebi = 1024 * 1024;
|
||||
|
||||
if (capacity <= Mebi) {
|
||||
capacity = mozilla::RoundUpPow2(capacity);
|
||||
|
||||
// When the required capacity is close to the array length, then round
|
||||
// up to the array length itself, as for NativeObject.
|
||||
if (length >= capacity && capacity > (length / 3) * 2)
|
||||
return CapacityMatchesLengthIndex;
|
||||
|
||||
if (capacity < MinimumDynamicCapacity)
|
||||
capacity = MinimumDynamicCapacity;
|
||||
|
||||
uint32_t bit = mozilla::FloorLog2Size(capacity);
|
||||
MOZ_ASSERT(capacity == uint32_t(1 << bit));
|
||||
MOZ_ASSERT(bit <= 20);
|
||||
MOZ_ASSERT(mozilla::ArrayLength(Pow2CapacityIndexes) == 21);
|
||||
|
||||
uint32_t index = Pow2CapacityIndexes[bit];
|
||||
MOZ_ASSERT(CapacityArray[index] == capacity);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(CapacityArray[MebiCapacityIndex] == Mebi);
|
||||
|
||||
for (uint32_t i = MebiCapacityIndex + 1;; i++) {
|
||||
if (CapacityArray[i] >= capacity)
|
||||
return i;
|
||||
}
|
||||
|
||||
MOZ_CRASH("Invalid capacity");
|
||||
}
|
||||
|
||||
/* static */ uint32_t
|
||||
UnboxedArrayObject::exactCapacityIndex(uint32_t capacity)
|
||||
{
|
||||
for (size_t i = CapacityMatchesLengthIndex + 1; i < ArrayLength(CapacityArray); i++) {
|
||||
if (CapacityArray[i] == capacity)
|
||||
return i;
|
||||
}
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
bool
|
||||
UnboxedArrayObject::growElements(ExclusiveContext* cx, size_t cap)
|
||||
{
|
||||
// The caller should have checked if this capacity is possible for an
|
||||
// unboxed array, so the only way this call can fail is from OOM.
|
||||
MOZ_ASSERT(cap <= MaximumCapacity);
|
||||
|
||||
uint32_t oldCapacity = capacity();
|
||||
uint32_t newCapacityIndex = chooseCapacityIndex(cap, length());
|
||||
uint32_t newCapacity = computeCapacity(newCapacityIndex, length());
|
||||
|
||||
MOZ_ASSERT(oldCapacity < cap);
|
||||
MOZ_ASSERT(cap <= newCapacity);
|
||||
|
||||
// The allocation size computation below cannot have integer overflows.
|
||||
JS_STATIC_ASSERT(MaximumCapacity < UINT32_MAX / sizeof(double));
|
||||
|
||||
uint8_t* newElements;
|
||||
if (hasInlineElements()) {
|
||||
newElements = AllocateObjectBuffer<uint8_t>(cx, this, newCapacity * elementSize());
|
||||
if (!newElements)
|
||||
return false;
|
||||
js_memcpy(newElements, elements(), initializedLength() * elementSize());
|
||||
} else {
|
||||
newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
|
||||
oldCapacity * elementSize(),
|
||||
newCapacity * elementSize());
|
||||
if (!newElements)
|
||||
return false;
|
||||
}
|
||||
|
||||
elements_ = newElements;
|
||||
setCapacityIndex(newCapacityIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
UnboxedArrayObject::shrinkElements(ExclusiveContext* cx, size_t cap)
|
||||
{
|
||||
if (hasInlineElements())
|
||||
return;
|
||||
|
||||
uint32_t oldCapacity = capacity();
|
||||
uint32_t newCapacityIndex = chooseCapacityIndex(cap, 0);
|
||||
uint32_t newCapacity = computeCapacity(newCapacityIndex, 0);
|
||||
|
||||
MOZ_ASSERT(cap < oldCapacity);
|
||||
MOZ_ASSERT(cap <= newCapacity);
|
||||
|
||||
if (newCapacity >= oldCapacity)
|
||||
return;
|
||||
|
||||
uint8_t* newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
|
||||
oldCapacity * elementSize(),
|
||||
newCapacity * elementSize());
|
||||
if (!newElements)
|
||||
return;
|
||||
|
||||
elements_ = newElements;
|
||||
setCapacityIndex(newCapacityIndex);
|
||||
}
|
||||
|
||||
bool
|
||||
UnboxedArrayObject::containsProperty(ExclusiveContext* cx, jsid id)
|
||||
{
|
||||
if (JSID_IS_INT(id) && uint32_t(JSID_TO_INT(id)) < initializedLength())
|
||||
return true;
|
||||
if (JSID_IS_ATOM(id) && JSID_TO_ATOM(id) == cx->names().length)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
|
||||
HandleId id, MutableHandleObject objp,
|
||||
MutableHandleShape propp)
|
||||
{
|
||||
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
|
||||
MarkNonNativePropertyFound<CanGC>(propp);
|
||||
objp.set(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->staticPrototype());
|
||||
if (!proto) {
|
||||
objp.set(nullptr);
|
||||
propp.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return LookupProperty(cx, proto, id, objp, propp);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result)
|
||||
{
|
||||
if (JSID_IS_INT(id) && !desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) {
|
||||
UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
|
||||
|
||||
uint32_t index = JSID_TO_INT(id);
|
||||
if (index < nobj->initializedLength()) {
|
||||
if (nobj->setElement(cx, index, desc.value()))
|
||||
return result.succeed();
|
||||
} else if (index == nobj->initializedLength() && index < MaximumCapacity) {
|
||||
if (nobj->initializedLength() == nobj->capacity()) {
|
||||
if (!nobj->growElements(cx, index + 1))
|
||||
return false;
|
||||
}
|
||||
nobj->setInitializedLength(index + 1);
|
||||
if (nobj->initElement(cx, index, desc.value())) {
|
||||
if (nobj->length() <= index)
|
||||
nobj->setLengthInt32(index + 1);
|
||||
return result.succeed();
|
||||
}
|
||||
nobj->setInitializedLengthNoBarrier(index);
|
||||
}
|
||||
}
|
||||
|
||||
if (!convertToNative(cx, obj))
|
||||
return false;
|
||||
|
||||
return DefineProperty(cx, obj, id, desc, result);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
|
||||
{
|
||||
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
|
||||
*foundp = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->staticPrototype());
|
||||
if (!proto) {
|
||||
*foundp = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return HasProperty(cx, proto, id, foundp);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
|
||||
HandleId id, MutableHandleValue vp)
|
||||
{
|
||||
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
|
||||
if (JSID_IS_INT(id))
|
||||
vp.set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id)));
|
||||
else
|
||||
vp.set(Int32Value(obj->as<UnboxedArrayObject>().length()));
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->staticPrototype());
|
||||
if (!proto) {
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
return GetProperty(cx, proto, receiver, id, vp);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
|
||||
HandleValue receiver, ObjectOpResult& result)
|
||||
{
|
||||
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
|
||||
if (receiver.isObject() && obj == &receiver.toObject()) {
|
||||
if (JSID_IS_INT(id)) {
|
||||
if (obj->as<UnboxedArrayObject>().setElement(cx, JSID_TO_INT(id), v))
|
||||
return result.succeed();
|
||||
} else {
|
||||
uint32_t len;
|
||||
if (!CanonicalizeArrayLengthValue(cx, v, &len))
|
||||
return false;
|
||||
UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
|
||||
if (len < nobj->initializedLength()) {
|
||||
nobj->setInitializedLength(len);
|
||||
nobj->shrinkElements(cx, len);
|
||||
}
|
||||
nobj->setLength(cx, len);
|
||||
return result.succeed();
|
||||
}
|
||||
|
||||
if (!convertToNative(cx, obj))
|
||||
return false;
|
||||
return SetProperty(cx, obj, id, v, receiver, result);
|
||||
}
|
||||
|
||||
return SetPropertyByDefining(cx, id, v, receiver, result);
|
||||
}
|
||||
|
||||
return SetPropertyOnProto(cx, obj, id, v, receiver, result);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc)
|
||||
{
|
||||
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
|
||||
if (JSID_IS_INT(id)) {
|
||||
desc.value().set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id)));
|
||||
desc.setAttributes(JSPROP_ENUMERATE);
|
||||
} else {
|
||||
desc.value().set(Int32Value(obj->as<UnboxedArrayObject>().length()));
|
||||
desc.setAttributes(JSPROP_PERMANENT);
|
||||
}
|
||||
desc.object().set(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
desc.object().set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
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);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
|
||||
bool enumerableOnly)
|
||||
{
|
||||
for (size_t i = 0; i < obj->as<UnboxedArrayObject>().initializedLength(); i++) {
|
||||
if (!properties.append(INT_TO_JSID(i)))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!enumerableOnly && !properties.append(NameToId(cx->names().length)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const ClassOps UnboxedArrayObjectClassOps = {
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* setProperty */
|
||||
nullptr, /* enumerate */
|
||||
nullptr, /* resolve */
|
||||
nullptr, /* mayResolve */
|
||||
UnboxedArrayObject::finalize,
|
||||
nullptr, /* call */
|
||||
nullptr, /* hasInstance */
|
||||
nullptr, /* construct */
|
||||
UnboxedArrayObject::trace,
|
||||
};
|
||||
|
||||
static const ClassExtension UnboxedArrayObjectClassExtension = {
|
||||
nullptr, /* weakmapKeyDelegateOp */
|
||||
UnboxedArrayObject::objectMoved
|
||||
};
|
||||
|
||||
static const ObjectOps UnboxedArrayObjectObjectOps = {
|
||||
UnboxedArrayObject::obj_lookupProperty,
|
||||
UnboxedArrayObject::obj_defineProperty,
|
||||
UnboxedArrayObject::obj_hasProperty,
|
||||
UnboxedArrayObject::obj_getProperty,
|
||||
UnboxedArrayObject::obj_setProperty,
|
||||
UnboxedArrayObject::obj_getOwnPropertyDescriptor,
|
||||
UnboxedArrayObject::obj_deleteProperty,
|
||||
nullptr, /* getElements */
|
||||
UnboxedArrayObject::obj_enumerate,
|
||||
nullptr /* funToString */
|
||||
};
|
||||
|
||||
const Class UnboxedArrayObject::class_ = {
|
||||
"Array",
|
||||
Class::NON_NATIVE |
|
||||
JSCLASS_SKIP_NURSERY_FINALIZE |
|
||||
JSCLASS_BACKGROUND_FINALIZE,
|
||||
&UnboxedArrayObjectClassOps,
|
||||
JS_NULL_CLASS_SPEC,
|
||||
&UnboxedArrayObjectClassExtension,
|
||||
&UnboxedArrayObjectObjectOps
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
@@ -1856,31 +1170,6 @@ GetValuesFromPreliminaryArrayObject(ArrayObject* obj, MutableHandle<GCVector<Val
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx,
|
||||
Handle<GCVector<Value>> values, size_t* valueCursor)
|
||||
{
|
||||
MOZ_ASSERT(CapacityArray[1] == 0);
|
||||
setCapacityIndex(1);
|
||||
setInitializedLengthNoBarrier(0);
|
||||
setInlineElements();
|
||||
|
||||
setLength(cx, NextValue(values, valueCursor).toInt32());
|
||||
|
||||
int32_t initlen = NextValue(values, valueCursor).toInt32();
|
||||
if (!initlen)
|
||||
return;
|
||||
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!growElements(cx, initlen))
|
||||
oomUnsafe.crash("UnboxedArrayObject::fillAfterConvert");
|
||||
|
||||
setInitializedLength(initlen);
|
||||
|
||||
for (size_t i = 0; i < size_t(initlen); i++)
|
||||
JS_ALWAYS_TRUE(initElement(cx, i, NextValue(values, valueCursor)));
|
||||
}
|
||||
|
||||
static bool
|
||||
GetValuesFromPreliminaryPlainObject(PlainObject* obj, MutableHandle<GCVector<Value>> values)
|
||||
{
|
||||
@@ -1907,17 +1196,10 @@ js::TryConvertToUnboxedLayout(ExclusiveContext* cx, AutoEnterAnalysis& enter, Sh
|
||||
{
|
||||
bool isArray = !templateShape;
|
||||
|
||||
// Unboxed arrays are nightly only for now. The getenv() call will be
|
||||
// removed when they are on by default. See bug 1153266.
|
||||
if (isArray) {
|
||||
#ifdef NIGHTLY_BUILD
|
||||
if (!getenv("JS_OPTION_USE_UNBOXED_ARRAYS")) {
|
||||
if (!cx->options().unboxedArrays())
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
// Unboxed arrays are a NOP.
|
||||
// All of this will go away so don't worry about removing unreachable code below.
|
||||
return true;
|
||||
#endif
|
||||
} else {
|
||||
if (jit::JitOptions.disableUnboxedObjects)
|
||||
return true;
|
||||
@@ -2022,7 +1304,7 @@ js::TryConvertToUnboxedLayout(ExclusiveContext* cx, AutoEnterAnalysis& enter, Sh
|
||||
// fallible stuff first before modifying any objects.
|
||||
|
||||
// Get an empty shape which we can use for the preliminary objects.
|
||||
const Class* clasp = isArray ? &UnboxedArrayObject::class_ : &UnboxedPlainObject::class_;
|
||||
const Class* clasp = &UnboxedPlainObject::class_;
|
||||
Shape* newShape = EmptyShape::getInitialShape(cx, clasp, group->proto(), 0);
|
||||
if (!newShape) {
|
||||
cx->recoverFromOutOfMemory();
|
||||
@@ -2066,67 +1348,9 @@ js::TryConvertToUnboxedLayout(ExclusiveContext* cx, AutoEnterAnalysis& enter, Sh
|
||||
if (!obj)
|
||||
continue;
|
||||
|
||||
if (isArray)
|
||||
obj->as<UnboxedArrayObject>().fillAfterConvert(cx, values, &valueCursor);
|
||||
else
|
||||
obj->as<UnboxedPlainObject>().fillAfterConvert(cx, values, &valueCursor);
|
||||
obj->as<UnboxedPlainObject>().fillAfterConvert(cx, values, &valueCursor);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(valueCursor == values.length());
|
||||
return true;
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor6(SetOrExtendBoxedOrUnboxedDenseElements,
|
||||
ExclusiveContext*, JSObject*, uint32_t, const Value*, uint32_t,
|
||||
ShouldUpdateTypes);
|
||||
|
||||
DenseElementResult
|
||||
js::SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
|
||||
uint32_t start, const Value* vp, uint32_t count,
|
||||
ShouldUpdateTypes updateTypes)
|
||||
{
|
||||
SetOrExtendBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, start, vp, count, updateTypes);
|
||||
return CallBoxedOrUnboxedSpecialization(functor, obj);
|
||||
};
|
||||
|
||||
DefineBoxedOrUnboxedFunctor5(MoveBoxedOrUnboxedDenseElements,
|
||||
JSContext*, JSObject*, uint32_t, uint32_t, uint32_t);
|
||||
|
||||
DenseElementResult
|
||||
js::MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
|
||||
uint32_t dstStart, uint32_t srcStart, uint32_t length)
|
||||
{
|
||||
MoveBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, dstStart, srcStart, length);
|
||||
return CallBoxedOrUnboxedSpecialization(functor, obj);
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctorPair6(CopyBoxedOrUnboxedDenseElements,
|
||||
JSContext*, JSObject*, JSObject*, uint32_t, uint32_t, uint32_t);
|
||||
|
||||
DenseElementResult
|
||||
js::CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
|
||||
uint32_t dstStart, uint32_t srcStart, uint32_t length)
|
||||
{
|
||||
CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, dstStart, srcStart, length);
|
||||
return CallBoxedOrUnboxedSpecialization(functor, dst, src);
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor3(SetBoxedOrUnboxedInitializedLength,
|
||||
JSContext*, JSObject*, size_t);
|
||||
|
||||
void
|
||||
js::SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen)
|
||||
{
|
||||
SetBoxedOrUnboxedInitializedLengthFunctor functor(cx, obj, initlen);
|
||||
JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor3(EnsureBoxedOrUnboxedDenseElements,
|
||||
JSContext*, JSObject*, size_t);
|
||||
|
||||
DenseElementResult
|
||||
js::EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t initlen)
|
||||
{
|
||||
EnsureBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, initlen);
|
||||
return CallBoxedOrUnboxedSpecialization(functor, obj);
|
||||
}
|
||||
@@ -331,193 +331,6 @@ UnboxedLayout::getAllocKind() const
|
||||
return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size());
|
||||
}
|
||||
|
||||
// Class for an array object using an unboxed representation.
|
||||
class UnboxedArrayObject : public JSObject
|
||||
{
|
||||
// Elements pointer for the object.
|
||||
uint8_t* elements_;
|
||||
|
||||
// The nominal array length. This always fits in an int32_t.
|
||||
uint32_t length_;
|
||||
|
||||
// Value indicating the allocated capacity and initialized length of the
|
||||
// array. The top CapacityBits bits are an index into CapacityArray, which
|
||||
// indicates the elements capacity. The low InitializedLengthBits store the
|
||||
// initialized length of the array.
|
||||
uint32_t capacityIndexAndInitializedLength_;
|
||||
|
||||
// If the elements are inline, they will point here.
|
||||
uint8_t inlineElements_[1];
|
||||
|
||||
public:
|
||||
static const uint32_t CapacityBits = 6;
|
||||
static const uint32_t CapacityShift = 26;
|
||||
|
||||
static const uint32_t CapacityMask = uint32_t(-1) << CapacityShift;
|
||||
static const uint32_t InitializedLengthMask = (1 << CapacityShift) - 1;
|
||||
|
||||
static const uint32_t MaximumCapacity = InitializedLengthMask;
|
||||
static const uint32_t MinimumDynamicCapacity = 8;
|
||||
|
||||
static const uint32_t CapacityArray[];
|
||||
|
||||
// Capacity index which indicates the array's length is also its capacity.
|
||||
static const uint32_t CapacityMatchesLengthIndex = 0;
|
||||
|
||||
private:
|
||||
static inline uint32_t computeCapacity(uint32_t index, uint32_t length) {
|
||||
if (index == CapacityMatchesLengthIndex)
|
||||
return length;
|
||||
return CapacityArray[index];
|
||||
}
|
||||
|
||||
static uint32_t chooseCapacityIndex(uint32_t capacity, uint32_t length);
|
||||
static uint32_t exactCapacityIndex(uint32_t capacity);
|
||||
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
|
||||
HandleId id, MutableHandleObject objp,
|
||||
MutableHandleShape propp);
|
||||
|
||||
static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result);
|
||||
|
||||
static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
|
||||
|
||||
static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
|
||||
HandleId id, MutableHandleValue vp);
|
||||
|
||||
static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
|
||||
HandleValue receiver, ObjectOpResult& result);
|
||||
|
||||
static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc);
|
||||
|
||||
static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
ObjectOpResult& result);
|
||||
|
||||
static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
|
||||
bool enumerableOnly);
|
||||
static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
|
||||
|
||||
inline const UnboxedLayout& layout() const;
|
||||
|
||||
const UnboxedLayout& layoutDontCheckGeneration() const {
|
||||
return group()->unboxedLayoutDontCheckGeneration();
|
||||
}
|
||||
|
||||
JSValueType elementType() const {
|
||||
return layoutDontCheckGeneration().elementType();
|
||||
}
|
||||
|
||||
uint32_t elementSize() const {
|
||||
return UnboxedTypeSize(elementType());
|
||||
}
|
||||
|
||||
static bool convertToNative(JSContext* cx, JSObject* obj);
|
||||
static UnboxedArrayObject* create(ExclusiveContext* cx, HandleObjectGroup group,
|
||||
uint32_t length, NewObjectKind newKind,
|
||||
uint32_t maxLength = MaximumCapacity);
|
||||
|
||||
static bool convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj,
|
||||
ObjectGroup* group, Shape* shape);
|
||||
bool convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group);
|
||||
|
||||
void fillAfterConvert(ExclusiveContext* cx,
|
||||
Handle<GCVector<Value>> values, size_t* valueCursor);
|
||||
|
||||
static void trace(JSTracer* trc, JSObject* object);
|
||||
static void objectMoved(JSObject* obj, const JSObject* old);
|
||||
static void finalize(FreeOp* fop, JSObject* obj);
|
||||
|
||||
static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src,
|
||||
gc::AllocKind allocKind);
|
||||
|
||||
uint8_t* elements() {
|
||||
return elements_;
|
||||
}
|
||||
|
||||
bool hasInlineElements() const {
|
||||
return elements_ == &inlineElements_[0];
|
||||
}
|
||||
|
||||
uint32_t length() const {
|
||||
return length_;
|
||||
}
|
||||
|
||||
uint32_t initializedLength() const {
|
||||
return capacityIndexAndInitializedLength_ & InitializedLengthMask;
|
||||
}
|
||||
|
||||
uint32_t capacityIndex() const {
|
||||
return (capacityIndexAndInitializedLength_ & CapacityMask) >> CapacityShift;
|
||||
}
|
||||
|
||||
uint32_t capacity() const {
|
||||
return computeCapacity(capacityIndex(), length());
|
||||
}
|
||||
|
||||
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);
|
||||
void initElementNoTypeChange(size_t index, const Value& v);
|
||||
Value getElement(size_t index);
|
||||
|
||||
template <JSValueType Type> inline bool setElementSpecific(ExclusiveContext* cx, size_t index,
|
||||
const Value& v);
|
||||
template <JSValueType Type> inline void setElementNoTypeChangeSpecific(size_t index, const Value& v);
|
||||
template <JSValueType Type> inline bool initElementSpecific(ExclusiveContext* cx, size_t index,
|
||||
const Value& v);
|
||||
template <JSValueType Type> inline void initElementNoTypeChangeSpecific(size_t index, const Value& v);
|
||||
template <JSValueType Type> inline Value getElementSpecific(size_t index);
|
||||
template <JSValueType Type> inline void triggerPreBarrier(size_t index);
|
||||
|
||||
bool growElements(ExclusiveContext* cx, size_t cap);
|
||||
void shrinkElements(ExclusiveContext* cx, size_t cap);
|
||||
|
||||
static uint32_t offsetOfElements() {
|
||||
return offsetof(UnboxedArrayObject, elements_);
|
||||
}
|
||||
static uint32_t offsetOfLength() {
|
||||
return offsetof(UnboxedArrayObject, length_);
|
||||
}
|
||||
static uint32_t offsetOfCapacityIndexAndInitializedLength() {
|
||||
return offsetof(UnboxedArrayObject, capacityIndexAndInitializedLength_);
|
||||
}
|
||||
static uint32_t offsetOfInlineElements() {
|
||||
return offsetof(UnboxedArrayObject, inlineElements_);
|
||||
}
|
||||
|
||||
void setLengthInt32(uint32_t length) {
|
||||
MOZ_ASSERT(length <= INT32_MAX);
|
||||
length_ = length;
|
||||
}
|
||||
|
||||
inline void setLength(ExclusiveContext* cx, uint32_t len);
|
||||
inline void setInitializedLength(uint32_t initlen);
|
||||
|
||||
inline void setInitializedLengthNoBarrier(uint32_t initlen) {
|
||||
MOZ_ASSERT(initlen <= InitializedLengthMask);
|
||||
capacityIndexAndInitializedLength_ =
|
||||
(capacityIndexAndInitializedLength_ & CapacityMask) | initlen;
|
||||
}
|
||||
|
||||
private:
|
||||
void setInlineElements() {
|
||||
elements_ = &inlineElements_[0];
|
||||
}
|
||||
|
||||
void setCapacityIndex(uint32_t index) {
|
||||
MOZ_ASSERT(index <= (CapacityMask >> CapacityShift));
|
||||
capacityIndexAndInitializedLength_ =
|
||||
(index << CapacityShift) | initializedLength();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
|
||||
Reference in new issue
Block a user