Bug 1172943 - Use unboxed arrays for JSON and script literal arrays, r=jandem.

This commit is contained in:
Brian Hackett 2015-06-13 07:54:06 -07:00
parent fb6842de07
commit fe2936f751
22 changed files with 241 additions and 362 deletions

View File

@ -4598,7 +4598,6 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
return true;
case PNK_CALLSITEOBJ:
case PNK_ARRAY: {
RootedValue value(cx);
unsigned count;
ParseNode* pn;
@ -4606,8 +4605,12 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
vp.setMagic(JS_GENERIC_MAGIC);
return true;
}
if (allowObjects == DontAllowNestedObjects)
ObjectGroup::NewArrayKind arrayKind = ObjectGroup::NewArrayKind::Normal;
if (allowObjects == ForCopyOnWriteArray) {
arrayKind = ObjectGroup::NewArrayKind::CopyOnWrite;
allowObjects = DontAllowObjects;
}
if (getKind() == PNK_CALLSITEOBJ) {
count = pn_count - 1;
@ -4618,26 +4621,25 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
pn = pn_head;
}
RootedArrayObject obj(cx, NewDenseFullyAllocatedArray(cx, count, nullptr, newKind));
if (!obj)
AutoValueVector values(cx);
if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), count))
return false;
unsigned idx = 0;
RootedId id(cx);
for (; pn; idx++, pn = pn->pn_next) {
if (!pn->getConstantValue(cx, allowObjects, &value))
size_t idx;
for (idx = 0; pn; idx++, pn = pn->pn_next) {
if (!pn->getConstantValue(cx, allowObjects, values[idx]))
return false;
if (value.isMagic(JS_GENERIC_MAGIC)) {
if (values[idx].isMagic(JS_GENERIC_MAGIC)) {
vp.setMagic(JS_GENERIC_MAGIC);
return true;
}
id = INT_TO_JSID(idx);
if (!DefineProperty(cx, obj, id, value, nullptr, nullptr, JSPROP_ENUMERATE))
return false;
}
MOZ_ASSERT(idx == count);
ObjectGroup::fixArrayGroup(cx, obj);
JSObject* obj = ObjectGroup::newArrayObject(cx, values.begin(), values.length(),
newKind, arrayKind);
if (!obj)
return false;
vp.setObject(*obj);
return true;
}
@ -4649,8 +4651,7 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
vp.setMagic(JS_GENERIC_MAGIC);
return true;
}
if (allowObjects == DontAllowNestedObjects)
allowObjects = DontAllowObjects;
MOZ_ASSERT(allowObjects == AllowObjects);
AutoIdValueVector properties(cx);
@ -7894,7 +7895,7 @@ BytecodeEmitter::emitTree(ParseNode* pn)
// every time the initializer executes.
if (emitterMode != BytecodeEmitter::SelfHosting && pn->pn_count != 0) {
RootedValue value(cx);
if (!pn->getConstantValue(cx, ParseNode::DontAllowNestedObjects, &value))
if (!pn->getConstantValue(cx, ParseNode::ForCopyOnWriteArray, &value))
return false;
if (!value.isMagic(JS_GENERIC_MAGIC)) {
// Note: the group of the template object might not yet reflect
@ -7904,9 +7905,9 @@ BytecodeEmitter::emitTree(ParseNode* pn)
// group for the template is accurate. We don't do this here as we
// want to use ObjectGroup::allocationSiteGroup, which requires a
// finished script.
NativeObject* obj = &value.toObject().as<NativeObject>();
if (!ObjectElements::MakeElementsCopyOnWrite(cx, obj))
return false;
JSObject* obj = &value.toObject();
MOZ_ASSERT(obj->is<ArrayObject>() &&
obj->as<ArrayObject>().denseElementsAreCopyOnWrite());
ObjectBox* objbox = parser->newObjectBox(obj);
if (!objbox)

View File

@ -898,8 +898,8 @@ class ParseNode
enum AllowConstantObjects {
DontAllowObjects = 0,
DontAllowNestedObjects,
AllowObjects
AllowObjects,
ForCopyOnWriteArray
};
bool getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObjects, MutableHandleValue vp,

View File

@ -16,6 +16,10 @@ if (getJitCompilerOptions()["ion.warmup.trigger"] <= 90)
if (getJitCompilerOptions()["ion.forceinlineCaches"])
setJitCompilerOption("ion.forceinlineCaches", 0);
// Frequent GCs can interfere with the tests being performed here.
if (typeof gczeal != "undefined")
gczeal(0);
var arr = new Array();
var max = 2000;
for (var i=0; i < max; i++)

View File

@ -3459,13 +3459,14 @@ BaselineCompiler::emit_JSOP_REST()
{
frame.syncStack(0);
ArrayObject* templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject);
JSObject* templateObject =
ObjectGroup::newArrayObject(cx, nullptr, 0, TenuredObject,
ObjectGroup::NewArrayKind::UnknownIndex);
if (!templateObject)
return false;
ObjectGroup::fixRestArgumentsGroup(cx, templateObject);
// Call IC.
ICRest_Fallback::Compiler compiler(cx, templateObject);
ICRest_Fallback::Compiler compiler(cx, &templateObject->as<ArrayObject>());
if (!emitOpIC(compiler.getStub(&stubSpace_)))
return false;

View File

@ -12580,10 +12580,10 @@ static bool DoRestFallback(JSContext* cx, BaselineFrame* frame, ICRest_Fallback*
unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
Value* rest = frame->argv() + numFormals;
ArrayObject* obj = NewDenseCopiedArray(cx, numRest, rest, nullptr);
JSObject* obj = ObjectGroup::newArrayObject(cx, rest, numRest, GenericObject,
ObjectGroup::NewArrayKind::UnknownIndex);
if (!obj)
return false;
ObjectGroup::fixRestArgumentsGroup(cx, obj);
res.setObject(*obj);
return true;
}

View File

@ -530,6 +530,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();

View File

@ -289,7 +289,8 @@ ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length)
{
*length = GetAnyBoxedOrUnboxedArrayLength(obj);
DenseElementResult result =
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, *length, v.address(), 1, DontUpdateTypes);
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, *length, v.address(), 1,
ShouldUpdateTypes::DontUpdate);
if (result != DenseElementResult::Incomplete) {
(*length)++;
return result == DenseElementResult::Success;
@ -1105,7 +1106,7 @@ SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index,
DenseElementResult result =
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, index, value.address(), 1,
DontUpdateTypes);
ShouldUpdateTypes::DontUpdate);
if (result != DenseElementResult::Incomplete)
return result == DenseElementResult::Success;

View File

@ -248,9 +248,8 @@ ElementAdder::append(JSContext* cx, HandleValue v)
{
MOZ_ASSERT(index_ < length_);
if (resObj_) {
DenseElementResult result = SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_,
v.address(), 1,
UpdateTypes);
DenseElementResult result =
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_, v.address(), 1);
if (result == DenseElementResult::Failure)
return false;
if (result == DenseElementResult::Incomplete) {
@ -366,8 +365,7 @@ SetArrayElement(JSContext* cx, HandleObject obj, double index, HandleValue v)
if ((obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) && !obj->isIndexed() && index <= UINT32_MAX) {
DenseElementResult result =
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(index), v.address(), 1,
UpdateTypes);
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(index), v.address(), 1);
if (result != DenseElementResult::Incomplete)
return result == DenseElementResult::Success;
}
@ -1208,7 +1206,9 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp)
/* vector must point to rooted memory. */
static bool
InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start, uint32_t count, const Value* vector, ShouldUpdateTypes updateTypes)
InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start,
uint32_t count, const Value* vector,
ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update)
{
MOZ_ASSERT(count <= MAX_ARRAY_INDEX);
@ -1954,7 +1954,7 @@ js::array_sort(JSContext* cx, unsigned argc, Value* vp)
}
}
if (!InitArrayElements(cx, obj, 0, uint32_t(n), vec.begin(), DontUpdateTypes))
if (!InitArrayElements(cx, obj, 0, uint32_t(n), vec.begin(), ShouldUpdateTypes::DontUpdate))
return false;
}
@ -2013,7 +2013,7 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp)
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
DenseElementResult result =
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, length,
args.array(), args.length(), UpdateTypes);
args.array(), args.length());
if (result != DenseElementResult::Incomplete) {
if (result == DenseElementResult::Failure)
return false;
@ -2031,7 +2031,7 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp)
}
/* Steps 4-5. */
if (!InitArrayElements(cx, obj, length, args.length(), args.array(), UpdateTypes))
if (!InitArrayElements(cx, obj, length, args.length(), args.array()))
return false;
/* Steps 6-7. */
@ -2277,7 +2277,7 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
}
/* Copy from args to the bottom of the array. */
if (!InitArrayElements(cx, obj, 0, args.length(), args.array(), UpdateTypes))
if (!InitArrayElements(cx, obj, 0, args.length(), args.array()))
return false;
newlen += args.length();
@ -3443,7 +3443,7 @@ js::NewDenseUnallocatedArray(ExclusiveContext* cx, uint32_t length,
}
ArrayObject*
js::NewDenseCopiedArray(JSContext* cx, uint32_t length, HandleArrayObject src,
js::NewDenseCopiedArray(ExclusiveContext* cx, uint32_t length, HandleArrayObject src,
uint32_t elementOffset, HandleObject proto /* = nullptr */)
{
MOZ_ASSERT(!src->isIndexed());
@ -3463,11 +3463,11 @@ js::NewDenseCopiedArray(JSContext* cx, uint32_t length, HandleArrayObject src,
// values must point at already-rooted Value objects
ArrayObject*
js::NewDenseCopiedArray(JSContext* cx, uint32_t length, const Value* values,
js::NewDenseCopiedArray(ExclusiveContext* cx, uint32_t length, const Value* values,
HandleObject proto /* = nullptr */,
NewObjectKind newKind /* = GenericObject */)
{
ArrayObject* arr = NewArray<UINT32_MAX>(cx, length, proto);
ArrayObject* arr = NewArray<UINT32_MAX>(cx, length, proto, newKind);
if (!arr)
return nullptr;
@ -3522,7 +3522,7 @@ js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc
// capacity (up to maxLength), using the specified group if possible.
template <uint32_t maxLength>
static inline JSObject*
NewArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length,
NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length,
NewObjectKind newKind = GenericObject, bool forceAnalyze = false)
{
MOZ_ASSERT(newKind != SingletonObject);
@ -3558,13 +3558,14 @@ NewArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length,
}
JSObject*
js::NewFullyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length)
js::NewFullyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length,
NewObjectKind newKind)
{
return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length);
return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind);
}
JSObject*
js::NewPartlyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length)
js::NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length)
{
return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
}
@ -3627,25 +3628,27 @@ js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length
}
JSObject*
js::NewCopiedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, const Value* vp, size_t length)
js::NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group,
const Value* vp, size_t length, NewObjectKind newKind,
ShouldUpdateTypes updateTypes)
{
JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length);
JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length, newKind);
if (!obj)
return nullptr;
DenseElementResult result =
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, UpdateTypes);
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, updateTypes);
if (result == DenseElementResult::Failure)
return nullptr;
if (result == DenseElementResult::Success)
return obj;
MOZ_ASSERT(obj->is<UnboxedArrayObject>());
if (!UnboxedArrayObject::convertToNative(cx, obj))
if (!UnboxedArrayObject::convertToNative(cx->asJSContext(), obj))
return nullptr;
result = SetOrExtendBoxedOrUnboxedDenseElements<JSVAL_TYPE_MAGIC>(cx, obj, 0, vp, length,
UpdateTypes);
updateTypes);
MOZ_ASSERT(result != DenseElementResult::Incomplete);
if (result == DenseElementResult::Failure)
return nullptr;

View File

@ -67,12 +67,12 @@ NewDenseFullyAllocatedArray(ExclusiveContext* cx, uint32_t length, HandleObject
/* Create a dense array with a copy of the dense array elements in src. */
extern ArrayObject*
NewDenseCopiedArray(JSContext* cx, uint32_t length, HandleArrayObject src,
NewDenseCopiedArray(ExclusiveContext* cx, uint32_t length, HandleArrayObject src,
uint32_t elementOffset, HandleObject proto = nullptr);
/* Create a dense array from the given array values, which must be rooted */
extern ArrayObject*
NewDenseCopiedArray(JSContext* cx, uint32_t length, const Value* values,
NewDenseCopiedArray(ExclusiveContext* cx, uint32_t length, const Value* values,
HandleObject proto = nullptr, NewObjectKind newKind = GenericObject);
/* Create a dense array based on templateObject with the given length. */
@ -86,10 +86,11 @@ NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::In
// The methods below can create either boxed or unboxed arrays.
extern JSObject*
NewFullyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length);
NewFullyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length,
NewObjectKind newKind = GenericObject);
extern JSObject*
NewPartlyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length);
NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length);
extern JSObject*
NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
@ -107,8 +108,17 @@ NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
extern JSObject*
NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length);
enum class ShouldUpdateTypes
{
Update,
DontUpdate
};
extern JSObject*
NewCopiedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, const Value* vp, size_t length);
NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group,
const Value* vp, size_t length,
NewObjectKind newKind = GenericObject,
ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update);
extern JSObject*
NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length);

View File

@ -1398,12 +1398,6 @@ class AutoEnterOOMUnsafeRegion
class AutoEnterOOMUnsafeRegion {};
#endif /* DEBUG */
// This tests whether something is inside the GGC's nursery only;
// use sparingly, mostly testing for any nursery, using IsInsideNursery,
// is appropriate.
bool
IsInsideGGCNursery(const gc::Cell* cell);
// A singly linked list of zones.
class ZoneList
{

View File

@ -1589,23 +1589,26 @@ js::CloneObject(JSContext* cx, HandleObject obj, Handle<js::TaggedProto> proto)
}
static bool
GetScriptArrayObjectElements(JSContext* cx, HandleArrayObject obj, AutoValueVector& values)
GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, AutoValueVector& values)
{
MOZ_ASSERT(!obj->isSingleton());
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
size_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), length))
return false;
if (obj->nonProxyIsExtensible()) {
MOZ_ASSERT(obj->slotSpan() == 0);
MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().slotSpan() == 0);
if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), obj->getDenseInitializedLength()))
return false;
for (size_t i = 0; i < obj->getDenseInitializedLength(); i++)
values[i].set(obj->getDenseElement(i));
size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
for (size_t i = 0; i < initlen; i++)
values[i].set(GetAnyBoxedOrUnboxedDenseElement(obj, i));
} else {
// Call site objects are frozen before they escape to script, which
// converts their dense elements into data properties.
for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) {
ArrayObject* aobj = &obj->as<ArrayObject>();
for (Shape::Range<NoGC> r(aobj->lastProperty()); !r.empty(); r.popFront()) {
Shape& shape = r.front();
if (shape.propid() == NameToId(cx->names().length))
continue;
@ -1619,12 +1622,7 @@ GetScriptArrayObjectElements(JSContext* cx, HandleArrayObject obj, AutoValueVect
continue;
uint32_t index = JSID_TO_INT(shape.propid());
while (index >= values.length()) {
if (!values.append(MagicValue(JS_ELEMENTS_HOLE)))
return false;
}
values[index].set(obj->getSlot(shape.slot()));
values[index].set(aobj->getSlot(shape.slot()));
}
}
@ -1695,40 +1693,27 @@ js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKin
/* NB: Keep this in sync with XDRObjectLiteral. */
MOZ_ASSERT_IF(obj->isSingleton(),
JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() || obj->is<ArrayObject>());
MOZ_ASSERT(cx->isInsideCurrentCompartment(obj));
MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() ||
obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
MOZ_ASSERT(newKind != SingletonObject);
if (obj->is<ArrayObject>()) {
HandleArrayObject aobj = obj.as<ArrayObject>();
if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) {
AutoValueVector values(cx);
if (!GetScriptArrayObjectElements(cx, aobj, values))
if (!GetScriptArrayObjectElements(cx, obj, values))
return nullptr;
// Deep clone any elements.
uint32_t initialized = aobj->getDenseInitializedLength();
for (uint32_t i = 0; i < initialized; ++i) {
for (uint32_t i = 0; i < values.length(); ++i) {
if (!DeepCloneValue(cx, values[i].address(), newKind))
return nullptr;
}
RootedArrayObject clone(cx, NewDenseUnallocatedArray(cx, aobj->length(),
nullptr, newKind));
if (!clone || !clone->ensureElements(cx, values.length()))
return nullptr;
ObjectGroup::NewArrayKind arrayKind = ObjectGroup::NewArrayKind::Normal;
if (obj->is<ArrayObject>() && obj->as<ArrayObject>().denseElementsAreCopyOnWrite())
arrayKind = ObjectGroup::NewArrayKind::CopyOnWrite;
clone->setDenseInitializedLength(values.length());
clone->initDenseElements(0, values.begin(), values.length());
if (aobj->denseElementsAreCopyOnWrite()) {
if (!ObjectElements::MakeElementsCopyOnWrite(cx, clone))
return nullptr;
} else {
ObjectGroup::fixArrayGroup(cx, &clone->as<ArrayObject>());
}
return clone;
return ObjectGroup::newArrayObject(cx, values.begin(), values.length(), newKind,
arrayKind);
}
AutoIdValueVector properties(cx);
@ -1830,8 +1815,9 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
if (mode == XDR_ENCODE) {
MOZ_ASSERT(obj->is<PlainObject>() ||
obj->is<UnboxedPlainObject>() ||
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))
@ -1842,69 +1828,39 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
RootedId tmpId(cx);
if (isArray) {
uint32_t length;
RootedArrayObject aobj(cx);
if (mode == XDR_ENCODE) {
aobj = &obj->as<ArrayObject>();
length = aobj->length();
}
if (!xdr->codeUint32(&length))
return false;
if (mode == XDR_DECODE) {
obj.set(NewDenseUnallocatedArray(cx, length, nullptr, TenuredObject));
if (!obj)
return false;
aobj = &obj->as<ArrayObject>();
}
AutoValueVector values(cx);
if (mode == XDR_ENCODE && !GetScriptArrayObjectElements(cx, aobj, values))
if (mode == XDR_ENCODE && !GetScriptArrayObjectElements(cx, obj, values))
return false;
uint32_t initialized;
{
if (mode == XDR_ENCODE)
initialized = values.length();
if (!xdr->codeUint32(&initialized))
return false;
if (mode == XDR_DECODE) {
if (initialized) {
if (!aobj->ensureElements(cx, initialized))
return false;
}
}
}
if (mode == XDR_ENCODE)
initialized = values.length();
if (!xdr->codeUint32(&initialized))
return false;
if (mode == XDR_DECODE && !values.appendN(MagicValue(JS_ELEMENTS_HOLE), initialized))
return false;
// Recursively copy dense elements.
for (unsigned i = 0; i < initialized; i++) {
if (mode == XDR_ENCODE)
tmpValue = values[i];
if (!xdr->codeConstValue(&tmpValue))
if (!xdr->codeConstValue(values[i]))
return false;
if (mode == XDR_DECODE) {
aobj->setDenseInitializedLength(i + 1);
aobj->initDenseElement(i, tmpValue);
}
}
uint32_t copyOnWrite;
if (mode == XDR_ENCODE)
copyOnWrite = aobj->denseElementsAreCopyOnWrite();
copyOnWrite = obj->is<ArrayObject>() &&
obj->as<ArrayObject>().denseElementsAreCopyOnWrite();
if (!xdr->codeUint32(&copyOnWrite))
return false;
if (mode == XDR_DECODE) {
if (copyOnWrite) {
if (!ObjectElements::MakeElementsCopyOnWrite(cx, aobj))
return false;
} else {
ObjectGroup::fixArrayGroup(cx, aobj);
}
ObjectGroup::NewArrayKind arrayKind = copyOnWrite
? ObjectGroup::NewArrayKind::CopyOnWrite
: ObjectGroup::NewArrayKind::Normal;
obj.set(ObjectGroup::newArrayObject(cx, values.begin(), values.length(),
TenuredObject, arrayKind));
if (!obj)
return false;
}
return true;
@ -1962,65 +1918,6 @@ js::XDRObjectLiteral(XDRState<XDR_ENCODE>* xdr, MutableHandleObject obj);
template bool
js::XDRObjectLiteral(XDRState<XDR_DECODE>* xdr, MutableHandleObject obj);
JSObject*
js::CloneObjectLiteral(JSContext* cx, HandleObject srcObj)
{
if (srcObj->is<PlainObject>()) {
AllocKind kind = GetBackgroundAllocKind(gc::GetGCObjectKind(srcObj->as<PlainObject>().numFixedSlots()));
MOZ_ASSERT_IF(srcObj->isTenured(), kind == srcObj->asTenured().getAllocKind());
RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx));
if (!proto)
return nullptr;
RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PlainObject::class_,
TaggedProto(proto)));
if (!group)
return nullptr;
RootedPlainObject res(cx, NewObjectWithGroup<PlainObject>(cx, group, kind, TenuredObject));
if (!res)
return nullptr;
// XXXbz Do we still need the reshape here? We got "kind" off the
// srcObj, no? See bug 1143270.
RootedShape newShape(cx, ReshapeForAllocKind(cx, srcObj->as<PlainObject>().lastProperty(),
TaggedProto(proto), kind));
if (!newShape || !res->setLastProperty(cx, newShape))
return nullptr;
return res;
}
RootedArrayObject srcArray(cx, &srcObj->as<ArrayObject>());
MOZ_ASSERT(srcArray->denseElementsAreCopyOnWrite());
MOZ_ASSERT(srcArray->getElementsHeader()->ownerObject() == srcObj);
size_t length = srcArray->as<ArrayObject>().length();
RootedArrayObject res(cx, NewDenseFullyAllocatedArray(cx, length, nullptr, TenuredObject));
if (!res)
return nullptr;
RootedId id(cx);
RootedValue value(cx);
for (size_t i = 0; i < length; i++) {
// The only markable values in copy on write arrays are atoms, which
// can be freely copied between compartments.
value = srcArray->getDenseElement(i);
MOZ_ASSERT_IF(value.isMarkable(),
value.toGCThing()->isTenured() &&
value.toGCThing()->asTenured().zoneFromAnyThread()->isAtomsZone());
id = INT_TO_JSID(i);
if (!DefineProperty(cx, res, id, value, nullptr, nullptr, JSPROP_ENUMERATE))
return nullptr;
}
if (!ObjectElements::MakeElementsCopyOnWrite(cx, res))
return nullptr;
return res;
}
void
NativeObject::fillInAfterSwap(JSContext* cx, const Vector<Value>& values, void* priv)
{

View File

@ -1277,9 +1277,6 @@ template<XDRMode mode>
bool
XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj);
extern JSObject*
CloneObjectLiteral(JSContext* cx, HandleObject srcObj);
extern bool
ReportGetterOnlyAssignment(JSContext* cx, bool strict);

View File

@ -3087,13 +3087,7 @@ js::CloneScript(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
clone = CloneFunctionAndScript(cx, enclosingScope, innerFun, polluted);
}
} else {
/*
* Clone object literals emitted for the JSOP_NEWOBJECT opcode. We only emit that
* instead of the less-optimized JSOP_NEWINIT for self-hosted code or code compiled
* with JSOPTION_COMPILE_N_GO set. As we don't clone the latter type of code, this
* case should only ever be hit when cloning objects from self-hosted code.
*/
clone = CloneObjectLiteral(cx, obj);
clone = DeepCloneObjectLiteral(cx, obj, TenuredObject);
}
if (!clone || !objects.append(clone))
return nullptr;

View File

@ -598,13 +598,11 @@ JSONParserBase::finishArray(MutableHandleValue vp, ElementVector& elements)
{
MOZ_ASSERT(&elements == &stack.back().elements());
ArrayObject* obj = NewDenseCopiedArray(cx, elements.length(), elements.begin());
JSObject* obj = ObjectGroup::newArrayObject(cx, elements.begin(), elements.length(),
GenericObject);
if (!obj)
return false;
/* Try to assign a new group to the array according to its elements. */
ObjectGroup::fixArrayGroup(cx, obj);
vp.setObject(*obj);
if (!freeElements.append(&elements))
return false;

View File

@ -240,29 +240,6 @@ NativeObject::getDenseOrTypedArrayElement(uint32_t idx)
return getDenseElement(idx);
}
inline void
NativeObject::initDenseElementsUnbarriered(uint32_t dstStart, const Value* src, uint32_t count) {
/*
* For use by parallel threads, which since they cannot see nursery
* things do not require a barrier.
*/
MOZ_ASSERT(dstStart + count <= getDenseCapacity());
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
#ifdef DEBUG
/*
* This asserts a global invariant: parallel code does not
* observe objects inside the generational GC's nursery.
*/
MOZ_ASSERT(!gc::IsInsideGGCNursery(this));
for (uint32_t index = 0; index < count; ++index) {
const Value& value = src[index];
if (value.isMarkable())
MOZ_ASSERT(!gc::IsInsideGGCNursery(static_cast<gc::Cell*>(value.toGCThing())));
}
#endif
memcpy(&elements_[dstStart], src, count * sizeof(HeapSlot));
}
/* static */ inline NativeObject*
NativeObject::copy(ExclusiveContext* cx, gc::AllocKind kind, gc::InitialHeap heap,
HandleNativeObject templateObject)

View File

@ -291,21 +291,6 @@ static inline bool
IsObjectValueInCompartment(Value v, JSCompartment* comp);
#endif
/*
* NOTE: This is a placeholder for bug 619558.
*
* Run a post write barrier that encompasses multiple contiguous slots in a
* single step.
*/
inline void
DenseRangeWriteBarrierPost(JSRuntime* rt, NativeObject* obj, uint32_t start, uint32_t count)
{
if (count > 0) {
JS::shadow::Runtime* shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
shadowRuntime->gcStoreBufferPtr()->putSlotFromAnyThread(obj, HeapSlot::Element, start, count);
}
}
// Operations which change an object's dense elements can either succeed, fail,
// 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
@ -940,6 +925,20 @@ class NativeObject : public JSObject
inline void ensureDenseInitializedLengthNoPackedCheck(ExclusiveContext* cx,
uint32_t index, uint32_t extra);
// Run a post write barrier that encompasses multiple contiguous elements in a
// single step.
inline void elementsRangeWriteBarrierPost(uint32_t start, uint32_t count) {
for (size_t i = 0; i < count; i++) {
const Value& v = elements_[start + i];
if (v.isObject() && IsInsideNursery(&v.toObject())) {
JS::shadow::Runtime* shadowRuntime = shadowRuntimeFromMainThread();
shadowRuntime->gcStoreBufferPtr()->putSlotFromAnyThread(this, HeapSlot::Element,
start + i, count - i);
return;
}
}
}
public:
void setDenseInitializedLength(uint32_t length) {
MOZ_ASSERT(length <= getDenseCapacity());
@ -987,7 +986,7 @@ class NativeObject : public JSObject
elements_[dstStart + i].set(this, HeapSlot::Element, dstStart + i, src[i]);
} else {
memcpy(&elements_[dstStart], src, count * sizeof(HeapSlot));
DenseRangeWriteBarrierPost(runtimeFromMainThread(), this, dstStart, count);
elementsRangeWriteBarrierPost(dstStart, count);
}
}
@ -995,11 +994,9 @@ class NativeObject : public JSObject
MOZ_ASSERT(dstStart + count <= getDenseCapacity());
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
memcpy(&elements_[dstStart], src, count * sizeof(HeapSlot));
DenseRangeWriteBarrierPost(runtimeFromMainThread(), this, dstStart, count);
elementsRangeWriteBarrierPost(dstStart, count);
}
void initDenseElementsUnbarriered(uint32_t dstStart, const Value* src, uint32_t count);
void moveDenseElements(uint32_t dstStart, uint32_t srcStart, uint32_t count) {
MOZ_ASSERT(dstStart + count <= getDenseCapacity());
MOZ_ASSERT(srcStart + count <= getDenseInitializedLength());
@ -1031,7 +1028,7 @@ class NativeObject : public JSObject
}
} else {
memmove(elements_ + dstStart, elements_ + srcStart, count * sizeof(HeapSlot));
DenseRangeWriteBarrierPost(runtimeFromMainThread(), this, dstStart, count);
elementsRangeWriteBarrierPost(dstStart, count);
}
}
@ -1043,7 +1040,7 @@ class NativeObject : public JSObject
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
memmove(elements_ + dstStart, elements_ + srcStart, count * sizeof(Value));
DenseRangeWriteBarrierPost(runtimeFromMainThread(), this, dstStart, count);
elementsRangeWriteBarrierPost(dstStart, count);
}
bool shouldConvertDoubleElements() {

View File

@ -16,8 +16,11 @@
#include "jsobjinlines.h"
#include "vm/UnboxedObject-inl.h"
using namespace js;
using mozilla::DebugOnly;
using mozilla::PodZero;
/////////////////////////////////////////////////////////////////////
@ -724,26 +727,25 @@ ObjectGroup::defaultNewGroup(JSContext* cx, JSProtoKey key)
struct ObjectGroupCompartment::ArrayObjectKey : public DefaultHasher<ArrayObjectKey>
{
TypeSet::Type type;
JSObject* proto;
ArrayObjectKey()
: type(TypeSet::UndefinedType()), proto(nullptr)
: type(TypeSet::UndefinedType())
{}
ArrayObjectKey(TypeSet::Type type, JSObject* proto)
: type(type), proto(proto)
explicit ArrayObjectKey(TypeSet::Type type)
: type(type)
{}
static inline uint32_t hash(const ArrayObjectKey& v) {
return (uint32_t) (v.type.raw() ^ ((uint32_t)(size_t)v.proto >> 2));
return v.type.raw();
}
static inline bool match(const ArrayObjectKey& v1, const ArrayObjectKey& v2) {
return v1.type == v2.type && v1.proto == v2.proto;
return v1.type == v2.type;
}
bool operator==(const ArrayObjectKey& other) {
return type == other.type && proto == other.proto;
return type == other.type;
}
bool operator!=(const ArrayObjectKey& other) {
@ -771,52 +773,39 @@ GetValueTypeForTable(const Value& v)
return type;
}
/* static */ void
ObjectGroup::fixArrayGroup(ExclusiveContext* cx, ArrayObject* obj)
/* static */ JSObject*
ObjectGroup::newArrayObject(ExclusiveContext* cx,
const Value* vp, size_t length,
NewObjectKind newKind, NewArrayKind arrayKind)
{
AutoEnterAnalysis enter(cx);
MOZ_ASSERT(newKind != SingletonObject);
/*
* If the array is of homogenous type, pick a group which will be
* shared with all other singleton/JSON arrays of the same type.
* If the array is heterogenous, keep the existing group, which has
* unknown properties.
*/
unsigned len = obj->getDenseInitializedLength();
if (len == 0)
return;
TypeSet::Type type = GetValueTypeForTable(obj->getDenseElement(0));
for (unsigned i = 1; i < len; i++) {
TypeSet::Type ntype = GetValueTypeForTable(obj->getDenseElement(i));
if (ntype != type) {
if (NumberTypes(type, ntype))
type = TypeSet::DoubleType();
else
return;
}
// If we are making a copy on write array, don't try to adjust the group as
// getOrFixupCopyOnWriteObject will do this before any objects are copied
// from this one.
if (arrayKind == NewArrayKind::CopyOnWrite) {
ArrayObject* obj = NewDenseCopiedArray(cx, length, vp, nullptr, newKind);
if (!obj || !ObjectElements::MakeElementsCopyOnWrite(cx, obj))
return nullptr;
return obj;
}
setGroupToHomogenousArray(cx, obj, type);
}
/* static */ void
ObjectGroup::fixRestArgumentsGroup(ExclusiveContext* cx, ArrayObject* obj)
{
AutoEnterAnalysis enter(cx);
// Tracking element types for rest argument arrays is not worth it, but we
// still want it to be known that it's a dense array.
setGroupToHomogenousArray(cx, obj, TypeSet::UnknownType());
}
/* static */ void
ObjectGroup::setGroupToHomogenousArray(ExclusiveContext* cx, JSObject* obj,
TypeSet::Type elementType)
{
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
// Get a type which captures all the elements in the array to be created.
TypeSet::Type elementType = TypeSet::UnknownType();
if (arrayKind != NewArrayKind::UnknownIndex && length != 0) {
elementType = GetValueTypeForTable(vp[0]);
for (unsigned i = 1; i < length; i++) {
TypeSet::Type ntype = GetValueTypeForTable(vp[i]);
if (ntype != elementType) {
if (NumberTypes(elementType, ntype)) {
elementType = TypeSet::DoubleType();
} else {
elementType = TypeSet::UnknownType();
break;
}
}
}
}
ObjectGroupCompartment::ArrayObjectTable*& table =
cx->compartment()->objectGroups.arrayObjectTable;
@ -827,29 +816,49 @@ ObjectGroup::setGroupToHomogenousArray(ExclusiveContext* cx, JSObject* obj,
ReportOutOfMemory(cx);
js_delete(table);
table = nullptr;
return;
return nullptr;
}
}
ObjectGroupCompartment::ArrayObjectKey key(elementType, obj->getProto());
ObjectGroupCompartment::ArrayObjectKey key(elementType);
DependentAddPtr<ObjectGroupCompartment::ArrayObjectTable> p(cx, *table, key);
if (p) {
obj->setGroup(p->value());
} else {
// Make a new group to use for future arrays with the same elements.
RootedObject objProto(cx, obj->getProto());
Rooted<TaggedProto> taggedProto(cx, TaggedProto(objProto));
ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, &ArrayObject::class_, taggedProto);
if (!p) {
RootedArrayObject obj(cx, NewDenseCopiedArray(cx, length, vp, nullptr, TenuredObject));
if (!obj)
return nullptr;
Rooted<TaggedProto> proto(cx, TaggedProto(obj->getProto()));
RootedObjectGroup group(cx, ObjectGroupCompartment::makeGroup(cx, &ArrayObject::class_,
proto));
if (!group)
return;
obj->setGroup(group);
return nullptr;
AddTypePropertyId(cx, group, nullptr, JSID_VOID, elementType);
key.proto = objProto;
obj->setGroup(group);
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);
preliminaryObjects->registerNewObject(obj);
}
if (!p.add(cx, *table, key, group))
cx->recoverFromOutOfMemory();
return nullptr;
return obj;
}
RootedObjectGroup group(cx, p->value());
return NewCopiedArrayTryUseGroup(cx, group, vp, length, newKind,
ShouldUpdateTypes::DontUpdate);
}
/////////////////////////////////////////////////////////////////////
@ -1450,11 +1459,6 @@ ObjectGroupCompartment::sweep(FreeOp* fop)
else
key.type = TypeSet::ObjectType(group);
}
if (key.proto && key.proto != TaggedProto::LazyProto &&
IsAboutToBeFinalizedUnbarriered(&key.proto))
{
remove = true;
}
if (IsAboutToBeFinalized(&e.front().value()))
remove = true;

View File

@ -586,14 +586,20 @@ class ObjectGroup : public gc::TenuredCell
// Static accessors for ObjectGroupCompartment ArrayObjectTable and PlainObjectTable.
// Update the group of a freshly created array according to
// the object's current contents.
static void fixArrayGroup(ExclusiveContext* cx, ArrayObject* obj);
enum class NewArrayKind {
Normal, // Specialize array group based on its element type.
CopyOnWrite, // Make an array with copy-on-write elements.
UnknownIndex // Make an array with an unknown element type.
};
// Update the group of a freshly created 'rest' arguments object.
static void fixRestArgumentsGroup(ExclusiveContext* cx, ArrayObject* obj);
// 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.
// Create a PlainObject or UnboxedPlainObject with the specified properties
// and a group specialized for those properties.
static JSObject* newPlainObject(ExclusiveContext* cx,
IdValuePair* properties, size_t nproperties,
NewObjectKind newKind);
@ -623,8 +629,6 @@ class ObjectGroup : public gc::TenuredCell
private:
static ObjectGroup* defaultNewGroup(JSContext* cx, JSProtoKey key);
static void setGroupToHomogenousArray(ExclusiveContext* cx, JSObject* obj,
TypeSet::Type type);
};
// Structure used to manage the groups in a compartment.

View File

@ -143,11 +143,8 @@ InterpreterFrame::createRestParameter(JSContext* cx)
unsigned nformal = fun()->nargs() - 1, nactual = numActualArgs();
unsigned nrest = (nactual > nformal) ? nactual - nformal : 0;
Value* restvp = argv() + nformal;
ArrayObject* obj = NewDenseCopiedArray(cx, nrest, restvp, nullptr);
if (!obj)
return nullptr;
ObjectGroup::fixRestArgumentsGroup(cx, obj);
return obj;
return ObjectGroup::newArrayObject(cx, restvp, nrest, GenericObject,
ObjectGroup::NewArrayKind::UnknownIndex);
}
static inline void

View File

@ -432,17 +432,11 @@ EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count)
return true;
}
enum ShouldUpdateTypes
{
UpdateTypes = true,
DontUpdateTypes = false
};
template <JSValueType Type>
static inline DenseElementResult
SetOrExtendBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
SetOrExtendBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
uint32_t start, const Value* vp, uint32_t count,
ShouldUpdateTypes updateTypes)
ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update)
{
if (Type == JSVAL_TYPE_MAGIC) {
NativeObject* nobj = &obj->as<NativeObject>();
@ -461,7 +455,7 @@ SetOrExtendBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
if (obj->is<ArrayObject>() && start + count >= obj->as<ArrayObject>().length())
obj->as<ArrayObject>().setLengthInt32(start + count);
if (updateTypes == DontUpdateTypes && !nobj->shouldConvertDoubleElements()) {
if (updateTypes == ShouldUpdateTypes::DontUpdate && !nobj->shouldConvertDoubleElements()) {
nobj->copyDenseElements(start, vp, count);
} else {
for (size_t i = 0; i < count; i++)
@ -491,7 +485,7 @@ SetOrExtendBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
// which will overwrite the already-modified elements as well as the ones
// that were left alone.
size_t i = 0;
if (updateTypes == DontUpdateTypes) {
if (updateTypes == ShouldUpdateTypes::DontUpdate) {
for (size_t j = start; i < count && j < oldInitlen; i++)
nobj->setElementNoTypeChangeSpecific<Type>(j, vp[i]);
} else {
@ -503,7 +497,7 @@ SetOrExtendBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
if (i != count) {
obj->as<UnboxedArrayObject>().setInitializedLength(start + count);
if (updateTypes == DontUpdateTypes) {
if (updateTypes == ShouldUpdateTypes::DontUpdate) {
for (; i < count; i++)
nobj->initElementNoTypeChangeSpecific<Type>(start + i, vp[i]);
} else {
@ -680,9 +674,9 @@ struct Signature ## Functor { \
}
DenseElementResult
SetOrExtendAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
uint32_t start, const Value* vp, uint32_t count,
ShouldUpdateTypes updateTypes);
ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update);
DenseElementResult
MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,

View File

@ -2006,10 +2006,11 @@ js::TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape,
}
DefineBoxedOrUnboxedFunctor6(SetOrExtendBoxedOrUnboxedDenseElements,
JSContext*, JSObject*, uint32_t, const Value*, uint32_t, ShouldUpdateTypes);
ExclusiveContext*, JSObject*, uint32_t, const Value*, uint32_t,
ShouldUpdateTypes);
DenseElementResult
js::SetOrExtendAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
js::SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
uint32_t start, const Value* vp, uint32_t count,
ShouldUpdateTypes updateTypes)
{

View File

@ -29,7 +29,7 @@ namespace js {
*
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
*/
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 291;
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 292;
static const uint32_t XDR_BYTECODE_VERSION =
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);