Remove JSObject::capacity and JSObject::initializedLength, split JSObject::slots into slots and elements, bug 693221.

This commit is contained in:
Brian Hackett
2011-10-10 11:41:03 -07:00
parent 43a6a0454e
commit 2e9764e92f
46 changed files with 1167 additions and 1042 deletions

View File

@@ -279,7 +279,7 @@ JSObject::willBeSparseDenseArray(uintN requiredCapacity, uintN newElementsHint)
JS_ASSERT(isDenseArray());
JS_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
uintN cap = numSlots();
uintN cap = getDenseArrayCapacity();
JS_ASSERT(requiredCapacity >= cap);
if (requiredCapacity >= JSObject::NSLOTS_LIMIT)
@@ -631,13 +631,10 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value
*/
jsuint oldcap = obj->getDenseArrayCapacity();
if (oldcap > newlen)
obj->shrinkDenseArrayElements(cx, newlen);
obj->shrinkElements(cx, newlen);
jsuint oldinit = obj->getDenseArrayInitializedLength();
if (oldinit > newlen) {
if (oldinit > newlen)
obj->setDenseArrayInitializedLength(newlen);
if (!cx->typeInferenceEnabled())
obj->backfillDenseArrayHoles(cx);
}
} else if (oldlen - newlen < (1 << 24)) {
do {
--oldlen;
@@ -1206,7 +1203,7 @@ array_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props)
Class js::ArrayClass = {
"Array",
Class::NON_NATIVE | JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
Class::NON_NATIVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
@@ -1273,12 +1270,36 @@ Class js::SlowArrayClass = {
JS_ConvertStub
};
bool
JSObject::allocateSlowArrayElements(JSContext *cx)
{
JS_ASSERT(hasClass(&js::SlowArrayClass));
JS_ASSERT(elements == emptyObjectElements);
ObjectElements *header = cx->new_<ObjectElements>(0);
if (!header)
return false;
elements = header->elements();
return true;
}
static bool
AddLengthProperty(JSContext *cx, JSObject *obj)
{
/*
* Add the 'length' property for a newly created or converted slow array,
* and update the elements to be an empty array owned by the object.
* The shared emptyObjectElements singleton cannot be used for slow arrays,
* as accesses to 'length' will use the elements header.
*/
const jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
JS_ASSERT(!obj->nativeLookup(cx, lengthId));
if (!obj->allocateSlowArrayElements(cx))
return false;
return obj->addProperty(cx, lengthId, array_length_getter, array_length_setter,
SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0);
}
@@ -1294,21 +1315,18 @@ JSObject::makeDenseArraySlow(JSContext *cx)
MarkTypeObjectFlags(cx, this,
OBJECT_FLAG_NON_PACKED_ARRAY |
OBJECT_FLAG_NON_DENSE_ARRAY);
markDenseArrayNotPacked(cx);
backfillDenseArrayHoles(cx);
uint32 arrayCapacity = getDenseArrayCapacity();
uint32 arrayInitialized = getDenseArrayInitializedLength();
/*
* Adjust the slots to account for the different layout between dense
* arrays and other objects. The slots must be dynamic, and the fixed slots
* are now available for newly added properties.
* Get an allocated array of the existing elements, evicting from the fixed
* slots if necessary.
*/
if (denseArrayHasInlineSlots()) {
if (!allocSlots(cx, numSlots()))
if (!hasDynamicElements()) {
if (!growElements(cx, arrayCapacity))
return false;
JS_ASSERT(!denseArrayHasInlineSlots());
JS_ASSERT(hasDynamicElements());
}
/*
@@ -1316,32 +1334,31 @@ JSObject::makeDenseArraySlow(JSContext *cx)
* on error. This is gross, but a better way is not obvious. Note: the
* exact contents of the array are not preserved on error.
*/
js::Shape *oldMap = lastProp;
js::Shape *oldShape = lastProperty();
shape_ = NULL;
/* Create a native scope. */
gc::AllocKind kind = getAllocKind();
if (!InitScopeForObject(cx, this, &SlowArrayClass, getProto()->getNewType(cx), kind))
if (!InitScopeForObject(cx, this, &SlowArrayClass, getProto()->getNewType(cx), kind)) {
setLastPropertyInfallible(oldShape);
return false;
}
capacity = numFixedSlots() + arrayCapacity;
/* Take ownership of the dense elements, reset to an empty dense array. */
Value *elems = elements;
elements = emptyObjectElements;
/*
* Root all values in the array during conversion, as SlowArrayClass only
* protects up to its slot span.
*/
AutoValueArray autoArray(cx, slots, arrayInitialized);
/* The initialized length is used iff this is a dense array. */
initializedLength = 0;
/* Root all values in the array during conversion. */
AutoValueArray autoArray(cx, elements, arrayInitialized);
/*
* Begin with the length property to share more of the property tree.
* The getter/setter here will directly access the object's private value.
*/
if (!AddLengthProperty(cx, this)) {
setMap(oldMap);
capacity = arrayCapacity;
initializedLength = arrayInitialized;
setLastPropertyInfallible(oldShape);
cx->free_(getElementsHeader());
elements = elems;
return false;
}
@@ -1350,27 +1367,30 @@ JSObject::makeDenseArraySlow(JSContext *cx)
* remove holes, so that shapes use successive slots (as for other objects).
*/
uint32 next = 0;
for (uint32 i = 0; i < arrayCapacity; i++) {
for (uint32 i = 0; i < arrayInitialized; i++) {
/* Dense array indexes can always fit in a jsid. */
jsid id;
JS_ALWAYS_TRUE(ValueToId(cx, Int32Value(i), &id));
if (slots[i].isMagic(JS_ARRAY_HOLE))
if (elems[i].isMagic(JS_ARRAY_HOLE))
continue;
setSlot(next, slots[i]);
if (!addDataProperty(cx, id, next, JSPROP_ENUMERATE)) {
setMap(oldMap);
capacity = arrayCapacity;
initializedLength = arrayInitialized;
JS_ALWAYS_TRUE(setLastProperty(cx, oldShape));
cx->free_(getElementsHeader());
elements = elems;
return false;
}
setSlot(next, elems[i]);
next++;
}
clearSlotRange(next, capacity - next);
ObjectElements *oldheader = ObjectElements::fromElements(elems);
getElementsHeader()->length = oldheader->length;
cx->free_(oldheader);
return true;
}
@@ -1796,13 +1816,10 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector
return false;
/* Avoid ensureDenseArrayElements to skip sparse array checks there. */
if (!obj->ensureSlots(cx, length))
if (!obj->ensureElements(cx, length))
return false;
if (cx->typeInferenceEnabled())
obj->setDenseArrayInitializedLength(length);
else
obj->backfillDenseArrayHoles(cx);
obj->setDenseArrayInitializedLength(length);
bool hole = false;
for (jsuint i = 0; i < length; i++) {
@@ -2445,11 +2462,10 @@ NewbornArrayPushImpl(JSContext *cx, JSObject *obj, const Value &v)
JS_ASSERT(obj->isDenseArray());
JS_ASSERT(length <= obj->getDenseArrayCapacity());
if (length == obj->getDenseArrayCapacity() && !obj->ensureSlots(cx, length + 1))
if (!obj->ensureElements(cx, length + 1))
return false;
if (cx->typeInferenceEnabled())
obj->setDenseArrayInitializedLength(length + 1);
obj->setDenseArrayInitializedLength(length + 1);
obj->setDenseArrayLength(length + 1);
obj->setDenseArrayElementWithType(cx, length, v);
return true;
@@ -2531,7 +2547,7 @@ array_pop_dense(JSContext *cx, JSObject* obj, Value *vp)
if (!hole && DeleteArrayElement(cx, obj, index, true) < 0)
return JS_FALSE;
if (cx->typeInferenceEnabled() && obj->getDenseArrayInitializedLength() > index)
if (obj->getDenseArrayInitializedLength() > index)
obj->setDenseArrayInitializedLength(index);
obj->setArrayLength(cx, index);
return JS_TRUE;
@@ -2571,10 +2587,7 @@ array_shift(JSContext *cx, uintN argc, Value *vp)
if (vp->isMagic(JS_ARRAY_HOLE))
vp->setUndefined();
obj->moveDenseArrayElements(0, 1, length);
if (cx->typeInferenceEnabled())
obj->setDenseArrayInitializedLength(obj->getDenseArrayInitializedLength() - 1);
else
obj->setDenseArrayElement(length, MagicValue(JS_ARRAY_HOLE));
obj->setDenseArrayInitializedLength(obj->getDenseArrayInitializedLength() - 1);
obj->setArrayLength(cx, length);
if (!js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(length)))
return JS_FALSE;
@@ -2861,8 +2874,6 @@ array_concat(JSContext *cx, uintN argc, Value *vp)
return JS_FALSE;
TryReuseArrayType(aobj, nobj);
nobj->setArrayLength(cx, length);
if (!aobj->isPackedDenseArray())
nobj->markDenseArrayNotPacked(cx);
vp->setObject(*nobj);
if (argc == 0)
return JS_TRUE;
@@ -2968,8 +2979,6 @@ array_slice(JSContext *cx, uintN argc, Value *vp)
if (!nobj)
return JS_FALSE;
TryReuseArrayType(obj, nobj);
if (!obj->isPackedDenseArray())
nobj->markDenseArrayNotPacked(cx);
vp->setObject(*nobj);
return JS_TRUE;
}
@@ -3477,19 +3486,14 @@ NewArray(JSContext *cx, jsuint length, JSObject *proto)
{
JS_ASSERT_IF(proto, proto->isArray());
gc::AllocKind kind = GuessObjectGCKind(length, true);
gc::AllocKind kind = GuessArrayGCKind(length);
JSObject *obj = detail::NewObject<WithProto::Class, false>(cx, &ArrayClass, proto, NULL, kind);
if (!obj)
return NULL;
obj->setArrayLength(cx, length);
if (!cx->typeInferenceEnabled()) {
obj->markDenseArrayNotPacked(cx);
obj->backfillDenseArrayHoles(cx);
}
if (allocateCapacity && !obj->ensureSlots(cx, length))
if (allocateCapacity && !obj->ensureElements(cx, length))
return NULL;
return obj;
@@ -3542,8 +3546,7 @@ NewDenseCopiedArray(JSContext *cx, uintN length, const Value *vp, JSObject *prot
JS_ASSERT(obj->getDenseArrayCapacity() >= length);
if (cx->typeInferenceEnabled())
obj->setDenseArrayInitializedLength(vp ? length : 0);
obj->setDenseArrayInitializedLength(vp ? length : 0);
if (vp)
obj->copyDenseArrayElements(0, vp, length);