Revert Remove unboxed arrays.
This commit is contained in:
parent
c9e0d090cd
commit
3f1a99cf0b
|
@ -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>());
|
||||
MOZ_ASSERT(exports->is<ArrayObject>() || exports->is<UnboxedArrayObject>());
|
||||
|
||||
RootedModuleNamespaceObject ns(cx, ModuleNamespaceObject::create(cx, self));
|
||||
if (!ns)
|
||||
|
|
|
@ -6495,8 +6495,8 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
|
|||
}
|
||||
MOZ_ASSERT(idx == count);
|
||||
|
||||
ArrayObject* obj = ObjectGroup::newArrayObject(cx, values.begin(), values.length(),
|
||||
newKind, arrayKind);
|
||||
JSObject* obj = ObjectGroup::newArrayObject(cx, values.begin(), values.length(),
|
||||
newKind, arrayKind);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
|
@ -9623,7 +9623,7 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUs
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!emitArray(args, argc))
|
||||
if (!emitArray(args, argc, JSOP_SPREADCALLARRAY))
|
||||
return false;
|
||||
|
||||
if (optCodeEmitted) {
|
||||
|
@ -10138,11 +10138,11 @@ BytecodeEmitter::emitArrayLiteral(ParseNode* pn)
|
|||
}
|
||||
}
|
||||
|
||||
return emitArray(pn->pn_head, pn->pn_count);
|
||||
return emitArray(pn->pn_head, pn->pn_count, JSOP_NEWARRAY);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count)
|
||||
BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op)
|
||||
{
|
||||
|
||||
/*
|
||||
|
@ -10153,6 +10153,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count)
|
|||
* to avoid dup'ing and popping the array as each element is added, as
|
||||
* JSOP_SETELEM/JSOP_SETPROP would do.
|
||||
*/
|
||||
MOZ_ASSERT(op == JSOP_NEWARRAY || op == JSOP_SPREADCALLARRAY);
|
||||
|
||||
uint32_t nspread = 0;
|
||||
for (ParseNode* elt = pn; elt; elt = elt->pn_next) {
|
||||
|
@ -10173,7 +10174,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count)
|
|||
|
||||
// For arrays with spread, this is a very pessimistic allocation, the
|
||||
// minimum possible final size.
|
||||
if (!emitUint32Operand(JSOP_NEWARRAY, count - nspread)) // ARRAY
|
||||
if (!emitUint32Operand(op, count - nspread)) // ARRAY
|
||||
return false;
|
||||
|
||||
ParseNode* pn2 = pn;
|
||||
|
@ -11314,8 +11315,8 @@ BytecodeEmitter::setSrcNoteOffset(unsigned index, unsigned which, ptrdiff_t offs
|
|||
/* Maybe this offset was already set to a four-byte value. */
|
||||
if (!(*sn & SN_4BYTE_OFFSET_FLAG)) {
|
||||
/* Insert three dummy bytes that will be overwritten shortly. */
|
||||
if (MOZ_UNLIKELY(notes.length() + 3 > MaxSrcNotesLength)) {
|
||||
ReportAllocationOverflow(cx);
|
||||
if (MOZ_UNLIKELY(notes.length() + 3 > MaxSrcNotesLength)) {
|
||||
ReportAllocationOverflow(cx);
|
||||
return false;
|
||||
}
|
||||
jssrcnote dummy = 0;
|
||||
|
|
|
@ -543,7 +543,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
MOZ_MUST_USE bool emitAtomOp(ParseNode* pn, JSOp op);
|
||||
|
||||
MOZ_MUST_USE bool emitArrayLiteral(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count);
|
||||
MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count, JSOp op);
|
||||
MOZ_MUST_USE bool emitArrayComp(ParseNode* pn);
|
||||
|
||||
MOZ_MUST_USE bool emitInternedScopeOp(uint32_t index, JSOp op);
|
||||
|
|
|
@ -2504,6 +2504,8 @@ 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>()) {
|
||||
|
|
|
@ -91,6 +91,10 @@ GetObject(const MDefinition* ins)
|
|||
case MDefinition::Op_Elements:
|
||||
case MDefinition::Op_MaybeCopyElementsForWrite:
|
||||
case MDefinition::Op_MaybeToDoubleElement:
|
||||
case MDefinition::Op_UnboxedArrayLength:
|
||||
case MDefinition::Op_UnboxedArrayInitializedLength:
|
||||
case MDefinition::Op_IncrementUnboxedArrayInitializedLength:
|
||||
case MDefinition::Op_SetUnboxedArrayInitializedLength:
|
||||
case MDefinition::Op_TypedArrayLength:
|
||||
case MDefinition::Op_SetTypedObjectOffset:
|
||||
case MDefinition::Op_SetDisjointTypedElements:
|
||||
|
|
|
@ -787,6 +787,9 @@ BaselineCacheIRCompiler::emitGuardClass()
|
|||
case GuardClassKind::Array:
|
||||
clasp = &ArrayObject::class_;
|
||||
break;
|
||||
case GuardClassKind::UnboxedArray:
|
||||
clasp = &UnboxedArrayObject::class_;
|
||||
break;
|
||||
case GuardClassKind::MappedArguments:
|
||||
clasp = &MappedArgumentsObject::class_;
|
||||
break;
|
||||
|
@ -1000,6 +1003,19 @@ BaselineCacheIRCompiler::emitLoadInt32ArrayLengthResult()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCacheIRCompiler::emitLoadUnboxedArrayLengthResult()
|
||||
{
|
||||
Register obj = allocator.useRegister(masm, reader.objOperandId());
|
||||
masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), R0.scratchReg());
|
||||
masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0);
|
||||
|
||||
// The int32 type was monitored when attaching the stub, so we can
|
||||
// just return.
|
||||
emitReturnFromIC();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCacheIRCompiler::emitLoadArgumentsObjectLengthResult()
|
||||
{
|
||||
|
|
|
@ -2050,7 +2050,13 @@ BaselineCompiler::emit_JSOP_NEWARRAY()
|
|||
return true;
|
||||
}
|
||||
|
||||
typedef ArrayObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject, gc::InitialHeap);
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_SPREADCALLARRAY()
|
||||
{
|
||||
return emit_JSOP_NEWARRAY();
|
||||
}
|
||||
|
||||
typedef JSObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject, gc::InitialHeap);
|
||||
const VMFunction jit::NewArrayCopyOnWriteInfo =
|
||||
FunctionInfo<NewArrayCopyOnWriteFn>(js::NewDenseCopyOnWriteArray, "NewDenseCopyOnWriteArray");
|
||||
|
||||
|
@ -4181,14 +4187,14 @@ BaselineCompiler::emit_JSOP_REST()
|
|||
{
|
||||
frame.syncStack(0);
|
||||
|
||||
ArrayObject* templateObject =
|
||||
JSObject* templateObject =
|
||||
ObjectGroup::newArrayObject(cx, nullptr, 0, TenuredObject,
|
||||
ObjectGroup::NewArrayKind::UnknownIndex);
|
||||
if (!templateObject)
|
||||
return false;
|
||||
|
||||
// Call IC.
|
||||
ICRest_Fallback::Compiler compiler(cx, templateObject);
|
||||
ICRest_Fallback::Compiler compiler(cx, &templateObject->as<ArrayObject>());
|
||||
if (!emitOpIC(compiler.getStub(&stubSpace_)))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -100,6 +100,7 @@ namespace jit {
|
|||
_(JSOP_BITNOT) \
|
||||
_(JSOP_NEG) \
|
||||
_(JSOP_NEWARRAY) \
|
||||
_(JSOP_SPREADCALLARRAY) \
|
||||
_(JSOP_NEWARRAY_COPYONWRITE) \
|
||||
_(JSOP_INITELEM_ARRAY) \
|
||||
_(JSOP_NEWOBJECT) \
|
||||
|
|
|
@ -1375,7 +1375,7 @@ IsNativeDenseElementAccess(HandleObject obj, HandleValue key)
|
|||
static bool
|
||||
IsNativeOrUnboxedDenseElementAccess(HandleObject obj, HandleValue key)
|
||||
{
|
||||
if (!obj->isNative())
|
||||
if (!obj->isNative() && !obj->is<UnboxedArrayObject>())
|
||||
return false;
|
||||
if (key.isInt32() && key.toInt32() >= 0 && !obj->is<TypedArrayObject>())
|
||||
return true;
|
||||
|
@ -1479,6 +1479,20 @@ 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() &&
|
||||
|
@ -2084,6 +2098,56 @@ 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
|
||||
//
|
||||
|
@ -2388,8 +2452,8 @@ CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index,
|
|||
Shape* oldShape, uint32_t oldCapacity, uint32_t oldInitLength,
|
||||
bool* isAddingCaseOut, size_t* protoDepthOut)
|
||||
{
|
||||
uint32_t initLength = obj->as<NativeObject>().getDenseInitializedLength();
|
||||
uint32_t capacity = obj->as<NativeObject>().getDenseCapacity();
|
||||
uint32_t initLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
|
||||
uint32_t capacity = GetAnyBoxedOrUnboxedCapacity(obj);
|
||||
|
||||
*isAddingCaseOut = false;
|
||||
*protoDepthOut = 0;
|
||||
|
@ -2398,6 +2462,10 @@ 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.
|
||||
|
@ -2479,8 +2547,8 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
|
|||
uint32_t oldCapacity = 0;
|
||||
uint32_t oldInitLength = 0;
|
||||
if (index.isInt32() && index.toInt32() >= 0) {
|
||||
oldCapacity = obj->as<NativeObject>().getDenseCapacity();
|
||||
oldInitLength = obj->as<NativeObject>().getDenseInitializedLength();
|
||||
oldCapacity = GetAnyBoxedOrUnboxedCapacity(obj);
|
||||
oldInitLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
|
||||
}
|
||||
|
||||
if (op == JSOP_INITELEM || op == JSOP_INITHIDDENELEM) {
|
||||
|
@ -2818,6 +2886,29 @@ 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);
|
||||
|
@ -3011,6 +3102,40 @@ 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);
|
||||
|
@ -5374,6 +5499,13 @@ GetTemplateObjectForSimd(JSContext* cx, JSFunction* target, MutableHandleObject
|
|||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
EnsureArrayGroupAnalyzed(JSContext* cx, JSObject* obj)
|
||||
{
|
||||
if (PreliminaryObjectArrayWithTemplate* objects = obj->group()->maybePreliminaryObjects())
|
||||
objects->maybeAnalyze(cx, obj->group(), /* forceAnalyze = */ true);
|
||||
}
|
||||
|
||||
static bool
|
||||
GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs& args,
|
||||
MutableHandleObject res, bool* skipAttach)
|
||||
|
@ -5405,7 +5537,10 @@ GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs&
|
|||
// With this and other array templates, analyze the group so that
|
||||
// we don't end up with a template whose structure might change later.
|
||||
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject));
|
||||
return !!res;
|
||||
if (!res)
|
||||
return false;
|
||||
EnsureArrayGroupAnalyzed(cx, res);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5430,7 +5565,10 @@ GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs&
|
|||
return true;
|
||||
}
|
||||
res.set(NewFullyAllocatedArrayTryReuseGroup(cx, obj, 0, TenuredObject));
|
||||
return !!res;
|
||||
if (!res)
|
||||
return false;
|
||||
EnsureArrayGroupAnalyzed(cx, res);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5447,7 +5585,10 @@ GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs&
|
|||
}
|
||||
|
||||
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, 0, TenuredObject));
|
||||
return !!res;
|
||||
if (!res)
|
||||
return false;
|
||||
EnsureArrayGroupAnalyzed(cx, res);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (native == StringConstructor) {
|
||||
|
@ -5760,24 +5901,15 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
|
|||
}
|
||||
|
||||
static bool
|
||||
CopyArray(JSContext* cx, HandleArrayObject arr, MutableHandleValue result)
|
||||
CopyArray(JSContext* cx, HandleObject obj, MutableHandleValue result)
|
||||
{
|
||||
uint32_t length = arr->length();
|
||||
ArrayObject* nobj = NewFullyAllocatedArrayTryReuseGroup(cx, arr, length, TenuredObject);
|
||||
uint32_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
|
||||
JSObject* nobj = NewFullyAllocatedArrayTryReuseGroup(cx, obj, length, TenuredObject);
|
||||
if (!nobj)
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(arr->isNative());
|
||||
MOZ_ASSERT(nobj->isNative());
|
||||
MOZ_ASSERT(nobj->as<NativeObject>().getDenseInitializedLength() == 0);
|
||||
MOZ_ASSERT(arr->as<NativeObject>().getDenseInitializedLength() >= length);
|
||||
MOZ_ASSERT(nobj->as<NativeObject>().getDenseCapacity() >= length);
|
||||
EnsureArrayGroupAnalyzed(cx, nobj);
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, nobj, obj, 0, 0, length);
|
||||
|
||||
nobj->as<NativeObject>().setDenseInitializedLength(length);
|
||||
|
||||
const Value* vp = arr->as<NativeObject>().getDenseElements();
|
||||
nobj->as<NativeObject>().initDenseElements(0, vp, length);
|
||||
|
||||
result.setObject(*nobj);
|
||||
return true;
|
||||
}
|
||||
|
@ -5808,22 +5940,26 @@ TryAttachStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
|
|||
RootedValue arr(cx);
|
||||
|
||||
// Copy the array before storing in stub.
|
||||
if (!CopyArray(cx, obj.as<ArrayObject>(), &arr))
|
||||
if (!CopyArray(cx, obj, &arr))
|
||||
return false;
|
||||
|
||||
// Atomize all elements of the array.
|
||||
RootedArrayObject arrObj(cx, &arr.toObject().as<ArrayObject>());
|
||||
uint32_t initLength = arrObj->length();
|
||||
RootedObject arrObj(cx, &arr.toObject());
|
||||
uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(arrObj);
|
||||
for (uint32_t i = 0; i < initLength; i++) {
|
||||
JSAtom* str = js::AtomizeString(cx, arrObj->getDenseElement(i).toString());
|
||||
JSAtom* str = js::AtomizeString(cx, GetAnyBoxedOrUnboxedDenseElement(arrObj, i).toString());
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
arrObj->setDenseElementWithType(cx, i, StringValue(str));
|
||||
if (!SetAnyBoxedOrUnboxedDenseElement(cx, arrObj, i, StringValue(str))) {
|
||||
// The value could not be stored to an unboxed dense element.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ICCall_StringSplit::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
script->pcToOffset(pc), str, sep, arrObj);
|
||||
script->pcToOffset(pc), str, sep,
|
||||
arr);
|
||||
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
return false;
|
||||
|
@ -6711,7 +6847,7 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
|
|||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*CopyArrayFn)(JSContext*, HandleArrayObject, MutableHandleValue);
|
||||
typedef bool (*CopyArrayFn)(JSContext*, HandleObject, MutableHandleValue);
|
||||
static const VMFunction CopyArrayInfo = FunctionInfo<CopyArrayFn>(CopyArray, "CopyArray");
|
||||
|
||||
bool
|
||||
|
@ -8188,6 +8324,19 @@ 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)
|
||||
|
@ -8563,8 +8712,8 @@ static bool DoRestFallback(JSContext* cx, BaselineFrame* frame, ICRest_Fallback*
|
|||
unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
|
||||
Value* rest = frame->argv() + numFormals;
|
||||
|
||||
ArrayObject* obj = ObjectGroup::newArrayObject(cx, rest, numRest, GenericObject,
|
||||
ObjectGroup::NewArrayKind::UnknownIndex);
|
||||
JSObject* obj = ObjectGroup::newArrayObject(cx, rest, numRest, GenericObject,
|
||||
ObjectGroup::NewArrayKind::UnknownIndex);
|
||||
if (!obj)
|
||||
return false;
|
||||
res.setObject(*obj);
|
||||
|
|
|
@ -892,6 +892,54 @@ 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
|
||||
{
|
||||
|
@ -1067,7 +1115,9 @@ class ICSetElem_DenseOrUnboxedArray : public ICUpdatedStub
|
|||
: ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArray, Engine::Baseline),
|
||||
shape_(cx, shape),
|
||||
group_(cx, group),
|
||||
unboxedType_(JSVAL_TYPE_MAGIC)
|
||||
unboxedType_(shape
|
||||
? JSVAL_TYPE_MAGIC
|
||||
: group->unboxedLayoutDontCheckGeneration().elementType())
|
||||
{}
|
||||
|
||||
ICUpdatedStub* getStub(ICStubSpace* space) {
|
||||
|
@ -1175,7 +1225,9 @@ class ICSetElemDenseOrUnboxedArrayAddCompiler : public ICStubCompiler {
|
|||
: ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd, Engine::Baseline),
|
||||
obj_(cx, obj),
|
||||
protoChainDepth_(protoChainDepth),
|
||||
unboxedType_(JSVAL_TYPE_MAGIC)
|
||||
unboxedType_(obj->is<UnboxedArrayObject>()
|
||||
? obj->as<UnboxedArrayObject>().elementType()
|
||||
: JSVAL_TYPE_MAGIC)
|
||||
{}
|
||||
|
||||
template <size_t ProtoChainDepth>
|
||||
|
@ -2822,10 +2874,10 @@ class ICCall_StringSplit : public ICMonitoredStub
|
|||
uint32_t pcOffset_;
|
||||
GCPtrString expectedStr_;
|
||||
GCPtrString expectedSep_;
|
||||
GCPtrArrayObject templateObject_;
|
||||
GCPtrObject templateObject_;
|
||||
|
||||
ICCall_StringSplit(JitCode* stubCode, ICStub* firstMonitorStub, uint32_t pcOffset, JSString* str,
|
||||
JSString* sep, ArrayObject* templateObject)
|
||||
JSString* sep, JSObject* templateObject)
|
||||
: ICMonitoredStub(ICStub::Call_StringSplit, stubCode, firstMonitorStub),
|
||||
pcOffset_(pcOffset), expectedStr_(str), expectedSep_(sep),
|
||||
templateObject_(templateObject)
|
||||
|
@ -2852,7 +2904,7 @@ class ICCall_StringSplit : public ICMonitoredStub
|
|||
return expectedSep_;
|
||||
}
|
||||
|
||||
GCPtrArrayObject& templateObject() {
|
||||
GCPtrObject& templateObject() {
|
||||
return templateObject_;
|
||||
}
|
||||
|
||||
|
@ -2862,7 +2914,7 @@ class ICCall_StringSplit : public ICMonitoredStub
|
|||
uint32_t pcOffset_;
|
||||
RootedString expectedStr_;
|
||||
RootedString expectedSep_;
|
||||
RootedArrayObject templateObject_;
|
||||
RootedObject templateObject_;
|
||||
|
||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
||||
|
||||
|
@ -2873,13 +2925,13 @@ class ICCall_StringSplit : public ICMonitoredStub
|
|||
|
||||
public:
|
||||
Compiler(JSContext* cx, ICStub* firstMonitorStub, uint32_t pcOffset, HandleString str,
|
||||
HandleString sep, HandleArrayObject templateObject)
|
||||
HandleString sep, HandleValue templateObject)
|
||||
: ICCallStubCompiler(cx, ICStub::Call_StringSplit),
|
||||
firstMonitorStub_(firstMonitorStub),
|
||||
pcOffset_(pcOffset),
|
||||
expectedStr_(cx, str),
|
||||
expectedSep_(cx, sep),
|
||||
templateObject_(cx, templateObject)
|
||||
templateObject_(cx, &templateObject.toObject())
|
||||
{ }
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
|
|
|
@ -580,7 +580,7 @@ BaselineInspector::getTemplateObjectForNative(jsbytecode* pc, Native native)
|
|||
|
||||
bool
|
||||
BaselineInspector::isOptimizableCallStringSplit(jsbytecode* pc, JSString** strOut, JSString** sepOut,
|
||||
ArrayObject** objOut)
|
||||
JSObject** objOut)
|
||||
{
|
||||
if (!hasBaselineScript())
|
||||
return false;
|
||||
|
|
|
@ -113,7 +113,7 @@ class BaselineInspector
|
|||
bool hasSeenNonStringIterMore(jsbytecode* pc);
|
||||
|
||||
MOZ_MUST_USE bool isOptimizableCallStringSplit(jsbytecode* pc, JSString** strOut,
|
||||
JSString** sepOut, ArrayObject** objOut);
|
||||
JSString** sepOut, JSObject** objOut);
|
||||
JSObject* getTemplateObject(jsbytecode* pc);
|
||||
JSObject* getTemplateObjectForNative(jsbytecode* pc, Native native);
|
||||
JSObject* getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp);
|
||||
|
|
|
@ -175,7 +175,7 @@ TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, Shape* shape, ObjOper
|
|||
} else {
|
||||
writer.guardNoUnboxedExpando(objId);
|
||||
}
|
||||
} else if (obj->is<TypedObject>()) {
|
||||
} else if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) {
|
||||
writer.guardGroup(objId, obj->group());
|
||||
} else {
|
||||
Shape* shape = obj->maybeShape();
|
||||
|
@ -368,6 +368,13 @@ 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);
|
||||
|
|
|
@ -96,6 +96,7 @@ class ObjOperandId : public OperandId
|
|||
_(LoadUnboxedPropertyResult) \
|
||||
_(LoadTypedObjectResult) \
|
||||
_(LoadInt32ArrayLengthResult) \
|
||||
_(LoadUnboxedArrayLengthResult) \
|
||||
_(LoadArgumentsObjectLengthResult) \
|
||||
_(LoadUndefinedResult)
|
||||
|
||||
|
@ -127,6 +128,7 @@ struct StubField {
|
|||
enum class GuardClassKind
|
||||
{
|
||||
Array,
|
||||
UnboxedArray,
|
||||
MappedArguments,
|
||||
UnmappedArguments,
|
||||
};
|
||||
|
@ -325,6 +327,9 @@ class MOZ_RAII CacheIRWriter
|
|||
void loadInt32ArrayLengthResult(ObjOperandId obj) {
|
||||
writeOpWithOperandId(CacheOp::LoadInt32ArrayLengthResult, obj);
|
||||
}
|
||||
void loadUnboxedArrayLengthResult(ObjOperandId obj) {
|
||||
writeOpWithOperandId(CacheOp::LoadUnboxedArrayLengthResult, obj);
|
||||
}
|
||||
void loadArgumentsObjectLengthResult(ObjOperandId obj) {
|
||||
writeOpWithOperandId(CacheOp::LoadArgumentsObjectLengthResult, obj);
|
||||
}
|
||||
|
|
|
@ -3184,7 +3184,9 @@ CodeGenerator::visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins)
|
|||
void
|
||||
CodeGenerator::visitElements(LElements* lir)
|
||||
{
|
||||
Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements());
|
||||
Address elements(ToRegister(lir->object()),
|
||||
lir->mir()->unboxed() ? UnboxedArrayObject::offsetOfElements()
|
||||
: NativeObject::offsetOfElements());
|
||||
masm.loadPtr(elements, ToRegister(lir->output()));
|
||||
}
|
||||
|
||||
|
@ -5169,11 +5171,11 @@ static JSObject*
|
|||
NewArrayWithGroup(JSContext* cx, uint32_t length, HandleObjectGroup group,
|
||||
bool convertDoubleElements)
|
||||
{
|
||||
ArrayObject* res = NewFullyAllocatedArrayTryUseGroup(cx, group, length);
|
||||
JSObject* res = NewFullyAllocatedArrayTryUseGroup(cx, group, length);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
if (convertDoubleElements)
|
||||
res->setShouldConvertDoubleElements();
|
||||
res->as<ArrayObject>().setShouldConvertDoubleElements();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -5319,7 +5321,7 @@ CodeGenerator::visitNewArrayCopyOnWrite(LNewArrayCopyOnWrite* lir)
|
|||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
||||
typedef ArrayObject* (*ArrayConstructorOneArgFn)(JSContext*, HandleObjectGroup, int32_t length);
|
||||
typedef JSObject* (*ArrayConstructorOneArgFn)(JSContext*, HandleObjectGroup, int32_t length);
|
||||
static const VMFunction ArrayConstructorOneArgInfo =
|
||||
FunctionInfo<ArrayConstructorOneArgFn>(ArrayConstructorOneArg, "ArrayConstructorOneArg");
|
||||
|
||||
|
@ -5339,11 +5341,21 @@ CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir)
|
|||
|
||||
bool canInline = true;
|
||||
size_t inlineLength = 0;
|
||||
if (templateObject->as<ArrayObject>().hasFixedElements()) {
|
||||
size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind());
|
||||
inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER;
|
||||
if (templateObject->is<ArrayObject>()) {
|
||||
if (templateObject->as<ArrayObject>().hasFixedElements()) {
|
||||
size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind());
|
||||
inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER;
|
||||
} else {
|
||||
canInline = false;
|
||||
}
|
||||
} else {
|
||||
canInline = false;
|
||||
if (templateObject->as<UnboxedArrayObject>().hasInlineElements()) {
|
||||
size_t nbytes =
|
||||
templateObject->tenuredSizeOfThis() - UnboxedArrayObject::offsetOfInlineElements();
|
||||
inlineLength = nbytes / templateObject->as<UnboxedArrayObject>().elementSize();
|
||||
} else {
|
||||
canInline = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (canInline) {
|
||||
|
@ -7765,7 +7777,7 @@ CodeGenerator::visitSinCos(LSinCos *lir)
|
|||
masm.freeStack(sizeof(double) * 2);
|
||||
}
|
||||
|
||||
typedef ArrayObject* (*StringSplitFn)(JSContext*, HandleObjectGroup, HandleString, HandleString, uint32_t);
|
||||
typedef JSObject* (*StringSplitFn)(JSContext*, HandleObjectGroup, HandleString, HandleString, uint32_t);
|
||||
static const VMFunction StringSplitInfo =
|
||||
FunctionInfo<StringSplitFn>(js::str_split_string, "str_split_string");
|
||||
|
||||
|
@ -7799,6 +7811,49 @@ CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir)
|
|||
masm.dec32(&index);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitUnboxedArrayLength(LUnboxedArrayLength* lir)
|
||||
{
|
||||
Register obj = ToRegister(lir->object());
|
||||
Register result = ToRegister(lir->output());
|
||||
masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), result);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir)
|
||||
{
|
||||
Register obj = ToRegister(lir->object());
|
||||
masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitNotO(LNotO* lir)
|
||||
{
|
||||
|
@ -8095,19 +8150,46 @@ CodeGenerator::emitStoreElementHoleT(T* lir)
|
|||
OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir);
|
||||
addOutOfLineCode(ool, lir->mir());
|
||||
|
||||
Register obj = ToRegister(lir->object());
|
||||
Register elements = ToRegister(lir->elements());
|
||||
const LAllocation* index = lir->index();
|
||||
RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index);
|
||||
|
||||
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry());
|
||||
JSValueType unboxedType = lir->mir()->unboxedType();
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry());
|
||||
|
||||
if (lir->mir()->needsBarrier())
|
||||
emitPreBarrier(elements, index, 0);
|
||||
if (lir->mir()->needsBarrier())
|
||||
emitPreBarrier(elements, index, 0);
|
||||
|
||||
masm.bind(ool->rejoinStore());
|
||||
emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(),
|
||||
elements, index, 0);
|
||||
masm.bind(ool->rejoinStore());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
@ -8127,22 +8209,47 @@ CodeGenerator::emitStoreElementHoleV(T* lir)
|
|||
OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir);
|
||||
addOutOfLineCode(ool, lir->mir());
|
||||
|
||||
Register obj = ToRegister(lir->object());
|
||||
Register elements = ToRegister(lir->elements());
|
||||
const LAllocation* index = lir->index();
|
||||
const ValueOperand value = ToValue(lir, T::Value);
|
||||
RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index);
|
||||
|
||||
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry());
|
||||
JSValueType unboxedType = lir->mir()->unboxedType();
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry());
|
||||
|
||||
if (lir->mir()->needsBarrier())
|
||||
emitPreBarrier(elements, index, 0);
|
||||
if (lir->mir()->needsBarrier())
|
||||
emitPreBarrier(elements, index, 0);
|
||||
|
||||
masm.bind(ool->rejoinStore());
|
||||
if (index->isConstant())
|
||||
masm.storeValue(value, Address(elements, ToInt32(index) * sizeof(js::Value)));
|
||||
else
|
||||
masm.storeValue(value, BaseIndex(elements, ToRegister(index), TimesEight));
|
||||
masm.bind(ool->rejoinStore());
|
||||
if (index->isConstant())
|
||||
masm.storeValue(value, Address(elements, ToInt32(index) * sizeof(js::Value)));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
@ -8213,10 +8320,11 @@ CodeGenerator::visitFallibleStoreElementV(LFallibleStoreElementV* lir)
|
|||
masm.bind(&isFrozen);
|
||||
}
|
||||
|
||||
typedef bool (*SetDenseElementFn)(JSContext*, HandleNativeObject, int32_t, HandleValue,
|
||||
bool strict);
|
||||
static const VMFunction SetDenseElementInfo =
|
||||
FunctionInfo<SetDenseElementFn>(jit::SetDenseElement, "SetDenseElement");
|
||||
typedef bool (*SetDenseOrUnboxedArrayElementFn)(JSContext*, HandleObject, int32_t,
|
||||
HandleValue, bool strict);
|
||||
static const VMFunction SetDenseOrUnboxedArrayElementInfo =
|
||||
FunctionInfo<SetDenseOrUnboxedArrayElementFn>(SetDenseOrUnboxedArrayElement,
|
||||
"SetDenseOrUnboxedArrayElement");
|
||||
|
||||
void
|
||||
CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
||||
|
@ -8226,6 +8334,8 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
|||
const LAllocation* index;
|
||||
MIRType valueType;
|
||||
ConstantOrRegister value;
|
||||
JSValueType unboxedType;
|
||||
LDefinition *temp = nullptr;
|
||||
|
||||
if (ins->isStoreElementHoleV()) {
|
||||
LStoreElementHoleV* store = ins->toStoreElementHoleV();
|
||||
|
@ -8234,6 +8344,8 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
|||
index = store->index();
|
||||
valueType = store->mir()->value()->type();
|
||||
value = TypedOrValueRegister(ToValue(store, LStoreElementHoleV::Value));
|
||||
unboxedType = store->mir()->unboxedType();
|
||||
temp = store->getTemp(0);
|
||||
} else if (ins->isFallibleStoreElementV()) {
|
||||
LFallibleStoreElementV* store = ins->toFallibleStoreElementV();
|
||||
object = ToRegister(store->object());
|
||||
|
@ -8241,6 +8353,8 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
|||
index = store->index();
|
||||
valueType = store->mir()->value()->type();
|
||||
value = TypedOrValueRegister(ToValue(store, LFallibleStoreElementV::Value));
|
||||
unboxedType = store->mir()->unboxedType();
|
||||
temp = store->getTemp(0);
|
||||
} else if (ins->isStoreElementHoleT()) {
|
||||
LStoreElementHoleT* store = ins->toStoreElementHoleT();
|
||||
object = ToRegister(store->object());
|
||||
|
@ -8251,6 +8365,8 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
|||
value = ConstantOrRegister(store->value()->toConstant()->toJSValue());
|
||||
else
|
||||
value = TypedOrValueRegister(valueType, ToAnyRegister(store->value()));
|
||||
unboxedType = store->mir()->unboxedType();
|
||||
temp = store->getTemp(0);
|
||||
} else { // ins->isFallibleStoreElementT()
|
||||
LFallibleStoreElementT* store = ins->toFallibleStoreElementT();
|
||||
object = ToRegister(store->object());
|
||||
|
@ -8261,6 +8377,8 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
|||
value = ConstantOrRegister(store->value()->toConstant()->toJSValue());
|
||||
else
|
||||
value = TypedOrValueRegister(valueType, ToAnyRegister(store->value()));
|
||||
unboxedType = store->mir()->unboxedType();
|
||||
temp = store->getTemp(0);
|
||||
}
|
||||
|
||||
RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index);
|
||||
|
@ -8271,32 +8389,54 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
|||
Label callStub;
|
||||
#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
// Had to reimplement for MIPS because there are no flags.
|
||||
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branch32(Assembler::NotEqual, initLength, key, &callStub);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
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);
|
||||
}
|
||||
#else
|
||||
masm.j(Assembler::NotEqual, &callStub);
|
||||
#endif
|
||||
|
||||
// Check array capacity.
|
||||
masm.branch32(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()),
|
||||
key, &callStub);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
// Check array capacity.
|
||||
masm.branch32(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()),
|
||||
key, &callStub);
|
||||
|
||||
// Update initialized length. The capacity guard above ensures this won't overflow,
|
||||
// due to MAX_DENSE_ELEMENTS_COUNT.
|
||||
masm.inc32(&key);
|
||||
masm.store32(key, Address(elements, ObjectElements::offsetOfInitializedLength()));
|
||||
// Update initialized length. The capacity guard above ensures this won't overflow,
|
||||
// due to MAX_DENSE_ELEMENTS_COUNT.
|
||||
masm.inc32(&key);
|
||||
masm.store32(key, Address(elements, ObjectElements::offsetOfInitializedLength()));
|
||||
|
||||
// Update length if length < initializedLength.
|
||||
Label dontUpdate;
|
||||
masm.branch32(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()),
|
||||
key, &dontUpdate);
|
||||
masm.store32(key, Address(elements, ObjectElements::offsetOfLength()));
|
||||
masm.bind(&dontUpdate);
|
||||
// Update length if length < initializedLength.
|
||||
Label dontUpdate;
|
||||
masm.branch32(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()),
|
||||
key, &dontUpdate);
|
||||
masm.store32(key, Address(elements, ObjectElements::offsetOfLength()));
|
||||
masm.bind(&dontUpdate);
|
||||
|
||||
masm.dec32(&key);
|
||||
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);
|
||||
}
|
||||
|
||||
if ((ins->isStoreElementHoleT() || ins->isFallibleStoreElementT()) &&
|
||||
valueType != MIRType::Double)
|
||||
unboxedType == JSVAL_TYPE_MAGIC && valueType != MIRType::Double)
|
||||
{
|
||||
// The inline path for StoreElementHoleT and FallibleStoreElementT does not always store
|
||||
// the type tag, so we do the store on the OOL path. We use MIRType::None for the element
|
||||
|
@ -8325,7 +8465,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
|||
else
|
||||
pushArg(ToRegister(index));
|
||||
pushArg(object);
|
||||
callVM(SetDenseElementInfo, ins);
|
||||
callVM(SetDenseOrUnboxedArrayElementInfo, ins);
|
||||
|
||||
restoreLive(ins);
|
||||
masm.jump(ool->rejoin());
|
||||
|
@ -8386,6 +8526,9 @@ 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");
|
||||
|
||||
typedef bool (*ArrayPopShiftFn)(JSContext*, HandleObject, MutableHandleValue);
|
||||
static const VMFunction ArrayPopDenseInfo =
|
||||
|
@ -8411,11 +8554,20 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
|
|||
|
||||
// Load elements and length, and VM call if length != initializedLength.
|
||||
RegisterOrInt32Constant key = RegisterOrInt32Constant(lengthTemp);
|
||||
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
|
||||
masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp);
|
||||
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
|
||||
masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp);
|
||||
|
||||
Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branch32(Assembler::NotEqual, initLength, key, ool->entry());
|
||||
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());
|
||||
}
|
||||
|
||||
// Test for length != 0. On zero length either take a VM call or generate
|
||||
// an undefined value, depending on whether the call is known to produce
|
||||
|
@ -8427,10 +8579,13 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
|
|||
|
||||
// According to the spec we need to set the length 0 (which is already 0).
|
||||
// This is observable when the array length is made non-writable.
|
||||
// Handle this case in the OOL.
|
||||
Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
|
||||
Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
|
||||
masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
|
||||
// Handle this case in the OOL. When freezing an unboxed array it is converted
|
||||
// to an normal array.
|
||||
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
|
||||
Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
|
||||
masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
|
||||
}
|
||||
|
||||
masm.moveValue(UndefinedValue(), out.valueReg());
|
||||
masm.jump(&done);
|
||||
|
@ -8442,25 +8597,41 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
|
|||
masm.dec32(&key);
|
||||
|
||||
if (mir->mode() == MArrayPopShift::Pop) {
|
||||
BaseIndex addr(elementsTemp, lengthTemp, TimesEight);
|
||||
masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry());
|
||||
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
BaseIndex addr(elementsTemp, lengthTemp, TimesEight);
|
||||
masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry());
|
||||
} else {
|
||||
size_t elemSize = UnboxedTypeSize(mir->unboxedType());
|
||||
BaseIndex addr(elementsTemp, lengthTemp, ScaleFromElemWidth(elemSize));
|
||||
masm.loadUnboxedProperty(addr, mir->unboxedType(), out);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(mir->mode() == MArrayPopShift::Shift);
|
||||
Address addr(elementsTemp, 0);
|
||||
masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry());
|
||||
if (mir->unboxedType() == JSVAL_TYPE_MAGIC)
|
||||
masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry());
|
||||
else
|
||||
masm.loadUnboxedProperty(addr, mir->unboxedType(), out);
|
||||
}
|
||||
|
||||
// Handle the failure case when the array length is non-writable in the
|
||||
// OOL path. (Unlike in the adding-an-element cases, we can't rely on the
|
||||
// capacity <= length invariant for such arrays to avoid an explicit
|
||||
// check.)
|
||||
Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
|
||||
Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
|
||||
masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
|
||||
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
// Handle the failure case when the array length is non-writable in the
|
||||
// OOL path. (Unlike in the adding-an-element cases, we can't rely on the
|
||||
// capacity <= length invariant for such arrays to avoid an explicit
|
||||
// check.)
|
||||
Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
|
||||
Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
|
||||
masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
|
||||
|
||||
// Now adjust length and initializedLength.
|
||||
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength()));
|
||||
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
|
||||
// Now adjust length and initializedLength.
|
||||
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength()));
|
||||
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
|
||||
} else {
|
||||
// Unboxed arrays always have writable lengths. Adjust length and
|
||||
// initializedLength.
|
||||
masm.store32(lengthTemp, Address(obj, UnboxedArrayObject::offsetOfLength()));
|
||||
masm.add32(Imm32(-1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
|
||||
}
|
||||
|
||||
if (mir->mode() == MArrayPopShift::Shift) {
|
||||
// Don't save the temp registers.
|
||||
|
@ -8499,7 +8670,7 @@ CodeGenerator::visitArrayPopShiftT(LArrayPopShiftT* lir)
|
|||
emitArrayPopShift(lir, lir->mir(), obj, elements, length, out);
|
||||
}
|
||||
|
||||
typedef bool (*ArrayPushDenseFn)(JSContext*, HandleArrayObject, HandleValue, uint32_t*);
|
||||
typedef bool (*ArrayPushDenseFn)(JSContext*, HandleObject, HandleValue, uint32_t*);
|
||||
static const VMFunction ArrayPushDenseInfo =
|
||||
FunctionInfo<ArrayPushDenseFn>(jit::ArrayPushDense, "ArrayPushDense");
|
||||
|
||||
|
@ -8510,27 +8681,50 @@ CodeGenerator::emitArrayPush(LInstruction* lir, const MArrayPush* mir, Register
|
|||
OutOfLineCode* ool = oolCallVM(ArrayPushDenseInfo, lir, ArgList(obj, value), StoreRegisterTo(length));
|
||||
|
||||
RegisterOrInt32Constant key = RegisterOrInt32Constant(length);
|
||||
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
// Load elements and length.
|
||||
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
|
||||
masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
|
||||
|
||||
// Load elements and length.
|
||||
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
|
||||
masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
|
||||
// Guard length == initializedLength.
|
||||
Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branch32(Assembler::NotEqual, initLength, key, ool->entry());
|
||||
|
||||
// Guard length == initializedLength.
|
||||
Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branch32(Assembler::NotEqual, initLength, key, ool->entry());
|
||||
// Guard length < capacity.
|
||||
Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
|
||||
masm.branch32(Assembler::BelowOrEqual, capacity, key, ool->entry());
|
||||
|
||||
// Guard length < capacity.
|
||||
Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
|
||||
masm.branch32(Assembler::BelowOrEqual, capacity, key, ool->entry());
|
||||
// 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);
|
||||
|
||||
// Do the store.
|
||||
masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight));
|
||||
// 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);
|
||||
}
|
||||
|
||||
masm.inc32(&key);
|
||||
|
||||
// Update length and initialized length.
|
||||
masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
|
||||
masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
|
||||
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
|
||||
masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
|
||||
} else {
|
||||
masm.store32(length, Address(obj, UnboxedArrayObject::offsetOfLength()));
|
||||
masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
|
||||
}
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
@ -10357,11 +10551,22 @@ CodeGenerator::visitLoadElementHole(LLoadElementHole* lir)
|
|||
else
|
||||
masm.branch32(Assembler::BelowOrEqual, initLength, ToRegister(lir->index()), &undefined);
|
||||
|
||||
if (lir->index()->isConstant()) {
|
||||
NativeObject::elementsSizeMustNotOverflow();
|
||||
masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out);
|
||||
if (mir->unboxedType() != JSVAL_TYPE_MAGIC) {
|
||||
size_t width = UnboxedTypeSize(mir->unboxedType());
|
||||
if (lir->index()->isConstant()) {
|
||||
Address addr(elements, ToInt32(lir->index()) * width);
|
||||
masm.loadUnboxedProperty(addr, mir->unboxedType(), out);
|
||||
} else {
|
||||
BaseIndex addr(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
|
||||
masm.loadUnboxedProperty(addr, mir->unboxedType(), out);
|
||||
}
|
||||
} else {
|
||||
masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out);
|
||||
if (lir->index()->isConstant()) {
|
||||
NativeObject::elementsSizeMustNotOverflow();
|
||||
masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out);
|
||||
} else {
|
||||
masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out);
|
||||
}
|
||||
}
|
||||
|
||||
// If a hole check is needed, and the value wasn't a hole, we're done.
|
||||
|
@ -10739,7 +10944,7 @@ CodeGenerator::visitInArray(LInArray* lir)
|
|||
}
|
||||
|
||||
masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index), failedInitLength);
|
||||
if (mir->needsHoleCheck()) {
|
||||
if (mir->needsHoleCheck() && mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
NativeObject::elementsSizeMustNotOverflow();
|
||||
Address address = Address(elements, index * sizeof(Value));
|
||||
masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
|
||||
|
@ -10752,7 +10957,7 @@ CodeGenerator::visitInArray(LInArray* lir)
|
|||
failedInitLength = &negativeIntCheck;
|
||||
|
||||
masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength);
|
||||
if (mir->needsHoleCheck()) {
|
||||
if (mir->needsHoleCheck() && mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
BaseIndex address = BaseIndex(elements, ToRegister(lir->index()), TimesEight);
|
||||
masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
|
||||
}
|
||||
|
|
|
@ -234,6 +234,10 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
|||
void visitSubstr(LSubstr* lir);
|
||||
void visitInitializedLength(LInitializedLength* lir);
|
||||
void visitSetInitializedLength(LSetInitializedLength* lir);
|
||||
void visitUnboxedArrayLength(LUnboxedArrayLength* lir);
|
||||
void visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir);
|
||||
void visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir);
|
||||
void visitSetUnboxedArrayInitializedLength(LSetUnboxedArrayInitializedLength* lir);
|
||||
void visitNotO(LNotO* ins);
|
||||
void visitNotV(LNotV* ins);
|
||||
void visitBoundsCheck(LBoundsCheck* lir);
|
||||
|
|
|
@ -2227,8 +2227,6 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
// update that stale value.
|
||||
#endif
|
||||
default:
|
||||
// Any unused opcodes and JSOP_LIMIT will end up here without having
|
||||
// to explicitly specify
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -7355,6 +7353,12 @@ 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 =
|
||||
|
@ -7610,6 +7614,7 @@ IonBuilder::jsop_initelem_array()
|
|||
// intializer, and that arrays are marked as non-packed when writing holes
|
||||
// to them during initialization.
|
||||
bool needStub = false;
|
||||
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
|
||||
if (shouldAbortOnPreliminaryGroups(obj)) {
|
||||
needStub = true;
|
||||
} else if (!obj->resultTypeSet() ||
|
||||
|
@ -7620,6 +7625,12 @@ 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;
|
||||
|
@ -7639,46 +7650,60 @@ IonBuilder::jsop_initelem_array()
|
|||
return resumeAfter(store);
|
||||
}
|
||||
|
||||
return initializeArrayElement(obj, index, value, /* addResumePoint = */ true);
|
||||
return initializeArrayElement(obj, index, value, unboxedType, /* addResumePoint = */ true);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
|
||||
JSValueType unboxedType,
|
||||
bool addResumePointAndIncrementInitializedLength)
|
||||
{
|
||||
MConstant* id = MConstant::New(alloc(), Int32Value(index));
|
||||
current->add(id);
|
||||
|
||||
// Get the elements vector.
|
||||
MElements* elements = MElements::New(alloc(), obj);
|
||||
MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
|
||||
current->add(elements);
|
||||
|
||||
if (NeedsPostBarrier(value))
|
||||
current->add(MPostWriteBarrier::New(alloc(), obj, value));
|
||||
if (unboxedType != JSVAL_TYPE_MAGIC) {
|
||||
// Note: storeUnboxedValue takes care of any post barriers on the value.
|
||||
storeUnboxedValue(obj, elements, 0, id, unboxedType, value, /* preBarrier = */ false);
|
||||
|
||||
if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) ||
|
||||
(obj->isNullarySharedStub() &&
|
||||
obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles))
|
||||
{
|
||||
MInstruction* valueDouble = MToDouble::New(alloc(), value);
|
||||
current->add(valueDouble);
|
||||
value = valueDouble;
|
||||
}
|
||||
if (addResumePointAndIncrementInitializedLength) {
|
||||
MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj);
|
||||
current->add(increment);
|
||||
|
||||
// Store the value.
|
||||
MStoreElement* store = MStoreElement::New(alloc(), elements, id, value,
|
||||
if (!resumeAfter(increment))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (NeedsPostBarrier(value))
|
||||
current->add(MPostWriteBarrier::New(alloc(), obj, value));
|
||||
|
||||
if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) ||
|
||||
(obj->isNullarySharedStub() &&
|
||||
obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles))
|
||||
{
|
||||
MInstruction* valueDouble = MToDouble::New(alloc(), value);
|
||||
current->add(valueDouble);
|
||||
value = valueDouble;
|
||||
}
|
||||
|
||||
// Store the value.
|
||||
MStoreElement* store = MStoreElement::New(alloc(), elements, id, value,
|
||||
/* needsHoleCheck = */ false);
|
||||
current->add(store);
|
||||
current->add(store);
|
||||
|
||||
if (addResumePointAndIncrementInitializedLength) {
|
||||
// Update the initialized length. (The template object for this
|
||||
// array has the array's ultimate length, so the length field is
|
||||
// already correct: no updating needed.)
|
||||
MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id);
|
||||
current->add(initLength);
|
||||
if (addResumePointAndIncrementInitializedLength) {
|
||||
// Update the initialized length. (The template object for this
|
||||
// array has the array's ultimate length, so the length field is
|
||||
// already correct: no updating needed.)
|
||||
MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id);
|
||||
current->add(initLength);
|
||||
|
||||
if (!resumeAfter(initLength))
|
||||
return false;
|
||||
if (!resumeAfter(initLength))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -8167,7 +8192,8 @@ IonBuilder::maybeMarkEmpty(MDefinition* ins)
|
|||
static bool
|
||||
ClassHasEffectlessLookup(const Class* clasp)
|
||||
{
|
||||
return IsTypedObjectClass(clasp) ||
|
||||
return (clasp == &UnboxedArrayObject::class_) ||
|
||||
IsTypedObjectClass(clasp) ||
|
||||
(clasp->isNative() && !clasp->getOpsLookupProperty());
|
||||
}
|
||||
|
||||
|
@ -9441,9 +9467,12 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index)
|
|||
{
|
||||
MOZ_ASSERT(*emitted == false);
|
||||
|
||||
if (!ElementAccessIsDenseNative(constraints(), obj, index)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
|
||||
return true;
|
||||
JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, index);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
if (!ElementAccessIsDenseNative(constraints(), obj, index)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't generate a fast path if there have been bounds check failures
|
||||
|
@ -9460,7 +9489,7 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!jsop_getelem_dense(obj, index))
|
||||
if (!jsop_getelem_dense(obj, index, unboxedType))
|
||||
return false;
|
||||
|
||||
trackOptimizationSuccess();
|
||||
|
@ -9812,7 +9841,7 @@ IonBuilder::computeHeapType(const TemporaryTypeSet* objTypes, const jsid id)
|
|||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index)
|
||||
IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType)
|
||||
{
|
||||
TemporaryTypeSet* types = bytecodeTypes(pc);
|
||||
|
||||
|
@ -9836,7 +9865,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index)
|
|||
!ElementAccessHasExtraIndexedProperty(this, obj);
|
||||
|
||||
MIRType knownType = MIRType::Value;
|
||||
if (barrier == BarrierKind::NoBarrier)
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier)
|
||||
knownType = GetElemKnownType(needsHoleCheck, types);
|
||||
|
||||
// Ensure index is an integer.
|
||||
|
@ -9845,13 +9874,13 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index)
|
|||
index = idInt32;
|
||||
|
||||
// Get the elements vector.
|
||||
MInstruction* elements = MElements::New(alloc(), obj);
|
||||
MInstruction* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
|
||||
current->add(elements);
|
||||
|
||||
// Note: to help GVN, use the original MElements instruction and not
|
||||
// MConvertElementsToDoubles as operand. This is fine because converting
|
||||
// elements to double does not change the initialized length.
|
||||
MInstruction* initLength = initializedLength(obj, elements);
|
||||
MInstruction* initLength = initializedLength(obj, elements, unboxedType);
|
||||
|
||||
// If we can load the element as a definite double, make sure to check that
|
||||
// the array has been converted to homogenous doubles first.
|
||||
|
@ -9867,6 +9896,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index)
|
|||
}
|
||||
|
||||
bool loadDouble =
|
||||
unboxedType == JSVAL_TYPE_MAGIC &&
|
||||
barrier == BarrierKind::NoBarrier &&
|
||||
loopDepth_ &&
|
||||
inBounds &&
|
||||
|
@ -9885,13 +9915,18 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index)
|
|||
// hoisting.
|
||||
index = addBoundsCheck(index, initLength);
|
||||
|
||||
load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble);
|
||||
current->add(load);
|
||||
if (unboxedType != JSVAL_TYPE_MAGIC) {
|
||||
load = loadUnboxedValue(elements, 0, index, unboxedType, barrier, types);
|
||||
} else {
|
||||
load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble);
|
||||
current->add(load);
|
||||
}
|
||||
} else {
|
||||
// This load may return undefined, so assume that we *can* read holes,
|
||||
// or that we can read out-of-bounds accesses. In this case, the bounds
|
||||
// check is part of the opcode.
|
||||
load = MLoadElementHole::New(alloc(), elements, index, initLength, needsHoleCheck);
|
||||
load = MLoadElementHole::New(alloc(), elements, index, initLength,
|
||||
unboxedType, needsHoleCheck);
|
||||
current->add(load);
|
||||
|
||||
// If maybeUndefined was true, the typeset must have undefined, and
|
||||
|
@ -9901,7 +9936,8 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index)
|
|||
}
|
||||
|
||||
if (knownType != MIRType::Value) {
|
||||
load->setResultType(knownType);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC)
|
||||
load->setResultType(knownType);
|
||||
load->setResultTypeSet(types);
|
||||
}
|
||||
|
||||
|
@ -10348,9 +10384,12 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object,
|
|||
{
|
||||
MOZ_ASSERT(*emitted == false);
|
||||
|
||||
if (!ElementAccessIsDenseNative(constraints(), object, index)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
|
||||
return true;
|
||||
JSValueType unboxedType = UnboxedArrayElementType(constraints(), object, index);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
if (!ElementAccessIsDenseNative(constraints(), object, index)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
|
||||
|
@ -10384,7 +10423,7 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object,
|
|||
}
|
||||
|
||||
// Emit dense setelem variant.
|
||||
if (!jsop_setelem_dense(conversion, object, index, value, writeHole, emitted))
|
||||
if (!jsop_setelem_dense(conversion, object, index, value, unboxedType, writeHole, emitted))
|
||||
return false;
|
||||
|
||||
if (!*emitted) {
|
||||
|
@ -10474,11 +10513,13 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object,
|
|||
bool
|
||||
IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
||||
MDefinition* obj, MDefinition* id, MDefinition* value,
|
||||
bool writeHole, bool* emitted)
|
||||
JSValueType unboxedType, bool writeHole, bool* emitted)
|
||||
{
|
||||
MOZ_ASSERT(*emitted == false);
|
||||
|
||||
MIRType elementType = DenseNativeElementType(constraints(), obj);
|
||||
MIRType elementType = MIRType::None;
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC)
|
||||
elementType = DenseNativeElementType(constraints(), obj);
|
||||
bool packed = ElementAccessIsPacked(constraints(), obj);
|
||||
|
||||
// Writes which are on holes in the object do not have to bail out if they
|
||||
|
@ -10508,7 +10549,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
|||
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
|
||||
|
||||
// Get the elements vector.
|
||||
MElements* elements = MElements::New(alloc(), obj);
|
||||
MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
|
||||
current->add(elements);
|
||||
|
||||
// Ensure the value is a double, if double conversion might be needed.
|
||||
|
@ -10545,7 +10586,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
|||
MInstruction* store;
|
||||
MStoreElementCommon* common = nullptr;
|
||||
if (writeHole && hasNoExtraIndexedProperty && !mayBeFrozen) {
|
||||
MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue);
|
||||
MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType);
|
||||
store = ins;
|
||||
common = ins;
|
||||
|
||||
|
@ -10557,23 +10598,27 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
|||
|
||||
bool strict = IsStrictSetPC(pc);
|
||||
MFallibleStoreElement* ins = MFallibleStoreElement::New(alloc(), obj, elements, id,
|
||||
newValue, strict);
|
||||
newValue, unboxedType, strict);
|
||||
store = ins;
|
||||
common = ins;
|
||||
|
||||
current->add(ins);
|
||||
current->push(value);
|
||||
} else {
|
||||
MInstruction* initLength = initializedLength(obj, elements);
|
||||
MInstruction* initLength = initializedLength(obj, elements, unboxedType);
|
||||
|
||||
id = addBoundsCheck(id, initLength);
|
||||
bool needsHoleCheck = !packed && !hasNoExtraIndexedProperty;
|
||||
|
||||
MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck);
|
||||
store = ins;
|
||||
common = ins;
|
||||
if (unboxedType != JSVAL_TYPE_MAGIC) {
|
||||
store = storeUnboxedValue(obj, elements, 0, id, unboxedType, newValue);
|
||||
} else {
|
||||
MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck);
|
||||
store = ins;
|
||||
common = ins;
|
||||
|
||||
current->add(store);
|
||||
current->add(store);
|
||||
}
|
||||
|
||||
current->push(value);
|
||||
}
|
||||
|
@ -10691,6 +10736,18 @@ IonBuilder::jsop_length_fastPath()
|
|||
return true;
|
||||
}
|
||||
|
||||
// Compute the length for unboxed array objects.
|
||||
if (UnboxedArrayElementType(constraints(), obj, nullptr) != JSVAL_TYPE_MAGIC &&
|
||||
!objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW))
|
||||
{
|
||||
current->pop();
|
||||
|
||||
MUnboxedArrayLength* length = MUnboxedArrayLength::New(alloc(), obj);
|
||||
current->add(length);
|
||||
current->push(length);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compute the length for array typed objects.
|
||||
TypedObjectPrediction prediction = typedObjectPrediction(obj);
|
||||
if (!prediction.isUseless()) {
|
||||
|
@ -13675,8 +13732,11 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id)
|
|||
if (shouldAbortOnPreliminaryGroups(obj))
|
||||
return true;
|
||||
|
||||
if (!ElementAccessIsDenseNative(constraints(), obj, id))
|
||||
return true;
|
||||
JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, id);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
if (!ElementAccessIsDenseNative(constraints(), obj, id))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ElementAccessHasExtraIndexedProperty(this, obj))
|
||||
return true;
|
||||
|
@ -13691,10 +13751,10 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id)
|
|||
id = idInt32;
|
||||
|
||||
// Get the elements vector.
|
||||
MElements* elements = MElements::New(alloc(), obj);
|
||||
MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
|
||||
current->add(elements);
|
||||
|
||||
MInstruction* initLength = initializedLength(obj, elements);
|
||||
MInstruction* initLength = initializedLength(obj, elements, unboxedType);
|
||||
|
||||
// If there are no holes, speculate the InArray check will not fail.
|
||||
if (!needsHoleCheck && !failedBoundsCheck_) {
|
||||
|
@ -13704,7 +13764,8 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id)
|
|||
}
|
||||
|
||||
// Check if id < initLength and elem[id] not a hole.
|
||||
MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck);
|
||||
MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck,
|
||||
unboxedType);
|
||||
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
@ -14382,24 +14443,32 @@ IonBuilder::constantInt(int32_t i)
|
|||
}
|
||||
|
||||
MInstruction*
|
||||
IonBuilder::initializedLength(MDefinition* obj, MDefinition* elements)
|
||||
IonBuilder::initializedLength(MDefinition* obj, MDefinition* elements, JSValueType unboxedType)
|
||||
{
|
||||
MInstruction* res = MInitializedLength::New(alloc(), elements);
|
||||
MInstruction* res;
|
||||
if (unboxedType != JSVAL_TYPE_MAGIC)
|
||||
res = MUnboxedArrayInitializedLength::New(alloc(), obj);
|
||||
else
|
||||
res = MInitializedLength::New(alloc(), elements);
|
||||
current->add(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
MInstruction*
|
||||
IonBuilder::setInitializedLength(MDefinition* obj, size_t count)
|
||||
IonBuilder::setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count)
|
||||
{
|
||||
MOZ_ASSERT(count);
|
||||
|
||||
// MSetInitializedLength takes the index of the last element, rather
|
||||
// than the count itself.
|
||||
MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false);
|
||||
current->add(elements);
|
||||
MInstruction* res =
|
||||
MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1)));
|
||||
MInstruction* res;
|
||||
if (unboxedType != JSVAL_TYPE_MAGIC) {
|
||||
res = MSetUnboxedArrayInitializedLength::New(alloc(), obj, constant(Int32Value(count)));
|
||||
} else {
|
||||
// MSetInitializedLength takes the index of the last element, rather
|
||||
// than the count itself.
|
||||
MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false);
|
||||
current->add(elements);
|
||||
res = MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1)));
|
||||
}
|
||||
current->add(res);
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -346,8 +346,9 @@ class IonBuilder
|
|||
|
||||
MConstant* constant(const Value& v);
|
||||
MConstant* constantInt(int32_t i);
|
||||
MInstruction* initializedLength(MDefinition* obj, MDefinition* elements);
|
||||
MInstruction* setInitializedLength(MDefinition* obj, size_t count);
|
||||
MInstruction* initializedLength(MDefinition* obj, MDefinition* elements,
|
||||
JSValueType unboxedType);
|
||||
MInstruction* setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count);
|
||||
|
||||
// Improve the type information at tests
|
||||
MOZ_MUST_USE bool improveTypesAtTest(MDefinition* ins, bool trueBranch, MTest* test);
|
||||
|
@ -610,6 +611,7 @@ class IonBuilder
|
|||
TypedObjectPrediction elemTypeReprs,
|
||||
uint32_t elemSize);
|
||||
MOZ_MUST_USE bool initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
|
||||
JSValueType unboxedType,
|
||||
bool addResumePointAndIncrementInitializedLength);
|
||||
|
||||
// jsop_getelem() helpers.
|
||||
|
@ -722,13 +724,15 @@ class IonBuilder
|
|||
MOZ_MUST_USE bool jsop_bindname(PropertyName* name);
|
||||
MOZ_MUST_USE bool jsop_bindvar();
|
||||
MOZ_MUST_USE bool jsop_getelem();
|
||||
MOZ_MUST_USE bool jsop_getelem_dense(MDefinition* obj, MDefinition* index);
|
||||
MOZ_MUST_USE bool jsop_getelem_dense(MDefinition* obj, MDefinition* index,
|
||||
JSValueType unboxedType);
|
||||
MOZ_MUST_USE bool jsop_getelem_typed(MDefinition* obj, MDefinition* index,
|
||||
ScalarTypeDescr::Type arrayType);
|
||||
MOZ_MUST_USE bool jsop_setelem();
|
||||
MOZ_MUST_USE bool jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
||||
MDefinition* object, MDefinition* index,
|
||||
MDefinition* value, bool writeHole, bool* emitted);
|
||||
MDefinition* value, JSValueType unboxedType,
|
||||
bool writeHole, bool* emitted);
|
||||
MOZ_MUST_USE bool jsop_setelem_typed(ScalarTypeDescr::Type arrayType,
|
||||
MDefinition* object, MDefinition* index,
|
||||
MDefinition* value);
|
||||
|
|
|
@ -639,6 +639,9 @@ TestMatchingReceiver(MacroAssembler& masm, IonCache::StubAttacher& attacher,
|
|||
} else {
|
||||
masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure);
|
||||
}
|
||||
} else if (obj->is<UnboxedArrayObject>()) {
|
||||
MOZ_ASSERT(failure);
|
||||
masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
|
||||
} else if (obj->is<TypedObject>()) {
|
||||
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
|
||||
Address(object, JSObject::offsetOfGroup()),
|
||||
|
@ -1185,6 +1188,39 @@ GenerateArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher&
|
|||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
GenerateUnboxedArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
|
||||
JSObject* array, Register object, TypedOrValueRegister output,
|
||||
Label* failures)
|
||||
{
|
||||
Register outReg;
|
||||
if (output.hasValue()) {
|
||||
outReg = output.valueReg().scratchReg();
|
||||
} else {
|
||||
MOZ_ASSERT(output.type() == MIRType::Int32);
|
||||
outReg = output.typedReg().gpr();
|
||||
}
|
||||
MOZ_ASSERT(object != outReg);
|
||||
|
||||
TestMatchingReceiver(masm, attacher, object, array, failures);
|
||||
|
||||
// Load length.
|
||||
masm.load32(Address(object, UnboxedArrayObject::offsetOfLength()), outReg);
|
||||
|
||||
// Check for a length that fits in an int32.
|
||||
masm.branchTest32(Assembler::Signed, outReg, outReg, failures);
|
||||
|
||||
if (output.hasValue())
|
||||
masm.tagValue(JSVAL_TYPE_INT32, outReg, output.valueReg());
|
||||
|
||||
// Success.
|
||||
attacher.jumpRejoin(masm);
|
||||
|
||||
// Failure.
|
||||
masm.bind(failures);
|
||||
attacher.jumpNextStub(masm);
|
||||
}
|
||||
|
||||
// In this case, the code for TypedArray and SharedTypedArray is not the same,
|
||||
// because the code embeds pointers to the respective class arrays. Code that
|
||||
// caches the stub code must distinguish between the two cases.
|
||||
|
@ -1558,6 +1594,40 @@ GetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript,
|
|||
JS::TrackedOutcome::ICGetPropStub_UnboxedReadExpando);
|
||||
}
|
||||
|
||||
bool
|
||||
GetPropertyIC::tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
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);
|
||||
}
|
||||
|
||||
bool
|
||||
GetPropertyIC::tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandleId id, bool* emitted)
|
||||
|
@ -2133,6 +2203,9 @@ GetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript*
|
|||
if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, id, returnAddr, emitted))
|
||||
return false;
|
||||
|
||||
if (!*emitted && !tryAttachUnboxedArrayLength(cx, outerScript, ion, obj, id, returnAddr, emitted))
|
||||
return false;
|
||||
|
||||
if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, id, emitted))
|
||||
return false;
|
||||
}
|
||||
|
@ -3953,7 +4026,7 @@ GetPropertyIC::tryAttachDenseElementHole(JSContext* cx, HandleScript outerScript
|
|||
GetPropertyIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval,
|
||||
TypedOrValueRegister output)
|
||||
{
|
||||
if (!obj->is<TypedArrayObject>())
|
||||
if (!obj->is<TypedArrayObject>() && !obj->is<UnboxedArrayObject>())
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(idval.isInt32() || idval.isString());
|
||||
|
@ -3984,6 +4057,13 @@ GetPropertyIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& i
|
|||
return output.hasValue() || !output.typedReg().isFloat();
|
||||
}
|
||||
|
||||
if (index >= obj->as<UnboxedArrayObject>().initializedLength())
|
||||
return false;
|
||||
|
||||
JSValueType elementType = obj->as<UnboxedArrayObject>().elementType();
|
||||
if (elementType == JSVAL_TYPE_DOUBLE)
|
||||
return output.hasValue();
|
||||
|
||||
return output.hasValue() || !output.typedReg().isFloat();
|
||||
}
|
||||
|
||||
|
@ -4060,27 +4140,46 @@ GenerateGetTypedOrUnboxedArrayElement(JSContext* cx, MacroAssembler& masm,
|
|||
|
||||
Label popObjectAndFail;
|
||||
|
||||
// Guard on the initialized length.
|
||||
Address length(object, TypedArrayObject::lengthOffset());
|
||||
masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
|
||||
if (array->is<TypedArrayObject>()) {
|
||||
// Guard on the initialized length.
|
||||
Address length(object, TypedArrayObject::lengthOffset());
|
||||
masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
|
||||
|
||||
// Save the object register on the stack in case of failure.
|
||||
Register elementReg = object;
|
||||
masm.push(object);
|
||||
// Save the object register on the stack in case of failure.
|
||||
Register elementReg = object;
|
||||
masm.push(object);
|
||||
|
||||
// Load elements vector.
|
||||
masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elementReg);
|
||||
// Load elements vector.
|
||||
masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elementReg);
|
||||
|
||||
// Load the value. We use an invalid register because the destination
|
||||
// register is necessary a non double register.
|
||||
Scalar::Type arrayType = array->as<TypedArrayObject>().type();
|
||||
int width = Scalar::byteSize(arrayType);
|
||||
BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width));
|
||||
if (output.hasValue()) {
|
||||
masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult,
|
||||
elementReg, &popObjectAndFail);
|
||||
// Load the value. We use an invalid register because the destination
|
||||
// register is necessary a non double register.
|
||||
Scalar::Type arrayType = array->as<TypedArrayObject>().type();
|
||||
int width = Scalar::byteSize(arrayType);
|
||||
BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width));
|
||||
if (output.hasValue()) {
|
||||
masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult,
|
||||
elementReg, &popObjectAndFail);
|
||||
} else {
|
||||
masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popObjectAndFail);
|
||||
}
|
||||
} else {
|
||||
masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popObjectAndFail);
|
||||
// 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);
|
||||
|
|
|
@ -2894,6 +2894,32 @@ LIRGenerator::visitSetInitializedLength(MSetInitializedLength* ins)
|
|||
useRegisterOrConstant(ins->index())), ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitUnboxedArrayLength(MUnboxedArrayLength* ins)
|
||||
{
|
||||
define(new(alloc()) LUnboxedArrayLength(useRegisterAtStart(ins->object())), ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins)
|
||||
{
|
||||
define(new(alloc()) LUnboxedArrayInitializedLength(useRegisterAtStart(ins->object())), ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins)
|
||||
{
|
||||
add(new(alloc()) LIncrementUnboxedArrayInitializedLength(useRegister(ins->object())), ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins)
|
||||
{
|
||||
add(new(alloc()) LSetUnboxedArrayInitializedLength(useRegister(ins->object()),
|
||||
useRegisterOrConstant(ins->length()),
|
||||
temp()), ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitNot(MNot* ins)
|
||||
{
|
||||
|
@ -3143,16 +3169,22 @@ LIRGenerator::visitStoreElementHole(MStoreElementHole* ins)
|
|||
const LUse elements = useRegister(ins->elements());
|
||||
const LAllocation index = useRegisterOrConstant(ins->index());
|
||||
|
||||
// Use a temp register when adding new elements to unboxed arrays.
|
||||
LDefinition tempDef = LDefinition::BogusTemp();
|
||||
if (ins->unboxedType() != JSVAL_TYPE_MAGIC)
|
||||
tempDef = temp();
|
||||
|
||||
LInstruction* lir;
|
||||
switch (ins->value()->type()) {
|
||||
case MIRType::Value:
|
||||
lir = new(alloc()) LStoreElementHoleV(object, elements, index, useBox(ins->value()));
|
||||
lir = new(alloc()) LStoreElementHoleV(object, elements, index, useBox(ins->value()),
|
||||
tempDef);
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
|
||||
lir = new(alloc()) LStoreElementHoleT(object, elements, index, value);
|
||||
lir = new(alloc()) LStoreElementHoleT(object, elements, index, value, tempDef);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -3171,14 +3203,20 @@ LIRGenerator::visitFallibleStoreElement(MFallibleStoreElement* ins)
|
|||
const LUse elements = useRegister(ins->elements());
|
||||
const LAllocation index = useRegisterOrConstant(ins->index());
|
||||
|
||||
// Use a temp register when adding new elements to unboxed arrays.
|
||||
LDefinition tempDef = LDefinition::BogusTemp();
|
||||
if (ins->unboxedType() != JSVAL_TYPE_MAGIC)
|
||||
tempDef = temp();
|
||||
|
||||
LInstruction* lir;
|
||||
switch (ins->value()->type()) {
|
||||
case MIRType::Value:
|
||||
lir = new(alloc()) LFallibleStoreElementV(object, elements, index, useBox(ins->value()));
|
||||
lir = new(alloc()) LFallibleStoreElementV(object, elements, index, useBox(ins->value()),
|
||||
tempDef);
|
||||
break;
|
||||
default:
|
||||
const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
|
||||
lir = new(alloc()) LFallibleStoreElementT(object, elements, index, value);
|
||||
lir = new(alloc()) LFallibleStoreElementT(object, elements, index, value, tempDef);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -217,6 +217,10 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
void visitTypedObjectDescr(MTypedObjectDescr* ins);
|
||||
void visitInitializedLength(MInitializedLength* ins);
|
||||
void visitSetInitializedLength(MSetInitializedLength* ins);
|
||||
void visitUnboxedArrayLength(MUnboxedArrayLength* ins);
|
||||
void visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins);
|
||||
void visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins);
|
||||
void visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins);
|
||||
void visitNot(MNot* ins);
|
||||
void visitBoundsCheck(MBoundsCheck* ins);
|
||||
void visitBoundsCheckLower(MBoundsCheckLower* ins);
|
||||
|
|
|
@ -471,6 +471,11 @@ 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();
|
||||
|
@ -518,7 +523,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 != templateObject->as<ArrayObject>().length())
|
||||
if (initLength != GetAnyBoxedOrUnboxedArrayLength(templateObject))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
// Don't inline large allocations.
|
||||
|
@ -533,15 +538,16 @@ IonBuilder::inlineArray(CallInfo& callInfo)
|
|||
|
||||
MDefinition* array = current->peek(-1);
|
||||
if (callInfo.argc() >= 2) {
|
||||
JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject);
|
||||
for (uint32_t i = 0; i < initLength; i++) {
|
||||
if (!alloc().ensureBallast())
|
||||
return InliningStatus_Error;
|
||||
MDefinition* value = callInfo.getArg(i);
|
||||
if (!initializeArrayElement(array, i, value, /* addResumePoint = */ false))
|
||||
if (!initializeArrayElement(array, i, value, unboxedType, /* addResumePoint = */ false))
|
||||
return InliningStatus_Error;
|
||||
}
|
||||
|
||||
MInstruction* setLength = setInitializedLength(array, initLength);
|
||||
MInstruction* setLength = setInitializedLength(array, unboxedType, initLength);
|
||||
if (!resumeAfter(setLength))
|
||||
return InliningStatus_Error;
|
||||
}
|
||||
|
@ -574,7 +580,7 @@ IonBuilder::inlineArrayIsArray(CallInfo& callInfo)
|
|||
if (!clasp || clasp->isProxy())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
isArray = (clasp == &ArrayObject::class_);
|
||||
isArray = (clasp == &ArrayObject::class_ || clasp == &UnboxedArrayObject::class_);
|
||||
}
|
||||
|
||||
pushConstant(BooleanValue(isArray));
|
||||
|
@ -610,7 +616,7 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
|
|||
if (!thisTypes)
|
||||
return InliningStatus_NotInlined;
|
||||
const Class* clasp = thisTypes->getKnownClass(constraints());
|
||||
if (clasp != &ArrayObject::class_)
|
||||
if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
|
||||
return InliningStatus_NotInlined;
|
||||
if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
|
||||
|
@ -623,9 +629,17 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
|
|||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
|
||||
if (clasp == &UnboxedArrayObject::class_) {
|
||||
unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
|
||||
if (clasp == &ArrayObject::class_)
|
||||
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
|
||||
|
||||
TemporaryTypeSet* returnTypes = getInlineReturnTypeSet();
|
||||
bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED);
|
||||
|
@ -636,7 +650,8 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
|
|||
if (barrier != BarrierKind::NoBarrier)
|
||||
returnType = MIRType::Value;
|
||||
|
||||
MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode, needsHoleCheck, maybeUndefined);
|
||||
MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode,
|
||||
unboxedType, needsHoleCheck, maybeUndefined);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
ins->setResultType(returnType);
|
||||
|
@ -718,6 +733,13 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
|
|||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
|
||||
if (clasp == &UnboxedArrayObject::class_) {
|
||||
unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles ||
|
||||
|
@ -728,12 +750,13 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
|
|||
value = valueDouble;
|
||||
}
|
||||
|
||||
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC)
|
||||
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
|
||||
|
||||
if (NeedsPostBarrier(value))
|
||||
current->add(MPostWriteBarrier::New(alloc(), obj, value));
|
||||
|
||||
MArrayPush* ins = MArrayPush::New(alloc(), obj, value);
|
||||
MArrayPush* ins = MArrayPush::New(alloc(), obj, value, unboxedType);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
|
@ -774,9 +797,16 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo)
|
|||
return InliningStatus_NotInlined;
|
||||
|
||||
const Class* clasp = thisTypes->getKnownClass(constraints());
|
||||
if (clasp != &ArrayObject::class_)
|
||||
if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::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)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
|
||||
|
@ -797,8 +827,15 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo)
|
|||
if (!templateObj)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (!templateObj->is<ArrayObject>())
|
||||
return InliningStatus_NotInlined;
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
if (!templateObj->is<ArrayObject>())
|
||||
return InliningStatus_NotInlined;
|
||||
} else {
|
||||
if (!templateObj->is<UnboxedArrayObject>())
|
||||
return InliningStatus_NotInlined;
|
||||
if (templateObj->as<UnboxedArrayObject>().elementType() != unboxedType)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
|
@ -817,12 +854,16 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo)
|
|||
|
||||
end = MArrayLength::New(alloc(), elements);
|
||||
current->add(end->toInstruction());
|
||||
} else {
|
||||
end = MUnboxedArrayLength::New(alloc(), obj);
|
||||
current->add(end->toInstruction());
|
||||
}
|
||||
|
||||
MArraySlice* ins = MArraySlice::New(alloc(), constraints(),
|
||||
obj, begin, end,
|
||||
templateObj,
|
||||
templateObj->group()->initialHeap(constraints()));
|
||||
templateObj->group()->initialHeap(constraints()),
|
||||
unboxedType);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
|
@ -1341,7 +1382,7 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo)
|
|||
// Check if exist a template object in stub.
|
||||
JSString* stringStr = nullptr;
|
||||
JSString* stringSep = nullptr;
|
||||
ArrayObject* templateObject = nullptr;
|
||||
JSObject* templateObject = nullptr;
|
||||
if (!inspector->isOptimizableCallStringSplit(pc, &stringStr, &stringSep, &templateObject))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
|
@ -1367,13 +1408,13 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo)
|
|||
if (!key.maybeTypes()->hasType(TypeSet::StringType()))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
uint32_t initLength = templateObject->length();
|
||||
if (templateObject->getDenseInitializedLength() != initLength)
|
||||
uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(templateObject);
|
||||
if (GetAnyBoxedOrUnboxedInitializedLength(templateObject) != initLength)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
Vector<MConstant*, 0, SystemAllocPolicy> arrayValues;
|
||||
for (uint32_t i = 0; i < initLength; i++) {
|
||||
Value str = templateObject->getDenseElement(i);
|
||||
Value str = GetAnyBoxedOrUnboxedDenseElement(templateObject, i);
|
||||
MOZ_ASSERT(str.toString()->isAtom());
|
||||
MConstant* value = MConstant::New(alloc().fallible(), str, constraints());
|
||||
if (!value)
|
||||
|
@ -1404,6 +1445,8 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo)
|
|||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject);
|
||||
|
||||
// Store all values, no need to initialize the length after each as
|
||||
// jsop_initelem_array is doing because we do not expect to bailout
|
||||
// because the memory is supposed to be allocated by now.
|
||||
|
@ -1414,11 +1457,11 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo)
|
|||
MConstant* value = arrayValues[i];
|
||||
current->add(value);
|
||||
|
||||
if (!initializeArrayElement(array, i, value, /* addResumePoint = */ false))
|
||||
if (!initializeArrayElement(array, i, value, unboxedType, /* addResumePoint = */ false))
|
||||
return InliningStatus_Error;
|
||||
}
|
||||
|
||||
MInstruction* setLength = setInitializedLength(array, initLength);
|
||||
MInstruction* setLength = setInitializedLength(array, unboxedType, initLength);
|
||||
if (!resumeAfter(setLength))
|
||||
return InliningStatus_Error;
|
||||
|
||||
|
|
|
@ -5789,6 +5789,46 @@ jit::ElementAccessIsDenseNative(CompilerConstraintList* constraints,
|
|||
return clasp && clasp->isNative() && !IsTypedArrayClass(clasp);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return elementType;
|
||||
}
|
||||
|
||||
bool
|
||||
jit::ElementAccessIsTypedArray(CompilerConstraintList* constraints,
|
||||
MDefinition* obj, MDefinition* id,
|
||||
|
@ -5948,6 +5988,11 @@ 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;
|
||||
}
|
||||
|
||||
|
|
188
js/src/jit/MIR.h
188
js/src/jit/MIR.h
|
@ -375,7 +375,8 @@ class AliasSet {
|
|||
Element = 1 << 1, // A Value member of obj->elements or
|
||||
// a typed object.
|
||||
UnboxedElement = 1 << 2, // An unboxed scalar or reference member of
|
||||
// typed object or unboxed object.
|
||||
// a typed array, typed object, or unboxed
|
||||
// object.
|
||||
DynamicSlot = 1 << 3, // A Value member of obj->slots.
|
||||
FixedSlot = 1 << 4, // A Value member of obj->fixedSlots().
|
||||
DOMProperty = 1 << 5, // A DOM property
|
||||
|
@ -432,6 +433,9 @@ class AliasSet {
|
|||
MOZ_ASSERT(flags && !(flags & Store_));
|
||||
return AliasSet(flags | Store_);
|
||||
}
|
||||
static uint32_t BoxedOrUnboxedElements(JSValueType type) {
|
||||
return (type == JSVAL_TYPE_MAGIC) ? Element : UnboxedElement;
|
||||
}
|
||||
};
|
||||
|
||||
typedef Vector<MDefinition*, 6, JitAllocPolicy> MDefinitionVector;
|
||||
|
@ -8758,6 +8762,102 @@ class MSetInitializedLength
|
|||
ALLOW_CLONE(MSetInitializedLength)
|
||||
};
|
||||
|
||||
// Load the length from an unboxed array.
|
||||
class MUnboxedArrayLength
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
explicit MUnboxedArrayLength(MDefinition* object)
|
||||
: MUnaryInstruction(object)
|
||||
{
|
||||
setResultType(MIRType::Int32);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(UnboxedArrayLength)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, object))
|
||||
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Load(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MUnboxedArrayLength)
|
||||
};
|
||||
|
||||
// Load the initialized length from an unboxed array.
|
||||
class MUnboxedArrayInitializedLength
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
explicit MUnboxedArrayInitializedLength(MDefinition* object)
|
||||
: MUnaryInstruction(object)
|
||||
{
|
||||
setResultType(MIRType::Int32);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(UnboxedArrayInitializedLength)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, object))
|
||||
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Load(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MUnboxedArrayInitializedLength)
|
||||
};
|
||||
|
||||
// Increment the initialized length of an unboxed array object.
|
||||
class MIncrementUnboxedArrayInitializedLength
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
explicit MIncrementUnboxedArrayInitializedLength(MDefinition* obj)
|
||||
: MUnaryInstruction(obj)
|
||||
{}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(IncrementUnboxedArrayInitializedLength)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, object))
|
||||
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Store(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MIncrementUnboxedArrayInitializedLength)
|
||||
};
|
||||
|
||||
// Set the initialized length of an unboxed array object.
|
||||
class MSetUnboxedArrayInitializedLength
|
||||
: public MBinaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
explicit MSetUnboxedArrayInitializedLength(MDefinition* obj, MDefinition* length)
|
||||
: MBinaryInstruction(obj, length)
|
||||
{}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(SetUnboxedArrayInitializedLength)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, object), (1, length))
|
||||
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Store(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MSetUnboxedArrayInitializedLength)
|
||||
};
|
||||
|
||||
// Load the array length from an elements header.
|
||||
class MArrayLength
|
||||
: public MUnaryInstruction,
|
||||
|
@ -9251,19 +9351,23 @@ class MLoadElement
|
|||
ALLOW_CLONE(MLoadElement)
|
||||
};
|
||||
|
||||
// Load a value from the elements vector of a native object.
|
||||
// Load a value from the elements vector for a dense native or unboxed array.
|
||||
// If the index is out-of-bounds, or the indexed slot has a hole, undefined is
|
||||
// returned instead.
|
||||
class MLoadElementHole
|
||||
: public MTernaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
// Unboxed element type, JSVAL_TYPE_MAGIC for dense native elements.
|
||||
JSValueType unboxedType_;
|
||||
|
||||
bool needsNegativeIntCheck_;
|
||||
bool needsHoleCheck_;
|
||||
|
||||
MLoadElementHole(MDefinition* elements, MDefinition* index, MDefinition* initLength,
|
||||
bool needsHoleCheck)
|
||||
JSValueType unboxedType, bool needsHoleCheck)
|
||||
: MTernaryInstruction(elements, index, initLength),
|
||||
unboxedType_(unboxedType),
|
||||
needsNegativeIntCheck_(true),
|
||||
needsHoleCheck_(needsHoleCheck)
|
||||
{
|
||||
|
@ -9285,6 +9389,9 @@ class MLoadElementHole
|
|||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, elements), (1, index), (2, initLength))
|
||||
|
||||
JSValueType unboxedType() const {
|
||||
return unboxedType_;
|
||||
}
|
||||
bool needsNegativeIntCheck() const {
|
||||
return needsNegativeIntCheck_;
|
||||
}
|
||||
|
@ -9295,6 +9402,8 @@ class MLoadElementHole
|
|||
if (!ins->isLoadElementHole())
|
||||
return false;
|
||||
const MLoadElementHole* other = ins->toLoadElementHole();
|
||||
if (unboxedType() != other->unboxedType())
|
||||
return false;
|
||||
if (needsHoleCheck() != other->needsHoleCheck())
|
||||
return false;
|
||||
if (needsNegativeIntCheck() != other->needsNegativeIntCheck())
|
||||
|
@ -9302,7 +9411,7 @@ class MLoadElementHole
|
|||
return congruentIfOperandsEqual(other);
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Load(AliasSet::Element);
|
||||
return AliasSet::Load(AliasSet::BoxedOrUnboxedElements(unboxedType()));
|
||||
}
|
||||
void collectRangeInfoPreTrunc() override;
|
||||
|
||||
|
@ -9482,17 +9591,20 @@ class MStoreElement
|
|||
ALLOW_CLONE(MStoreElement)
|
||||
};
|
||||
|
||||
// Like MStoreElement, but supports indexes >= initialized length. The downside
|
||||
// is that we cannot hoist the elements vector and bounds check, since this
|
||||
// instruction may update the (initialized) length and reallocate the elements
|
||||
// vector.
|
||||
// Like MStoreElement, but supports indexes >= initialized length, and can
|
||||
// handle unboxed arrays. The downside is that we cannot hoist the elements
|
||||
// vector and bounds check, since this instruction may update the (initialized)
|
||||
// length and reallocate the elements vector.
|
||||
class MStoreElementHole
|
||||
: public MAryInstruction<4>,
|
||||
public MStoreElementCommon,
|
||||
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >::Data
|
||||
{
|
||||
JSValueType unboxedType_;
|
||||
|
||||
MStoreElementHole(MDefinition* object, MDefinition* elements,
|
||||
MDefinition* index, MDefinition* value)
|
||||
MDefinition* index, MDefinition* value, JSValueType unboxedType)
|
||||
: unboxedType_(unboxedType)
|
||||
{
|
||||
initOperand(0, object);
|
||||
initOperand(1, elements);
|
||||
|
@ -9507,6 +9619,10 @@ class MStoreElementHole
|
|||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value))
|
||||
|
||||
JSValueType unboxedType() const {
|
||||
return unboxedType_;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MStoreElementHole)
|
||||
};
|
||||
|
||||
|
@ -9517,11 +9633,13 @@ class MFallibleStoreElement
|
|||
public MStoreElementCommon,
|
||||
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >::Data
|
||||
{
|
||||
JSValueType unboxedType_;
|
||||
bool strict_;
|
||||
|
||||
MFallibleStoreElement(MDefinition* object, MDefinition* elements,
|
||||
MDefinition* index, MDefinition* value,
|
||||
bool strict)
|
||||
JSValueType unboxedType, bool strict)
|
||||
: unboxedType_(unboxedType)
|
||||
{
|
||||
initOperand(0, object);
|
||||
initOperand(1, elements);
|
||||
|
@ -9537,6 +9655,10 @@ class MFallibleStoreElement
|
|||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value))
|
||||
|
||||
JSValueType unboxedType() const {
|
||||
return unboxedType_;
|
||||
}
|
||||
|
||||
bool strict() const {
|
||||
return strict_;
|
||||
}
|
||||
|
@ -9640,12 +9762,13 @@ class MArrayPopShift
|
|||
|
||||
private:
|
||||
Mode mode_;
|
||||
JSValueType unboxedType_;
|
||||
bool needsHoleCheck_;
|
||||
bool maybeUndefined_;
|
||||
|
||||
MArrayPopShift(MDefinition* object, Mode mode,
|
||||
MArrayPopShift(MDefinition* object, Mode mode, JSValueType unboxedType,
|
||||
bool needsHoleCheck, bool maybeUndefined)
|
||||
: MUnaryInstruction(object), mode_(mode),
|
||||
: MUnaryInstruction(object), mode_(mode), unboxedType_(unboxedType),
|
||||
needsHoleCheck_(needsHoleCheck), maybeUndefined_(maybeUndefined)
|
||||
{ }
|
||||
|
||||
|
@ -9663,8 +9786,12 @@ class MArrayPopShift
|
|||
bool mode() const {
|
||||
return mode_;
|
||||
}
|
||||
JSValueType unboxedType() const {
|
||||
return unboxedType_;
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
|
||||
return AliasSet::Store(AliasSet::ObjectFields |
|
||||
AliasSet::BoxedOrUnboxedElements(unboxedType()));
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MArrayPopShift)
|
||||
|
@ -9675,8 +9802,10 @@ class MArrayPush
|
|||
: public MBinaryInstruction,
|
||||
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data
|
||||
{
|
||||
MArrayPush(MDefinition* object, MDefinition* value)
|
||||
: MBinaryInstruction(object, value)
|
||||
JSValueType unboxedType_;
|
||||
|
||||
MArrayPush(MDefinition* object, MDefinition* value, JSValueType unboxedType)
|
||||
: MBinaryInstruction(object, value), unboxedType_(unboxedType)
|
||||
{
|
||||
setResultType(MIRType::Int32);
|
||||
}
|
||||
|
@ -9686,8 +9815,12 @@ class MArrayPush
|
|||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, object), (1, value))
|
||||
|
||||
JSValueType unboxedType() const {
|
||||
return unboxedType_;
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
|
||||
return AliasSet::Store(AliasSet::ObjectFields |
|
||||
AliasSet::BoxedOrUnboxedElements(unboxedType()));
|
||||
}
|
||||
void computeRange(TempAllocator& alloc) override;
|
||||
|
||||
|
@ -9701,13 +9834,15 @@ class MArraySlice
|
|||
{
|
||||
CompilerObject templateObj_;
|
||||
gc::InitialHeap initialHeap_;
|
||||
JSValueType unboxedType_;
|
||||
|
||||
MArraySlice(CompilerConstraintList* constraints, MDefinition* obj,
|
||||
MDefinition* begin, MDefinition* end,
|
||||
JSObject* templateObj, gc::InitialHeap initialHeap)
|
||||
JSObject* templateObj, gc::InitialHeap initialHeap, JSValueType unboxedType)
|
||||
: MTernaryInstruction(obj, begin, end),
|
||||
templateObj_(templateObj),
|
||||
initialHeap_(initialHeap)
|
||||
initialHeap_(initialHeap),
|
||||
unboxedType_(unboxedType)
|
||||
{
|
||||
setResultType(MIRType::Object);
|
||||
}
|
||||
|
@ -9725,6 +9860,10 @@ class MArraySlice
|
|||
return initialHeap_;
|
||||
}
|
||||
|
||||
JSValueType unboxedType() const {
|
||||
return unboxedType_;
|
||||
}
|
||||
|
||||
bool possiblyCalls() const override {
|
||||
return true;
|
||||
}
|
||||
|
@ -12093,13 +12232,15 @@ class MInArray
|
|||
{
|
||||
bool needsHoleCheck_;
|
||||
bool needsNegativeIntCheck_;
|
||||
JSValueType unboxedType_;
|
||||
|
||||
MInArray(MDefinition* elements, MDefinition* index,
|
||||
MDefinition* initLength, MDefinition* object,
|
||||
bool needsHoleCheck)
|
||||
bool needsHoleCheck, JSValueType unboxedType)
|
||||
: MQuaternaryInstruction(elements, index, initLength, object),
|
||||
needsHoleCheck_(needsHoleCheck),
|
||||
needsNegativeIntCheck_(true)
|
||||
needsNegativeIntCheck_(true),
|
||||
unboxedType_(unboxedType)
|
||||
{
|
||||
setResultType(MIRType::Boolean);
|
||||
setMovable();
|
||||
|
@ -12119,6 +12260,9 @@ class MInArray
|
|||
bool needsNegativeIntCheck() const {
|
||||
return needsNegativeIntCheck_;
|
||||
}
|
||||
JSValueType unboxedType() const {
|
||||
return unboxedType_;
|
||||
}
|
||||
void collectRangeInfoPreTrunc() override;
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Load(AliasSet::Element);
|
||||
|
@ -12131,6 +12275,8 @@ class MInArray
|
|||
return false;
|
||||
if (needsNegativeIntCheck() != other->needsNegativeIntCheck())
|
||||
return false;
|
||||
if (unboxedType() != other->unboxedType())
|
||||
return false;
|
||||
return congruentIfOperandsEqual(other);
|
||||
}
|
||||
};
|
||||
|
@ -14031,6 +14177,8 @@ MDefinition::maybeConstantValue()
|
|||
|
||||
bool ElementAccessIsDenseNative(CompilerConstraintList* constraints,
|
||||
MDefinition* obj, MDefinition* id);
|
||||
JSValueType UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* obj,
|
||||
MDefinition* id);
|
||||
bool ElementAccessIsTypedArray(CompilerConstraintList* constraints,
|
||||
MDefinition* obj, MDefinition* id,
|
||||
Scalar::Type* arrayType);
|
||||
|
|
|
@ -199,6 +199,10 @@ namespace jit {
|
|||
_(SetTypedObjectOffset) \
|
||||
_(InitializedLength) \
|
||||
_(SetInitializedLength) \
|
||||
_(UnboxedArrayLength) \
|
||||
_(UnboxedArrayInitializedLength) \
|
||||
_(IncrementUnboxedArrayInitializedLength) \
|
||||
_(SetUnboxedArrayInitializedLength) \
|
||||
_(Not) \
|
||||
_(BoundsCheck) \
|
||||
_(BoundsCheckLower) \
|
||||
|
|
|
@ -705,6 +705,31 @@ template void
|
|||
MacroAssembler::storeUnboxedProperty(BaseIndex address, JSValueType type,
|
||||
const ConstantOrRegister& value, Label* failure);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Inlined version of gc::CheckAllocatorState that checks the bare essentials
|
||||
// and bails for anything that cannot be handled with our jit allocators.
|
||||
void
|
||||
|
@ -1256,6 +1281,16 @@ 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.
|
||||
// Load a property from an UnboxedPlainObject or UnboxedArrayObject.
|
||||
template <typename T>
|
||||
void loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output);
|
||||
|
||||
|
@ -1637,6 +1637,9 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
void storeUnboxedProperty(T address, JSValueType type,
|
||||
const ConstantOrRegister& value, Label* failure);
|
||||
|
||||
void checkUnboxedArrayCapacity(Register obj, const RegisterOrInt32Constant& index,
|
||||
Register temp, Label* failure);
|
||||
|
||||
Register extractString(const Address& address, Register scratch) {
|
||||
return extractObject(address, scratch);
|
||||
}
|
||||
|
|
|
@ -1355,7 +1355,7 @@ RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const
|
|||
RootedValue result(cx);
|
||||
RootedObjectGroup group(cx, templateObject->group());
|
||||
|
||||
ArrayObject* resultObject = NewFullyAllocatedArrayTryUseGroup(cx, group, count_);
|
||||
JSObject* resultObject = NewFullyAllocatedArrayTryUseGroup(cx, group, count_);
|
||||
if (!resultObject)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -795,6 +795,11 @@ 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;
|
||||
|
|
|
@ -286,6 +286,11 @@ 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");
|
||||
|
@ -2245,6 +2250,7 @@ IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy)
|
|||
if (obj == holder)
|
||||
return false;
|
||||
if (!obj->is<UnboxedPlainObject>() &&
|
||||
!obj->is<UnboxedArrayObject>() &&
|
||||
!obj->is<TypedObject>())
|
||||
{
|
||||
return false;
|
||||
|
@ -2576,6 +2582,9 @@ 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>());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
|
||||
AutoDetectInvalidation adi(cx, rval);
|
||||
|
||||
|
@ -325,11 +325,12 @@ ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
|
|||
}
|
||||
|
||||
bool
|
||||
ArrayPushDense(JSContext* cx, HandleArrayObject arr, HandleValue v, uint32_t* length)
|
||||
ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length)
|
||||
{
|
||||
*length = arr->length();
|
||||
DenseElementResult result = arr->setOrExtendDenseElements(cx, *length, v.address(), 1,
|
||||
ShouldUpdateTypes::DontUpdate);
|
||||
*length = GetAnyBoxedOrUnboxedArrayLength(obj);
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, *length, v.address(), 1,
|
||||
ShouldUpdateTypes::DontUpdate);
|
||||
if (result != DenseElementResult::Incomplete) {
|
||||
(*length)++;
|
||||
return result == DenseElementResult::Success;
|
||||
|
@ -337,7 +338,7 @@ ArrayPushDense(JSContext* cx, HandleArrayObject arr, HandleValue v, uint32_t* le
|
|||
|
||||
JS::AutoValueArray<3> argv(cx);
|
||||
argv[0].setUndefined();
|
||||
argv[1].setObject(*arr);
|
||||
argv[1].setObject(*obj);
|
||||
argv[2].set(v);
|
||||
if (!js::array_push(cx, 1, argv.begin()))
|
||||
return false;
|
||||
|
@ -349,7 +350,7 @@ ArrayPushDense(JSContext* cx, HandleArrayObject arr, HandleValue v, uint32_t* le
|
|||
bool
|
||||
ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
|
||||
{
|
||||
MOZ_ASSERT(obj->is<ArrayObject>());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
|
||||
AutoDetectInvalidation adi(cx, rval);
|
||||
|
||||
|
@ -1130,14 +1131,16 @@ Recompile(JSContext* cx)
|
|||
}
|
||||
|
||||
bool
|
||||
SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, HandleValue value, bool strict)
|
||||
SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index,
|
||||
HandleValue value, bool strict)
|
||||
{
|
||||
// This function is called from Ion code for StoreElementHole's OOL path.
|
||||
// In this case we know the object is native and that no type changes are
|
||||
// needed.
|
||||
// In this case we know the object is native or an unboxed array and that
|
||||
// no type changes are needed.
|
||||
|
||||
DenseElementResult result = obj->setOrExtendDenseElements(cx, index, value.address(), 1,
|
||||
ShouldUpdateTypes::DontUpdate);
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, index, value.address(), 1,
|
||||
ShouldUpdateTypes::DontUpdate);
|
||||
if (result != DenseElementResult::Incomplete)
|
||||
return result == DenseElementResult::Success;
|
||||
|
||||
|
|
|
@ -622,7 +622,7 @@ template<bool Equal>
|
|||
bool StringsEqual(JSContext* cx, HandleString left, HandleString right, bool* res);
|
||||
|
||||
MOZ_MUST_USE bool ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval);
|
||||
MOZ_MUST_USE bool ArrayPushDense(JSContext* cx, HandleArrayObject obj, HandleValue v, uint32_t* length);
|
||||
MOZ_MUST_USE bool ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length);
|
||||
MOZ_MUST_USE bool ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval);
|
||||
JSString* ArrayJoin(JSContext* cx, HandleObject array, HandleString sep);
|
||||
|
||||
|
@ -745,8 +745,8 @@ ForcedRecompile(JSContext* cx);
|
|||
JSString* StringReplace(JSContext* cx, HandleString string, HandleString pattern,
|
||||
HandleString repl);
|
||||
|
||||
MOZ_MUST_USE bool SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index,
|
||||
HandleValue value, bool strict);
|
||||
MOZ_MUST_USE bool SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index,
|
||||
HandleValue value, bool strict);
|
||||
|
||||
void AssertValidObjectPtr(JSContext* cx, JSObject* obj);
|
||||
void AssertValidObjectOrNullPtr(JSContext* cx, JSObject* obj);
|
||||
|
|
|
@ -5166,6 +5166,72 @@ class LSetInitializedLength : public LInstructionHelper<0, 2, 0>
|
|||
}
|
||||
};
|
||||
|
||||
class LUnboxedArrayLength : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(UnboxedArrayLength)
|
||||
|
||||
explicit LUnboxedArrayLength(const LAllocation& object) {
|
||||
setOperand(0, object);
|
||||
}
|
||||
|
||||
const LAllocation* object() {
|
||||
return getOperand(0);
|
||||
}
|
||||
};
|
||||
|
||||
class LUnboxedArrayInitializedLength : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(UnboxedArrayInitializedLength)
|
||||
|
||||
explicit LUnboxedArrayInitializedLength(const LAllocation& object) {
|
||||
setOperand(0, object);
|
||||
}
|
||||
|
||||
const LAllocation* object() {
|
||||
return getOperand(0);
|
||||
}
|
||||
};
|
||||
|
||||
class LIncrementUnboxedArrayInitializedLength : public LInstructionHelper<0, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(IncrementUnboxedArrayInitializedLength)
|
||||
|
||||
explicit LIncrementUnboxedArrayInitializedLength(const LAllocation& object) {
|
||||
setOperand(0, object);
|
||||
}
|
||||
|
||||
const LAllocation* object() {
|
||||
return getOperand(0);
|
||||
}
|
||||
};
|
||||
|
||||
class LSetUnboxedArrayInitializedLength : public LInstructionHelper<0, 2, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(SetUnboxedArrayInitializedLength)
|
||||
|
||||
explicit LSetUnboxedArrayInitializedLength(const LAllocation& object,
|
||||
const LAllocation& length,
|
||||
const LDefinition& temp) {
|
||||
setOperand(0, object);
|
||||
setOperand(1, length);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const LAllocation* object() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const LAllocation* length() {
|
||||
return getOperand(1);
|
||||
}
|
||||
const LDefinition* temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
};
|
||||
|
||||
// Load the length from an elements header.
|
||||
class LArrayLength : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
|
@ -5670,17 +5736,19 @@ class LStoreElementT : public LInstructionHelper<0, 3, 0>
|
|||
};
|
||||
|
||||
// Like LStoreElementV, but supports indexes >= initialized length.
|
||||
class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 0>
|
||||
class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(StoreElementHoleV)
|
||||
|
||||
LStoreElementHoleV(const LAllocation& object, const LAllocation& elements,
|
||||
const LAllocation& index, const LBoxAllocation& value) {
|
||||
const LAllocation& index, const LBoxAllocation& value,
|
||||
const LDefinition& temp) {
|
||||
setOperand(0, object);
|
||||
setOperand(1, elements);
|
||||
setOperand(2, index);
|
||||
setBoxOperand(Value, value);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
static const size_t Value = 3;
|
||||
|
@ -5700,17 +5768,19 @@ class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 0>
|
|||
};
|
||||
|
||||
// Like LStoreElementT, but supports indexes >= initialized length.
|
||||
class LStoreElementHoleT : public LInstructionHelper<0, 4, 0>
|
||||
class LStoreElementHoleT : public LInstructionHelper<0, 4, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(StoreElementHoleT)
|
||||
|
||||
LStoreElementHoleT(const LAllocation& object, const LAllocation& elements,
|
||||
const LAllocation& index, const LAllocation& value) {
|
||||
const LAllocation& index, const LAllocation& value,
|
||||
const LDefinition& temp) {
|
||||
setOperand(0, object);
|
||||
setOperand(1, elements);
|
||||
setOperand(2, index);
|
||||
setOperand(3, value);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const MStoreElementHole* mir() const {
|
||||
|
@ -5731,17 +5801,19 @@ class LStoreElementHoleT : public LInstructionHelper<0, 4, 0>
|
|||
};
|
||||
|
||||
// Like LStoreElementV, but can just ignore assignment (for eg. frozen objects)
|
||||
class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 0>
|
||||
class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(FallibleStoreElementV)
|
||||
|
||||
LFallibleStoreElementV(const LAllocation& object, const LAllocation& elements,
|
||||
const LAllocation& index, const LBoxAllocation& value) {
|
||||
const LAllocation& index, const LBoxAllocation& value,
|
||||
const LDefinition& temp) {
|
||||
setOperand(0, object);
|
||||
setOperand(1, elements);
|
||||
setOperand(2, index);
|
||||
setBoxOperand(Value, value);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
static const size_t Value = 3;
|
||||
|
@ -5761,17 +5833,19 @@ class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 0>
|
|||
};
|
||||
|
||||
// Like LStoreElementT, but can just ignore assignment (for eg. frozen objects)
|
||||
class LFallibleStoreElementT : public LInstructionHelper<0, 4, 0>
|
||||
class LFallibleStoreElementT : public LInstructionHelper<0, 4, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(FallibleStoreElementT)
|
||||
|
||||
LFallibleStoreElementT(const LAllocation& object, const LAllocation& elements,
|
||||
const LAllocation& index, const LAllocation& value) {
|
||||
const LAllocation& index, const LAllocation& value,
|
||||
const LDefinition& temp) {
|
||||
setOperand(0, object);
|
||||
setOperand(1, elements);
|
||||
setOperand(2, index);
|
||||
setOperand(3, value);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const MFallibleStoreElement* mir() const {
|
||||
|
|
|
@ -266,6 +266,10 @@
|
|||
_(PostWriteElementBarrierV) \
|
||||
_(InitializedLength) \
|
||||
_(SetInitializedLength) \
|
||||
_(UnboxedArrayLength) \
|
||||
_(UnboxedArrayInitializedLength) \
|
||||
_(IncrementUnboxedArrayInitializedLength) \
|
||||
_(SetUnboxedArrayInitializedLength) \
|
||||
_(BoundsCheck) \
|
||||
_(BoundsCheckRange) \
|
||||
_(BoundsCheckLower) \
|
||||
|
|
|
@ -1096,6 +1096,7 @@ class JS_PUBLIC_API(ContextOptions) {
|
|||
wasmAlwaysBaseline_(false),
|
||||
throwOnAsmJSValidationFailure_(false),
|
||||
nativeRegExp_(true),
|
||||
unboxedArrays_(false),
|
||||
asyncStack_(true),
|
||||
throwOnDebuggeeWouldRun_(true),
|
||||
dumpStackOnDebuggeeWouldRun_(false),
|
||||
|
@ -1172,6 +1173,12 @@ 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;
|
||||
|
@ -1234,6 +1241,7 @@ class JS_PUBLIC_API(ContextOptions) {
|
|||
bool wasmAlwaysBaseline_ : 1;
|
||||
bool throwOnAsmJSValidationFailure_ : 1;
|
||||
bool nativeRegExp_ : 1;
|
||||
bool unboxedArrays_ : 1;
|
||||
bool asyncStack_ : 1;
|
||||
bool throwOnDebuggeeWouldRun_ : 1;
|
||||
bool dumpStackOnDebuggeeWouldRun_ : 1;
|
||||
|
|
|
@ -64,7 +64,7 @@ using JS::ToUint32;
|
|||
bool
|
||||
JS::IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer)
|
||||
{
|
||||
if (obj->is<ArrayObject>()) {
|
||||
if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) {
|
||||
*answer = IsArrayAnswer::Array;
|
||||
return true;
|
||||
}
|
||||
|
@ -100,6 +100,11 @@ 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()) {
|
||||
|
@ -248,20 +253,18 @@ static bool
|
|||
GetElement(JSContext* cx, HandleObject obj, HandleObject receiver,
|
||||
uint32_t index, bool* hole, MutableHandleValue vp)
|
||||
{
|
||||
if (obj->isNative()) {
|
||||
NativeObject* nobj = &obj->as<NativeObject>();
|
||||
if (index < nobj->getDenseInitializedLength()) {
|
||||
vp.set(nobj->getDenseElement(size_t(index)));
|
||||
if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
|
||||
*hole = false;
|
||||
return true;
|
||||
}
|
||||
AssertGreaterThanZero(index);
|
||||
if (index < GetAnyBoxedOrUnboxedInitializedLength(obj)) {
|
||||
vp.set(GetAnyBoxedOrUnboxedDenseElement(obj, uint32_t(index)));
|
||||
if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
|
||||
*hole = false;
|
||||
return true;
|
||||
}
|
||||
if (nobj->is<ArgumentsObject>() && index <= UINT32_MAX) {
|
||||
if (nobj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) {
|
||||
*hole = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (obj->is<ArgumentsObject>()) {
|
||||
if (obj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) {
|
||||
*hole = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,8 +283,8 @@ ElementAdder::append(JSContext* cx, HandleValue v)
|
|||
{
|
||||
MOZ_ASSERT(index_ < length_);
|
||||
if (resObj_) {
|
||||
NativeObject* resObj = &resObj_->as<NativeObject>();
|
||||
DenseElementResult result = resObj->setOrExtendDenseElements(cx, index_, v.address(), 1);
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_, v.address(), 1);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
if (result == DenseElementResult::Incomplete) {
|
||||
|
@ -333,31 +336,37 @@ js::GetElementsWithAdder(JSContext* cx, HandleObject obj, HandleObject receiver,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetDenseElements(NativeObject* aobj, uint32_t length, Value* vp)
|
||||
template <JSValueType Type>
|
||||
DenseElementResult
|
||||
GetBoxedOrUnboxedDenseElements(JSObject* aobj, uint32_t length, Value* vp)
|
||||
{
|
||||
MOZ_ASSERT(!ObjectMayHaveExtraIndexedProperties(aobj));
|
||||
|
||||
if (length > aobj->getDenseInitializedLength())
|
||||
return false;
|
||||
if (length > GetBoxedOrUnboxedInitializedLength<Type>(aobj))
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
vp[i] = aobj->getDenseElement(i);
|
||||
vp[i] = GetBoxedOrUnboxedDenseElement<Type>(aobj, i);
|
||||
|
||||
// No other indexed properties so hole => undefined.
|
||||
if (vp[i].isMagic(JS_ELEMENTS_HOLE))
|
||||
vp[i] = UndefinedValue();
|
||||
}
|
||||
|
||||
return true;
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor3(GetBoxedOrUnboxedDenseElements,
|
||||
JSObject*, uint32_t, Value*);
|
||||
|
||||
bool
|
||||
js::GetElements(JSContext* cx, HandleObject aobj, uint32_t length, Value* vp)
|
||||
{
|
||||
if (!ObjectMayHaveExtraIndexedProperties(aobj)) {
|
||||
if (GetDenseElements(&aobj->as<NativeObject>(), length, vp))
|
||||
return true;
|
||||
GetBoxedOrUnboxedDenseElementsFunctor functor(aobj, length, vp);
|
||||
DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, aobj);
|
||||
if (result != DenseElementResult::Incomplete)
|
||||
return result == DenseElementResult::Success;
|
||||
}
|
||||
|
||||
if (aobj->is<ArgumentsObject>()) {
|
||||
|
@ -389,9 +398,9 @@ SetArrayElement(JSContext* cx, HandleObject obj, double index, HandleValue v)
|
|||
{
|
||||
MOZ_ASSERT(index >= 0);
|
||||
|
||||
if (obj->is<ArrayObject>() && !obj->isIndexed() && index <= UINT32_MAX) {
|
||||
NativeObject* nobj = &obj->as<NativeObject>();
|
||||
DenseElementResult result = nobj->setOrExtendDenseElements(cx, uint32_t(index), v.address(), 1);
|
||||
if ((obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) && !obj->isIndexed() && index <= UINT32_MAX) {
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(index), v.address(), 1);
|
||||
if (result != DenseElementResult::Incomplete)
|
||||
return result == DenseElementResult::Success;
|
||||
}
|
||||
|
@ -511,6 +520,24 @@ struct ReverseIndexComparator
|
|||
}
|
||||
};
|
||||
|
||||
bool
|
||||
js::CanonicalizeArrayLengthValue(JSContext* cx, HandleValue v, uint32_t* newLen)
|
||||
{
|
||||
double d;
|
||||
|
||||
if (!ToUint32(cx, v, newLen))
|
||||
return false;
|
||||
|
||||
if (!ToNumber(cx, v, &d))
|
||||
return false;
|
||||
|
||||
if (d == *newLen)
|
||||
return true;
|
||||
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ES6 draft rev 34 (2015 Feb 20) 9.4.2.4 ArraySetLength */
|
||||
bool
|
||||
js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
|
||||
|
@ -532,22 +559,12 @@ js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
|
|||
} else {
|
||||
// Step 2 is irrelevant in our implementation.
|
||||
|
||||
// Step 3.
|
||||
if (!ToUint32(cx, value, &newLen))
|
||||
// Steps 3-7.
|
||||
MOZ_ASSERT_IF(attrs & JSPROP_IGNORE_VALUE, value.isUndefined());
|
||||
if (!CanonicalizeArrayLengthValue(cx, value, &newLen))
|
||||
return false;
|
||||
|
||||
// Step 4.
|
||||
double d;
|
||||
if (!ToNumber(cx, value, &d))
|
||||
return false;
|
||||
|
||||
// Step 5.
|
||||
if (d != newLen) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Steps 6-8 are irrelevant in our implementation.
|
||||
// Step 8 is irrelevant in our implementation.
|
||||
}
|
||||
|
||||
// Steps 9-11.
|
||||
|
@ -806,7 +823,7 @@ array_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
|
|||
static inline bool
|
||||
ObjectMayHaveExtraIndexedOwnProperties(JSObject* obj)
|
||||
{
|
||||
return !obj->isNative() ||
|
||||
return (!obj->isNative() && !obj->is<UnboxedArrayObject>()) ||
|
||||
obj->isIndexed() ||
|
||||
obj->is<TypedArrayObject>() ||
|
||||
ClassMayResolveId(*obj->runtimeFromAnyThread()->commonNames,
|
||||
|
@ -837,7 +854,7 @@ js::ObjectMayHaveExtraIndexedProperties(JSObject* obj)
|
|||
|
||||
if (ObjectMayHaveExtraIndexedOwnProperties(obj))
|
||||
return true;
|
||||
if (obj->as<NativeObject>().getDenseInitializedLength() != 0)
|
||||
if (GetAnyBoxedOrUnboxedInitializedLength(obj) != 0)
|
||||
return true;
|
||||
} while (true);
|
||||
}
|
||||
|
@ -1047,32 +1064,32 @@ struct StringSeparatorOp
|
|||
}
|
||||
};
|
||||
|
||||
template <typename SeparatorOp>
|
||||
static bool
|
||||
ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleNativeObject obj, uint32_t length,
|
||||
template <typename SeparatorOp, JSValueType Type>
|
||||
static DenseElementResult
|
||||
ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t length,
|
||||
StringBuffer& sb, uint32_t* numProcessed)
|
||||
{
|
||||
// This loop handles all elements up to initializedLength. If
|
||||
// length > initLength we rely on the second loop to add the
|
||||
// other elements.
|
||||
MOZ_ASSERT(*numProcessed == 0);
|
||||
uint32_t initLength = Min<uint32_t>(obj->getDenseInitializedLength(),
|
||||
uint32_t initLength = Min<uint32_t>(GetBoxedOrUnboxedInitializedLength<Type>(obj),
|
||||
length);
|
||||
while (*numProcessed < initLength) {
|
||||
if (!CheckForInterrupt(cx))
|
||||
return false;
|
||||
return DenseElementResult::Failure;
|
||||
|
||||
Value elem = obj->as<NativeObject>().getDenseElement(*numProcessed);
|
||||
|
||||
if (elem.isString()) {
|
||||
if (!sb.append(elem.toString()))
|
||||
return false;
|
||||
return DenseElementResult::Failure;
|
||||
} else if (elem.isNumber()) {
|
||||
if (!NumberValueToStringBuffer(cx, elem, sb))
|
||||
return false;
|
||||
return DenseElementResult::Failure;
|
||||
} else if (elem.isBoolean()) {
|
||||
if (!BooleanToStringBuffer(elem.toBoolean(), sb))
|
||||
return false;
|
||||
return DenseElementResult::Failure;
|
||||
} else if (elem.isObject() || elem.isSymbol()) {
|
||||
/*
|
||||
* Object stringifying could modify the initialized length or make
|
||||
|
@ -1088,12 +1105,32 @@ ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleNativeObject obj, u
|
|||
}
|
||||
|
||||
if (++(*numProcessed) != length && !sepOp(cx, sb))
|
||||
return false;
|
||||
return DenseElementResult::Failure;
|
||||
}
|
||||
|
||||
return true;
|
||||
return DenseElementResult::Incomplete;
|
||||
}
|
||||
|
||||
template <typename SeparatorOp>
|
||||
struct ArrayJoinDenseKernelFunctor {
|
||||
JSContext* cx;
|
||||
SeparatorOp sepOp;
|
||||
HandleObject obj;
|
||||
uint32_t length;
|
||||
StringBuffer& sb;
|
||||
uint32_t* numProcessed;
|
||||
|
||||
ArrayJoinDenseKernelFunctor(JSContext* cx, SeparatorOp sepOp, HandleObject obj,
|
||||
uint32_t length, StringBuffer& sb, uint32_t* numProcessed)
|
||||
: cx(cx), sepOp(sepOp), obj(obj), length(length), sb(sb), numProcessed(numProcessed)
|
||||
{}
|
||||
|
||||
template <JSValueType Type>
|
||||
DenseElementResult operator()() {
|
||||
return ArrayJoinDenseKernel<SeparatorOp, Type>(cx, sepOp, obj, length, sb, numProcessed);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename SeparatorOp>
|
||||
static bool
|
||||
ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t length,
|
||||
|
@ -1102,10 +1139,10 @@ ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t len
|
|||
uint32_t i = 0;
|
||||
|
||||
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
|
||||
if (!ArrayJoinDenseKernel<SeparatorOp>(cx, sepOp, obj.as<NativeObject>(), length, sb, &i))
|
||||
{
|
||||
ArrayJoinDenseKernelFunctor<SeparatorOp> functor(cx, sepOp, obj, length, sb, &i);
|
||||
DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != length) {
|
||||
|
@ -1176,14 +1213,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 && obj->isNative()) {
|
||||
NativeObject* nobj = &obj->as<NativeObject>();
|
||||
if (nobj->getDenseInitializedLength() == 1) {
|
||||
Value elem0 = nobj->getDenseElement(0);
|
||||
if (elem0.isString()) {
|
||||
args.rval().set(elem0);
|
||||
return true;
|
||||
}
|
||||
if (length == 1 && GetAnyBoxedOrUnboxedInitializedLength(obj) == 1) {
|
||||
Value elem0 = GetAnyBoxedOrUnboxedDenseElement(obj, 0);
|
||||
if (elem0.isString()) {
|
||||
args.rval().set(elem0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1226,7 +1260,7 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp)
|
|||
}
|
||||
|
||||
// Step 11
|
||||
JSString* str = sb.finishString();
|
||||
JSString *str = sb.finishString();
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
|
@ -1255,6 +1289,10 @@ 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())
|
||||
|
@ -1291,9 +1329,8 @@ InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start,
|
|||
return false;
|
||||
|
||||
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
|
||||
NativeObject* nobj = &obj->as<NativeObject>();
|
||||
DenseElementResult result = nobj->setOrExtendDenseElements(cx, uint32_t(start), vector,
|
||||
count, updateTypes);
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, start, vector, count, updateTypes);
|
||||
if (result != DenseElementResult::Incomplete)
|
||||
return result == DenseElementResult::Success;
|
||||
}
|
||||
|
@ -1327,45 +1364,54 @@ InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start,
|
|||
return true;
|
||||
}
|
||||
|
||||
static DenseElementResult
|
||||
ArrayReverseDenseKernel(JSContext* cx, HandleNativeObject obj, uint32_t length)
|
||||
template <JSValueType Type>
|
||||
DenseElementResult
|
||||
ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
|
||||
{
|
||||
/* An empty array or an array with no elements is already reversed. */
|
||||
if (length == 0 || obj->getDenseInitializedLength() == 0)
|
||||
if (length == 0 || GetBoxedOrUnboxedInitializedLength<Type>(obj) == 0)
|
||||
return DenseElementResult::Success;
|
||||
|
||||
if (obj->denseElementsAreFrozen())
|
||||
return DenseElementResult::Incomplete;
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
if (obj->as<NativeObject>().denseElementsAreFrozen())
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
/*
|
||||
* It's actually surprisingly complicated to reverse an array due to the
|
||||
* orthogonality of array length and array capacity while handling
|
||||
* leading and trailing holes correctly. Reversing seems less likely to
|
||||
* be a common operation than other array mass-mutation methods, so for
|
||||
* now just take a probably-small memory hit (in the absence of too many
|
||||
* holes in the array at its start) and ensure that the capacity is
|
||||
* sufficient to hold all the elements in the array if it were full.
|
||||
*/
|
||||
DenseElementResult result = obj->ensureDenseElements(cx, length, 0);
|
||||
if (result != DenseElementResult::Success)
|
||||
return result;
|
||||
/*
|
||||
* It's actually surprisingly complicated to reverse an array due to the
|
||||
* orthogonality of array length and array capacity while handling
|
||||
* leading and trailing holes correctly. Reversing seems less likely to
|
||||
* be a common operation than other array mass-mutation methods, so for
|
||||
* now just take a probably-small memory hit (in the absence of too many
|
||||
* holes in the array at its start) and ensure that the capacity is
|
||||
* sufficient to hold all the elements in the array if it were full.
|
||||
*/
|
||||
DenseElementResult result = obj->as<NativeObject>().ensureDenseElements(cx, length, 0);
|
||||
if (result != DenseElementResult::Success)
|
||||
return result;
|
||||
|
||||
/* Fill out the array's initialized length to its proper length. */
|
||||
obj->ensureDenseInitializedLength(cx, length, 0);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
RootedValue origlo(cx), orighi(cx);
|
||||
|
||||
uint32_t lo = 0, hi = length - 1;
|
||||
for (; lo < hi; lo++, hi--) {
|
||||
origlo = obj->getDenseElement(lo);
|
||||
orighi = obj->getDenseElement(hi);
|
||||
obj->setDenseElement(lo, orighi);
|
||||
origlo = GetBoxedOrUnboxedDenseElement<Type>(obj, lo);
|
||||
orighi = GetBoxedOrUnboxedDenseElement<Type>(obj, hi);
|
||||
SetBoxedOrUnboxedDenseElementNoTypeChange<Type>(obj, lo, orighi);
|
||||
if (orighi.isMagic(JS_ELEMENTS_HOLE) &&
|
||||
!SuppressDeletedProperty(cx, obj, INT_TO_JSID(lo)))
|
||||
{
|
||||
return DenseElementResult::Failure;
|
||||
}
|
||||
obj->setDenseElement(hi, origlo);
|
||||
SetBoxedOrUnboxedDenseElementNoTypeChange<Type>(obj, hi, origlo);
|
||||
if (origlo.isMagic(JS_ELEMENTS_HOLE) &&
|
||||
!SuppressDeletedProperty(cx, obj, INT_TO_JSID(hi)))
|
||||
{
|
||||
|
@ -1376,6 +1422,9 @@ ArrayReverseDenseKernel(JSContext* cx, HandleNativeObject obj, uint32_t length)
|
|||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor3(ArrayReverseDenseKernel,
|
||||
JSContext*, HandleObject, uint32_t);
|
||||
|
||||
bool
|
||||
js::array_reverse(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
|
@ -1390,8 +1439,8 @@ js::array_reverse(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
|
||||
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
|
||||
DenseElementResult result =
|
||||
ArrayReverseDenseKernel(cx, obj.as<NativeObject>(), uint32_t(len));
|
||||
ArrayReverseDenseKernelFunctor functor(cx, obj, len);
|
||||
DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
|
||||
if (result != DenseElementResult::Incomplete) {
|
||||
/*
|
||||
* Per ECMA-262, don't update the length of the array, even if the new
|
||||
|
@ -2034,8 +2083,8 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
|
||||
DenseElementResult result =
|
||||
obj->as<NativeObject>().setOrExtendDenseElements(cx, uint32_t(length),
|
||||
args.array(), args.length());
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, length,
|
||||
args.array(), args.length());
|
||||
if (result != DenseElementResult::Incomplete) {
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
|
@ -2043,8 +2092,14 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp)
|
|||
uint32_t newlength = length + args.length();
|
||||
args.rval().setNumber(newlength);
|
||||
|
||||
// Handle updates to the length of non-arrays here.
|
||||
if (!obj->is<ArrayObject>())
|
||||
// SetOrExtendAnyBoxedOrUnboxedDenseElements takes care of updating the
|
||||
// length for boxed and unboxed arrays. Handle updates to the length of
|
||||
// non-arrays here.
|
||||
bool isArray;
|
||||
if (!IsArray(cx, obj, &isArray))
|
||||
return false;
|
||||
|
||||
if (!isArray)
|
||||
return SetLengthProperty(cx, obj, newlength);
|
||||
|
||||
return true;
|
||||
|
@ -2100,46 +2155,42 @@ js::array_pop(JSContext* cx, unsigned argc, Value* vp)
|
|||
return SetLengthProperty(cx, obj, index);
|
||||
}
|
||||
|
||||
void
|
||||
js::ArrayShiftMoveElements(NativeObject* obj)
|
||||
template <JSValueType Type>
|
||||
static inline DenseElementResult
|
||||
ShiftMoveBoxedOrUnboxedDenseElements(JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable());
|
||||
|
||||
size_t initlen = obj->getDenseInitializedLength();
|
||||
MOZ_ASSERT(initlen > 0);
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
|
||||
|
||||
/*
|
||||
* At this point the length and initialized length have already been
|
||||
* decremented and the result fetched, so just shift the array elements
|
||||
* themselves.
|
||||
*/
|
||||
obj->moveDenseElementsNoPreBarrier(0, 1, initlen);
|
||||
}
|
||||
|
||||
static inline void
|
||||
SetInitializedLength(JSContext* cx, NativeObject* obj, size_t initlen)
|
||||
{
|
||||
size_t oldInitlen = obj->getDenseInitializedLength();
|
||||
obj->setDenseInitializedLength(initlen);
|
||||
if (initlen < oldInitlen)
|
||||
obj->shrinkElements(cx, initlen);
|
||||
}
|
||||
|
||||
static DenseElementResult
|
||||
MoveDenseElements(JSContext* cx, NativeObject* obj, uint32_t dstStart, uint32_t srcStart,
|
||||
uint32_t length)
|
||||
{
|
||||
if (obj->denseElementsAreFrozen())
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
if (!obj->maybeCopyElementsForWrite(cx))
|
||||
return DenseElementResult::Failure;
|
||||
obj->moveDenseElements(dstStart, srcStart, length);
|
||||
size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
obj->as<NativeObject>().moveDenseElementsNoPreBarrier(0, 1, initlen);
|
||||
} else {
|
||||
uint8_t* data = obj->as<UnboxedArrayObject>().elements();
|
||||
size_t elementSize = UnboxedTypeSize(Type);
|
||||
memmove(data, data + elementSize, initlen * elementSize);
|
||||
}
|
||||
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
static DenseElementResult
|
||||
DefineBoxedOrUnboxedFunctor1(ShiftMoveBoxedOrUnboxedDenseElements, JSObject*);
|
||||
|
||||
void
|
||||
js::ArrayShiftMoveElements(JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable());
|
||||
|
||||
ShiftMoveBoxedOrUnboxedDenseElementsFunctor functor(obj);
|
||||
JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
DenseElementResult
|
||||
ArrayShiftDenseKernel(JSContext* cx, HandleObject obj, MutableHandleValue rval)
|
||||
{
|
||||
if (ObjectMayHaveExtraIndexedProperties(obj))
|
||||
|
@ -2152,22 +2203,25 @@ ArrayShiftDenseKernel(JSContext* cx, HandleObject obj, MutableHandleValue rval)
|
|||
if (MOZ_UNLIKELY(group->hasAllFlags(OBJECT_FLAG_ITERATED)))
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
size_t initlen = obj->as<NativeObject>().getDenseInitializedLength();
|
||||
size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
|
||||
if (initlen == 0)
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
rval.set(obj->as<NativeObject>().getDenseElement(0));
|
||||
rval.set(GetBoxedOrUnboxedDenseElement<Type>(obj, 0));
|
||||
if (rval.isMagic(JS_ELEMENTS_HOLE))
|
||||
rval.setUndefined();
|
||||
|
||||
DenseElementResult result = MoveDenseElements(cx, &obj->as<NativeObject>(), 0, 1, initlen - 1);
|
||||
DenseElementResult result = MoveBoxedOrUnboxedDenseElements<Type>(cx, obj, 0, 1, initlen - 1);
|
||||
if (result != DenseElementResult::Success)
|
||||
return result;
|
||||
|
||||
SetInitializedLength(cx, obj.as<NativeObject>(), initlen - 1);
|
||||
SetBoxedOrUnboxedInitializedLength<Type>(cx, obj, initlen - 1);
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor3(ArrayShiftDenseKernel,
|
||||
JSContext*, HandleObject, MutableHandleValue);
|
||||
|
||||
/* ES5 15.4.4.9 */
|
||||
bool
|
||||
js::array_shift(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
@ -2199,7 +2253,8 @@ js::array_shift(JSContext* cx, unsigned argc, Value* vp)
|
|||
uint32_t newlen = len - 1;
|
||||
|
||||
/* Fast paths. */
|
||||
DenseElementResult result = ArrayShiftDenseKernel(cx, obj, args.rval());
|
||||
ArrayShiftDenseKernelFunctor functor(cx, obj, args.rval());
|
||||
DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
|
||||
if (result != DenseElementResult::Incomplete) {
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
|
@ -2253,6 +2308,9 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (args.length() > 0) {
|
||||
/* Slide up the array to make room for all args at the bottom. */
|
||||
if (length > 0) {
|
||||
// Only include a fast path for boxed arrays. Unboxed arrays can'nt
|
||||
// be optimized here because unshifting temporarily places holes at
|
||||
// the start of the array.
|
||||
bool optimized = false;
|
||||
do {
|
||||
if (!obj->is<ArrayObject>())
|
||||
|
@ -2312,10 +2370,10 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
|
|||
}
|
||||
|
||||
/*
|
||||
* Returns true if this is a dense array whose properties ending at |endIndex|
|
||||
* (exclusive) may be accessed (get, set, delete) directly through its
|
||||
* contiguous vector of elements without fear of getters, setters, etc. along
|
||||
* the prototype chain, or of enumerators requiring notification of
|
||||
* Returns true if this is a dense or unboxed array whose |count| properties
|
||||
* starting from |startingIndex| may be accessed (get, set, delete) directly
|
||||
* through its contiguous vector of elements without fear of getters, setters,
|
||||
* etc. along the prototype chain, or of enumerators requiring notification of
|
||||
* modifications.
|
||||
*/
|
||||
static inline bool
|
||||
|
@ -2326,11 +2384,11 @@ 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>())
|
||||
if (!arr->is<ArrayObject>() && !arr->is<UnboxedArrayObject>())
|
||||
return false;
|
||||
|
||||
/* If it's a frozen array, always pick the slow path */
|
||||
if (arr->as<ArrayObject>().denseElementsAreFrozen())
|
||||
if (arr->is<ArrayObject>() && arr->as<ArrayObject>().denseElementsAreFrozen())
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
@ -2362,23 +2420,7 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co
|
|||
* is subsumed by the initializedLength comparison.)
|
||||
*/
|
||||
return !ObjectMayHaveExtraIndexedProperties(arr) &&
|
||||
startingIndex + count <= arr->as<NativeObject>().getDenseInitializedLength();
|
||||
}
|
||||
|
||||
static inline DenseElementResult
|
||||
CopyDenseElements(JSContext* cx, NativeObject* dst, NativeObject* src,
|
||||
uint32_t dstStart, uint32_t srcStart, uint32_t length)
|
||||
{
|
||||
MOZ_ASSERT(dst->getDenseInitializedLength() == dstStart);
|
||||
MOZ_ASSERT(src->getDenseInitializedLength() >= srcStart + length);
|
||||
MOZ_ASSERT(dst->getDenseCapacity() >= dstStart + length);
|
||||
|
||||
dst->setDenseInitializedLength(dstStart + length);
|
||||
|
||||
const Value* vp = src->getDenseElements() + srcStart;
|
||||
dst->initDenseElements(dstStart, vp, length);
|
||||
|
||||
return DenseElementResult::Success;
|
||||
startingIndex + count <= GetAnyBoxedOrUnboxedInitializedLength(arr);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
|
@ -2472,9 +2514,7 @@ array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUse
|
|||
|
||||
/* Steps 10-11. */
|
||||
DebugOnly<DenseElementResult> result =
|
||||
CopyDenseElements(cx, &arr->as<NativeObject>(),
|
||||
&obj->as<NativeObject>(), 0,
|
||||
actualStart, actualDeleteCount);
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, 0, actualStart, actualDeleteCount);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
|
||||
/* Step 12 (implicit). */
|
||||
|
@ -2511,13 +2551,14 @@ array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUse
|
|||
if (CanOptimizeForDenseStorage(obj, 0, len, cx)) {
|
||||
/* Steps 15.a-b. */
|
||||
DenseElementResult result =
|
||||
MoveDenseElements(cx, &obj->as<NativeObject>(), targetIndex, sourceIndex, len - sourceIndex);
|
||||
MoveAnyBoxedOrUnboxedDenseElements(cx, obj, targetIndex, sourceIndex,
|
||||
len - sourceIndex);
|
||||
MOZ_ASSERT(result != DenseElementResult::Incomplete);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
|
||||
/* Steps 15.c-d. */
|
||||
SetInitializedLength(cx, obj.as<NativeObject>(), finalLength);
|
||||
SetAnyBoxedOrUnboxedInitializedLength(cx, obj, finalLength);
|
||||
} else {
|
||||
/*
|
||||
* This is all very slow if the length is very large. We don't yet
|
||||
|
@ -2597,15 +2638,15 @@ array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUse
|
|||
|
||||
if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) {
|
||||
DenseElementResult result =
|
||||
MoveDenseElements(cx, &obj->as<NativeObject>(), actualStart + itemCount,
|
||||
actualStart + actualDeleteCount,
|
||||
len - (actualStart + actualDeleteCount));
|
||||
MoveAnyBoxedOrUnboxedDenseElements(cx, obj, actualStart + itemCount,
|
||||
actualStart + actualDeleteCount,
|
||||
len - (actualStart + actualDeleteCount));
|
||||
MOZ_ASSERT(result != DenseElementResult::Incomplete);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
|
||||
/* Steps 16.a-b. */
|
||||
SetInitializedLength(cx, obj.as<NativeObject>(), len + itemCount - actualDeleteCount);
|
||||
SetAnyBoxedOrUnboxedInitializedLength(cx, obj, len + itemCount - actualDeleteCount);
|
||||
} else {
|
||||
RootedValue fromValue(cx);
|
||||
for (double k = len - actualDeleteCount; k > actualStart; k--) {
|
||||
|
@ -2790,7 +2831,7 @@ SliceSlowly(JSContext* cx, HandleObject obj, HandleObject receiver,
|
|||
}
|
||||
|
||||
static bool
|
||||
SliceSparse(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end, HandleArrayObject result)
|
||||
SliceSparse(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end, HandleObject result)
|
||||
{
|
||||
MOZ_ASSERT(begin <= end);
|
||||
|
||||
|
@ -2840,28 +2881,26 @@ ArraySliceOrdinary(JSContext* cx, HandleObject obj, uint32_t length, uint32_t be
|
|||
begin = end;
|
||||
|
||||
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
|
||||
size_t initlen = obj->as<NativeObject>().getDenseInitializedLength();
|
||||
size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
|
||||
size_t count = 0;
|
||||
if (initlen > begin)
|
||||
count = Min<size_t>(initlen - begin, end - begin);
|
||||
|
||||
RootedArrayObject narr(cx, NewFullyAllocatedArrayTryReuseGroup(cx, obj, count));
|
||||
RootedObject narr(cx, NewFullyAllocatedArrayTryReuseGroup(cx, obj, count));
|
||||
if (!narr)
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(count >= narr->as<ArrayObject>().length());
|
||||
narr->as<ArrayObject>().setLength(cx, count);
|
||||
SetAnyBoxedOrUnboxedArrayLength(cx, narr, end - begin);
|
||||
|
||||
if (count) {
|
||||
DebugOnly<DenseElementResult> result =
|
||||
CopyDenseElements(cx, &narr->as<NativeObject>(), &obj->as<NativeObject>(), 0, begin, count);
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, count);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
}
|
||||
arr.set(narr);
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedArrayObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin));
|
||||
RootedObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin));
|
||||
if (!narr)
|
||||
return false;
|
||||
|
||||
|
@ -2978,10 +3017,11 @@ js::array_slice(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ArraySliceDenseKernel(JSContext* cx, ArrayObject* arr, int32_t beginArg, int32_t endArg, ArrayObject* result)
|
||||
template <JSValueType Type>
|
||||
DenseElementResult
|
||||
ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t endArg, JSObject* result)
|
||||
{
|
||||
int32_t length = arr->length();
|
||||
int32_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
|
||||
|
||||
uint32_t begin = NormalizeSliceTerm(beginArg, length);
|
||||
uint32_t end = NormalizeSliceTerm(endArg, length);
|
||||
|
@ -2989,33 +3029,33 @@ ArraySliceDenseKernel(JSContext* cx, ArrayObject* arr, int32_t beginArg, int32_t
|
|||
if (begin > end)
|
||||
begin = end;
|
||||
|
||||
size_t initlen = arr->getDenseInitializedLength();
|
||||
size_t count = Min<size_t>(initlen - begin, end - begin);
|
||||
size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
|
||||
if (initlen > begin) {
|
||||
size_t count = Min<size_t>(initlen - begin, end - begin);
|
||||
if (count) {
|
||||
if (!result->ensureElements(cx, count))
|
||||
return false;
|
||||
CopyDenseElements(cx, &result->as<NativeObject>(), &arr->as<NativeObject>(), 0, begin, count);
|
||||
DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements<Type>(cx, result, count);
|
||||
if (rv != DenseElementResult::Success)
|
||||
return rv;
|
||||
CopyBoxedOrUnboxedDenseElements<Type, Type>(cx, result, obj, 0, begin, count);
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(count >= result->length());
|
||||
result->setLength(cx, count);
|
||||
|
||||
return true;
|
||||
SetAnyBoxedOrUnboxedArrayLength(cx, result, end - begin);
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor5(ArraySliceDenseKernel,
|
||||
JSContext*, JSObject*, int32_t, int32_t, JSObject*);
|
||||
|
||||
JSObject*
|
||||
js::array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end,
|
||||
HandleObject result)
|
||||
{
|
||||
if (result && IsArraySpecies(cx, obj)) {
|
||||
if (!ArraySliceDenseKernel(cx, &obj->as<ArrayObject>(), begin, end,
|
||||
&result->as<ArrayObject>()))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return result;
|
||||
ArraySliceDenseKernelFunctor functor(cx, obj, begin, end, result);
|
||||
DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result);
|
||||
MOZ_ASSERT(rv != DenseElementResult::Incomplete);
|
||||
return rv == DenseElementResult::Success ? result : nullptr;
|
||||
}
|
||||
|
||||
// Slower path if the JIT wasn't able to allocate an object inline.
|
||||
|
@ -3046,7 +3086,7 @@ array_isArray(JSContext* cx, unsigned argc, Value* vp)
|
|||
static bool
|
||||
ArrayFromCallArgs(JSContext* cx, CallArgs& args, HandleObject proto = nullptr)
|
||||
{
|
||||
ArrayObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto);
|
||||
JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
|
@ -3220,7 +3260,7 @@ ArrayConstructorImpl(JSContext* cx, CallArgs& args, bool isConstructor)
|
|||
}
|
||||
}
|
||||
|
||||
ArrayObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto);
|
||||
JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
|
@ -3246,7 +3286,7 @@ js::array_construct(JSContext* cx, unsigned argc, Value* vp)
|
|||
return ArrayConstructorImpl(cx, args, /* isConstructor = */ false);
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
JSObject*
|
||||
js::ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt)
|
||||
{
|
||||
if (lengthInt < 0) {
|
||||
|
@ -3541,7 +3581,7 @@ js::NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSOb
|
|||
return arr;
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
JSObject*
|
||||
js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap)
|
||||
{
|
||||
MOZ_ASSERT(!gc::IsInsideNursery(templateObject));
|
||||
|
@ -3554,21 +3594,30 @@ js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc
|
|||
return arr;
|
||||
}
|
||||
|
||||
// Return a new array with the specified length and allocated capacity (up to
|
||||
// maxLength), using the specified group if possible. If the specified group
|
||||
// cannot be used, ensure that the created array at least has the given
|
||||
// [[Prototype]].
|
||||
// Return a new boxed or unboxed array with the specified length and allocated
|
||||
// capacity (up to maxLength), using the specified group if possible. If the
|
||||
// specified group cannot be used, ensure that the created array at least has
|
||||
// the given [[Prototype]].
|
||||
template <uint32_t maxLength>
|
||||
static inline ArrayObject*
|
||||
static inline JSObject*
|
||||
NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length,
|
||||
NewObjectKind newKind = GenericObject)
|
||||
{
|
||||
MOZ_ASSERT(newKind != SingletonObject);
|
||||
|
||||
if (group->shouldPreTenure())
|
||||
if (group->maybePreliminaryObjects())
|
||||
group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
|
||||
|
||||
if (group->shouldPreTenure() || group->maybePreliminaryObjects())
|
||||
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)
|
||||
return nullptr;
|
||||
|
@ -3580,17 +3629,20 @@ NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length
|
|||
if (res->length() > INT32_MAX)
|
||||
res->setLength(cx, res->length());
|
||||
|
||||
if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects())
|
||||
preliminaryObjects->registerNewObject(res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
JSObject*
|
||||
js::NewFullyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length,
|
||||
NewObjectKind newKind)
|
||||
{
|
||||
return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind);
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
JSObject*
|
||||
js::NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length)
|
||||
{
|
||||
return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
|
||||
|
@ -3599,13 +3651,16 @@ js::NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup g
|
|||
// Return a new array with the default prototype and specified allocated
|
||||
// capacity and length. If possible, try to reuse the group of the input
|
||||
// object. The resulting array will either reuse the input object's group or
|
||||
// will have unknown property types.
|
||||
// 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).
|
||||
template <uint32_t maxLength>
|
||||
static inline ArrayObject*
|
||||
static inline JSObject*
|
||||
NewArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length,
|
||||
NewObjectKind newKind = GenericObject)
|
||||
{
|
||||
if (!obj->is<ArrayObject>())
|
||||
if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
|
||||
return NewArray<maxLength>(cx, length, nullptr, newKind);
|
||||
|
||||
if (obj->staticPrototype() != cx->global()->maybeGetArrayPrototype())
|
||||
|
@ -3618,20 +3673,20 @@ NewArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length,
|
|||
return NewArrayTryUseGroup<maxLength>(cx, group, length, newKind);
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
JSObject*
|
||||
js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length,
|
||||
NewObjectKind newKind)
|
||||
{
|
||||
return NewArrayTryReuseGroup<UINT32_MAX>(cx, obj, length, newKind);
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
JSObject*
|
||||
js::NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length)
|
||||
{
|
||||
return NewArrayTryReuseGroup<ArrayObject::EagerAllocationMaxLength>(cx, obj, length);
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
JSObject*
|
||||
js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
|
||||
NewObjectKind newKind)
|
||||
{
|
||||
|
@ -3641,7 +3696,7 @@ js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
|
|||
return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind);
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
JSObject*
|
||||
js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto)
|
||||
{
|
||||
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto));
|
||||
|
@ -3650,23 +3705,68 @@ js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length
|
|||
return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
bool
|
||||
js::MaybeAnalyzeBeforeCreatingLargeArray(ExclusiveContext* cx, HandleObjectGroup group,
|
||||
const Value* vp, size_t length)
|
||||
{
|
||||
static const size_t EagerPreliminaryObjectAnalysisThreshold = 800;
|
||||
|
||||
// Force analysis to see if an unboxed array can be used when making a
|
||||
// sufficiently large array, to avoid excessive analysis and copying later
|
||||
// on. If this is the first array of its group that is being created, first
|
||||
// make a dummy array with the initial elements of the array we are about
|
||||
// to make, so there is some basis for the unboxed array analysis.
|
||||
if (length > EagerPreliminaryObjectAnalysisThreshold) {
|
||||
if (PreliminaryObjectArrayWithTemplate* objects = group->maybePreliminaryObjects()) {
|
||||
if (objects->empty()) {
|
||||
size_t nlength = Min<size_t>(length, 100);
|
||||
JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, nlength);
|
||||
if (!obj)
|
||||
return false;
|
||||
DebugOnly<DenseElementResult> result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, nlength,
|
||||
ShouldUpdateTypes::Update);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
}
|
||||
objects->maybeAnalyze(cx, group, /* forceAnalyze = */ true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group,
|
||||
const Value* vp, size_t length, NewObjectKind newKind,
|
||||
ShouldUpdateTypes updateTypes)
|
||||
{
|
||||
ArrayObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length, newKind);
|
||||
if (!MaybeAnalyzeBeforeCreatingLargeArray(cx, group, vp, length))
|
||||
return nullptr;
|
||||
|
||||
JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length, newKind);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
DenseElementResult result = obj->setOrExtendDenseElements(cx, 0, vp, length, updateTypes);
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, updateTypes);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return nullptr;
|
||||
MOZ_ASSERT(result == DenseElementResult::Success);
|
||||
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;
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
JSObject*
|
||||
js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length,
|
||||
HandleObject proto /* = nullptr */)
|
||||
{
|
||||
|
|
|
@ -72,37 +72,49 @@ extern ArrayObject*
|
|||
NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSObject* templateObject);
|
||||
|
||||
/* Create a dense array with the same copy-on-write elements as another object. */
|
||||
extern ArrayObject*
|
||||
extern JSObject*
|
||||
NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap);
|
||||
|
||||
extern ArrayObject*
|
||||
// The methods below can create either boxed or unboxed arrays.
|
||||
|
||||
extern JSObject*
|
||||
NewFullyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length,
|
||||
NewObjectKind newKind = GenericObject);
|
||||
|
||||
extern ArrayObject*
|
||||
extern JSObject*
|
||||
NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length);
|
||||
|
||||
extern ArrayObject*
|
||||
extern JSObject*
|
||||
NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length,
|
||||
NewObjectKind newKind = GenericObject);
|
||||
|
||||
extern ArrayObject*
|
||||
extern JSObject*
|
||||
NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length);
|
||||
|
||||
extern ArrayObject*
|
||||
extern JSObject*
|
||||
NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
|
||||
NewObjectKind newKind = GenericObject);
|
||||
|
||||
extern ArrayObject*
|
||||
extern JSObject*
|
||||
NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto);
|
||||
|
||||
extern ArrayObject*
|
||||
enum class ShouldUpdateTypes
|
||||
{
|
||||
Update,
|
||||
DontUpdate
|
||||
};
|
||||
|
||||
extern bool
|
||||
MaybeAnalyzeBeforeCreatingLargeArray(ExclusiveContext* cx, HandleObjectGroup group,
|
||||
const Value* vp, size_t length);
|
||||
|
||||
extern JSObject*
|
||||
NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group,
|
||||
const Value* vp, size_t length,
|
||||
NewObjectKind newKind = GenericObject,
|
||||
ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update);
|
||||
|
||||
extern ArrayObject*
|
||||
extern JSObject*
|
||||
NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length,
|
||||
HandleObject proto = nullptr);
|
||||
|
||||
|
@ -117,6 +129,13 @@ NewValuePair(JSContext* cx, const Value& val1, const Value& val2, MutableHandleV
|
|||
extern bool
|
||||
WouldDefinePastNonwritableLength(HandleNativeObject obj, uint32_t index);
|
||||
|
||||
/*
|
||||
* Canonicalize |vp| to a uint32_t value potentially suitable for use as an
|
||||
* array length.
|
||||
*/
|
||||
extern bool
|
||||
CanonicalizeArrayLengthValue(JSContext* cx, HandleValue v, uint32_t* canonicalized);
|
||||
|
||||
extern bool
|
||||
GetLengthProperty(JSContext* cx, HandleObject obj, uint32_t* lengthp);
|
||||
|
||||
|
@ -150,7 +169,7 @@ extern bool
|
|||
array_join(JSContext* cx, unsigned argc, js::Value* vp);
|
||||
|
||||
extern void
|
||||
ArrayShiftMoveElements(NativeObject* obj);
|
||||
ArrayShiftMoveElements(JSObject* obj);
|
||||
|
||||
extern bool
|
||||
array_shift(JSContext* cx, unsigned argc, js::Value* vp);
|
||||
|
@ -182,7 +201,7 @@ extern const JSJitInfo array_splice_info;
|
|||
extern bool
|
||||
NewbornArrayPush(JSContext* cx, HandleObject obj, const Value& v);
|
||||
|
||||
extern ArrayObject*
|
||||
extern JSObject*
|
||||
ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt);
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -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>())
|
||||
else if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>())
|
||||
*cls = ESClass::Array;
|
||||
else if (obj->is<NumberObject>())
|
||||
*cls = ESClass::Number;
|
||||
|
|
|
@ -1136,18 +1136,19 @@ js::CloneObject(JSContext* cx, HandleObject obj, Handle<js::TaggedProto> proto)
|
|||
}
|
||||
|
||||
static bool
|
||||
GetScriptArrayObjectElements(JSContext* cx, HandleArrayObject arr, MutableHandle<GCVector<Value>> values)
|
||||
GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, MutableHandle<GCVector<Value>> values)
|
||||
{
|
||||
MOZ_ASSERT(!arr->isSingleton());
|
||||
MOZ_ASSERT(!arr->isIndexed());
|
||||
MOZ_ASSERT(!obj->isSingleton());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
MOZ_ASSERT(!obj->isIndexed());
|
||||
|
||||
size_t length = arr->length();
|
||||
size_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
|
||||
if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), length))
|
||||
return false;
|
||||
|
||||
size_t initlen = arr->getDenseInitializedLength();
|
||||
size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
|
||||
for (size_t i = 0; i < initlen; i++)
|
||||
values[i].set(arr->getDenseElement(i));
|
||||
values[i].set(GetAnyBoxedOrUnboxedDenseElement(obj, i));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1199,12 +1200,13 @@ js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKin
|
|||
MOZ_ASSERT_IF(obj->isSingleton(),
|
||||
cx->compartment()->behaviors().getSingletonsAsTemplates());
|
||||
MOZ_ASSERT(obj->is<PlainObject>() ||
|
||||
obj->is<ArrayObject>());
|
||||
obj->is<ArrayObject>() ||
|
||||
obj->is<UnboxedArrayObject>());
|
||||
MOZ_ASSERT(newKind != SingletonObject);
|
||||
|
||||
if (obj->is<ArrayObject>()) {
|
||||
if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) {
|
||||
Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
|
||||
if (!GetScriptArrayObjectElements(cx, obj.as<ArrayObject>(), &values))
|
||||
if (!GetScriptArrayObjectElements(cx, obj, &values))
|
||||
return nullptr;
|
||||
|
||||
// Deep clone any elements.
|
||||
|
@ -1318,8 +1320,9 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
|
|||
{
|
||||
if (mode == XDR_ENCODE) {
|
||||
MOZ_ASSERT(obj->is<PlainObject>() ||
|
||||
obj->is<ArrayObject>());
|
||||
isArray = obj->is<ArrayObject>() ? 1 : 0;
|
||||
obj->is<ArrayObject>() ||
|
||||
obj->is<UnboxedArrayObject>());
|
||||
isArray = (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (!xdr->codeUint32(&isArray))
|
||||
|
@ -1331,11 +1334,8 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
|
|||
|
||||
if (isArray) {
|
||||
Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
|
||||
if (mode == XDR_ENCODE) {
|
||||
RootedArrayObject arr(cx, &obj->as<ArrayObject>());
|
||||
if (!GetScriptArrayObjectElements(cx, arr, &values))
|
||||
return false;
|
||||
}
|
||||
if (mode == XDR_ENCODE && !GetScriptArrayObjectElements(cx, obj, &values))
|
||||
return false;
|
||||
|
||||
uint32_t initialized;
|
||||
if (mode == XDR_ENCODE)
|
||||
|
@ -2315,6 +2315,11 @@ js::LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Shape**
|
|||
// us the resolve hook won't define a property with this id.
|
||||
if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
|
||||
return false;
|
||||
} 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);
|
||||
|
@ -3606,6 +3611,16 @@ JSObject::allocKindForTenure(const js::Nursery& nursery) const
|
|||
if (IsProxy(this))
|
||||
return as<ProxyObject>().allocKindForTenure();
|
||||
|
||||
// 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,6 +40,8 @@ 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -2373,7 +2373,7 @@ js::str_replace_string_raw(JSContext* cx, HandleString string, HandleString patt
|
|||
}
|
||||
|
||||
// ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.
|
||||
static ArrayObject*
|
||||
static JSObject*
|
||||
SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleLinearString sep,
|
||||
HandleObjectGroup group)
|
||||
{
|
||||
|
@ -2470,7 +2470,7 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleLinearS
|
|||
}
|
||||
|
||||
// Fast-path for splitting a string into a character array via split("").
|
||||
static ArrayObject*
|
||||
static JSObject*
|
||||
CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleObjectGroup group)
|
||||
{
|
||||
size_t strLength = str->length();
|
||||
|
@ -2495,7 +2495,7 @@ CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleObj
|
|||
}
|
||||
|
||||
// ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.
|
||||
ArrayObject*
|
||||
JSObject*
|
||||
js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep, uint32_t limit)
|
||||
|
||||
{
|
||||
|
|
|
@ -465,7 +465,7 @@ FileEscapedString(FILE* fp, const char* chars, size_t length, uint32_t quote)
|
|||
return res;
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
JSObject*
|
||||
str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep,
|
||||
uint32_t limit);
|
||||
|
||||
|
|
|
@ -317,6 +317,7 @@ 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;
|
||||
|
@ -7262,6 +7263,7 @@ 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");
|
||||
|
||||
|
@ -7271,6 +7273,7 @@ SetContextOptions(JSContext* cx, const OptionParser& op)
|
|||
.setWasm(enableWasm)
|
||||
.setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
|
||||
.setNativeRegExp(enableNativeRegExp)
|
||||
.setUnboxedArrays(enableUnboxedArrays)
|
||||
.setArrayProtoValues(enableArrayProtoValues);
|
||||
|
||||
if (op.getBoolOption("wasm-check-bce"))
|
||||
|
@ -7710,6 +7713,7 @@ 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")
|
||||
|
|
|
@ -593,7 +593,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>());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
|
||||
if (op == JSOP_INITELEM_INC && index == INT32_MAX) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SPREAD_TOO_LARGE);
|
||||
|
|
|
@ -1939,7 +1939,6 @@ CASE(EnableInterruptsPseudoOpcode)
|
|||
/* Various 1-byte no-ops. */
|
||||
CASE(JSOP_NOP)
|
||||
CASE(JSOP_NOP_DESTRUCTURING)
|
||||
CASE(JSOP_UNUSED126)
|
||||
CASE(JSOP_UNUSED211)
|
||||
CASE(JSOP_TRY_DESTRUCTURING_ITERCLOSE)
|
||||
CASE(JSOP_UNUSED221)
|
||||
|
@ -3682,6 +3681,7 @@ CASE(JSOP_NEWINIT)
|
|||
END_CASE(JSOP_NEWINIT)
|
||||
|
||||
CASE(JSOP_NEWARRAY)
|
||||
CASE(JSOP_SPREADCALLARRAY)
|
||||
{
|
||||
uint32_t length = GET_UINT32(REGS.pc);
|
||||
JSObject* obj = NewArrayOperation(cx, script, REGS.pc, length);
|
||||
|
@ -4979,7 +4979,7 @@ js::NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc,
|
|||
newKind = TenuredObject;
|
||||
}
|
||||
|
||||
RootedPlainObject obj(cx);
|
||||
RootedObject obj(cx);
|
||||
|
||||
if (*pc == JSOP_NEWOBJECT) {
|
||||
RootedPlainObject baseObject(cx, &script->getObject(pc)->as<PlainObject>());
|
||||
|
@ -5047,6 +5047,9 @@ 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);
|
||||
|
@ -5057,6 +5060,9 @@ js::NewArrayOperation(JSContext* cx, HandleScript script, jsbytecode* pc, uint32
|
|||
MOZ_ASSERT(obj->isSingleton());
|
||||
} else {
|
||||
obj->setGroup(group);
|
||||
|
||||
if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects())
|
||||
preliminaryObjects->registerNewObject(obj);
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
@ -5069,6 +5075,12 @@ 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)
|
||||
|
|
|
@ -606,8 +606,8 @@ JSONParserBase::finishArray(MutableHandleValue vp, ElementVector& elements)
|
|||
{
|
||||
MOZ_ASSERT(&elements == &stack.back().elements());
|
||||
|
||||
ArrayObject* obj = ObjectGroup::newArrayObject(cx, elements.begin(), elements.length(),
|
||||
GenericObject);
|
||||
JSObject* obj = ObjectGroup::newArrayObject(cx, elements.begin(), elements.length(),
|
||||
GenericObject);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -235,38 +235,6 @@ NativeObject::ensureDenseElements(ExclusiveContext* cx, uint32_t index, uint32_t
|
|||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
inline DenseElementResult
|
||||
NativeObject::setOrExtendDenseElements(ExclusiveContext* cx, uint32_t start, const Value* vp,
|
||||
uint32_t count,
|
||||
ShouldUpdateTypes updateTypes)
|
||||
{
|
||||
if (denseElementsAreFrozen())
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
if (is<ArrayObject>() &&
|
||||
!as<ArrayObject>().lengthIsWritable() &&
|
||||
start + count >= as<ArrayObject>().length())
|
||||
{
|
||||
return DenseElementResult::Incomplete;
|
||||
}
|
||||
|
||||
DenseElementResult result = ensureDenseElements(cx, start, count);
|
||||
if (result != DenseElementResult::Success)
|
||||
return result;
|
||||
|
||||
if (is<ArrayObject>() && start + count >= as<ArrayObject>().length())
|
||||
as<ArrayObject>().setLengthInt32(start + count);
|
||||
|
||||
if (updateTypes == ShouldUpdateTypes::DontUpdate && !shouldConvertDoubleElements()) {
|
||||
copyDenseElements(start, vp, count);
|
||||
} else {
|
||||
for (size_t i = 0; i < count; i++)
|
||||
setDenseElementWithType(cx, start + i, vp[i]);
|
||||
}
|
||||
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
inline Value
|
||||
NativeObject::getDenseOrTypedArrayElement(uint32_t idx)
|
||||
{
|
||||
|
|
|
@ -339,19 +339,16 @@ IsObjectValueInCompartment(const Value& v, JSCompartment* comp);
|
|||
#endif
|
||||
|
||||
// Operations which change an object's dense elements can either succeed, fail,
|
||||
// or be unable to complete. The latter is used when the object's elements must
|
||||
// become sparse instead. The enum below is used for such operations.
|
||||
// or be unable to complete. For native objects, the latter is used when the
|
||||
// object's elements must become sparse instead. The enum below is used for
|
||||
// such operations, and for similar operations on unboxed arrays and methods
|
||||
// that work on both kinds of objects.
|
||||
enum class DenseElementResult {
|
||||
Failure,
|
||||
Success,
|
||||
Incomplete
|
||||
};
|
||||
|
||||
enum class ShouldUpdateTypes {
|
||||
Update,
|
||||
DontUpdate
|
||||
};
|
||||
|
||||
/*
|
||||
* NativeObject specifies the internal implementation of a native object.
|
||||
*
|
||||
|
@ -1154,10 +1151,6 @@ class NativeObject : public ShapedObject
|
|||
elementsRangeWriteBarrierPost(dstStart, count);
|
||||
}
|
||||
|
||||
inline DenseElementResult
|
||||
setOrExtendDenseElements(ExclusiveContext* cx, uint32_t start, const Value* vp, uint32_t count,
|
||||
ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update);
|
||||
|
||||
bool shouldConvertDoubleElements() {
|
||||
return getElementsHeader()->shouldConvertDoubleElements();
|
||||
}
|
||||
|
|
|
@ -777,7 +777,7 @@ GetValueTypeForTable(const Value& v)
|
|||
return type;
|
||||
}
|
||||
|
||||
/* static */ ArrayObject*
|
||||
/* static */ JSObject*
|
||||
ObjectGroup::newArrayObject(ExclusiveContext* cx,
|
||||
const Value* vp, size_t length,
|
||||
NewObjectKind newKind, NewArrayKind arrayKind)
|
||||
|
@ -841,13 +841,56 @@ ObjectGroup::newArrayObject(ExclusiveContext* cx,
|
|||
|
||||
AddTypePropertyId(cx, group, nullptr, JSID_VOID, elementType);
|
||||
|
||||
if (elementType != TypeSet::UnknownType()) {
|
||||
// Keep track of the initial objects we create with this type.
|
||||
// If the initial ones have a consistent shape and property types, we
|
||||
// will try to use an unboxed layout for the group.
|
||||
PreliminaryObjectArrayWithTemplate* preliminaryObjects =
|
||||
cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr);
|
||||
if (!preliminaryObjects)
|
||||
return nullptr;
|
||||
group->setPreliminaryObjects(preliminaryObjects);
|
||||
}
|
||||
|
||||
if (!p.add(cx, *table, ObjectGroupCompartment::ArrayObjectKey(elementType), group))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The type of the elements being added will already be reflected in type
|
||||
// information.
|
||||
// information, but make sure when creating an unboxed array that the
|
||||
// common element type is suitable for the unboxed representation.
|
||||
ShouldUpdateTypes updateTypes = ShouldUpdateTypes::DontUpdate;
|
||||
if (!MaybeAnalyzeBeforeCreatingLargeArray(cx, group, vp, length))
|
||||
return nullptr;
|
||||
if (group->maybePreliminaryObjects())
|
||||
group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
|
||||
if (group->maybeUnboxedLayout()) {
|
||||
switch (group->unboxedLayout().elementType()) {
|
||||
case JSVAL_TYPE_BOOLEAN:
|
||||
if (elementType != TypeSet::BooleanType())
|
||||
updateTypes = ShouldUpdateTypes::Update;
|
||||
break;
|
||||
case JSVAL_TYPE_INT32:
|
||||
if (elementType != TypeSet::Int32Type())
|
||||
updateTypes = ShouldUpdateTypes::Update;
|
||||
break;
|
||||
case JSVAL_TYPE_DOUBLE:
|
||||
if (elementType != TypeSet::Int32Type() && elementType != TypeSet::DoubleType())
|
||||
updateTypes = ShouldUpdateTypes::Update;
|
||||
break;
|
||||
case JSVAL_TYPE_STRING:
|
||||
if (elementType != TypeSet::StringType())
|
||||
updateTypes = ShouldUpdateTypes::Update;
|
||||
break;
|
||||
case JSVAL_TYPE_OBJECT:
|
||||
if (elementType != TypeSet::NullType() && !elementType.get().isObjectUnchecked())
|
||||
updateTypes = ShouldUpdateTypes::Update;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
return NewCopiedArrayTryUseGroup(cx, group, vp, length, newKind, updateTypes);
|
||||
}
|
||||
|
||||
|
@ -857,15 +900,49 @@ GiveObjectGroup(ExclusiveContext* cx, JSObject* source, JSObject* target)
|
|||
{
|
||||
MOZ_ASSERT(source->group() != target->group());
|
||||
|
||||
if (!target->is<ArrayObject>() || !source->is<ArrayObject>()) {
|
||||
if (!target->is<ArrayObject>() && !target->is<UnboxedArrayObject>())
|
||||
return true;
|
||||
|
||||
if (target->group()->maybePreliminaryObjects()) {
|
||||
bool force = IsInsideNursery(source);
|
||||
target->group()->maybePreliminaryObjects()->maybeAnalyze(cx, target->group(), force);
|
||||
}
|
||||
|
||||
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>()) {
|
||||
source->setGroup(target->group());
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sourceGroup->maybePreliminaryObjects())
|
||||
sourceGroup->maybePreliminaryObjects()->unregisterObject(source);
|
||||
if (target->group()->maybePreliminaryObjects())
|
||||
target->group()->maybePreliminaryObjects()->registerNewObject(source);
|
||||
|
||||
for (size_t i = 0; i < source->as<ArrayObject>().getDenseInitializedLength(); i++) {
|
||||
Value v = source->as<ArrayObject>().getDenseElement(i);
|
||||
AddTypePropertyId(cx, source->group(), source, JSID_VOID, v);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
source->setGroup(target->group());
|
||||
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;
|
||||
|
||||
for (size_t i = 0; i < source->as<ArrayObject>().getDenseInitializedLength(); i++) {
|
||||
Value v = source->as<ArrayObject>().getDenseElement(i);
|
||||
AddTypePropertyId(cx, source->group(), source, JSID_VOID, v);
|
||||
return source->as<UnboxedArrayObject>().convertInt32ToDouble(cx, target->group());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1429,6 +1506,18 @@ 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,11 +505,11 @@ class ObjectGroup : public gc::TenuredCell
|
|||
UnknownIndex // Make an array with an unknown element type.
|
||||
};
|
||||
|
||||
// Create an ArrayObject with the specified elements and a group specialized
|
||||
// for the elements.
|
||||
static ArrayObject* newArrayObject(ExclusiveContext* cx, const Value* vp, size_t length,
|
||||
NewObjectKind newKind,
|
||||
NewArrayKind arrayKind = NewArrayKind::Normal);
|
||||
// Create an ArrayObject or UnboxedArrayObject with the specified elements
|
||||
// and a group specialized for the elements.
|
||||
static JSObject* newArrayObject(ExclusiveContext* cx, const Value* vp, size_t length,
|
||||
NewObjectKind newKind,
|
||||
NewArrayKind arrayKind = NewArrayKind::Normal);
|
||||
|
||||
// Create a PlainObject or UnboxedPlainObject with the specified properties
|
||||
// and a group specialized for those properties.
|
||||
|
|
|
@ -1281,7 +1281,17 @@
|
|||
* Stack: receiver, obj, propval => obj[propval]
|
||||
*/ \
|
||||
macro(JSOP_GETELEM_SUPER, 125, "getelem-super", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) \
|
||||
macro(JSOP_UNUSED126, 126, "unused126", NULL, 5, 0, 1, JOF_UINT32) \
|
||||
/*
|
||||
* Pushes newly created array for a spread call onto the stack. This has
|
||||
* the same semantics as JSOP_NEWARRAY, but is distinguished to avoid
|
||||
* using unboxed arrays in spread calls, which would make compiling spread
|
||||
* calls in baseline more complex.
|
||||
* Category: Literals
|
||||
* Type: Array
|
||||
* Operands: uint32_t length
|
||||
* Stack: => obj
|
||||
*/ \
|
||||
macro(JSOP_SPREADCALLARRAY, 126, "spreadcallarray", NULL, 5, 0, 1, JOF_UINT32) \
|
||||
\
|
||||
/*
|
||||
* Defines the given function on the current scope.
|
||||
|
|
|
@ -19,7 +19,7 @@ ReceiverGuard::ReceiverGuard(JSObject* obj)
|
|||
group = obj->group();
|
||||
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
|
||||
shape = expando->lastProperty();
|
||||
} else if (obj->is<TypedObject>()) {
|
||||
} else if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) {
|
||||
group = obj->group();
|
||||
} else {
|
||||
shape = obj->maybeShape();
|
||||
|
@ -34,7 +34,7 @@ ReceiverGuard::ReceiverGuard(ObjectGroup* group, Shape* shape)
|
|||
const Class* clasp = group->clasp();
|
||||
if (clasp == &UnboxedPlainObject::class_) {
|
||||
// Keep both group and shape.
|
||||
} else if (IsTypedObjectClass(clasp)) {
|
||||
} else if (clasp == &UnboxedArrayObject::class_ || IsTypedObjectClass(clasp)) {
|
||||
this->shape = nullptr;
|
||||
} else {
|
||||
this->group = nullptr;
|
||||
|
@ -49,8 +49,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<TypedObject>()) {
|
||||
// Only the group needs to be guarded for typed objects.
|
||||
if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) {
|
||||
// Only the group needs to be guarded for unboxed arrays and typed objects.
|
||||
return 2;
|
||||
}
|
||||
// Other objects only need the shape to be guarded.
|
||||
|
|
|
@ -82,7 +82,7 @@ InterpreterFrame::isNonGlobalEvalFrame() const
|
|||
return isEvalFrame() && script()->bodyScope()->as<EvalScope>().isNonGlobal();
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
JSObject*
|
||||
InterpreterFrame::createRestParameter(JSContext* cx)
|
||||
{
|
||||
MOZ_ASSERT(script()->hasRest());
|
||||
|
|
|
@ -523,7 +523,7 @@ class InterpreterFrame
|
|||
ArgumentsObject& argsObj() const;
|
||||
void initArgsObj(ArgumentsObject& argsobj);
|
||||
|
||||
ArrayObject* createRestParameter(JSContext* cx);
|
||||
JSObject* createRestParameter(JSContext* cx);
|
||||
|
||||
/*
|
||||
* Environment chain
|
||||
|
|
|
@ -2509,7 +2509,7 @@ TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList* constraints, jsid
|
|||
bool
|
||||
js::ClassCanHaveExtraProperties(const Class* clasp)
|
||||
{
|
||||
if (clasp == &UnboxedPlainObject::class_)
|
||||
if (clasp == &UnboxedPlainObject::class_ || clasp == &UnboxedArrayObject::class_)
|
||||
return false;
|
||||
return clasp->getResolve()
|
||||
|| clasp->getOpsLookupProperty()
|
||||
|
@ -3400,7 +3400,7 @@ JSFunction::setTypeForScriptedFunction(ExclusiveContext* cx, HandleFunction fun,
|
|||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
PreliminaryObjectArray::registerNewObject(PlainObject* res)
|
||||
PreliminaryObjectArray::registerNewObject(JSObject* res)
|
||||
{
|
||||
// The preliminary object pointers are weak, and won't be swept properly
|
||||
// during nursery collections, so the preliminary objects need to be
|
||||
|
@ -3418,7 +3418,7 @@ PreliminaryObjectArray::registerNewObject(PlainObject* res)
|
|||
}
|
||||
|
||||
void
|
||||
PreliminaryObjectArray::unregisterObject(PlainObject* obj)
|
||||
PreliminaryObjectArray::unregisterObject(JSObject* obj)
|
||||
{
|
||||
for (size_t i = 0; i < COUNT; i++) {
|
||||
if (objects[i] == obj) {
|
||||
|
|
|
@ -871,8 +871,8 @@ class PreliminaryObjectArray
|
|||
public:
|
||||
PreliminaryObjectArray() = default;
|
||||
|
||||
void registerNewObject(PlainObject* res);
|
||||
void unregisterObject(PlainObject* obj);
|
||||
void registerNewObject(JSObject* res);
|
||||
void unregisterObject(JSObject* obj);
|
||||
|
||||
JSObject* get(size_t i) const {
|
||||
MOZ_ASSERT(i < COUNT);
|
||||
|
|
|
@ -172,6 +172,669 @@ 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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
|
||||
|
||||
if (start > nobj->initializedLength())
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
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]);
|
||||
} else {
|
||||
for (size_t j = start; i < count && j < oldInitlen; i++, j++) {
|
||||
if (!nobj->setElementSpecific<Type>(cx, j, vp[i]))
|
||||
return DenseElementResult::Incomplete;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
SetBoxedOrUnboxedInitializedLength<DstType>(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);
|
||||
}
|
||||
}
|
||||
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Dispatch to specialized methods based on the type of an object.
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Goop to fix MSVC. See DispatchTraceKindTyped in TraceKind.h.
|
||||
// The clang-cl front end defines _MSC_VER, but still requires the explicit
|
||||
// template declaration, so we must test for __clang__ here as well.
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# define DEPENDENT_TEMPLATE_HINT
|
||||
#else
|
||||
# define DEPENDENT_TEMPLATE_HINT template
|
||||
#endif
|
||||
|
||||
// Function to dispatch a method specialized to whatever boxed or unboxed dense
|
||||
// elements which an input object has.
|
||||
template <typename F>
|
||||
DenseElementResult
|
||||
CallBoxedOrUnboxedSpecialization(F f, JSObject* obj)
|
||||
{
|
||||
if (!HasAnyBoxedOrUnboxedDenseElements(obj))
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// As above, except the specialization can reflect the unboxed type of two objects.
|
||||
template <typename F>
|
||||
DenseElementResult
|
||||
CallBoxedOrUnboxedSpecialization(F f, JSObject* obj1, JSObject* obj2)
|
||||
{
|
||||
if (!HasAnyBoxedOrUnboxedDenseElements(obj1) || !HasAnyBoxedOrUnboxedDenseElements(obj2))
|
||||
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
|
||||
}
|
||||
|
||||
#undef DEPENDENT_TEMPLATE_HINT
|
||||
|
||||
#define DefineBoxedOrUnboxedFunctor1(Signature, A) \
|
||||
struct Signature ## Functor { \
|
||||
A a; \
|
||||
explicit Signature ## Functor(A a) \
|
||||
: a(a) \
|
||||
{} \
|
||||
template <JSValueType Type> \
|
||||
DenseElementResult operator()() { \
|
||||
return Signature<Type>(a); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define DefineBoxedOrUnboxedFunctor3(Signature, A, B, C) \
|
||||
struct Signature ## Functor { \
|
||||
A a; B b; C c; \
|
||||
Signature ## Functor(A a, B b, C c) \
|
||||
: a(a), b(b), c(c) \
|
||||
{} \
|
||||
template <JSValueType Type> \
|
||||
DenseElementResult operator()() { \
|
||||
return Signature<Type>(a, b, c); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define DefineBoxedOrUnboxedFunctor4(Signature, A, B, C, D) \
|
||||
struct Signature ## Functor { \
|
||||
A a; B b; C c; D d; \
|
||||
Signature ## Functor(A a, B b, C c, D d) \
|
||||
: a(a), b(b), c(c), d(d) \
|
||||
{} \
|
||||
template <JSValueType Type> \
|
||||
DenseElementResult operator()() { \
|
||||
return Signature<Type>(a, b, c, d); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define DefineBoxedOrUnboxedFunctorPair4(Signature, A, B, C, D) \
|
||||
struct Signature ## Functor { \
|
||||
A a; B b; C c; D d; \
|
||||
Signature ## Functor(A a, B b, C c, D d) \
|
||||
: a(a), b(b), c(c), d(d) \
|
||||
{} \
|
||||
template <JSValueType TypeOne, JSValueType TypeTwo> \
|
||||
DenseElementResult operator()() { \
|
||||
return Signature<TypeOne, TypeTwo>(a, b, c, d); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define DefineBoxedOrUnboxedFunctor5(Signature, A, B, C, D, E) \
|
||||
struct Signature ## Functor { \
|
||||
A a; B b; C c; D d; E e; \
|
||||
Signature ## Functor(A a, B b, C c, D d, E e) \
|
||||
: a(a), b(b), c(c), d(d), e(e) \
|
||||
{} \
|
||||
template <JSValueType Type> \
|
||||
DenseElementResult operator()() { \
|
||||
return Signature<Type>(a, b, c, d, e); \
|
||||
} \
|
||||
}
|
||||
|
||||
#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; \
|
||||
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 TypeOne, JSValueType TypeTwo> \
|
||||
DenseElementResult operator()() { \
|
||||
return Signature<TypeOne, TypeTwo>(a, b, c, d, e, f); \
|
||||
} \
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -417,6 +417,8 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
|
|||
|
||||
RootedObjectGroup replacementGroup(cx);
|
||||
|
||||
const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_;
|
||||
|
||||
// Immediately clear any new script on the group. This is done by replacing
|
||||
// the existing new script with one for a replacement default new group.
|
||||
// This is done so that the size of the replacment group's objects is the
|
||||
|
@ -424,6 +426,8 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
|
|||
// slot accesses later on for sites that see converted objects from this
|
||||
// group and objects that were allocated using the replacement new group.
|
||||
if (layout.newScript()) {
|
||||
MOZ_ASSERT(!layout.isArray());
|
||||
|
||||
replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
|
||||
if (!replacementGroup)
|
||||
return false;
|
||||
|
@ -449,14 +453,15 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
|
|||
RootedScript script(cx, layout.allocationScript());
|
||||
jsbytecode* pc = layout.allocationPc();
|
||||
|
||||
replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
|
||||
replacementGroup = ObjectGroupCompartment::makeGroup(cx, clasp, proto);
|
||||
if (!replacementGroup)
|
||||
return false;
|
||||
|
||||
PlainObject* templateObject = &script->getObject(pc)->as<PlainObject>();
|
||||
replacementGroup->addDefiniteProperties(cx, templateObject->lastProperty());
|
||||
|
||||
cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc, JSProto_Object,
|
||||
JSProtoKey key = layout.isArray() ? JSProto_Array : JSProto_Object;
|
||||
cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc, key,
|
||||
replacementGroup);
|
||||
|
||||
// Clear any baseline information at this opcode which might use the old group.
|
||||
|
@ -472,12 +477,22 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
|
|||
}
|
||||
}
|
||||
|
||||
size_t nfixed = gc::GetGCKindSlots(layout.getAllocKind());
|
||||
size_t nfixed = layout.isArray() ? 0 : gc::GetGCKindSlots(layout.getAllocKind());
|
||||
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PlainObject::class_, proto, nfixed, 0));
|
||||
if (layout.isArray()) {
|
||||
// The length shape to use for arrays is cached via a modified initial
|
||||
// shape for array objects. Create an array now to make sure this entry
|
||||
// is instantiated.
|
||||
if (!NewDenseEmptyArray(cx))
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, proto, nfixed, 0));
|
||||
if (!shape)
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT_IF(layout.isArray(), !shape->isEmptyShape() && shape->slotSpan() == 0);
|
||||
|
||||
// Add shapes for each property, if this is for a plain object.
|
||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
||||
const UnboxedLayout::Property& property = layout.properties()[i];
|
||||
|
@ -490,7 +505,7 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
|
|||
}
|
||||
|
||||
ObjectGroup* nativeGroup =
|
||||
ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto,
|
||||
ObjectGroupCompartment::makeGroup(cx, clasp, proto,
|
||||
group->flags() & OBJECT_FLAG_DYNAMIC_MASK);
|
||||
if (!nativeGroup)
|
||||
return false;
|
||||
|
@ -498,19 +513,24 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
|
|||
// No sense propagating if we don't know what we started with.
|
||||
if (!group->unknownProperties()) {
|
||||
// Propagate all property types from the old group to the new group.
|
||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
||||
const UnboxedLayout::Property& property = layout.properties()[i];
|
||||
jsid id = NameToId(property.name);
|
||||
if (!PropagatePropertyTypes(cx, id, group, nativeGroup))
|
||||
if (layout.isArray()) {
|
||||
if (!PropagatePropertyTypes(cx, JSID_VOID, group, nativeGroup))
|
||||
return false;
|
||||
} else {
|
||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
||||
const UnboxedLayout::Property& property = layout.properties()[i];
|
||||
jsid id = NameToId(property.name);
|
||||
if (!PropagatePropertyTypes(cx, id, group, nativeGroup))
|
||||
return false;
|
||||
|
||||
// If we are OOM we may not be able to propagate properties.
|
||||
if (nativeGroup->unknownProperties())
|
||||
break;
|
||||
// If we are OOM we may not be able to propagate properties.
|
||||
if (nativeGroup->unknownProperties())
|
||||
break;
|
||||
|
||||
HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id);
|
||||
if (nativeProperty && nativeProperty->canSetDefinite(i))
|
||||
nativeProperty->setDefinite(i);
|
||||
HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id);
|
||||
if (nativeProperty && nativeProperty->canSetDefinite(i))
|
||||
nativeProperty->setDefinite(i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If we skip, though, the new group had better agree.
|
||||
|
@ -925,6 +945,692 @@ 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
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
@ -935,6 +1641,31 @@ NextValue(Handle<GCVector<Value>> values, size_t* valueCursor)
|
|||
return values[(*valueCursor)++];
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
void
|
||||
UnboxedPlainObject::fillAfterConvert(ExclusiveContext* cx,
|
||||
Handle<GCVector<Value>> values, size_t* valueCursor)
|
||||
|
@ -944,3 +1675,58 @@ UnboxedPlainObject::fillAfterConvert(ExclusiveContext* cx,
|
|||
for (size_t i = 0; i < layout().properties().length(); i++)
|
||||
JS_ALWAYS_TRUE(setValue(cx, layout().properties()[i], NextValue(values, valueCursor)));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -96,11 +96,17 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
|||
// from an array of values.
|
||||
GCPtrJitCode constructorCode_;
|
||||
|
||||
// The following members are only used for unboxed arrays.
|
||||
|
||||
// The type of array elements.
|
||||
JSValueType elementType_;
|
||||
|
||||
public:
|
||||
UnboxedLayout()
|
||||
: nativeGroup_(nullptr), nativeShape_(nullptr),
|
||||
allocationScript_(nullptr), allocationPc_(nullptr), replacementGroup_(nullptr),
|
||||
size_(0), newScript_(nullptr), traceList_(nullptr), constructorCode_(nullptr)
|
||||
size_(0), newScript_(nullptr), traceList_(nullptr), constructorCode_(nullptr),
|
||||
elementType_(JSVAL_TYPE_MAGIC)
|
||||
{}
|
||||
|
||||
bool initProperties(const PropertyVector& properties, size_t size) {
|
||||
|
@ -108,6 +114,10 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
|||
return properties_.appendAll(properties);
|
||||
}
|
||||
|
||||
void initArray(JSValueType elementType) {
|
||||
elementType_ = elementType;
|
||||
}
|
||||
|
||||
~UnboxedLayout() {
|
||||
if (newScript_)
|
||||
newScript_->clear();
|
||||
|
@ -120,6 +130,10 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
|||
constructorCode_.init(nullptr);
|
||||
}
|
||||
|
||||
bool isArray() const {
|
||||
return elementType_ != JSVAL_TYPE_MAGIC;
|
||||
}
|
||||
|
||||
void detachFromCompartment();
|
||||
|
||||
const PropertyVector& properties() const {
|
||||
|
@ -187,6 +201,10 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
|||
constructorCode_ = code;
|
||||
}
|
||||
|
||||
JSValueType elementType() const {
|
||||
return elementType_;
|
||||
}
|
||||
|
||||
inline gc::AllocKind getAllocKind() const;
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
|
@ -306,6 +324,193 @@ 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 {
|
||||
|
|
Loading…
Reference in New Issue