Bug 556187 - encapsulate JSSLOT_ARRAY_* within JSObject. r=brendan.

This commit is contained in:
Nicholas Nethercote 2010-04-05 18:32:16 -07:00
parent 06c049adbc
commit 4542317997
8 changed files with 189 additions and 107 deletions

View File

@ -103,6 +103,7 @@
#include "jsvector.h" #include "jsvector.h"
#include "jsatominlines.h" #include "jsatominlines.h"
#include "jsobjinlines.h"
using namespace js; using namespace js;
@ -122,7 +123,7 @@ INDEX_TOO_BIG(jsuint index)
#define INDEX_TOO_SPARSE(array, index) \ #define INDEX_TOO_SPARSE(array, index) \
(INDEX_TOO_BIG(index) || \ (INDEX_TOO_BIG(index) || \
((index) > js_DenseArrayCapacity(array) && (index) >= MIN_SPARSE_INDEX && \ ((index) > js_DenseArrayCapacity(array) && (index) >= MIN_SPARSE_INDEX && \
(index) > (uint32)((array)->fslots[JSSLOT_ARRAY_COUNT] + 1) * 4)) (index) > ((array)->getArrayCount() + 1) * 4))
JS_STATIC_ASSERT(sizeof(JSScopeProperty) > 4 * sizeof(jsval)); JS_STATIC_ASSERT(sizeof(JSScopeProperty) > 4 * sizeof(jsval));
@ -227,7 +228,7 @@ JSBool
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
{ {
if (obj->isArray()) { if (obj->isArray()) {
*lengthp = obj->fslots[JSSLOT_ARRAY_LENGTH]; *lengthp = obj->getArrayLength();
return true; return true;
} }
@ -497,10 +498,10 @@ SetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, jsval v)
JS_ASSERT(idx + 1 > idx); JS_ASSERT(idx + 1 > idx);
if (!EnsureCapacity(cx, obj, idx + 1)) if (!EnsureCapacity(cx, obj, idx + 1))
return JS_FALSE; return JS_FALSE;
if (idx >= uint32(obj->fslots[JSSLOT_ARRAY_LENGTH])) if (idx >= obj->getArrayLength())
obj->fslots[JSSLOT_ARRAY_LENGTH] = idx + 1; obj->setArrayLength(idx + 1);
if (obj->dslots[idx] == JSVAL_HOLE) if (obj->dslots[idx] == JSVAL_HOLE)
obj->fslots[JSSLOT_ARRAY_COUNT]++; obj->incArrayCountBy(1);
obj->dslots[idx] = v; obj->dslots[idx] = v;
return JS_TRUE; return JS_TRUE;
} }
@ -528,7 +529,7 @@ DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index)
jsuint idx = jsuint(index); jsuint idx = jsuint(index);
if (!INDEX_TOO_SPARSE(obj, idx) && idx < js_DenseArrayCapacity(obj)) { if (!INDEX_TOO_SPARSE(obj, idx) && idx < js_DenseArrayCapacity(obj)) {
if (obj->dslots[idx] != JSVAL_HOLE) if (obj->dslots[idx] != JSVAL_HOLE)
obj->fslots[JSSLOT_ARRAY_COUNT]--; obj->decArrayCountBy(1);
obj->dslots[idx] = JSVAL_HOLE; obj->dslots[idx] = JSVAL_HOLE;
return JS_TRUE; return JS_TRUE;
} }
@ -619,7 +620,7 @@ array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{ {
do { do {
if (obj->isArray()) if (obj->isArray())
return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp); return IndexToValue(cx, obj->getArrayLength(), vp);
} while ((obj = obj->getProto()) != NULL); } while ((obj = obj->getProto()) != NULL);
return JS_TRUE; return JS_TRUE;
} }
@ -639,7 +640,7 @@ array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
newlen = ValueIsLength(cx, vp); newlen = ValueIsLength(cx, vp);
if (JSVAL_IS_NULL(*vp)) if (JSVAL_IS_NULL(*vp))
return false; return false;
oldlen = obj->fslots[JSSLOT_ARRAY_LENGTH]; oldlen = obj->getArrayLength();
if (oldlen == newlen) if (oldlen == newlen)
return true; return true;
@ -648,7 +649,7 @@ array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
return false; return false;
if (oldlen < newlen) { if (oldlen < newlen) {
obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen; obj->setArrayLength(newlen);
return true; return true;
} }
@ -691,7 +692,7 @@ array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
} }
} }
obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen; obj->setArrayLength(newlen);
return true; return true;
} }
@ -707,7 +708,7 @@ IsDenseArrayId(JSContext *cx, JSObject *obj, jsid id)
uint32 i; uint32 i;
return id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) || return id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) ||
(js_IdIsIndex(id, &i) && (js_IdIsIndex(id, &i) &&
obj->fslots[JSSLOT_ARRAY_LENGTH] != 0 && obj->getArrayLength() != 0 &&
i < js_DenseArrayCapacity(obj) && i < js_DenseArrayCapacity(obj) &&
obj->dslots[i] != JSVAL_HOLE); obj->dslots[i] != JSVAL_HOLE);
} }
@ -750,7 +751,7 @@ js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, JSProperty *prop,
uint32 i; uint32 i;
if (!js_IdIsIndex(id, &i)) { if (!js_IdIsIndex(id, &i)) {
JS_ASSERT(id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)); JS_ASSERT(id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom));
return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp); return IndexToValue(cx, obj->getArrayLength(), vp);
} }
*vp = obj->dslots[i]; *vp = obj->dslots[i];
return JS_TRUE; return JS_TRUE;
@ -762,7 +763,7 @@ array_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
uint32 i; uint32 i;
if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp); return IndexToValue(cx, obj->getArrayLength(), vp);
if (id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom)) { if (id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom)) {
*vp = OBJECT_TO_JSVAL(obj->getProto()); *vp = OBJECT_TO_JSVAL(obj->getProto());
@ -811,9 +812,9 @@ slowarray_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
if (!js_IdIsIndex(id, &index)) if (!js_IdIsIndex(id, &index))
return JS_TRUE; return JS_TRUE;
length = obj->fslots[JSSLOT_ARRAY_LENGTH]; length = obj->getArrayLength();
if (index >= length) if (index >= length)
obj->fslots[JSSLOT_ARRAY_LENGTH] = index + 1; obj->setArrayLength(index + 1);
return JS_TRUE; return JS_TRUE;
} }
@ -867,10 +868,10 @@ array_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
if (!EnsureCapacity(cx, obj, i + 1)) if (!EnsureCapacity(cx, obj, i + 1))
return JS_FALSE; return JS_FALSE;
if (i >= (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH]) if (i >= obj->getArrayLength())
obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1; obj->setArrayLength(i + 1);
if (obj->dslots[i] == JSVAL_HOLE) if (obj->dslots[i] == JSVAL_HOLE)
obj->fslots[JSSLOT_ARRAY_COUNT]++; obj->incArrayCountBy(1);
obj->dslots[i] = *vp; obj->dslots[i] = *vp;
return JS_TRUE; return JS_TRUE;
} }
@ -927,9 +928,9 @@ dense_grow(JSContext* cx, JSObject* obj, jsint i, jsval v)
if (js_PrototypeHasIndexedProperties(cx, obj)) if (js_PrototypeHasIndexedProperties(cx, obj))
return JS_FALSE; return JS_FALSE;
if (u >= jsuint(obj->fslots[JSSLOT_ARRAY_LENGTH])) if (u >= obj->getArrayLength())
obj->fslots[JSSLOT_ARRAY_LENGTH] = u + 1; obj->setArrayLength(u + 1);
++obj->fslots[JSSLOT_ARRAY_COUNT]; obj->incArrayCountBy(1);
} }
obj->dslots[u] = v; obj->dslots[u] = v;
@ -1039,7 +1040,7 @@ array_deleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *rval)
if (js_IdIsIndex(id, &i) && i < js_DenseArrayCapacity(obj) && if (js_IdIsIndex(id, &i) && i < js_DenseArrayCapacity(obj) &&
obj->dslots[i] != JSVAL_HOLE) { obj->dslots[i] != JSVAL_HOLE) {
obj->fslots[JSSLOT_ARRAY_COUNT]--; obj->decArrayCountBy(1);
obj->dslots[i] = JSVAL_HOLE; obj->dslots[i] = JSVAL_HOLE;
} }
@ -1120,7 +1121,7 @@ array_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
JS_ASSERT(obj->isDenseArray()); JS_ASSERT(obj->isDenseArray());
capacity = js_DenseArrayCapacity(obj); capacity = js_DenseArrayCapacity(obj);
if (idp) if (idp)
*idp = INT_TO_JSVAL(obj->fslots[JSSLOT_ARRAY_COUNT]); *idp = INT_TO_JSVAL(obj->getArrayCount());
ii = NULL; ii = NULL;
for (i = 0; i != capacity; ++i) { for (i = 0; i != capacity; ++i) {
if (obj->dslots[i] == JSVAL_HOLE) { if (obj->dslots[i] == JSVAL_HOLE) {
@ -1338,15 +1339,13 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj)
} }
/* /*
* Render our formerly-reserved count property GC-safe. * Render our formerly-reserved count property GC-safe. We do not need to
* We do not need to make the length slot GC-safe as this slot is private * make the length slot GC-safe because it is the private slot (this is
* where the implementation can store an arbitrary value. * statically asserted within JSObject) where the implementation can store
* an arbitrary value.
*/ */
{ JS_ASSERT(js_SlowArrayClass.flags & JSCLASS_HAS_PRIVATE);
JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH == JSSLOT_PRIVATE); obj->voidDenseArrayCount();
JS_ASSERT(js_SlowArrayClass.flags & JSCLASS_HAS_PRIVATE);
obj->fslots[JSSLOT_ARRAY_COUNT] = JSVAL_VOID;
}
/* Make sure we preserve any flags borrowing bits in classword. */ /* Make sure we preserve any flags borrowing bits in classword. */
obj->classword ^= (jsuword) &js_ArrayClass; obj->classword ^= (jsuword) &js_ArrayClass;
@ -1659,8 +1658,8 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, jsva
if (!EnsureCapacity(cx, obj, newlen)) if (!EnsureCapacity(cx, obj, newlen))
return JS_FALSE; return JS_FALSE;
if (newlen > uint32(obj->fslots[JSSLOT_ARRAY_LENGTH])) if (newlen > obj->getArrayLength())
obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen; obj->setArrayLength(newlen);
JS_ASSERT(count < size_t(-1) / sizeof(jsval)); JS_ASSERT(count < size_t(-1) / sizeof(jsval));
if (targetType == TargetElementsMayContainValues) { if (targetType == TargetElementsMayContainValues) {
@ -1669,19 +1668,19 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, jsva
if (obj->dslots[start + i] != JSVAL_HOLE) if (obj->dslots[start + i] != JSVAL_HOLE)
valueCount++; valueCount++;
} }
JS_ASSERT(uint32(obj->fslots[JSSLOT_ARRAY_COUNT]) >= valueCount); JS_ASSERT(obj->getArrayCount() >= valueCount);
obj->fslots[JSSLOT_ARRAY_COUNT] -= valueCount; obj->decArrayCountBy(valueCount);
} }
memcpy(obj->dslots + start, vector, sizeof(jsval) * count); memcpy(obj->dslots + start, vector, sizeof(jsval) * count);
if (vectorType == SourceVectorAllValues) { if (vectorType == SourceVectorAllValues) {
obj->fslots[JSSLOT_ARRAY_COUNT] += count; obj->incArrayCountBy(count);
} else { } else {
jsuint valueCount = 0; jsuint valueCount = 0;
for (jsuint i = 0; i < count; i++) { for (jsuint i = 0; i < count; i++) {
if (obj->dslots[start + i] != JSVAL_HOLE) if (obj->dslots[start + i] != JSVAL_HOLE)
valueCount++; valueCount++;
} }
obj->fslots[JSSLOT_ARRAY_COUNT] += valueCount; obj->incArrayCountBy(valueCount);
} }
JS_ASSERT_IF(count != 0, obj->dslots[newlen - 1] != JSVAL_HOLE); JS_ASSERT_IF(count != 0, obj->dslots[newlen - 1] != JSVAL_HOLE);
return JS_TRUE; return JS_TRUE;
@ -1728,7 +1727,7 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const jsval *vector
{ {
JS_ASSERT(obj->isArray()); JS_ASSERT(obj->isArray());
obj->fslots[JSSLOT_ARRAY_LENGTH] = length; obj->setArrayLength(length);
if (vector) { if (vector) {
if (!EnsureCapacity(cx, obj, length)) if (!EnsureCapacity(cx, obj, length))
@ -1744,9 +1743,9 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const jsval *vector
obj->dslots[i] = vector[i]; obj->dslots[i] = vector[i];
} }
} }
obj->fslots[JSSLOT_ARRAY_COUNT] = count; obj->setArrayCount(count);
} else { } else {
obj->fslots[JSSLOT_ARRAY_COUNT] = 0; obj->setArrayCount(0);
} }
return JS_TRUE; return JS_TRUE;
} }
@ -2353,7 +2352,7 @@ array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *
static JSBool static JSBool
array_push1_dense(JSContext* cx, JSObject* obj, jsval v, jsval *rval) array_push1_dense(JSContext* cx, JSObject* obj, jsval v, jsval *rval)
{ {
uint32 length = obj->fslots[JSSLOT_ARRAY_LENGTH]; uint32 length = obj->getArrayLength();
if (INDEX_TOO_SPARSE(obj, length)) { if (INDEX_TOO_SPARSE(obj, length)) {
if (!js_MakeArraySlow(cx, obj)) if (!js_MakeArraySlow(cx, obj))
return JS_FALSE; return JS_FALSE;
@ -2362,19 +2361,19 @@ array_push1_dense(JSContext* cx, JSObject* obj, jsval v, jsval *rval)
if (!EnsureCapacity(cx, obj, length + 1)) if (!EnsureCapacity(cx, obj, length + 1))
return JS_FALSE; return JS_FALSE;
obj->fslots[JSSLOT_ARRAY_LENGTH] = length + 1; obj->setArrayLength(length + 1);
JS_ASSERT(obj->dslots[length] == JSVAL_HOLE); JS_ASSERT(obj->dslots[length] == JSVAL_HOLE);
obj->fslots[JSSLOT_ARRAY_COUNT]++; obj->incArrayCountBy(1);
obj->dslots[length] = v; obj->dslots[length] = v;
return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], rval); return IndexToValue(cx, obj->getArrayLength(), rval);
} }
JSBool JS_FASTCALL JSBool JS_FASTCALL
js_ArrayCompPush(JSContext *cx, JSObject *obj, jsval v) js_ArrayCompPush(JSContext *cx, JSObject *obj, jsval v)
{ {
JS_ASSERT(obj->isDenseArray()); JS_ASSERT(obj->isDenseArray());
uint32_t length = (uint32_t) obj->fslots[JSSLOT_ARRAY_LENGTH]; uint32_t length = obj->getArrayLength();
JS_ASSERT(length <= js_DenseArrayCapacity(obj)); JS_ASSERT(length <= js_DenseArrayCapacity(obj));
if (length == js_DenseArrayCapacity(obj)) { if (length == js_DenseArrayCapacity(obj)) {
@ -2387,8 +2386,8 @@ js_ArrayCompPush(JSContext *cx, JSObject *obj, jsval v)
if (!EnsureCapacity(cx, obj, length + 1)) if (!EnsureCapacity(cx, obj, length + 1))
return JS_FALSE; return JS_FALSE;
} }
obj->fslots[JSSLOT_ARRAY_LENGTH] = length + 1; obj->setArrayLength(length + 1);
obj->fslots[JSSLOT_ARRAY_COUNT]++; obj->incArrayCountBy(1);
obj->dslots[length] = v; obj->dslots[length] = v;
return JS_TRUE; return JS_TRUE;
} }
@ -2453,7 +2452,7 @@ array_pop_dense(JSContext *cx, JSObject* obj, jsval *vp)
jsuint index; jsuint index;
JSBool hole; JSBool hole;
index = obj->fslots[JSSLOT_ARRAY_LENGTH]; index = obj->getArrayLength();
if (index == 0) { if (index == 0) {
*vp = JSVAL_VOID; *vp = JSVAL_VOID;
return JS_TRUE; return JS_TRUE;
@ -2463,7 +2462,7 @@ array_pop_dense(JSContext *cx, JSObject* obj, jsval *vp)
return JS_FALSE; return JS_FALSE;
if (!hole && !DeleteArrayElement(cx, obj, index)) if (!hole && !DeleteArrayElement(cx, obj, index))
return JS_FALSE; return JS_FALSE;
obj->fslots[JSSLOT_ARRAY_LENGTH] = index; obj->setArrayLength(index);
return JS_TRUE; return JS_TRUE;
} }
@ -2517,7 +2516,7 @@ array_shift(JSContext *cx, uintN argc, jsval *vp)
if (*vp == JSVAL_HOLE) if (*vp == JSVAL_HOLE)
*vp = JSVAL_VOID; *vp = JSVAL_VOID;
else else
obj->fslots[JSSLOT_ARRAY_COUNT]--; obj->decArrayCountBy(1);
memmove(obj->dslots, obj->dslots + 1, length * sizeof(jsval)); memmove(obj->dslots, obj->dslots + 1, length * sizeof(jsval));
obj->dslots[length] = JSVAL_HOLE; obj->dslots[length] = JSVAL_HOLE;
} else { } else {
@ -2526,7 +2525,7 @@ array_shift(JSContext *cx, uintN argc, jsval *vp)
* with an explicitly set non-zero length when shift() is called on * with an explicitly set non-zero length when shift() is called on
* it, but note fallthrough to reduce the length by one. * it, but note fallthrough to reduce the length by one.
*/ */
JS_ASSERT(obj->fslots[JSSLOT_ARRAY_COUNT] == 0); JS_ASSERT(obj->getArrayCount() == 0);
*vp = JSVAL_VOID; *vp = JSVAL_VOID;
} }
} else { } else {
@ -2678,8 +2677,7 @@ array_splice(JSContext *cx, uintN argc, jsval *vp)
!js_PrototypeHasIndexedProperties(cx, obj2) && !js_PrototypeHasIndexedProperties(cx, obj2) &&
end <= js_DenseArrayCapacity(obj)) { end <= js_DenseArrayCapacity(obj)) {
if (!InitArrayObject(cx, obj2, count, obj->dslots + begin, if (!InitArrayObject(cx, obj2, count, obj->dslots + begin,
obj->fslots[JSSLOT_ARRAY_COUNT] != obj->getArrayCount() != obj->getArrayLength())) {
obj->fslots[JSSLOT_ARRAY_LENGTH])) {
return JS_FALSE; return JS_FALSE;
} }
} else { } else {
@ -2713,10 +2711,10 @@ array_splice(JSContext *cx, uintN argc, jsval *vp)
jsval srcval = obj->dslots[last]; jsval srcval = obj->dslots[last];
jsval* dest = &obj->dslots[last + delta]; jsval* dest = &obj->dslots[last + delta];
if (*dest == JSVAL_HOLE && srcval != JSVAL_HOLE) if (*dest == JSVAL_HOLE && srcval != JSVAL_HOLE)
obj->fslots[JSSLOT_ARRAY_COUNT]++; obj->incArrayCountBy(1);
*dest = srcval; *dest = srcval;
} }
obj->fslots[JSSLOT_ARRAY_LENGTH] += delta; obj->setArrayLength(obj->getArrayLength() + delta);
} else { } else {
/* (uint) end could be 0, so we can't use a vanilla >= test. */ /* (uint) end could be 0, so we can't use a vanilla >= test. */
while (last-- > end) { while (last-- > end) {
@ -2737,7 +2735,7 @@ array_splice(JSContext *cx, uintN argc, jsval *vp)
jsval srcval = obj->dslots[last]; jsval srcval = obj->dslots[last];
jsval* dest = &obj->dslots[last - delta]; jsval* dest = &obj->dslots[last - delta];
if (*dest == JSVAL_HOLE && srcval != JSVAL_HOLE) if (*dest == JSVAL_HOLE && srcval != JSVAL_HOLE)
obj->fslots[JSSLOT_ARRAY_COUNT]++; obj->incArrayCountBy(1);
*dest = srcval; *dest = srcval;
} }
} else { } else {
@ -2788,14 +2786,13 @@ array_concat(JSContext *cx, uintN argc, jsval *vp)
* where length is <= capacity, nobj and aobj will have the same * where length is <= capacity, nobj and aobj will have the same
* capacity. * capacity.
*/ */
length = aobj->fslots[JSSLOT_ARRAY_LENGTH]; length = aobj->getArrayLength();
jsuint capacity = js_DenseArrayCapacity(aobj); jsuint capacity = js_DenseArrayCapacity(aobj);
nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->dslots, nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->dslots,
aobj->fslots[JSSLOT_ARRAY_COUNT] != aobj->getArrayCount() != length);
(jsval) length);
if (!nobj) if (!nobj)
return JS_FALSE; return JS_FALSE;
nobj->fslots[JSSLOT_ARRAY_LENGTH] = length; nobj->setArrayLength(length);
*vp = OBJECT_TO_JSVAL(nobj); *vp = OBJECT_TO_JSVAL(nobj);
if (argc == 0) if (argc == 0)
return JS_TRUE; return JS_TRUE;
@ -2909,8 +2906,7 @@ array_slice(JSContext *cx, uintN argc, jsval *vp)
if (obj->isDenseArray() && end <= js_DenseArrayCapacity(obj) && if (obj->isDenseArray() && end <= js_DenseArrayCapacity(obj) &&
!js_PrototypeHasIndexedProperties(cx, obj)) { !js_PrototypeHasIndexedProperties(cx, obj)) {
nobj = js_NewArrayObject(cx, end - begin, obj->dslots + begin, nobj = js_NewArrayObject(cx, end - begin, obj->dslots + begin,
obj->fslots[JSSLOT_ARRAY_COUNT] != obj->getArrayCount() != obj->getArrayLength());
obj->fslots[JSSLOT_ARRAY_LENGTH]);
if (!nobj) if (!nobj)
return JS_FALSE; return JS_FALSE;
*vp = OBJECT_TO_JSVAL(nobj); *vp = OBJECT_TO_JSVAL(nobj);
@ -3357,9 +3353,6 @@ js_Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
return InitArrayObject(cx, obj, length, vector); return InitArrayObject(cx, obj, length, vector);
} }
JS_STATIC_ASSERT(JSSLOT_PRIVATE == JSSLOT_ARRAY_LENGTH);
JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH + 1 == JSSLOT_ARRAY_COUNT);
JSObject* JS_FASTCALL JSObject* JS_FASTCALL
js_NewEmptyArray(JSContext* cx, JSObject* proto) js_NewEmptyArray(JSContext* cx, JSObject* proto)
{ {
@ -3375,10 +3368,9 @@ js_NewEmptyArray(JSContext* cx, JSObject* proto)
obj->setProto(proto); obj->setProto(proto);
obj->setParent(proto->getParent()); obj->setParent(proto->getParent());
obj->fslots[JSSLOT_ARRAY_LENGTH] = 0; obj->setArrayLength(0);
obj->fslots[JSSLOT_ARRAY_COUNT] = 0; obj->setArrayCount(0);
for (unsigned i = JSSLOT_ARRAY_COUNT + 1; i != JS_INITIAL_NSLOTS; ++i) obj->voidArrayUnused();
obj->fslots[i] = JSVAL_VOID;
obj->dslots = NULL; obj->dslots = NULL;
return obj; return obj;
} }
@ -3394,7 +3386,7 @@ js_NewEmptyArrayWithLength(JSContext* cx, JSObject* proto, int32 len)
JSObject *obj = js_NewEmptyArray(cx, proto); JSObject *obj = js_NewEmptyArray(cx, proto);
if (!obj) if (!obj)
return NULL; return NULL;
obj->fslots[JSSLOT_ARRAY_LENGTH] = len; obj->setArrayLength(len);
return obj; return obj;
} }
#ifdef JS_TRACER #ifdef JS_TRACER
@ -3408,7 +3400,7 @@ js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len)
JSObject* obj = js_NewEmptyArray(cx, proto); JSObject* obj = js_NewEmptyArray(cx, proto);
if (!obj) if (!obj)
return NULL; return NULL;
obj->fslots[JSSLOT_ARRAY_LENGTH] = len; obj->setArrayLength(len);
if (!ResizeSlots(cx, obj, 0, JS_MAX(len, ARRAY_CAPACITY_MIN))) if (!ResizeSlots(cx, obj, 0, JS_MAX(len, ARRAY_CAPACITY_MIN)))
return NULL; return NULL;
return obj; return obj;
@ -3459,7 +3451,7 @@ js_NewSlowArrayObject(JSContext *cx)
{ {
JSObject *obj = js_NewObject(cx, &js_SlowArrayClass, NULL, NULL); JSObject *obj = js_NewObject(cx, &js_SlowArrayClass, NULL, NULL);
if (obj) if (obj)
obj->fslots[JSSLOT_ARRAY_LENGTH] = 0; obj->setArrayLength(0);
return obj; return obj;
} }
@ -3485,10 +3477,10 @@ js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
} }
fprintf(stderr, "%s: %s (len %lu", bytes, fprintf(stderr, "%s: %s (len %lu", bytes,
array->isDenseArray()) ? "dense" : "sparse", array->isDenseArray()) ? "dense" : "sparse",
array->fslots[JSSLOT_ARRAY_LENGTH]); array->getArrayLength());
if (array->isDenseArray()) { if (array->isDenseArray()) {
fprintf(stderr, ", count %lu, capacity %lu", fprintf(stderr, ", count %lu, capacity %lu",
array->fslots[JSSLOT_ARRAY_COUNT], array->getArrayCount(),
js_DenseArrayCapacity(array)); js_DenseArrayCapacity(array));
} }
fputs(")\n", stderr); fputs(")\n", stderr);
@ -3507,7 +3499,7 @@ js_CoerceArrayToCanvasImageData(JSObject *obj, jsuint offset, jsuint count,
if (!obj || !obj->isDenseArray()) if (!obj || !obj->isDenseArray())
return JS_FALSE; return JS_FALSE;
length = obj->fslots[JSSLOT_ARRAY_LENGTH]; length = obj->getArrayLength();
if (length < offset + count) if (length < offset + count)
return JS_FALSE; return JS_FALSE;
@ -3572,7 +3564,7 @@ js_NewArrayObjectWithCapacity(JSContext *cx, jsuint capacity, jsval **vector)
if (!obj) if (!obj)
return NULL; return NULL;
obj->fslots[JSSLOT_ARRAY_COUNT] = capacity; obj->setArrayCount(capacity);
*vector = obj->dslots; *vector = obj->dslots;
return obj; return obj;
} }

View File

@ -61,10 +61,16 @@ JSObject::isDenseArray() const
return getClass() == &js_ArrayClass; return getClass() == &js_ArrayClass;
} }
inline bool
JSObject::isSlowArray() const
{
return getClass() == &js_SlowArrayClass;
}
inline bool inline bool
JSObject::isArray() const JSObject::isArray() const
{ {
return isDenseArray() || getClass() == &js_SlowArrayClass; return isDenseArray() || isSlowArray();
} }
/* /*
@ -115,10 +121,6 @@ js_NewSlowArrayObject(JSContext *cx);
extern JSBool extern JSBool
js_MakeArraySlow(JSContext *cx, JSObject *obj); js_MakeArraySlow(JSContext *cx, JSObject *obj);
#define JSSLOT_ARRAY_LENGTH JSSLOT_PRIVATE
#define JSSLOT_ARRAY_COUNT (JSSLOT_ARRAY_LENGTH + 1)
#define JSSLOT_ARRAY_UNUSED (JSSLOT_ARRAY_COUNT + 1)
static JS_INLINE uint32 static JS_INLINE uint32
js_DenseArrayCapacity(JSObject *obj) js_DenseArrayCapacity(JSObject *obj)
{ {

View File

@ -2071,7 +2071,7 @@ obj_keys(JSContext *cx, uintN argc, jsval *vp)
} }
JS_ASSERT(len <= UINT32_MAX); JS_ASSERT(len <= UINT32_MAX);
aobj->fslots[JSSLOT_ARRAY_COUNT] = len; aobj->setArrayCount(len);
return JS_TRUE; return JS_TRUE;
} }
@ -2513,7 +2513,7 @@ DefinePropertyArray(JSContext *cx, JSObject *obj, const PropertyDescriptor &desc
if (obj->isDenseArray() && !js_MakeArraySlow(cx, obj)) if (obj->isDenseArray() && !js_MakeArraySlow(cx, obj))
return JS_FALSE; return JS_FALSE;
jsuint oldLen = obj->fslots[JSSLOT_ARRAY_LENGTH]; jsuint oldLen = obj->getArrayLength();
if (desc.id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) { if (desc.id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
/* /*
@ -2541,7 +2541,7 @@ DefinePropertyArray(JSContext *cx, JSObject *obj, const PropertyDescriptor &desc
if (index >= oldLen) { if (index >= oldLen) {
JS_ASSERT(index != UINT32_MAX); JS_ASSERT(index != UINT32_MAX);
obj->fslots[JSSLOT_ARRAY_LENGTH] = index + 1; obj->setArrayLength(index + 1);
} }
*rval = true; *rval = true;
@ -6937,8 +6937,7 @@ js_DumpObject(JSObject *obj)
fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name); fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name);
if (obj->isDenseArray()) { if (obj->isDenseArray()) {
slots = JS_MIN((jsuint) obj->fslots[JSSLOT_ARRAY_LENGTH], slots = JS_MIN(obj->getArrayLength(), js_DenseArrayCapacity(obj));
js_DenseArrayCapacity(obj));
fprintf(stderr, "elements\n"); fprintf(stderr, "elements\n");
for (i = 0; i < slots; i++) { for (i = 0; i < slots; i++) {
fprintf(stderr, " %3d: ", i); fprintf(stderr, " %3d: ", i);

View File

@ -248,6 +248,12 @@ const uintptr_t JSSLOT_CLASS_MASK_BITS = 3;
* records the number of available slots. * records the number of available slots.
*/ */
struct JSObject { struct JSObject {
/*
* TraceRecorder must be a friend because it generates code that
* manipulates JSObjects, which requires peeking under any encapsulation.
*/
friend class js::TraceRecorder;
JSObjectMap *map; /* property map, see jsscope.h */ JSObjectMap *map; /* property map, see jsscope.h */
jsuword classword; /* JSClass ptr | bits, see above */ jsuword classword; /* JSClass ptr | bits, see above */
jsval fslots[JS_INITIAL_NSLOTS]; /* small number of fixed slots */ jsval fslots[JS_INITIAL_NSLOTS]; /* small number of fixed slots */
@ -378,6 +384,33 @@ struct JSObject {
: JSVAL_VOID; : JSVAL_VOID;
} }
/*
* Array-specific getters and setters (for both dense and slow arrays).
*/
private:
static const uint32 JSSLOT_ARRAY_LENGTH = JSSLOT_PRIVATE;
static const uint32 JSSLOT_ARRAY_COUNT = JSSLOT_PRIVATE + 1;
static const uint32 JSSLOT_ARRAY_UNUSED = JSSLOT_PRIVATE + 2;
// This must remain true; see comment in js_MakeArraySlow().
JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH == JSSLOT_PRIVATE);
public:
inline uint32 getArrayLength() const;
inline uint32 getArrayCount() const;
inline void setArrayLength(uint32 length);
inline void setArrayCount(uint32 count);
inline void voidDenseArrayCount();
inline void incArrayCountBy(uint32 posDelta);
inline void decArrayCountBy(uint32 negDelta);
inline void voidArrayUnused();
/*
* Back to generic stuff.
*/
bool isCallable(); bool isCallable();
/* The map field is not initialized here and should be set separately. */ /* The map field is not initialized here and should be set separately. */
@ -477,6 +510,7 @@ struct JSObject {
inline bool isArguments() const; inline bool isArguments() const;
inline bool isArray() const; inline bool isArray() const;
inline bool isDenseArray() const; inline bool isDenseArray() const;
inline bool isSlowArray() const;
inline bool isFunction() const; inline bool isFunction() const;
inline bool isRegExp() const; inline bool isRegExp() const;
inline bool isXML() const; inline bool isXML() const;

View File

@ -45,7 +45,8 @@
#include "jsscope.h" #include "jsscope.h"
inline jsval inline jsval
JSObject::getSlotMT(JSContext *cx, uintN slot) { JSObject::getSlotMT(JSContext *cx, uintN slot)
{
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
/* /*
* If thread-safe, define a getSlotMT() that bypasses, for a native * If thread-safe, define a getSlotMT() that bypasses, for a native
@ -66,7 +67,8 @@ JSObject::getSlotMT(JSContext *cx, uintN slot) {
} }
inline void inline void
JSObject::setSlotMT(JSContext *cx, uintN slot, jsval value) { JSObject::setSlotMT(JSContext *cx, uintN slot, jsval value)
{
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
/* Thread-safe way to set a slot. */ /* Thread-safe way to set a slot. */
OBJ_CHECK_SLOT(this, slot); OBJ_CHECK_SLOT(this, slot);
@ -79,6 +81,62 @@ JSObject::setSlotMT(JSContext *cx, uintN slot, jsval value) {
#endif #endif
} }
inline uint32
JSObject::getArrayLength() const
{
JS_ASSERT(isArray());
return uint32(fslots[JSSLOT_ARRAY_LENGTH]);
}
inline uint32
JSObject::getArrayCount() const
{
JS_ASSERT(isArray());
return uint32(fslots[JSSLOT_ARRAY_COUNT]);
}
inline void
JSObject::setArrayLength(uint32 length)
{
JS_ASSERT(isArray());
fslots[JSSLOT_ARRAY_LENGTH] = length;
}
inline void
JSObject::setArrayCount(uint32 count)
{
JS_ASSERT(isArray());
fslots[JSSLOT_ARRAY_COUNT] = count;
}
inline void
JSObject::voidDenseArrayCount()
{
JS_ASSERT(isDenseArray());
fslots[JSSLOT_ARRAY_COUNT] = JSVAL_VOID;
}
inline void
JSObject::incArrayCountBy(uint32 posDelta)
{
JS_ASSERT(isArray());
fslots[JSSLOT_ARRAY_COUNT] += posDelta;
}
inline void
JSObject::decArrayCountBy(uint32 negDelta)
{
JS_ASSERT(isArray());
fslots[JSSLOT_ARRAY_COUNT] -= negDelta;
}
inline void
JSObject::voidArrayUnused()
{
JS_ASSERT(isArray());
fslots[JSSLOT_ARRAY_COUNT] = JSVAL_VOID;
}
inline void inline void
JSObject::initSharingEmptyScope(JSClass *clasp, JSObject *proto, JSObject *parent, JSObject::initSharingEmptyScope(JSClass *clasp, JSObject *proto, JSObject *parent,
jsval privateSlotValue) jsval privateSlotValue)

View File

@ -1523,12 +1523,7 @@ BEGIN_CASE(JSOP_LENGTH)
} else if (!JSVAL_IS_PRIMITIVE(lval)) { } else if (!JSVAL_IS_PRIMITIVE(lval)) {
obj = JSVAL_TO_OBJECT(lval); obj = JSVAL_TO_OBJECT(lval);
if (obj->isArray()) { if (obj->isArray()) {
/* jsuint length = obj->getArrayLength();
* We know that the array is created with its 'length' private data
* in a fixed slot at JSSLOT_ARRAY_LENGTH. See also JSOP_ARRAYPUSH,
* far below.
*/
jsuint length = obj->fslots[JSSLOT_ARRAY_LENGTH];
if (length <= JSVAL_INT_MAX) if (length <= JSVAL_INT_MAX)
regs.sp[-1] = INT_TO_JSVAL(length); regs.sp[-1] = INT_TO_JSVAL(length);
@ -1876,7 +1871,7 @@ BEGIN_CASE(JSOP_GETELEM)
if (obj->isDenseArray()) { if (obj->isDenseArray()) {
jsuint idx = jsuint(JSVAL_TO_INT(rval)); jsuint idx = jsuint(JSVAL_TO_INT(rval));
if (idx < jsuint(obj->fslots[JSSLOT_ARRAY_LENGTH]) && if (idx < obj->getArrayLength() &&
idx < js_DenseArrayCapacity(obj)) { idx < js_DenseArrayCapacity(obj)) {
rval = obj->dslots[idx]; rval = obj->dslots[idx];
if (rval != JSVAL_HOLE) if (rval != JSVAL_HOLE)
@ -1948,9 +1943,9 @@ BEGIN_CASE(JSOP_SETELEM)
if (obj->dslots[i] == JSVAL_HOLE) { if (obj->dslots[i] == JSVAL_HOLE) {
if (js_PrototypeHasIndexedProperties(cx, obj)) if (js_PrototypeHasIndexedProperties(cx, obj))
break; break;
if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH]) if ((jsuint)i >= obj->getArrayLength())
obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1; obj->setArrayLength(i + 1);
obj->fslots[JSSLOT_ARRAY_COUNT]++; obj->incArrayCountBy(1);
} }
obj->dslots[i] = rval; obj->dslots[i] = rval;
goto end_setelem; goto end_setelem;

View File

@ -10448,7 +10448,7 @@ TraceRecorder::newArray(JSObject* ctor, uint32 argc, jsval* argv, jsval* rval)
} }
if (argc > 0) if (argc > 0)
stobj_set_fslot(arr_ins, JSSLOT_ARRAY_COUNT, INS_CONST(argc)); stobj_set_fslot(arr_ins, JSObject::JSSLOT_ARRAY_COUNT, INS_CONST(argc));
} }
set(rval, arr_ins); set(rval, arr_ins);
@ -12584,10 +12584,10 @@ TraceRecorder::record_JSOP_APPLY()
*/ */
if (aobj->isDenseArray()) { if (aobj->isDenseArray()) {
guardDenseArray(aobj, aobj_ins, MISMATCH_EXIT); guardDenseArray(aobj, aobj_ins, MISMATCH_EXIT);
length = jsuint(aobj->fslots[JSSLOT_ARRAY_LENGTH]); length = aobj->getArrayLength();
guard(true, guard(true,
lir->ins2i(LIR_eq, lir->ins2i(LIR_eq,
p2i(stobj_get_fslot(aobj_ins, JSSLOT_ARRAY_LENGTH)), p2i(stobj_get_fslot(aobj_ins, JSObject::JSSLOT_ARRAY_LENGTH)),
length), length),
BRANCH_EXIT); BRANCH_EXIT);
} else if (aobj->isArguments()) { } else if (aobj->isArguments()) {
@ -12957,7 +12957,7 @@ TraceRecorder::denseArrayElement(jsval& oval, jsval& ival, jsval*& vp, LIns*& v_
/* check that the index is within bounds */ /* check that the index is within bounds */
LIns* dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACC_OTHER); LIns* dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACC_OTHER);
jsuint capacity = js_DenseArrayCapacity(obj); jsuint capacity = js_DenseArrayCapacity(obj);
bool within = (jsuint(idx) < jsuint(obj->fslots[JSSLOT_ARRAY_LENGTH]) && jsuint(idx) < capacity); bool within = (jsuint(idx) < obj->getArrayLength() && jsuint(idx) < capacity);
if (!within) { if (!within) {
/* If idx < 0, stay on trace (and read value as undefined, since this is a dense array). */ /* If idx < 0, stay on trace (and read value as undefined, since this is a dense array). */
LIns* br1 = NULL; LIns* br1 = NULL;
@ -12973,7 +12973,7 @@ TraceRecorder::denseArrayElement(jsval& oval, jsval& ival, jsval*& vp, LIns*& v_
LIns* br2 = lir->insBranch(LIR_jf, LIns* br2 = lir->insBranch(LIR_jf,
lir->ins2(LIR_pult, lir->ins2(LIR_pult,
pidx_ins, pidx_ins,
stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH)), stobj_get_fslot(obj_ins, JSObject::JSSLOT_ARRAY_LENGTH)),
NULL); NULL);
/* If dslots is NULL, stay on trace (and read value as undefined). */ /* If dslots is NULL, stay on trace (and read value as undefined). */
@ -13013,7 +13013,7 @@ TraceRecorder::denseArrayElement(jsval& oval, jsval& ival, jsval*& vp, LIns*& v_
/* Guard array length */ /* Guard array length */
guard(true, guard(true,
lir->ins2(LIR_pult, pidx_ins, stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH)), lir->ins2(LIR_pult, pidx_ins, stobj_get_fslot(obj_ins, JSObject::JSSLOT_ARRAY_LENGTH)),
exit); exit);
/* dslots must not be NULL */ /* dslots must not be NULL */
@ -15026,7 +15026,7 @@ TraceRecorder::record_JSOP_LENGTH()
if (!guardClass(obj, obj_ins, &js_SlowArrayClass, snapshot(BRANCH_EXIT), ACC_OTHER)) if (!guardClass(obj, obj_ins, &js_SlowArrayClass, snapshot(BRANCH_EXIT), ACC_OTHER))
RETURN_STOP_A("can't trace length property access on non-array"); RETURN_STOP_A("can't trace length property access on non-array");
} }
v_ins = lir->ins1(LIR_i2f, p2i(stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH))); v_ins = lir->ins1(LIR_i2f, p2i(stobj_get_fslot(obj_ins, JSObject::JSSLOT_ARRAY_LENGTH)));
} else if (OkToTraceTypedArrays && js_IsTypedArray(obj)) { } else if (OkToTraceTypedArrays && js_IsTypedArray(obj)) {
// Ensure array is a typed array and is the same type as what was written // Ensure array is a typed array and is the same type as what was written
guardClass(obj, obj_ins, obj->getClass(), snapshot(BRANCH_EXIT), ACC_OTHER); guardClass(obj, obj_ins, obj->getClass(), snapshot(BRANCH_EXIT), ACC_OTHER);
@ -15066,7 +15066,7 @@ TraceRecorder::record_JSOP_NEWARRAY()
} }
if (count > 0) if (count > 0)
stobj_set_fslot(v_ins, JSSLOT_ARRAY_COUNT, INS_CONST(count)); stobj_set_fslot(v_ins, JSObject::JSSLOT_ARRAY_COUNT, INS_CONST(count));
stack(-int(len), v_ins); stack(-int(len), v_ins);
return ARECORD_CONTINUE; return ARECORD_CONTINUE;

View File

@ -63,6 +63,8 @@
#include "jsvector.h" #include "jsvector.h"
#include "jstypedarray.h" #include "jstypedarray.h"
#include "jsobjinlines.h"
using namespace js; using namespace js;
/* /*
@ -1014,7 +1016,7 @@ class TypedArrayTemplate
NativeType *dest = static_cast<NativeType*>(data); NativeType *dest = static_cast<NativeType*>(data);
if (ar->isDenseArray() && js_DenseArrayCapacity(ar) >= len) { if (ar->isDenseArray() && js_DenseArrayCapacity(ar) >= len) {
JS_ASSERT(ar->fslots[JSSLOT_ARRAY_LENGTH] == (jsval)len); JS_ASSERT(ar->getArrayLength() == len);
jsval *src = ar->dslots; jsval *src = ar->dslots;