mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 15:58:39 +09:00
Remove UnboxedArray code part 2
This commit is contained in:
parent
5c15924907
commit
54b6b407a9
17 changed files with 124 additions and 1611 deletions
|
|
@ -2504,8 +2504,6 @@ js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind
|
|||
InlineTypedObject::objectMovedDuringMinorGC(this, dst, src);
|
||||
} else if (src->is<TypedArrayObject>()) {
|
||||
tenuredSize += TypedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
|
||||
} else if (src->is<UnboxedArrayObject>()) {
|
||||
tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
|
||||
} else if (src->is<ArgumentsObject>()) {
|
||||
tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src);
|
||||
} else if (src->is<ProxyObject>()) {
|
||||
|
|
|
|||
|
|
@ -280,6 +280,12 @@ DoTypeUpdateFallback(JSContext* cx, BaselineFrame* frame, ICUpdatedStub* stub, H
|
|||
RootedId id(cx);
|
||||
|
||||
switch(stub->kind()) {
|
||||
case ICStub::SetElem_DenseOrUnboxedArray:
|
||||
case ICStub::SetElem_DenseOrUnboxedArrayAdd: {
|
||||
id = JSID_VOID;
|
||||
AddTypePropertyId(cx, obj, id, value);
|
||||
break;
|
||||
}
|
||||
case ICStub::SetProp_Native:
|
||||
case ICStub::SetProp_NativeAdd:
|
||||
case ICStub::SetProp_Unboxed: {
|
||||
|
|
@ -5763,7 +5769,7 @@ CopyArray(JSContext* cx, HandleObject obj, MutableHandleValue result)
|
|||
if (!nobj)
|
||||
return false;
|
||||
EnsureArrayGroupAnalyzed(cx, nobj);
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, nobj, obj, 0, 0, length);
|
||||
CopyBoxedOrUnboxedDenseElements(cx, nobj, obj, 0, 0, length);
|
||||
|
||||
result.setObject(*nobj);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -892,54 +892,6 @@ class ICGetElem_Dense : public ICMonitoredStub
|
|||
};
|
||||
};
|
||||
|
||||
class ICGetElem_UnboxedArray : public ICMonitoredStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
GCPtrObjectGroup group_;
|
||||
|
||||
ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub, ObjectGroup* group);
|
||||
|
||||
public:
|
||||
static ICGetElem_UnboxedArray* Clone(JSContext* cx, ICStubSpace* space,
|
||||
ICStub* firstMonitorStub, ICGetElem_UnboxedArray& other);
|
||||
|
||||
static size_t offsetOfGroup() {
|
||||
return offsetof(ICGetElem_UnboxedArray, group_);
|
||||
}
|
||||
|
||||
GCPtrObjectGroup& group() {
|
||||
return group_;
|
||||
}
|
||||
|
||||
class Compiler : public ICStubCompiler {
|
||||
ICStub* firstMonitorStub_;
|
||||
RootedObjectGroup group_;
|
||||
JSValueType elementType_;
|
||||
|
||||
protected:
|
||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
||||
|
||||
virtual int32_t getKey() const {
|
||||
return static_cast<int32_t>(engine_) |
|
||||
(static_cast<int32_t>(kind) << 1) |
|
||||
(static_cast<int32_t>(elementType_) << 17);
|
||||
}
|
||||
|
||||
public:
|
||||
Compiler(JSContext* cx, ICStub* firstMonitorStub, ObjectGroup* group)
|
||||
: ICStubCompiler(cx, ICStub::GetElem_UnboxedArray, Engine::Baseline),
|
||||
firstMonitorStub_(firstMonitorStub),
|
||||
group_(cx, group),
|
||||
elementType_(group->unboxedLayoutDontCheckGeneration().elementType())
|
||||
{}
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
return newStub<ICGetElem_UnboxedArray>(space, getStubCode(), firstMonitorStub_, group_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Accesses scalar elements of a typed array or typed object.
|
||||
class ICGetElem_TypedArray : public ICStub
|
||||
{
|
||||
|
|
@ -1115,9 +1067,7 @@ class ICSetElem_DenseOrUnboxedArray : public ICUpdatedStub
|
|||
: ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArray, Engine::Baseline),
|
||||
shape_(cx, shape),
|
||||
group_(cx, group),
|
||||
unboxedType_(shape
|
||||
? JSVAL_TYPE_MAGIC
|
||||
: group->unboxedLayoutDontCheckGeneration().elementType())
|
||||
unboxedType_(JSVAL_TYPE_MAGIC)
|
||||
{}
|
||||
|
||||
ICUpdatedStub* getStub(ICStubSpace* space) {
|
||||
|
|
@ -1225,9 +1175,7 @@ class ICSetElemDenseOrUnboxedArrayAddCompiler : public ICStubCompiler {
|
|||
: ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd, Engine::Baseline),
|
||||
obj_(cx, obj),
|
||||
protoChainDepth_(protoChainDepth),
|
||||
unboxedType_(obj->is<UnboxedArrayObject>()
|
||||
? obj->as<UnboxedArrayObject>().elementType()
|
||||
: JSVAL_TYPE_MAGIC)
|
||||
unboxedType_(JSVAL_TYPE_MAGIC)
|
||||
{}
|
||||
|
||||
template <size_t ProtoChainDepth>
|
||||
|
|
|
|||
|
|
@ -10336,22 +10336,11 @@ CodeGenerator::visitLoadElementHole(LLoadElementHole* lir)
|
|||
else
|
||||
masm.branch32(Assembler::BelowOrEqual, initLength, ToRegister(lir->index()), &undefined);
|
||||
|
||||
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);
|
||||
}
|
||||
if (lir->index()->isConstant()) {
|
||||
NativeObject::elementsSizeMustNotOverflow();
|
||||
masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out);
|
||||
}
|
||||
|
||||
// If a hole check is needed, and the value wasn't a hole, we're done.
|
||||
|
|
|
|||
|
|
@ -541,7 +541,7 @@ IonBuilder::inlineArray(CallInfo& callInfo)
|
|||
if (!alloc().ensureBallast())
|
||||
return InliningStatus_Error;
|
||||
MDefinition* value = callInfo.getArg(i);
|
||||
if (!initializeArrayElement(array, i, /* addResumePoint = */ false))
|
||||
if (!initializeArrayElement(array, i, value, /* addResumePoint = */ false))
|
||||
return InliningStatus_Error;
|
||||
}
|
||||
|
||||
|
|
@ -578,7 +578,7 @@ IonBuilder::inlineArrayIsArray(CallInfo& callInfo)
|
|||
if (!clasp || clasp->isProxy())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
isArray = (clasp == &ArrayObject::class_ || clasp == &UnboxedArrayObject::class_);
|
||||
isArray = (clasp == &ArrayObject::class_);
|
||||
}
|
||||
|
||||
pushConstant(BooleanValue(isArray));
|
||||
|
|
|
|||
|
|
@ -9245,7 +9245,7 @@ class MLoadElementHole
|
|||
bool needsHoleCheck_;
|
||||
|
||||
MLoadElementHole(MDefinition* elements, MDefinition* index, MDefinition* initLength,
|
||||
JSValueType unboxedType, bool needsHoleCheck)
|
||||
bool needsHoleCheck)
|
||||
: MTernaryInstruction(elements, index, initLength),
|
||||
needsNegativeIntCheck_(true),
|
||||
needsHoleCheck_(needsHoleCheck)
|
||||
|
|
@ -9717,10 +9717,6 @@ class MArraySlice
|
|||
return initialHeap_;
|
||||
}
|
||||
|
||||
JSValueType unboxedType() const {
|
||||
return unboxedType_;
|
||||
}
|
||||
|
||||
bool possiblyCalls() const override {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@ SetArrayElement(JSContext* cx, HandleObject obj, double index, HandleValue v)
|
|||
|
||||
if (obj->is<ArrayObject>() && !obj->isIndexed() && index <= UINT32_MAX) {
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(index), v.address(), 1);
|
||||
SetOrExtendBoxedOrUnboxedDenseElements(cx, obj, uint32_t(index), v.address(), 1);
|
||||
if (result != DenseElementResult::Incomplete)
|
||||
return result == DenseElementResult::Success;
|
||||
}
|
||||
|
|
@ -1068,12 +1068,12 @@ ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_
|
|||
// length > initLength we rely on the second loop to add the
|
||||
// other elements.
|
||||
MOZ_ASSERT(*numProcessed == 0);
|
||||
uint32_t initLength = Min<uint32_t>(GetBoxedOrUnboxedInitializedLength<Type>(obj), length);
|
||||
uint32_t initLength = Min<uint32_t>(GetBoxedOrUnboxedInitializedLength(obj), length);
|
||||
while (*numProcessed < initLength) {
|
||||
if (!CheckForInterrupt(cx))
|
||||
return DenseElementResult::Failure;
|
||||
|
||||
Value elem = GetBoxedOrUnboxedDenseElement<Type>(obj, *numProcessed);
|
||||
Value elem = GetBoxedOrUnboxedDenseElement(obj, *numProcessed);
|
||||
|
||||
if (elem.isString()) {
|
||||
if (!sb.append(elem.toString()))
|
||||
|
|
@ -1320,7 +1320,7 @@ InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start,
|
|||
|
||||
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, start, vector, count, updateTypes);
|
||||
SetOrExtendBoxedOrUnboxedDenseElements(cx, obj, start, vector, count, updateTypes);
|
||||
if (result != DenseElementResult::Incomplete)
|
||||
return result == DenseElementResult::Success;
|
||||
}
|
||||
|
|
@ -1359,7 +1359,7 @@ DenseElementResult
|
|||
ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
|
||||
{
|
||||
/* An empty array or an array with no elements is already reversed. */
|
||||
if (length == 0 || GetBoxedOrUnboxedInitializedLength<Type>(obj) == 0)
|
||||
if (length == 0 || GetBoxedOrUnboxedInitializedLength(obj) == 0)
|
||||
return DenseElementResult::Success;
|
||||
|
||||
if (obj->as<NativeObject>().denseElementsAreFrozen())
|
||||
|
|
@ -2074,7 +2074,7 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp)
|
|||
uint32_t newlength = length + args.length();
|
||||
args.rval().setNumber(newlength);
|
||||
|
||||
// SetOrExtendAnyBoxedOrUnboxedDenseElements takes care of updating the
|
||||
// SetOrExtendBoxedOrUnboxedDenseElements takes care of updating the
|
||||
// length for boxed and unboxed arrays. Handle updates to the length of
|
||||
// non-arrays here.
|
||||
bool isArray;
|
||||
|
|
@ -2360,7 +2360,7 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co
|
|||
return false;
|
||||
|
||||
/* There's no optimizing possible if it's not an array. */
|
||||
if (!arr->is<ArrayObject>() && !arr->is<UnboxedArrayObject>())
|
||||
if (!arr->is<ArrayObject>())
|
||||
return false;
|
||||
|
||||
/* If it's a frozen array, always pick the slow path */
|
||||
|
|
@ -2863,7 +2863,7 @@ ArraySliceOrdinary(JSContext* cx, HandleObject obj, uint32_t length, uint32_t be
|
|||
|
||||
if (count) {
|
||||
DebugOnly<DenseElementResult> result =
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, count);
|
||||
CopyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, count);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
}
|
||||
arr.set(narr);
|
||||
|
|
@ -3615,7 +3615,7 @@ static inline JSObject*
|
|||
NewArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
|
||||
NewObjectKind newKind = GenericObject)
|
||||
{
|
||||
if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
|
||||
if (!obj->is<ArrayObject>())
|
||||
return NewArray<maxLength>(cx, length, nullptr, newKind);
|
||||
|
||||
if (obj->staticPrototype() != cx->global()->maybeGetArrayPrototype())
|
||||
|
|
|
|||
|
|
@ -1146,7 +1146,7 @@ static bool
|
|||
GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, MutableHandle<GCVector<Value>> values)
|
||||
{
|
||||
MOZ_ASSERT(!obj->isSingleton());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>());
|
||||
MOZ_ASSERT(!obj->isIndexed());
|
||||
|
||||
size_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
|
||||
|
|
@ -1207,11 +1207,10 @@ js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKin
|
|||
MOZ_ASSERT_IF(obj->isSingleton(),
|
||||
cx->compartment()->behaviors().getSingletonsAsTemplates());
|
||||
MOZ_ASSERT(obj->is<PlainObject>() ||
|
||||
obj->is<ArrayObject>() ||
|
||||
obj->is<UnboxedArrayObject>());
|
||||
obj->is<ArrayObject>());
|
||||
MOZ_ASSERT(newKind != SingletonObject);
|
||||
|
||||
if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) {
|
||||
if (obj->is<ArrayObject>()) {
|
||||
Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
|
||||
if (!GetScriptArrayObjectElements(cx, obj, &values))
|
||||
return nullptr;
|
||||
|
|
@ -1327,9 +1326,8 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
|
|||
{
|
||||
if (mode == XDR_ENCODE) {
|
||||
MOZ_ASSERT(obj->is<PlainObject>() ||
|
||||
obj->is<ArrayObject>() ||
|
||||
obj->is<UnboxedArrayObject>());
|
||||
isArray = (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) ? 1 : 0;
|
||||
obj->is<ArrayObject>());
|
||||
isArray = obj->is<ArrayObject>() ? 1 : 0;
|
||||
}
|
||||
|
||||
if (!xdr->codeUint32(&isArray))
|
||||
|
|
@ -2312,11 +2310,6 @@ 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);
|
||||
|
|
@ -3680,16 +3673,6 @@ 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,8 +40,6 @@ MaybeConvertUnboxedObjectToNative(ExclusiveContext* cx, JSObject* obj)
|
|||
{
|
||||
if (obj->is<UnboxedPlainObject>())
|
||||
return UnboxedPlainObject::convertToNative(cx->asJSContext(), obj);
|
||||
if (obj->is<UnboxedArrayObject>())
|
||||
return UnboxedArrayObject::convertToNative(cx->asJSContext(), obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>() || obj->is<UnboxedArrayObject>());
|
||||
MOZ_ASSERT(obj->is<ArrayObject>());
|
||||
|
||||
if (op == JSOP_INITELEM_INC && index == INT32_MAX) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SPREAD_TOO_LARGE);
|
||||
|
|
|
|||
|
|
@ -5001,9 +5001,6 @@ js::NewArrayOperation(JSContext* cx, HandleScript script, jsbytecode* pc, uint32
|
|||
|
||||
if (group->shouldPreTenure() || group->maybePreliminaryObjects())
|
||||
newKind = TenuredObject;
|
||||
|
||||
if (group->maybeUnboxedLayout())
|
||||
return UnboxedArrayObject::create(cx, group, length, newKind);
|
||||
}
|
||||
|
||||
ArrayObject* obj = NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
|
||||
|
|
@ -5029,12 +5026,6 @@ js::NewArrayOperationWithTemplate(JSContext* cx, HandleObject templateObject)
|
|||
|
||||
NewObjectKind newKind = templateObject->group()->shouldPreTenure() ? TenuredObject : GenericObject;
|
||||
|
||||
if (templateObject->is<UnboxedArrayObject>()) {
|
||||
uint32_t length = templateObject->as<UnboxedArrayObject>().length();
|
||||
RootedObjectGroup group(cx, templateObject->group());
|
||||
return UnboxedArrayObject::create(cx, group, length, newKind);
|
||||
}
|
||||
|
||||
ArrayObject* obj = NewDenseFullyAllocatedArray(cx, templateObject->as<ArrayObject>().length(),
|
||||
nullptr, newKind);
|
||||
if (!obj)
|
||||
|
|
|
|||
|
|
@ -861,32 +861,6 @@ ObjectGroup::newArrayObject(ExclusiveContext* cx,
|
|||
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);
|
||||
}
|
||||
|
|
@ -897,7 +871,7 @@ GiveObjectGroup(ExclusiveContext* cx, JSObject* source, JSObject* target)
|
|||
{
|
||||
MOZ_ASSERT(source->group() != target->group());
|
||||
|
||||
if (!target->is<ArrayObject>() && !target->is<UnboxedArrayObject>())
|
||||
if (!target->is<ArrayObject>())
|
||||
return true;
|
||||
|
||||
if (target->group()->maybePreliminaryObjects()) {
|
||||
|
|
@ -905,41 +879,21 @@ GiveObjectGroup(ExclusiveContext* cx, JSObject* source, JSObject* target)
|
|||
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);
|
||||
}
|
||||
ObjectGroup* sourceGroup = source->group();
|
||||
|
||||
if (source->is<ArrayObject>()) {
|
||||
source->setGroup(target->group());
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target->is<UnboxedArrayObject>()) {
|
||||
if (!source->is<UnboxedArrayObject>())
|
||||
return true;
|
||||
if (source->as<UnboxedArrayObject>().elementType() != JSVAL_TYPE_INT32)
|
||||
return true;
|
||||
if (target->as<UnboxedArrayObject>().elementType() != JSVAL_TYPE_DOUBLE)
|
||||
return true;
|
||||
|
||||
return source->as<UnboxedArrayObject>().convertInt32ToDouble(cx, target->group());
|
||||
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;
|
||||
|
|
@ -1503,7 +1457,8 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode*
|
|||
}
|
||||
}
|
||||
|
||||
if (kind == JSProto_Array &&
|
||||
// FIXME: This block can probably go because of cx->options().unboxedArrays()
|
||||
if (kind == JSProto_Array &&
|
||||
(JSOp(*pc) == JSOP_NEWARRAY || IsCallPC(pc)) &&
|
||||
cx->options().unboxedArrays())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<UnboxedArrayObject>() || obj->is<TypedObject>()) {
|
||||
} else if (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 (clasp == &UnboxedArrayObject::class_ || IsTypedObjectClass(clasp)) {
|
||||
} else if (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<UnboxedArrayObject>() || obj->is<TypedObject>()) {
|
||||
// Only the group needs to be guarded for unboxed arrays and typed objects.
|
||||
if (obj->is<TypedObject>()) {
|
||||
// Only the group needs to be guarded for typed objects.
|
||||
return 2;
|
||||
}
|
||||
// Other objects only need the shape to be guarded.
|
||||
|
|
|
|||
|
|
@ -2505,7 +2505,7 @@ TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList* constraints, jsid
|
|||
bool
|
||||
js::ClassCanHaveExtraProperties(const Class* clasp)
|
||||
{
|
||||
if (clasp == &UnboxedPlainObject::class_ || clasp == &UnboxedArrayObject::class_)
|
||||
if (clasp == &UnboxedPlainObject::class_)
|
||||
return false;
|
||||
return clasp->getResolve()
|
||||
|| clasp->getOpsLookupProperty()
|
||||
|
|
|
|||
|
|
@ -172,123 +172,6 @@ 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.
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -296,7 +179,7 @@ UnboxedArrayObject::triggerPreBarrier(size_t index)
|
|||
static inline bool
|
||||
HasAnyBoxedOrUnboxedDenseElements(JSObject* obj)
|
||||
{
|
||||
return obj->isNative() || obj->is<UnboxedArrayObject>();
|
||||
return obj->isNative();
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
|
|
@ -304,8 +187,6 @@ GetAnyBoxedOrUnboxedInitializedLength(JSObject* obj)
|
|||
{
|
||||
if (obj->isNative())
|
||||
return obj->as<NativeObject>().getDenseInitializedLength();
|
||||
if (obj->is<UnboxedArrayObject>())
|
||||
return obj->as<UnboxedArrayObject>().initializedLength();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -314,57 +195,40 @@ 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);
|
||||
return obj->as<NativeObject>().getDenseElement(index);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
GetAnyBoxedOrUnboxedArrayLength(JSObject* obj)
|
||||
{
|
||||
if (obj->is<ArrayObject>())
|
||||
return obj->as<ArrayObject>().length();
|
||||
return obj->as<UnboxedArrayObject>().length();
|
||||
return obj->as<ArrayObject>().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);
|
||||
}
|
||||
MOZ_ASSERT(length >= obj->as<ArrayObject>().length());
|
||||
obj->as<ArrayObject>().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);
|
||||
obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
obj->as<NativeObject>().initDenseElementWithType(cx, index, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -374,273 +238,128 @@ InitAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, co
|
|||
static inline JSValueType
|
||||
GetBoxedOrUnboxedType(JSObject* obj)
|
||||
{
|
||||
if (obj->isNative())
|
||||
return JSVAL_TYPE_MAGIC;
|
||||
return obj->as<UnboxedArrayObject>().elementType();
|
||||
return JSVAL_TYPE_MAGIC;
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline bool
|
||||
HasBoxedOrUnboxedDenseElements(JSObject* obj)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC)
|
||||
return obj->isNative();
|
||||
return obj->is<UnboxedArrayObject>() && obj->as<UnboxedArrayObject>().elementType() == Type;
|
||||
return obj->isNative();
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline size_t
|
||||
GetBoxedOrUnboxedInitializedLength(JSObject* obj)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC)
|
||||
return obj->as<NativeObject>().getDenseInitializedLength();
|
||||
return obj->as<UnboxedArrayObject>().initializedLength();
|
||||
return obj->as<NativeObject>().getDenseInitializedLength();
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline DenseElementResult
|
||||
SetBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen)
|
||||
{
|
||||
size_t oldInitlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
obj->as<NativeObject>().setDenseInitializedLength(initlen);
|
||||
if (initlen < oldInitlen)
|
||||
obj->as<NativeObject>().shrinkElements(cx, initlen);
|
||||
} else {
|
||||
obj->as<UnboxedArrayObject>().setInitializedLength(initlen);
|
||||
if (initlen < oldInitlen)
|
||||
obj->as<UnboxedArrayObject>().shrinkElements(cx, initlen);
|
||||
}
|
||||
size_t oldInitlen = GetBoxedOrUnboxedInitializedLength(obj);
|
||||
obj->as<NativeObject>().setDenseInitializedLength(initlen);
|
||||
if (initlen < oldInitlen)
|
||||
obj->as<NativeObject>().shrinkElements(cx, initlen);
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline size_t
|
||||
GetBoxedOrUnboxedCapacity(JSObject* obj)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC)
|
||||
return obj->as<NativeObject>().getDenseCapacity();
|
||||
return obj->as<UnboxedArrayObject>().capacity();
|
||||
return obj->as<NativeObject>().getDenseCapacity();
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline Value
|
||||
GetBoxedOrUnboxedDenseElement(JSObject* obj, size_t index)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC)
|
||||
return obj->as<NativeObject>().getDenseElement(index);
|
||||
return obj->as<UnboxedArrayObject>().getElementSpecific<Type>(index);
|
||||
return obj->as<NativeObject>().getDenseElement(index);
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline void
|
||||
SetBoxedOrUnboxedDenseElementNoTypeChange(JSObject* obj, size_t index, const Value& value)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC)
|
||||
obj->as<NativeObject>().setDenseElement(index, value);
|
||||
else
|
||||
obj->as<UnboxedArrayObject>().setElementNoTypeChangeSpecific<Type>(index, value);
|
||||
obj->as<NativeObject>().setDenseElement(index, value);
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline bool
|
||||
SetBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
|
||||
return true;
|
||||
}
|
||||
return obj->as<UnboxedArrayObject>().setElementSpecific<Type>(cx, index, value);
|
||||
obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline DenseElementResult
|
||||
EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
if (!obj->as<ArrayObject>().ensureElements(cx, count))
|
||||
return DenseElementResult::Failure;
|
||||
} else {
|
||||
if (obj->as<UnboxedArrayObject>().capacity() < count) {
|
||||
if (!obj->as<UnboxedArrayObject>().growElements(cx, count))
|
||||
return DenseElementResult::Failure;
|
||||
}
|
||||
}
|
||||
if (!obj->as<ArrayObject>().ensureElements(cx, count))
|
||||
return DenseElementResult::Failure;
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline DenseElementResult
|
||||
SetOrExtendBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
|
||||
uint32_t start, const Value* vp, uint32_t count,
|
||||
ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
NativeObject* nobj = &obj->as<NativeObject>();
|
||||
NativeObject* nobj = &obj->as<NativeObject>();
|
||||
|
||||
if (nobj->denseElementsAreFrozen())
|
||||
return DenseElementResult::Incomplete;
|
||||
if (nobj->denseElementsAreFrozen())
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
if (obj->is<ArrayObject>() &&
|
||||
!obj->as<ArrayObject>().lengthIsWritable() &&
|
||||
start + count >= obj->as<ArrayObject>().length())
|
||||
{
|
||||
return DenseElementResult::Incomplete;
|
||||
}
|
||||
|
||||
DenseElementResult result = nobj->ensureDenseElements(cx, start, count);
|
||||
if (result != DenseElementResult::Success)
|
||||
return result;
|
||||
|
||||
if (obj->is<ArrayObject>() && start + count >= obj->as<ArrayObject>().length())
|
||||
obj->as<ArrayObject>().setLengthInt32(start + count);
|
||||
|
||||
if (updateTypes == ShouldUpdateTypes::DontUpdate && !nobj->shouldConvertDoubleElements()) {
|
||||
nobj->copyDenseElements(start, vp, count);
|
||||
} else {
|
||||
for (size_t i = 0; i < count; i++)
|
||||
nobj->setDenseElementWithType(cx, start + i, vp[i]);
|
||||
}
|
||||
|
||||
return DenseElementResult::Success;
|
||||
if (obj->is<ArrayObject>() &&
|
||||
!obj->as<ArrayObject>().lengthIsWritable() &&
|
||||
start + count >= obj->as<ArrayObject>().length())
|
||||
{
|
||||
return DenseElementResult::Incomplete;
|
||||
}
|
||||
|
||||
UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
|
||||
DenseElementResult result = nobj->ensureDenseElements(cx, start, count);
|
||||
if (result != DenseElementResult::Success)
|
||||
return result;
|
||||
|
||||
if (start > nobj->initializedLength())
|
||||
return DenseElementResult::Incomplete;
|
||||
if (obj->is<ArrayObject>() && start + count >= obj->as<ArrayObject>().length())
|
||||
obj->as<ArrayObject>().setLengthInt32(start + count);
|
||||
|
||||
if (start + count >= UnboxedArrayObject::MaximumCapacity)
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
if (start + count > nobj->capacity() && !nobj->growElements(cx, start + count))
|
||||
return DenseElementResult::Failure;
|
||||
|
||||
size_t oldInitlen = nobj->initializedLength();
|
||||
|
||||
// Overwrite any existing elements covered by the new range. If we fail
|
||||
// after this point due to some incompatible type being written to the
|
||||
// object's elements, afterwards the contents will be different from when
|
||||
// we started. The caller must retry the operation using a generic path,
|
||||
// which will overwrite the already-modified elements as well as the ones
|
||||
// that were left alone.
|
||||
size_t i = 0;
|
||||
if (updateTypes == ShouldUpdateTypes::DontUpdate) {
|
||||
for (size_t j = start; i < count && j < oldInitlen; i++, j++)
|
||||
nobj->setElementNoTypeChangeSpecific<Type>(j, vp[i]);
|
||||
if (updateTypes == ShouldUpdateTypes::DontUpdate && !nobj->shouldConvertDoubleElements()) {
|
||||
nobj->copyDenseElements(start, vp, count);
|
||||
} else {
|
||||
for (size_t j = start; i < count && j < oldInitlen; i++, j++) {
|
||||
if (!nobj->setElementSpecific<Type>(cx, j, vp[i]))
|
||||
return DenseElementResult::Incomplete;
|
||||
}
|
||||
for (size_t i = 0; i < count; i++)
|
||||
nobj->setDenseElementWithType(cx, start + i, vp[i]);
|
||||
}
|
||||
|
||||
if (i != count) {
|
||||
obj->as<UnboxedArrayObject>().setInitializedLength(start + count);
|
||||
if (updateTypes == ShouldUpdateTypes::DontUpdate) {
|
||||
for (; i < count; i++)
|
||||
nobj->initElementNoTypeChangeSpecific<Type>(start + i, vp[i]);
|
||||
} else {
|
||||
for (; i < count; i++) {
|
||||
if (!nobj->initElementSpecific<Type>(cx, start + i, vp[i])) {
|
||||
nobj->setInitializedLengthNoBarrier(oldInitlen);
|
||||
return DenseElementResult::Incomplete;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (start + count >= nobj->length())
|
||||
nobj->setLength(cx, start + count);
|
||||
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline DenseElementResult
|
||||
MoveBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, uint32_t dstStart, uint32_t srcStart,
|
||||
uint32_t length)
|
||||
{
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(obj));
|
||||
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
if (obj->as<NativeObject>().denseElementsAreFrozen())
|
||||
return DenseElementResult::Incomplete;
|
||||
if (obj->as<NativeObject>().denseElementsAreFrozen())
|
||||
return DenseElementResult::Incomplete;
|
||||
|
||||
if (!obj->as<NativeObject>().maybeCopyElementsForWrite(cx))
|
||||
return DenseElementResult::Failure;
|
||||
obj->as<NativeObject>().moveDenseElements(dstStart, srcStart, length);
|
||||
} else {
|
||||
uint8_t* data = obj->as<UnboxedArrayObject>().elements();
|
||||
size_t elementSize = UnboxedTypeSize(Type);
|
||||
|
||||
if (UnboxedTypeNeedsPreBarrier(Type) &&
|
||||
JS::shadow::Zone::asShadowZone(obj->zone())->needsIncrementalBarrier())
|
||||
{
|
||||
// Trigger pre barriers on any elements we are overwriting. See
|
||||
// NativeObject::moveDenseElements. No post barrier is needed as
|
||||
// only whole cell post barriers are used with unboxed objects.
|
||||
for (size_t i = 0; i < length; i++)
|
||||
obj->as<UnboxedArrayObject>().triggerPreBarrier<Type>(dstStart + i);
|
||||
}
|
||||
|
||||
memmove(data + dstStart * elementSize,
|
||||
data + srcStart * elementSize,
|
||||
length * elementSize);
|
||||
}
|
||||
if (!obj->as<NativeObject>().maybeCopyElementsForWrite(cx))
|
||||
return DenseElementResult::Failure;
|
||||
obj->as<NativeObject>().moveDenseElements(dstStart, srcStart, length);
|
||||
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
template <JSValueType DstType, JSValueType SrcType>
|
||||
static inline DenseElementResult
|
||||
CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
|
||||
uint32_t dstStart, uint32_t srcStart, uint32_t length)
|
||||
{
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<SrcType>(src));
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<DstType>(dst));
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<DstType>(dst) == dstStart);
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<SrcType>(src) >= srcStart + length);
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedCapacity<DstType>(dst) >= dstStart + length);
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(src));
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(dst));
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength(dst) == dstStart);
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength(src) >= srcStart + length);
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedCapacity(dst) >= dstStart + length);
|
||||
|
||||
SetBoxedOrUnboxedInitializedLength<DstType>(cx, dst, dstStart + length);
|
||||
SetBoxedOrUnboxedInitializedLength(cx, dst, dstStart + length);
|
||||
|
||||
if (DstType == JSVAL_TYPE_MAGIC) {
|
||||
if (SrcType == JSVAL_TYPE_MAGIC) {
|
||||
const Value* vp = src->as<NativeObject>().getDenseElements() + srcStart;
|
||||
dst->as<NativeObject>().initDenseElements(dstStart, vp, length);
|
||||
} else {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
Value v = GetBoxedOrUnboxedDenseElement<SrcType>(src, srcStart + i);
|
||||
dst->as<NativeObject>().initDenseElement(dstStart + i, v);
|
||||
}
|
||||
}
|
||||
} else if (DstType == SrcType) {
|
||||
uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
|
||||
uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
|
||||
size_t elementSize = UnboxedTypeSize(DstType);
|
||||
|
||||
memcpy(dstData + dstStart * elementSize,
|
||||
srcData + srcStart * elementSize,
|
||||
length * elementSize);
|
||||
|
||||
// Add a store buffer entry if we might have copied a nursery pointer to dst.
|
||||
if (UnboxedTypeNeedsPostBarrier(DstType) && !IsInsideNursery(dst))
|
||||
dst->runtimeFromMainThread()->gc.storeBuffer.putWholeCell(dst);
|
||||
} else if (DstType == JSVAL_TYPE_DOUBLE && SrcType == JSVAL_TYPE_INT32) {
|
||||
uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
|
||||
uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
int32_t v = *reinterpret_cast<int32_t*>(srcData + (srcStart + i) * sizeof(int32_t));
|
||||
*reinterpret_cast<double*>(dstData + (dstStart + i) * sizeof(double)) = v;
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
Value v = GetBoxedOrUnboxedDenseElement<SrcType>(src, srcStart + i);
|
||||
dst->as<UnboxedArrayObject>().initElementNoTypeChangeSpecific<DstType>(dstStart + i, v);
|
||||
}
|
||||
}
|
||||
const Value* vp = src->as<NativeObject>().getDenseElements() + srcStart;
|
||||
dst->as<NativeObject>().initDenseElements(dstStart, vp, length);
|
||||
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
|
@ -666,22 +385,7 @@ 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();
|
||||
}
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_MAGIC>();
|
||||
}
|
||||
|
||||
// As above, except the specialization can reflect the unboxed type of two objects.
|
||||
|
|
@ -692,42 +396,7 @@ 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
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_MAGIC, JSVAL_TYPE_MAGIC>();
|
||||
}
|
||||
|
||||
#undef DEPENDENT_TEMPLATE_HINT
|
||||
|
|
@ -816,25 +485,6 @@ struct Signature ## Functor { \
|
|||
} \
|
||||
}
|
||||
|
||||
DenseElementResult
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
|
||||
uint32_t start, const Value* vp, uint32_t count,
|
||||
ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update);
|
||||
|
||||
DenseElementResult
|
||||
MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
|
||||
uint32_t dstStart, uint32_t srcStart, uint32_t length);
|
||||
|
||||
DenseElementResult
|
||||
CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
|
||||
uint32_t dstStart, uint32_t srcStart, uint32_t length);
|
||||
|
||||
void
|
||||
SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen);
|
||||
|
||||
DenseElementResult
|
||||
EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // vm_UnboxedObject_inl_h
|
||||
|
|
|
|||
|
|
@ -417,8 +417,6 @@ 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
|
||||
|
|
@ -426,8 +424,6 @@ 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;
|
||||
|
|
@ -453,15 +449,14 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
|
|||
RootedScript script(cx, layout.allocationScript());
|
||||
jsbytecode* pc = layout.allocationPc();
|
||||
|
||||
replacementGroup = ObjectGroupCompartment::makeGroup(cx, clasp, proto);
|
||||
replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
|
||||
if (!replacementGroup)
|
||||
return false;
|
||||
|
||||
PlainObject* templateObject = &script->getObject(pc)->as<PlainObject>();
|
||||
replacementGroup->addDefiniteProperties(cx, templateObject->lastProperty());
|
||||
|
||||
JSProtoKey key = layout.isArray() ? JSProto_Array : JSProto_Object;
|
||||
cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc, key,
|
||||
cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc, JSProto_Object,
|
||||
replacementGroup);
|
||||
|
||||
// Clear any baseline information at this opcode which might use the old group.
|
||||
|
|
@ -477,22 +472,12 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
|
|||
}
|
||||
}
|
||||
|
||||
size_t nfixed = layout.isArray() ? 0 : gc::GetGCKindSlots(layout.getAllocKind());
|
||||
size_t nfixed = gc::GetGCKindSlots(layout.getAllocKind());
|
||||
|
||||
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));
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PlainObject::class_, 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];
|
||||
|
|
@ -505,7 +490,7 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
|
|||
}
|
||||
|
||||
ObjectGroup* nativeGroup =
|
||||
ObjectGroupCompartment::makeGroup(cx, clasp, proto,
|
||||
ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto,
|
||||
group->flags() & OBJECT_FLAG_DYNAMIC_MASK);
|
||||
if (!nativeGroup)
|
||||
return false;
|
||||
|
|
@ -513,24 +498,19 @@ 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.
|
||||
if (layout.isArray()) {
|
||||
if (!PropagatePropertyTypes(cx, JSID_VOID, group, nativeGroup))
|
||||
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;
|
||||
} 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.
|
||||
|
|
@ -955,702 +935,6 @@ const Class UnboxedPlainObject::class_ = {
|
|||
&UnboxedPlainObjectObjectOps
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// UnboxedArrayObject
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <JSValueType Type>
|
||||
DenseElementResult
|
||||
AppendUnboxedDenseElements(UnboxedArrayObject* obj, uint32_t initlen,
|
||||
MutableHandle<GCVector<Value>> values)
|
||||
{
|
||||
for (size_t i = 0; i < initlen; i++)
|
||||
values.infallibleAppend(obj->getElementSpecific<Type>(i));
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor3(AppendUnboxedDenseElements,
|
||||
UnboxedArrayObject*, uint32_t, MutableHandle<GCVector<Value>>);
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj,
|
||||
ObjectGroup* group, Shape* shape)
|
||||
{
|
||||
size_t length = obj->as<UnboxedArrayObject>().length();
|
||||
size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
|
||||
|
||||
Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
|
||||
if (!values.reserve(initlen))
|
||||
return false;
|
||||
|
||||
AppendUnboxedDenseElementsFunctor functor(&obj->as<UnboxedArrayObject>(), initlen, &values);
|
||||
DebugOnly<DenseElementResult> result = CallBoxedOrUnboxedSpecialization(functor, obj);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
|
||||
obj->setGroup(group);
|
||||
|
||||
ArrayObject* aobj = &obj->as<ArrayObject>();
|
||||
aobj->setLastPropertyMakeNative(cx, shape);
|
||||
|
||||
// Make sure there is at least one element, so that this array does not
|
||||
// use emptyObjectElements / emptyObjectElementsShared.
|
||||
if (!aobj->ensureElements(cx, Max<size_t>(initlen, 1)))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(!aobj->getDenseInitializedLength());
|
||||
aobj->setDenseInitializedLength(initlen);
|
||||
aobj->initDenseElements(0, values.begin(), initlen);
|
||||
aobj->setLengthInt32(length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
|
||||
{
|
||||
const UnboxedLayout& layout = obj->as<UnboxedArrayObject>().layout();
|
||||
|
||||
if (!layout.nativeGroup()) {
|
||||
if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return convertToNativeWithGroup(cx, obj, layout.nativeGroup(), layout.nativeShape());
|
||||
}
|
||||
|
||||
bool
|
||||
UnboxedArrayObject::convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group)
|
||||
{
|
||||
MOZ_ASSERT(elementType() == JSVAL_TYPE_INT32);
|
||||
MOZ_ASSERT(group->unboxedLayout().elementType() == JSVAL_TYPE_DOUBLE);
|
||||
|
||||
Vector<int32_t> values(cx);
|
||||
if (!values.reserve(initializedLength()))
|
||||
return false;
|
||||
for (size_t i = 0; i < initializedLength(); i++)
|
||||
values.infallibleAppend(getElementSpecific<JSVAL_TYPE_INT32>(i).toInt32());
|
||||
|
||||
uint8_t* newElements;
|
||||
if (hasInlineElements()) {
|
||||
newElements = AllocateObjectBuffer<uint8_t>(cx, this, capacity() * sizeof(double));
|
||||
} else {
|
||||
newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
|
||||
capacity() * sizeof(int32_t),
|
||||
capacity() * sizeof(double));
|
||||
}
|
||||
if (!newElements)
|
||||
return false;
|
||||
|
||||
setGroup(group);
|
||||
elements_ = newElements;
|
||||
|
||||
for (size_t i = 0; i < initializedLength(); i++)
|
||||
setElementNoTypeChangeSpecific<JSVAL_TYPE_DOUBLE>(i, DoubleValue(values[i]));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ UnboxedArrayObject*
|
||||
UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32_t length,
|
||||
NewObjectKind newKind, uint32_t maxLength)
|
||||
{
|
||||
MOZ_ASSERT(length <= MaximumCapacity);
|
||||
|
||||
MOZ_ASSERT(group->clasp() == &class_);
|
||||
uint32_t elementSize = UnboxedTypeSize(group->unboxedLayout().elementType());
|
||||
uint32_t capacity = Min(length, maxLength);
|
||||
uint32_t nbytes = offsetOfInlineElements() + elementSize * capacity;
|
||||
|
||||
UnboxedArrayObject* res;
|
||||
if (nbytes <= JSObject::MAX_BYTE_SIZE) {
|
||||
gc::AllocKind allocKind = gc::GetGCObjectKindForBytes(nbytes);
|
||||
|
||||
// If there was no provided length information, pick an allocation kind
|
||||
// to accommodate small arrays (as is done for normal native arrays).
|
||||
if (capacity == 0)
|
||||
allocKind = gc::AllocKind::OBJECT8;
|
||||
|
||||
res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, allocKind, newKind);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
res->setInitializedLengthNoBarrier(0);
|
||||
res->setInlineElements();
|
||||
|
||||
size_t actualCapacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize;
|
||||
MOZ_ASSERT(actualCapacity >= capacity);
|
||||
res->setCapacityIndex(exactCapacityIndex(actualCapacity));
|
||||
} else {
|
||||
res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, gc::AllocKind::OBJECT0, newKind);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
res->setInitializedLengthNoBarrier(0);
|
||||
|
||||
uint32_t capacityIndex = (capacity == length)
|
||||
? CapacityMatchesLengthIndex
|
||||
: chooseCapacityIndex(capacity, length);
|
||||
uint32_t actualCapacity = computeCapacity(capacityIndex, length);
|
||||
|
||||
res->elements_ = AllocateObjectBuffer<uint8_t>(cx, res, actualCapacity * elementSize);
|
||||
if (!res->elements_) {
|
||||
// Make the object safe for GC.
|
||||
res->setInlineElements();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
res->setCapacityIndex(capacityIndex);
|
||||
}
|
||||
|
||||
res->setLength(cx, length);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool
|
||||
UnboxedArrayObject::setElement(ExclusiveContext* cx, size_t index, const Value& v)
|
||||
{
|
||||
MOZ_ASSERT(index < initializedLength());
|
||||
uint8_t* p = elements() + index * elementSize();
|
||||
return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ true);
|
||||
}
|
||||
|
||||
bool
|
||||
UnboxedArrayObject::initElement(ExclusiveContext* cx, size_t index, const Value& v)
|
||||
{
|
||||
MOZ_ASSERT(index < initializedLength());
|
||||
uint8_t* p = elements() + index * elementSize();
|
||||
return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ false);
|
||||
}
|
||||
|
||||
void
|
||||
UnboxedArrayObject::initElementNoTypeChange(size_t index, const Value& v)
|
||||
{
|
||||
MOZ_ASSERT(index < initializedLength());
|
||||
uint8_t* p = elements() + index * elementSize();
|
||||
if (UnboxedTypeNeedsPreBarrier(elementType()))
|
||||
*reinterpret_cast<void**>(p) = nullptr;
|
||||
SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ false);
|
||||
}
|
||||
|
||||
Value
|
||||
UnboxedArrayObject::getElement(size_t index)
|
||||
{
|
||||
MOZ_ASSERT(index < initializedLength());
|
||||
uint8_t* p = elements() + index * elementSize();
|
||||
return GetUnboxedValue(p, elementType(), /* maybeUninitialized = */ false);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
UnboxedArrayObject::trace(JSTracer* trc, JSObject* obj)
|
||||
{
|
||||
JSValueType type = obj->as<UnboxedArrayObject>().elementType();
|
||||
if (!UnboxedTypeNeedsPreBarrier(type))
|
||||
return;
|
||||
|
||||
MOZ_ASSERT(obj->as<UnboxedArrayObject>().elementSize() == sizeof(uintptr_t));
|
||||
size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
|
||||
void** elements = reinterpret_cast<void**>(obj->as<UnboxedArrayObject>().elements());
|
||||
|
||||
switch (type) {
|
||||
case JSVAL_TYPE_OBJECT:
|
||||
for (size_t i = 0; i < initlen; i++) {
|
||||
GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(elements + i);
|
||||
TraceNullableEdge(trc, heap, "unboxed_object");
|
||||
}
|
||||
break;
|
||||
|
||||
case JSVAL_TYPE_STRING:
|
||||
for (size_t i = 0; i < initlen; i++) {
|
||||
GCPtrString* heap = reinterpret_cast<GCPtrString*>(elements + i);
|
||||
TraceEdge(trc, heap, "unboxed_string");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
UnboxedArrayObject::objectMoved(JSObject* obj, const JSObject* old)
|
||||
{
|
||||
UnboxedArrayObject& dst = obj->as<UnboxedArrayObject>();
|
||||
const UnboxedArrayObject& src = old->as<UnboxedArrayObject>();
|
||||
|
||||
// Fix up possible inline data pointer.
|
||||
if (src.hasInlineElements())
|
||||
dst.setInlineElements();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
UnboxedArrayObject::finalize(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(!IsInsideNursery(obj));
|
||||
if (!obj->as<UnboxedArrayObject>().hasInlineElements())
|
||||
js_free(obj->as<UnboxedArrayObject>().elements());
|
||||
}
|
||||
|
||||
/* static */ size_t
|
||||
UnboxedArrayObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src,
|
||||
gc::AllocKind allocKind)
|
||||
{
|
||||
UnboxedArrayObject* ndst = &dst->as<UnboxedArrayObject>();
|
||||
UnboxedArrayObject* nsrc = &src->as<UnboxedArrayObject>();
|
||||
MOZ_ASSERT(ndst->elements() == nsrc->elements());
|
||||
|
||||
Nursery& nursery = trc->runtime()->gc.nursery;
|
||||
|
||||
if (!nursery.isInside(nsrc->elements())) {
|
||||
nursery.removeMallocedBuffer(nsrc->elements());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Determine if we can use inline data for the target array. If this is
|
||||
// possible, the nursery will have picked an allocation size that is large
|
||||
// enough.
|
||||
size_t nbytes = nsrc->capacity() * nsrc->elementSize();
|
||||
if (offsetOfInlineElements() + nbytes <= GetGCKindBytes(allocKind)) {
|
||||
ndst->setInlineElements();
|
||||
} else {
|
||||
MOZ_ASSERT(allocKind == gc::AllocKind::OBJECT0);
|
||||
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
uint8_t* data = nsrc->zone()->pod_malloc<uint8_t>(nbytes);
|
||||
if (!data)
|
||||
oomUnsafe.crash("Failed to allocate unboxed array elements while tenuring.");
|
||||
ndst->elements_ = data;
|
||||
}
|
||||
|
||||
PodCopy(ndst->elements(), nsrc->elements(), nsrc->initializedLength() * nsrc->elementSize());
|
||||
|
||||
// Set a forwarding pointer for the element buffers in case they were
|
||||
// preserved on the stack by Ion.
|
||||
bool direct = nsrc->capacity() * nsrc->elementSize() >= sizeof(uintptr_t);
|
||||
nursery.maybeSetForwardingPointer(trc, nsrc->elements(), ndst->elements(), direct);
|
||||
|
||||
return ndst->hasInlineElements() ? 0 : nbytes;
|
||||
}
|
||||
|
||||
// Possible capacities for unboxed arrays. Some of these capacities might seem
|
||||
// a little weird, but were chosen to allow the inline data of objects of each
|
||||
// size to be fully utilized for arrays of the various types on both 32 bit and
|
||||
// 64 bit platforms.
|
||||
//
|
||||
// To find the possible inline capacities, the following script was used:
|
||||
//
|
||||
// var fixedSlotCapacities = [0, 2, 4, 8, 12, 16];
|
||||
// var dataSizes = [1, 4, 8];
|
||||
// var header32 = 4 * 2 + 4 * 2;
|
||||
// var header64 = 8 * 2 + 4 * 2;
|
||||
//
|
||||
// for (var i = 0; i < fixedSlotCapacities.length; i++) {
|
||||
// var nfixed = fixedSlotCapacities[i];
|
||||
// var size32 = 4 * 4 + 8 * nfixed - header32;
|
||||
// var size64 = 8 * 4 + 8 * nfixed - header64;
|
||||
// for (var j = 0; j < dataSizes.length; j++) {
|
||||
// print(size32 / dataSizes[j]);
|
||||
// print(size64 / dataSizes[j]);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
/* static */ const uint32_t
|
||||
UnboxedArrayObject::CapacityArray[] = {
|
||||
UINT32_MAX, // For CapacityMatchesLengthIndex.
|
||||
0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 13, 16, 17, 18, 24, 26, 32, 34, 40, 64, 72, 96, 104, 128, 136,
|
||||
256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288,
|
||||
1048576, 2097152, 3145728, 4194304, 5242880, 6291456, 7340032, 8388608, 9437184, 11534336,
|
||||
13631488, 15728640, 17825792, 20971520, 24117248, 27262976, 31457280, 35651584, 40894464,
|
||||
46137344, 52428800, 59768832, MaximumCapacity
|
||||
};
|
||||
|
||||
static const uint32_t
|
||||
Pow2CapacityIndexes[] = {
|
||||
2, // 1
|
||||
3, // 2
|
||||
5, // 4
|
||||
8, // 8
|
||||
13, // 16
|
||||
18, // 32
|
||||
21, // 64
|
||||
25, // 128
|
||||
27, // 256
|
||||
28, // 512
|
||||
29, // 1024
|
||||
30, // 2048
|
||||
31, // 4096
|
||||
32, // 8192
|
||||
33, // 16384
|
||||
34, // 32768
|
||||
35, // 65536
|
||||
36, // 131072
|
||||
37, // 262144
|
||||
38, // 524288
|
||||
39 // 1048576
|
||||
};
|
||||
|
||||
static const uint32_t MebiCapacityIndex = 39;
|
||||
|
||||
/* static */ uint32_t
|
||||
UnboxedArrayObject::chooseCapacityIndex(uint32_t capacity, uint32_t length)
|
||||
{
|
||||
// Note: the structure and behavior of this method follow along with
|
||||
// NativeObject::goodAllocated. Changes to the allocation strategy in one
|
||||
// should generally be matched by the other.
|
||||
|
||||
// Make sure we have enough space to store all possible values for the capacity index.
|
||||
// This ought to be a static_assert, but MSVC doesn't like that.
|
||||
MOZ_ASSERT(mozilla::ArrayLength(CapacityArray) - 1 <= (CapacityMask >> CapacityShift));
|
||||
|
||||
// The caller should have ensured the capacity is possible for an unboxed array.
|
||||
MOZ_ASSERT(capacity <= MaximumCapacity);
|
||||
|
||||
static const uint32_t Mebi = 1024 * 1024;
|
||||
|
||||
if (capacity <= Mebi) {
|
||||
capacity = mozilla::RoundUpPow2(capacity);
|
||||
|
||||
// When the required capacity is close to the array length, then round
|
||||
// up to the array length itself, as for NativeObject.
|
||||
if (length >= capacity && capacity > (length / 3) * 2)
|
||||
return CapacityMatchesLengthIndex;
|
||||
|
||||
if (capacity < MinimumDynamicCapacity)
|
||||
capacity = MinimumDynamicCapacity;
|
||||
|
||||
uint32_t bit = mozilla::FloorLog2Size(capacity);
|
||||
MOZ_ASSERT(capacity == uint32_t(1 << bit));
|
||||
MOZ_ASSERT(bit <= 20);
|
||||
MOZ_ASSERT(mozilla::ArrayLength(Pow2CapacityIndexes) == 21);
|
||||
|
||||
uint32_t index = Pow2CapacityIndexes[bit];
|
||||
MOZ_ASSERT(CapacityArray[index] == capacity);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(CapacityArray[MebiCapacityIndex] == Mebi);
|
||||
|
||||
for (uint32_t i = MebiCapacityIndex + 1;; i++) {
|
||||
if (CapacityArray[i] >= capacity)
|
||||
return i;
|
||||
}
|
||||
|
||||
MOZ_CRASH("Invalid capacity");
|
||||
}
|
||||
|
||||
/* static */ uint32_t
|
||||
UnboxedArrayObject::exactCapacityIndex(uint32_t capacity)
|
||||
{
|
||||
for (size_t i = CapacityMatchesLengthIndex + 1; i < ArrayLength(CapacityArray); i++) {
|
||||
if (CapacityArray[i] == capacity)
|
||||
return i;
|
||||
}
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
bool
|
||||
UnboxedArrayObject::growElements(ExclusiveContext* cx, size_t cap)
|
||||
{
|
||||
// The caller should have checked if this capacity is possible for an
|
||||
// unboxed array, so the only way this call can fail is from OOM.
|
||||
MOZ_ASSERT(cap <= MaximumCapacity);
|
||||
|
||||
uint32_t oldCapacity = capacity();
|
||||
uint32_t newCapacityIndex = chooseCapacityIndex(cap, length());
|
||||
uint32_t newCapacity = computeCapacity(newCapacityIndex, length());
|
||||
|
||||
MOZ_ASSERT(oldCapacity < cap);
|
||||
MOZ_ASSERT(cap <= newCapacity);
|
||||
|
||||
// The allocation size computation below cannot have integer overflows.
|
||||
JS_STATIC_ASSERT(MaximumCapacity < UINT32_MAX / sizeof(double));
|
||||
|
||||
uint8_t* newElements;
|
||||
if (hasInlineElements()) {
|
||||
newElements = AllocateObjectBuffer<uint8_t>(cx, this, newCapacity * elementSize());
|
||||
if (!newElements)
|
||||
return false;
|
||||
js_memcpy(newElements, elements(), initializedLength() * elementSize());
|
||||
} else {
|
||||
newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
|
||||
oldCapacity * elementSize(),
|
||||
newCapacity * elementSize());
|
||||
if (!newElements)
|
||||
return false;
|
||||
}
|
||||
|
||||
elements_ = newElements;
|
||||
setCapacityIndex(newCapacityIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
UnboxedArrayObject::shrinkElements(ExclusiveContext* cx, size_t cap)
|
||||
{
|
||||
if (hasInlineElements())
|
||||
return;
|
||||
|
||||
uint32_t oldCapacity = capacity();
|
||||
uint32_t newCapacityIndex = chooseCapacityIndex(cap, 0);
|
||||
uint32_t newCapacity = computeCapacity(newCapacityIndex, 0);
|
||||
|
||||
MOZ_ASSERT(cap < oldCapacity);
|
||||
MOZ_ASSERT(cap <= newCapacity);
|
||||
|
||||
if (newCapacity >= oldCapacity)
|
||||
return;
|
||||
|
||||
uint8_t* newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
|
||||
oldCapacity * elementSize(),
|
||||
newCapacity * elementSize());
|
||||
if (!newElements)
|
||||
return;
|
||||
|
||||
elements_ = newElements;
|
||||
setCapacityIndex(newCapacityIndex);
|
||||
}
|
||||
|
||||
bool
|
||||
UnboxedArrayObject::containsProperty(ExclusiveContext* cx, jsid id)
|
||||
{
|
||||
if (JSID_IS_INT(id) && uint32_t(JSID_TO_INT(id)) < initializedLength())
|
||||
return true;
|
||||
if (JSID_IS_ATOM(id) && JSID_TO_ATOM(id) == cx->names().length)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
|
||||
HandleId id, MutableHandleObject objp,
|
||||
MutableHandleShape propp)
|
||||
{
|
||||
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
|
||||
MarkNonNativePropertyFound<CanGC>(propp);
|
||||
objp.set(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->staticPrototype());
|
||||
if (!proto) {
|
||||
objp.set(nullptr);
|
||||
propp.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return LookupProperty(cx, proto, id, objp, propp);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result)
|
||||
{
|
||||
if (JSID_IS_INT(id) && !desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) {
|
||||
UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
|
||||
|
||||
uint32_t index = JSID_TO_INT(id);
|
||||
if (index < nobj->initializedLength()) {
|
||||
if (nobj->setElement(cx, index, desc.value()))
|
||||
return result.succeed();
|
||||
} else if (index == nobj->initializedLength() && index < MaximumCapacity) {
|
||||
if (nobj->initializedLength() == nobj->capacity()) {
|
||||
if (!nobj->growElements(cx, index + 1))
|
||||
return false;
|
||||
}
|
||||
nobj->setInitializedLength(index + 1);
|
||||
if (nobj->initElement(cx, index, desc.value())) {
|
||||
if (nobj->length() <= index)
|
||||
nobj->setLengthInt32(index + 1);
|
||||
return result.succeed();
|
||||
}
|
||||
nobj->setInitializedLengthNoBarrier(index);
|
||||
}
|
||||
}
|
||||
|
||||
if (!convertToNative(cx, obj))
|
||||
return false;
|
||||
|
||||
return DefineProperty(cx, obj, id, desc, result);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
|
||||
{
|
||||
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
|
||||
*foundp = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->staticPrototype());
|
||||
if (!proto) {
|
||||
*foundp = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return HasProperty(cx, proto, id, foundp);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
|
||||
HandleId id, MutableHandleValue vp)
|
||||
{
|
||||
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
|
||||
if (JSID_IS_INT(id))
|
||||
vp.set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id)));
|
||||
else
|
||||
vp.set(Int32Value(obj->as<UnboxedArrayObject>().length()));
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->staticPrototype());
|
||||
if (!proto) {
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
return GetProperty(cx, proto, receiver, id, vp);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
|
||||
HandleValue receiver, ObjectOpResult& result)
|
||||
{
|
||||
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
|
||||
if (receiver.isObject() && obj == &receiver.toObject()) {
|
||||
if (JSID_IS_INT(id)) {
|
||||
if (obj->as<UnboxedArrayObject>().setElement(cx, JSID_TO_INT(id), v))
|
||||
return result.succeed();
|
||||
} else {
|
||||
uint32_t len;
|
||||
if (!CanonicalizeArrayLengthValue(cx, v, &len))
|
||||
return false;
|
||||
UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
|
||||
if (len < nobj->initializedLength()) {
|
||||
nobj->setInitializedLength(len);
|
||||
nobj->shrinkElements(cx, len);
|
||||
}
|
||||
nobj->setLength(cx, len);
|
||||
return result.succeed();
|
||||
}
|
||||
|
||||
if (!convertToNative(cx, obj))
|
||||
return false;
|
||||
return SetProperty(cx, obj, id, v, receiver, result);
|
||||
}
|
||||
|
||||
return SetPropertyByDefining(cx, id, v, receiver, result);
|
||||
}
|
||||
|
||||
return SetPropertyOnProto(cx, obj, id, v, receiver, result);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc)
|
||||
{
|
||||
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
|
||||
if (JSID_IS_INT(id)) {
|
||||
desc.value().set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id)));
|
||||
desc.setAttributes(JSPROP_ENUMERATE);
|
||||
} else {
|
||||
desc.value().set(Int32Value(obj->as<UnboxedArrayObject>().length()));
|
||||
desc.setAttributes(JSPROP_PERMANENT);
|
||||
}
|
||||
desc.object().set(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
desc.object().set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
ObjectOpResult& result)
|
||||
{
|
||||
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
|
||||
size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
|
||||
if (JSID_IS_INT(id) && JSID_TO_INT(id) == int32_t(initlen - 1)) {
|
||||
obj->as<UnboxedArrayObject>().setInitializedLength(initlen - 1);
|
||||
obj->as<UnboxedArrayObject>().shrinkElements(cx, initlen - 1);
|
||||
return result.succeed();
|
||||
}
|
||||
}
|
||||
|
||||
if (!convertToNative(cx, obj))
|
||||
return false;
|
||||
return DeleteProperty(cx, obj, id, result);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedArrayObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
|
||||
{
|
||||
if (!convertToNative(cx, obj))
|
||||
return false;
|
||||
return WatchProperty(cx, obj, id, callable);
|
||||
}
|
||||
|
||||
/* 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,
|
||||
UnboxedArrayObject::obj_watch,
|
||||
nullptr, /* No unwatch needed, as watch() converts the object to native */
|
||||
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
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -1661,31 +945,6 @@ 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)
|
||||
|
|
@ -1695,58 +954,3 @@ 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,17 +96,11 @@ 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),
|
||||
elementType_(JSVAL_TYPE_MAGIC)
|
||||
size_(0), newScript_(nullptr), traceList_(nullptr), constructorCode_(nullptr)
|
||||
{}
|
||||
|
||||
bool initProperties(const PropertyVector& properties, size_t size) {
|
||||
|
|
@ -114,10 +108,6 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
|||
return properties_.appendAll(properties);
|
||||
}
|
||||
|
||||
void initArray(JSValueType elementType) {
|
||||
elementType_ = elementType;
|
||||
}
|
||||
|
||||
~UnboxedLayout() {
|
||||
if (newScript_)
|
||||
newScript_->clear();
|
||||
|
|
@ -130,10 +120,6 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
|||
constructorCode_.init(nullptr);
|
||||
}
|
||||
|
||||
bool isArray() const {
|
||||
return elementType_ != JSVAL_TYPE_MAGIC;
|
||||
}
|
||||
|
||||
void detachFromCompartment();
|
||||
|
||||
const PropertyVector& properties() const {
|
||||
|
|
@ -201,10 +187,6 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
|||
constructorCode_ = code;
|
||||
}
|
||||
|
||||
JSValueType elementType() const {
|
||||
return elementType_;
|
||||
}
|
||||
|
||||
inline gc::AllocKind getAllocKind() const;
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
|
|
@ -324,193 +306,6 @@ UnboxedLayout::getAllocKind() const
|
|||
return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size());
|
||||
}
|
||||
|
||||
// Class for an array object using an unboxed representation.
|
||||
class UnboxedArrayObject : public JSObject
|
||||
{
|
||||
// Elements pointer for the object.
|
||||
uint8_t* elements_;
|
||||
|
||||
// The nominal array length. This always fits in an int32_t.
|
||||
uint32_t length_;
|
||||
|
||||
// Value indicating the allocated capacity and initialized length of the
|
||||
// array. The top CapacityBits bits are an index into CapacityArray, which
|
||||
// indicates the elements capacity. The low InitializedLengthBits store the
|
||||
// initialized length of the array.
|
||||
uint32_t capacityIndexAndInitializedLength_;
|
||||
|
||||
// If the elements are inline, they will point here.
|
||||
uint8_t inlineElements_[1];
|
||||
|
||||
public:
|
||||
static const uint32_t CapacityBits = 6;
|
||||
static const uint32_t CapacityShift = 26;
|
||||
|
||||
static const uint32_t CapacityMask = uint32_t(-1) << CapacityShift;
|
||||
static const uint32_t InitializedLengthMask = (1 << CapacityShift) - 1;
|
||||
|
||||
static const uint32_t MaximumCapacity = InitializedLengthMask;
|
||||
static const uint32_t MinimumDynamicCapacity = 8;
|
||||
|
||||
static const uint32_t CapacityArray[];
|
||||
|
||||
// Capacity index which indicates the array's length is also its capacity.
|
||||
static const uint32_t CapacityMatchesLengthIndex = 0;
|
||||
|
||||
private:
|
||||
static inline uint32_t computeCapacity(uint32_t index, uint32_t length) {
|
||||
if (index == CapacityMatchesLengthIndex)
|
||||
return length;
|
||||
return CapacityArray[index];
|
||||
}
|
||||
|
||||
static uint32_t chooseCapacityIndex(uint32_t capacity, uint32_t length);
|
||||
static uint32_t exactCapacityIndex(uint32_t capacity);
|
||||
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
|
||||
HandleId id, MutableHandleObject objp,
|
||||
MutableHandleShape propp);
|
||||
|
||||
static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result);
|
||||
|
||||
static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
|
||||
|
||||
static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
|
||||
HandleId id, MutableHandleValue vp);
|
||||
|
||||
static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
|
||||
HandleValue receiver, ObjectOpResult& result);
|
||||
|
||||
static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc);
|
||||
|
||||
static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
ObjectOpResult& result);
|
||||
|
||||
static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
|
||||
bool enumerableOnly);
|
||||
static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
|
||||
|
||||
inline const UnboxedLayout& layout() const;
|
||||
|
||||
const UnboxedLayout& layoutDontCheckGeneration() const {
|
||||
return group()->unboxedLayoutDontCheckGeneration();
|
||||
}
|
||||
|
||||
JSValueType elementType() const {
|
||||
return layoutDontCheckGeneration().elementType();
|
||||
}
|
||||
|
||||
uint32_t elementSize() const {
|
||||
return UnboxedTypeSize(elementType());
|
||||
}
|
||||
|
||||
static bool convertToNative(JSContext* cx, JSObject* obj);
|
||||
static UnboxedArrayObject* create(ExclusiveContext* cx, HandleObjectGroup group,
|
||||
uint32_t length, NewObjectKind newKind,
|
||||
uint32_t maxLength = MaximumCapacity);
|
||||
|
||||
static bool convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj,
|
||||
ObjectGroup* group, Shape* shape);
|
||||
bool convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group);
|
||||
|
||||
void fillAfterConvert(ExclusiveContext* cx,
|
||||
Handle<GCVector<Value>> values, size_t* valueCursor);
|
||||
|
||||
static void trace(JSTracer* trc, JSObject* object);
|
||||
static void objectMoved(JSObject* obj, const JSObject* old);
|
||||
static void finalize(FreeOp* fop, JSObject* obj);
|
||||
|
||||
static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src,
|
||||
gc::AllocKind allocKind);
|
||||
|
||||
uint8_t* elements() {
|
||||
return elements_;
|
||||
}
|
||||
|
||||
bool hasInlineElements() const {
|
||||
return elements_ == &inlineElements_[0];
|
||||
}
|
||||
|
||||
uint32_t length() const {
|
||||
return length_;
|
||||
}
|
||||
|
||||
uint32_t initializedLength() const {
|
||||
return capacityIndexAndInitializedLength_ & InitializedLengthMask;
|
||||
}
|
||||
|
||||
uint32_t capacityIndex() const {
|
||||
return (capacityIndexAndInitializedLength_ & CapacityMask) >> CapacityShift;
|
||||
}
|
||||
|
||||
uint32_t capacity() const {
|
||||
return computeCapacity(capacityIndex(), length());
|
||||
}
|
||||
|
||||
bool containsProperty(ExclusiveContext* cx, jsid id);
|
||||
|
||||
bool setElement(ExclusiveContext* cx, size_t index, const Value& v);
|
||||
bool initElement(ExclusiveContext* cx, size_t index, const Value& v);
|
||||
void initElementNoTypeChange(size_t index, const Value& v);
|
||||
Value getElement(size_t index);
|
||||
|
||||
template <JSValueType Type> inline bool setElementSpecific(ExclusiveContext* cx, size_t index,
|
||||
const Value& v);
|
||||
template <JSValueType Type> inline void setElementNoTypeChangeSpecific(size_t index, const Value& v);
|
||||
template <JSValueType Type> inline bool initElementSpecific(ExclusiveContext* cx, size_t index,
|
||||
const Value& v);
|
||||
template <JSValueType Type> inline void initElementNoTypeChangeSpecific(size_t index, const Value& v);
|
||||
template <JSValueType Type> inline Value getElementSpecific(size_t index);
|
||||
template <JSValueType Type> inline void triggerPreBarrier(size_t index);
|
||||
|
||||
bool growElements(ExclusiveContext* cx, size_t cap);
|
||||
void shrinkElements(ExclusiveContext* cx, size_t cap);
|
||||
|
||||
static uint32_t offsetOfElements() {
|
||||
return offsetof(UnboxedArrayObject, elements_);
|
||||
}
|
||||
static uint32_t offsetOfLength() {
|
||||
return offsetof(UnboxedArrayObject, length_);
|
||||
}
|
||||
static uint32_t offsetOfCapacityIndexAndInitializedLength() {
|
||||
return offsetof(UnboxedArrayObject, capacityIndexAndInitializedLength_);
|
||||
}
|
||||
static uint32_t offsetOfInlineElements() {
|
||||
return offsetof(UnboxedArrayObject, inlineElements_);
|
||||
}
|
||||
|
||||
void setLengthInt32(uint32_t length) {
|
||||
MOZ_ASSERT(length <= INT32_MAX);
|
||||
length_ = length;
|
||||
}
|
||||
|
||||
inline void setLength(ExclusiveContext* cx, uint32_t len);
|
||||
inline void setInitializedLength(uint32_t initlen);
|
||||
|
||||
inline void setInitializedLengthNoBarrier(uint32_t initlen) {
|
||||
MOZ_ASSERT(initlen <= InitializedLengthMask);
|
||||
capacityIndexAndInitializedLength_ =
|
||||
(capacityIndexAndInitializedLength_ & CapacityMask) | initlen;
|
||||
}
|
||||
|
||||
private:
|
||||
void setInlineElements() {
|
||||
elements_ = &inlineElements_[0];
|
||||
}
|
||||
|
||||
void setCapacityIndex(uint32_t index) {
|
||||
MOZ_ASSERT(index <= (CapacityMask >> CapacityShift));
|
||||
capacityIndexAndInitializedLength_ =
|
||||
(index << CapacityShift) | initializedLength();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue