Bug 479566 - Rename array "dense length" to capacity. r=shaver.

This commit is contained in:
Jason Orendorff 2009-02-21 15:33:50 -06:00
parent bf8b9e17e4
commit 15b9f03458
5 changed files with 86 additions and 81 deletions

View File

@ -49,7 +49,7 @@
* We track these pieces of metadata for arrays in dense mode:
* - the array's length property as a uint32, in JSSLOT_ARRAY_LENGTH,
* - the number of indices that are filled (non-holes), in JSSLOT_ARRAY_COUNT,
* - the net number of slots starting at dslots (DENSELEN), in dslots[-1] if
* - the net number of slots starting at dslots (capacity), in dslots[-1] if
* dslots is non-NULL.
*
* In dense mode, holes in the array are represented by JSVAL_HOLE. The final
@ -58,22 +58,18 @@
*
* Arrays are converted to use js_SlowArrayClass when any of these conditions
* are met:
* - the load factor (COUNT / DENSELEN) is less than 0.25, and there are
* - the load factor (COUNT / capacity) is less than 0.25, and there are
* more than MIN_SPARSE_INDEX slots total
* - a property is set that is non-numeric (and not "length"); or
* - a hole is filled below DENSELEN (possibly implicitly through methods like
* |reverse| or |splice|).
* - a property is defined that has non-default property attributes.
*
* In the latter two cases, property creation order is no longer index order,
* which necessitates use of a structure that keeps track of property creation
* order. (ES4, due to expectations baked into web script, requires that
* enumeration order be the order in which properties were created.)
*
* An alternative in the latter case (out-of-order index set) would be to
* maintain the scope to track property enumeration order, but still use
* the fast slot access. That would have the same memory cost as just using
* a js_SlowArrayClass, but have the same performance characteristics as
* a dense array for slot accesses, at some cost in code complexity.
* Dense arrays do not track property creation order, so unlike other native
* objects and slow arrays, enumerating an array does not necessarily visit the
* properties in the order they were created. We could instead maintain the
* scope to track property enumeration order, but still use the fast slot
* access. That would have the same memory cost as just using a
* js_SlowArrayClass, but have the same performance characteristics as a dense
* array for slot accesses, at some cost in code complexity.
*/
#include "jsstddef.h"
#include <stdlib.h>
@ -110,7 +106,7 @@
#define INDEX_TOO_BIG(index) ((index) > JS_BIT(29) - 1)
#define INDEX_TOO_SPARSE(array, index) \
(INDEX_TOO_BIG(index) || \
((index) > ARRAY_DENSE_LENGTH(array) && (index) >= MIN_SPARSE_INDEX && \
((index) > js_DenseArrayCapacity(array) && (index) >= MIN_SPARSE_INDEX && \
(index) > (uint32)((array)->fslots[JSSLOT_ARRAY_COUNT] + 1) * 4))
JS_STATIC_ASSERT(sizeof(JSScopeProperty) > 4 * sizeof(jsval));
@ -312,11 +308,11 @@ BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom,
}
static JSBool
ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldlen, uint32 len)
ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldsize, uint32 size)
{
jsval *slots, *newslots;
if (len == 0) {
if (size == 0) {
if (obj->dslots) {
JS_free(cx, obj->dslots - 1);
obj->dslots = NULL;
@ -324,29 +320,29 @@ ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldlen, uint32 len)
return JS_TRUE;
}
if (len > ~(uint32)0 / sizeof(jsval)) {
if (size > ~(uint32)0 / sizeof(jsval)) {
js_ReportAllocationOverflow(cx);
return JS_FALSE;
}
slots = obj->dslots ? obj->dslots - 1 : NULL;
newslots = (jsval *) JS_realloc(cx, slots, sizeof (jsval) * (len + 1));
newslots = (jsval *) JS_realloc(cx, slots, sizeof (jsval) * (size + 1));
if (!newslots)
return JS_FALSE;
obj->dslots = newslots + 1;
ARRAY_SET_DENSE_LENGTH(obj, len);
js_SetDenseArrayCapacity(obj, size);
for (slots = obj->dslots + oldlen; slots < obj->dslots + len; slots++)
for (slots = obj->dslots + oldsize; slots < obj->dslots + size; slots++)
*slots = JSVAL_HOLE;
return JS_TRUE;
}
static JSBool
EnsureLength(JSContext *cx, JSObject *obj, uint32 len)
EnsureCapacity(JSContext *cx, JSObject *obj, uint32 len)
{
uint32 oldlen = ARRAY_DENSE_LENGTH(obj);
uint32 oldlen = js_DenseArrayCapacity(obj);
if (len > oldlen) {
return ResizeSlots(cx, obj, oldlen,
@ -369,7 +365,7 @@ GetArrayElement(JSContext *cx, JSObject *obj, jsuint index, JSBool *hole,
JSObject *obj2;
JSProperty *prop;
if (OBJ_IS_DENSE_ARRAY(cx, obj) && index < ARRAY_DENSE_LENGTH(obj) &&
if (OBJ_IS_DENSE_ARRAY(cx, obj) && index < js_DenseArrayCapacity(obj) &&
(*vp = obj->dslots[index]) != JSVAL_HOLE) {
*hole = JS_FALSE;
return JS_TRUE;
@ -412,7 +408,7 @@ SetArrayElement(JSContext *cx, JSObject *obj, jsuint index, jsval v)
if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
/* Predicted/prefeched code should favor the remains-dense case. */
if (!INDEX_TOO_SPARSE(obj, index)) {
if (!EnsureLength(cx, obj, index + 1))
if (!EnsureCapacity(cx, obj, index + 1))
return JS_FALSE;
if (index >= (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH])
obj->fslots[JSSLOT_ARRAY_LENGTH] = index + 1;
@ -443,7 +439,7 @@ DeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index)
jsval junk;
if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
if (index < ARRAY_DENSE_LENGTH(obj)) {
if (index < js_DenseArrayCapacity(obj)) {
if (obj->dslots[index] != JSVAL_HOLE)
obj->fslots[JSSLOT_ARRAY_COUNT]--;
obj->dslots[index] = JSVAL_HOLE;
@ -581,7 +577,7 @@ array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
/* Don't reallocate if we're not actually shrinking our slots. */
jsuint oldsize = ARRAY_DENSE_LENGTH(obj);
jsuint oldsize = js_DenseArrayCapacity(obj);
if (oldsize >= newlen && !ResizeSlots(cx, obj, oldsize, newlen))
return JS_FALSE;
} else if (oldlen - newlen < (1 << 24)) {
@ -640,13 +636,13 @@ array_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
return js_LookupProperty(cx, obj, id, objp, propp);
/*
* We have only indexed properties up to DENSELEN (excepting holes), plus
* We have only indexed properties up to capacity (excepting holes), plus
* the length property. For all else, we delegate to the prototype.
*/
if (id != ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) &&
(!js_IdIsIndex(id, &i) ||
obj->fslots[JSSLOT_ARRAY_LENGTH] == 0 ||
i >= ARRAY_DENSE_LENGTH(obj) ||
i >= js_DenseArrayCapacity(obj) ||
obj->dslots[i] == JSVAL_HOLE))
{
JSObject *proto = STOBJ_GET_PROTO(obj);
@ -697,7 +693,7 @@ array_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
if (!OBJ_IS_DENSE_ARRAY(cx, obj))
return js_GetProperty(cx, obj, id, vp);
if (!js_IdIsIndex(ID_TO_VALUE(id), &i) || i >= ARRAY_DENSE_LENGTH(obj) ||
if (!js_IdIsIndex(ID_TO_VALUE(id), &i) || i >= js_DenseArrayCapacity(obj) ||
obj->dslots[i] == JSVAL_HOLE) {
JSObject *obj2;
JSProperty *prop;
@ -783,7 +779,7 @@ array_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
return js_SetProperty(cx, obj, id, vp);
}
if (!EnsureLength(cx, obj, i + 1))
if (!EnsureCapacity(cx, obj, i + 1))
return JS_FALSE;
if (i >= (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH])
@ -801,8 +797,8 @@ js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, jsval v)
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
do {
jsuint length = ARRAY_DENSE_LENGTH(obj);
if ((jsuint)i < length) {
jsuint capacity = js_DenseArrayCapacity(obj);
if ((jsuint)i < capacity) {
if (obj->dslots[i] == JSVAL_HOLE) {
if (cx->runtime->anyArrayProtoHasElement)
break;
@ -870,7 +866,7 @@ array_deleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *rval)
return JS_TRUE;
}
if (js_IdIsIndex(id, &i) && i < ARRAY_DENSE_LENGTH(obj) &&
if (js_IdIsIndex(id, &i) && i < js_DenseArrayCapacity(obj) &&
obj->dslots[i] != JSVAL_HOLE) {
obj->fslots[JSSLOT_ARRAY_COUNT]--;
obj->dslots[i] = JSVAL_HOLE;
@ -945,34 +941,34 @@ static JSBool
array_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
uint32 length, i;
uint32 capacity, i;
JSIndexIterState *ii;
switch (enum_op) {
case JSENUMERATE_INIT:
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
length = ARRAY_DENSE_LENGTH(obj);
capacity = js_DenseArrayCapacity(obj);
if (idp)
*idp = INT_TO_JSVAL(obj->fslots[JSSLOT_ARRAY_COUNT]);
ii = NULL;
for (i = 0; i != length; ++i) {
for (i = 0; i != capacity; ++i) {
if (obj->dslots[i] == JSVAL_HOLE) {
if (!ii) {
ii = (JSIndexIterState *)
JS_malloc(cx, offsetof(JSIndexIterState, holes) +
JS_BITMAP_SIZE(length));
JS_BITMAP_SIZE(capacity));
if (!ii)
return JS_FALSE;
ii->hasHoles = JS_TRUE;
memset(ii->holes, 0, JS_BITMAP_SIZE(length));
memset(ii->holes, 0, JS_BITMAP_SIZE(capacity));
}
JS_SET_BIT(ii->holes, i);
}
}
if (!ii) {
/* Array has no holes. */
if (length <= PACKED_UINT_PAIR_MASK) {
*statep = UINT_PAIR_TO_BOOLEAN_JSVAL(0, length);
if (capacity <= PACKED_UINT_PAIR_MASK) {
*statep = UINT_PAIR_TO_BOOLEAN_JSVAL(0, capacity);
break;
}
ii = (JSIndexIterState *)
@ -982,17 +978,17 @@ array_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
ii->hasHoles = JS_FALSE;
}
ii->index = 0;
ii->length = length;
ii->length = capacity;
*statep = (jsval) ii | INDEX_ITER_TAG;
JS_ASSERT(*statep & JSVAL_INT);
break;
case JSENUMERATE_NEXT:
if (JSVAL_TAG(*statep) == JSVAL_BOOLEAN) {
BOOLEAN_JSVAL_TO_UINT_PAIR(*statep, i, length);
if (i != length) {
BOOLEAN_JSVAL_TO_UINT_PAIR(*statep, i, capacity);
if (i != capacity) {
*idp = INT_TO_JSID(i);
*statep = UINT_PAIR_TO_BOOLEAN_JSVAL(i + 1, length);
*statep = UINT_PAIR_TO_BOOLEAN_JSVAL(i + 1, capacity);
break;
}
} else {
@ -1055,14 +1051,14 @@ array_finalize(JSContext *cx, JSObject *obj)
static void
array_trace(JSTracer *trc, JSObject *obj)
{
uint32 length;
uint32 capacity;
size_t i;
jsval v;
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
length = ARRAY_DENSE_LENGTH(obj);
for (i = 0; i < length; i++) {
capacity = js_DenseArrayCapacity(obj);
for (i = 0; i < capacity; i++) {
v = obj->dslots[i];
if (JSVAL_IS_TRACEABLE(v)) {
JS_SET_TRACING_INDEX(trc, "array_dslots", i);
@ -1153,7 +1149,7 @@ JSBool
js_MakeArraySlow(JSContext *cx, JSObject *obj)
{
JSObjectMap *map, *oldmap;
uint32 i, length;
uint32 i, capacity;
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass);
@ -1163,16 +1159,16 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj)
if (!map)
return JS_FALSE;
length = ARRAY_DENSE_LENGTH(obj);
if (length) {
capacity = js_DenseArrayCapacity(obj);
if (capacity) {
map->freeslot = STOBJ_NSLOTS(obj) + JS_INITIAL_NSLOTS;
obj->dslots[-1] = JS_INITIAL_NSLOTS + length;
obj->dslots[-1] = JS_INITIAL_NSLOTS + capacity;
} else {
map->freeslot = STOBJ_NSLOTS(obj);
}
/* Create new properties pointing to existing values in dslots */
for (i = 0; i < length; i++) {
for (i = 0; i < capacity; i++) {
jsid id;
JSScopeProperty *sprop;
@ -1197,7 +1193,7 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj)
* we can tell when only named properties have been added to a dense array
* to make it slow-but-not-sparse.
*/
length = obj->fslots[JSSLOT_ARRAY_LENGTH];
uint32 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
obj->fslots[JSSLOT_ARRAY_COUNT] = INT_FITS_IN_JSVAL(length)
? INT_TO_JSVAL(length)
: JSVAL_VOID;
@ -1488,7 +1484,7 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint end,
jsval *vector)
{
if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
if (!EnsureLength(cx, obj, end))
if (!EnsureCapacity(cx, obj, end))
return JS_FALSE;
if (end > (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH])
@ -1516,7 +1512,7 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector,
obj->fslots[JSSLOT_ARRAY_LENGTH] = length;
if (vector) {
if (!EnsureLength(cx, obj, length))
if (!EnsureCapacity(cx, obj, length))
return JS_FALSE;
jsuint count = length;
@ -2126,7 +2122,7 @@ array_push1_dense(JSContext* cx, JSObject* obj, jsval v, jsval *rval)
return array_push_slowly(cx, obj, 1, &v, rval);
}
if (!EnsureLength(cx, obj, length + 1))
if (!EnsureCapacity(cx, obj, length + 1))
return JS_FALSE;
obj->fslots[JSSLOT_ARRAY_LENGTH] = length + 1;
@ -2141,9 +2137,9 @@ js_ArrayCompPush(JSContext *cx, JSObject *obj, jsval v)
{
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
uint32_t length = (uint32_t) obj->fslots[JSSLOT_ARRAY_LENGTH];
JS_ASSERT(length <= ARRAY_DENSE_LENGTH(obj));
JS_ASSERT(length <= js_DenseArrayCapacity(obj));
if (length == ARRAY_DENSE_LENGTH(obj)) {
if (length == js_DenseArrayCapacity(obj)) {
if (length >= ARRAY_INIT_LIMIT) {
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
JSMSG_ARRAY_INIT_TOO_BIG);
@ -2497,15 +2493,15 @@ array_concat(JSContext *cx, uintN argc, jsval *vp)
aobj = JS_THIS_OBJECT(cx, vp);
if (OBJ_IS_DENSE_ARRAY(cx, aobj)) {
/*
* Clone aobj but pass the minimum of its length and capacity (aka
* "dense length"), to handle a = [1,2,3]; a.length = 10000 "dense"
* cases efficiently. In such a case we'll pass 8 (not 3) due to the
* ARRAY_GROWBY over-allocation policy, which will cause nobj to be
* over-allocated to 16. But in the normal case where length is <=
* capacity, nobj and aobj will have the same dense length.
* Clone aobj but pass the minimum of its length and capacity, to handle
* a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In such a
* case we'll pass 8 (not 3) due to the ARRAY_GROWBY over-allocation
* policy, which will cause nobj to be over-allocated to 16. But in the
* normal case where length is <= capacity, nobj and aobj will have the
* same capacity.
*/
length = aobj->fslots[JSSLOT_ARRAY_LENGTH];
jsuint capacity = ARRAY_DENSE_LENGTH(aobj);
jsuint capacity = js_DenseArrayCapacity(aobj);
nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->dslots,
aobj->fslots[JSSLOT_ARRAY_COUNT] !=
(jsval) length);
@ -2637,7 +2633,7 @@ array_slice(JSContext *cx, uintN argc, jsval *vp)
if (begin > end)
begin = end;
if (OBJ_IS_DENSE_ARRAY(cx, obj) && end <= ARRAY_DENSE_LENGTH(obj)) {
if (OBJ_IS_DENSE_ARRAY(cx, obj) && end <= js_DenseArrayCapacity(obj)) {
nobj = js_NewArrayObject(cx, end - begin, obj->dslots + begin,
obj->fslots[JSSLOT_ARRAY_COUNT] !=
obj->fslots[JSSLOT_ARRAY_LENGTH]);
@ -3141,7 +3137,7 @@ js_NewUninitializedArray(JSContext* cx, JSObject* proto, uint32 len)
jsval* newslots = (jsval*) JS_malloc(cx, sizeof (jsval) * (len + 1)); \
if (newslots) { \
obj->dslots = newslots + 1; \
ARRAY_SET_DENSE_LENGTH(obj, len); \
js_SetDenseArrayCapacity(obj, len); \
{newslots_code} \
while (++newslots < obj->dslots + len) \
*newslots = JSVAL_HOLE; \
@ -3232,9 +3228,9 @@ js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
OBJ_IS_DENSE_ARRAY(cx, array) ? "dense" : "sparse",
array->fslots[JSSLOT_ARRAY_LENGTH]);
if (OBJ_IS_DENSE_ARRAY(cx, array)) {
fprintf(stderr, ", count %lu, denselen %lu",
fprintf(stderr, ", count %lu, capacity %lu",
array->fslots[JSSLOT_ARRAY_COUNT],
ARRAY_DENSE_LENGTH(array));
js_DenseArrayCapacity(array));
}
fputs(")\n", stderr);
JS_free(cx, bytes);

View File

@ -44,6 +44,7 @@
*/
#include "jsprvtd.h"
#include "jspubtd.h"
#include "jsobj.h"
JS_BEGIN_EXTERN_C
@ -78,12 +79,20 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj);
#define JSSLOT_ARRAY_COUNT (JSSLOT_ARRAY_LENGTH + 1)
#define JSSLOT_ARRAY_LOOKUP_HOLDER (JSSLOT_ARRAY_COUNT + 1)
#define ARRAY_DENSE_LENGTH(obj) \
(JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)), \
(obj)->dslots ? (uint32)(obj)->dslots[-1] : 0)
static JS_INLINE uint32
js_DenseArrayCapacity(JSObject *obj)
{
JS_ASSERT(OBJ_IS_DENSE_ARRAY(BOGUS_CX, obj));
return obj->dslots ? (uint32) obj->dslots[-1] : 0;
}
#define ARRAY_SET_DENSE_LENGTH(obj, max) \
(JS_ASSERT((obj)->dslots), (obj)->dslots[-1] = (jsval)(max))
static JS_INLINE void
js_SetDenseArrayCapacity(JSObject *obj, uint32 capacity)
{
JS_ASSERT(OBJ_IS_DENSE_ARRAY(BOGUS_CX, obj));
JS_ASSERT(obj->dslots);
obj->dslots[-1] = (jsval) capacity;
}
#define ARRAY_GROWBY 8

View File

@ -4671,7 +4671,7 @@ js_Interpret(JSContext *cx)
if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
jsuint length;
length = ARRAY_DENSE_LENGTH(obj);
length = js_DenseArrayCapacity(obj);
i = JSVAL_TO_INT(rval);
if ((jsuint)i < length &&
i < obj->fslots[JSSLOT_ARRAY_LENGTH]) {
@ -4724,7 +4724,7 @@ js_Interpret(JSContext *cx)
if (OBJ_IS_DENSE_ARRAY(cx, obj) && JSID_IS_INT(id)) {
jsuint length;
length = ARRAY_DENSE_LENGTH(obj);
length = js_DenseArrayCapacity(obj);
i = JSID_TO_INT(id);
if ((jsuint)i < length) {
if (obj->dslots[i] == JSVAL_HOLE) {

View File

@ -5743,7 +5743,7 @@ js_DumpObject(JSObject *obj)
/* OBJ_IS_DENSE_ARRAY ignores the cx argument. */
if (OBJ_IS_DENSE_ARRAY(BOGUS_CX, obj)) {
slots = JS_MIN((jsuint) obj->fslots[JSSLOT_ARRAY_LENGTH],
ARRAY_DENSE_LENGTH(obj));
js_DenseArrayCapacity(obj));
fprintf(stderr, "elements\n");
for (i = 0; i < slots; i++) {
fprintf(stderr, " %3d: ", i);

View File

@ -5929,9 +5929,9 @@ JS_REQUIRES_STACK bool
TraceRecorder::guardDenseArrayIndex(JSObject* obj, jsint idx, LIns* obj_ins,
LIns* dslots_ins, LIns* idx_ins, ExitType exitType)
{
jsuint length = ARRAY_DENSE_LENGTH(obj);
jsuint capacity = js_DenseArrayCapacity(obj);
bool cond = (jsuint(idx) < jsuint(obj->fslots[JSSLOT_ARRAY_LENGTH]) && jsuint(idx) < length);
bool cond = (jsuint(idx) < jsuint(obj->fslots[JSSLOT_ARRAY_LENGTH]) && jsuint(idx) < capacity);
if (cond) {
/* Guard array length */
LIns* exit = guard(true,
@ -7156,7 +7156,7 @@ TraceRecorder::record_JSOP_GETELEM()
idx = ID_TO_VALUE(id);
jsuint index;
if (js_IdIsIndex(idx, &index) && guardDenseArray(obj, obj_ins, BRANCH_EXIT)) {
v = (index >= ARRAY_DENSE_LENGTH(obj)) ? JSVAL_HOLE : obj->dslots[index];
v = (index >= js_DenseArrayCapacity(obj)) ? JSVAL_HOLE : obj->dslots[index];
if (v == JSVAL_HOLE)
ABORT_TRACE("can't see through hole in dense array");
} else {