Bug 443746 – Optimizing the enumeration state allocation. r=brendan

This commit is contained in:
Igor Bukanov 2008-07-06 21:02:44 +02:00
Родитель e6ae905711
Коммит dbdb00824b
4 изменённых файлов: 129 добавлений и 141 удалений

Просмотреть файл

@ -1628,6 +1628,36 @@ JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj)
return JS_TRUE;
}
static JSIdArray *
NewIdArray(JSContext *cx, jsint length)
{
JSIdArray *ida;
ida = (JSIdArray *)
JS_malloc(cx, offsetof(JSIdArray, vector) + length * sizeof(jsval));
if (ida)
ida->length = length;
return ida;
}
/*
* Unlike realloc(3), this function frees ida on failure.
*/
static JSIdArray *
SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length)
{
JSIdArray *rida;
rida = (JSIdArray *)
JS_realloc(cx, ida,
offsetof(JSIdArray, vector) + length * sizeof(jsval));
if (!rida)
JS_DestroyIdArray(cx, ida);
else
rida->length = length;
return rida;
}
static JSIdArray *
AddAtomToArray(JSContext *cx, JSAtom *atom, JSIdArray *ida, jsint *ip)
{
@ -1636,7 +1666,7 @@ AddAtomToArray(JSContext *cx, JSAtom *atom, JSIdArray *ida, jsint *ip)
i = *ip;
length = ida->length;
if (i >= length) {
ida = js_SetIdArrayLength(cx, ida, JS_MAX(length * 2, 8));
ida = SetIdArrayLength(cx, ida, JS_MAX(length * 2, 8));
if (!ida)
return NULL;
JS_ASSERT(i < ida->length);
@ -1671,7 +1701,7 @@ JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj,
if (ida) {
i = ida->length;
} else {
ida = js_NewIdArray(cx, 8);
ida = NewIdArray(cx, 8);
if (!ida)
return NULL;
i = 0;
@ -1713,8 +1743,8 @@ JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj,
}
}
/* Trim to exact length via js_SetIdArrayLength. */
return js_SetIdArrayLength(cx, ida, i);
/* Trim to exact length. */
return SetIdArrayLength(cx, ida, i);
}
#undef CLASP
@ -3921,7 +3951,7 @@ JS_Enumerate(JSContext *cx, JSObject *obj)
n = 8;
/* Create an array of jsids large enough to hold all the properties */
ida = js_NewIdArray(cx, n);
ida = NewIdArray(cx, n);
if (!ida)
goto error;
@ -3936,14 +3966,14 @@ JS_Enumerate(JSContext *cx, JSObject *obj)
break;
if (i == ida->length) {
ida = js_SetIdArrayLength(cx, ida, ida->length * 2);
ida = SetIdArrayLength(cx, ida, ida->length * 2);
if (!ida)
goto error;
vector = &ida->vector[0];
}
vector[i++] = id;
}
return js_SetIdArrayLength(cx, ida, i);
return SetIdArrayLength(cx, ida, i);
error:
if (iter_state != JSVAL_NULL)

Просмотреть файл

@ -164,16 +164,6 @@ typedef struct JSPropertyTreeEntry {
JSScopeProperty *child;
} JSPropertyTreeEntry;
/*
* Private type used to enumerate properties of a native JS object.
*/
struct JSNativeEnumerator {
jsint next_index; /* index into jsid array */
JSIdArray *ida; /* all property ids in enumeration */
JSNativeEnumerator *next; /* double-linked list support */
JSNativeEnumerator **prevp;
};
typedef struct JSSetSlotRequest JSSetSlotRequest;
struct JSSetSlotRequest {

Просмотреть файл

@ -4126,31 +4126,19 @@ out:
return JS_TRUE;
}
JSIdArray *
js_NewIdArray(JSContext *cx, jsint length)
{
JSIdArray *ida;
ida = (JSIdArray *)
JS_malloc(cx, sizeof(JSIdArray) + (length-1) * sizeof(jsval));
if (ida)
ida->length = length;
return ida;
}
JSIdArray *
js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length)
{
JSIdArray *rida;
rida = (JSIdArray *)
JS_realloc(cx, ida, sizeof(JSIdArray) + (length-1) * sizeof(jsval));
if (!rida)
JS_DestroyIdArray(cx, ida);
else
rida->length = length;
return rida;
}
/*
* Private type used to enumerate properties of a native JS object. It is not
* allocated when there are no enumerable properties in the object. Instead
* for empty enumerators the code uses JSVAL_ZERO as the enumeration state.
*/
struct JSNativeEnumerator {
uint32 cursor; /* index into ids array, runs from
length down to 0 */
uint32 length; /* length of ids array */
JSNativeEnumerator *next; /* double-linked list support */
JSNativeEnumerator **prevp;
jsid ids[1]; /* enumeration id array */
};
/*
* This function is used to enumerate the properties of native JSObjects
@ -4161,17 +4149,14 @@ JSBool
js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
JSRuntime *rt;
JSObject *proto;
JSClass *clasp;
JSEnumerateOp enumerate;
JSScopeProperty *sprop, *lastProp;
jsint i, length;
JSNativeEnumerator *ne;
uint32 length;
JSScope *scope;
JSIdArray *ida;
JSNativeEnumerator *state;
JSScopeProperty *sprop;
jsid *ids;
rt = cx->runtime;
clasp = OBJ_GET_CLASS(cx, obj);
enumerate = clasp->enumerate;
if (clasp->flags & JSCLASS_NEW_ENUMERATE) {
@ -4183,32 +4168,30 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
case JSENUMERATE_INIT:
if (!enumerate(cx, obj))
return JS_FALSE;
length = 0;
/*
* The set of all property ids is pre-computed when the iterator
* is initialized so as to avoid problems with properties being
* deleted during the iteration.
* The set of all property ids is pre-computed when the iterator is
* initialized to avoid problems caused by properties being deleted
* during the iteration.
*
* Use a do-while(0) loop to avoid too many nested ifs. If ne is null
* after the loop, it indicates an empty enumerator.
*/
ne = NULL;
length = 0;
JS_LOCK_OBJ(cx, obj);
scope = OBJ_SCOPE(obj);
do {
/*
* If this object shares a scope with its prototype, don't
* enumerate its properties. Otherwise they will be enumerated
* a second time when the prototype object is enumerated.
*/
if (scope->object != obj)
break;
/*
* If this object shares a scope with its prototype, don't enumerate
* its properties. Otherwise they will be enumerated a second time
* when the prototype object is enumerated.
*/
proto = OBJ_GET_PROTO(cx, obj);
if (proto && scope == OBJ_SCOPE(proto)) {
ida = js_NewIdArray(cx, 0);
if (!ida) {
JS_UNLOCK_OBJ(cx, obj);
return JS_FALSE;
}
} else {
/* Object has a private scope; Enumerate all props in scope. */
for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop;
sprop = sprop->parent) {
/* Count all enumerable properties in object's scope. */
for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
if ((sprop->attrs & JSPROP_ENUMERATE) &&
!(sprop->flags & SPROP_IS_ALIAS) &&
(!SCOPE_HAD_MIDDLE_DELETE(scope) ||
@ -4216,70 +4199,70 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
length++;
}
}
ida = js_NewIdArray(cx, length);
if (!ida) {
JS_UNLOCK_OBJ(cx, obj);
if (length == 0)
break;
ne = (JSNativeEnumerator *)
JS_malloc(cx, offsetof(JSNativeEnumerator, ids) +
length * sizeof(jsid));
if (!ne) {
JS_UNLOCK_SCOPE(cx, scope);
return JS_FALSE;
}
i = length;
for (sprop = lastProp; sprop; sprop = sprop->parent) {
ne->cursor = length;
ne->length = length;
ids = ne->ids;
for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
if ((sprop->attrs & JSPROP_ENUMERATE) &&
!(sprop->flags & SPROP_IS_ALIAS) &&
(!SCOPE_HAD_MIDDLE_DELETE(scope) ||
SCOPE_HAS_PROPERTY(scope, sprop))) {
JS_ASSERT(i > 0);
ida->vector[--i] = sprop->id;
JS_ASSERT(ids < ne->ids + length);
*ids++ = sprop->id;
}
}
}
JS_UNLOCK_OBJ(cx, obj);
JS_ASSERT(ids == ne->ids + length);
} while (0);
JS_UNLOCK_SCOPE(cx, scope);
state = (JSNativeEnumerator *)JS_malloc(cx, sizeof(JSNativeEnumerator));
if (!state) {
JS_DestroyIdArray(cx, ida);
return JS_FALSE;
}
state->ida = ida;
state->next_index = 0;
JS_LOCK_RUNTIME(rt);
state->next = rt->nativeEnumerators;
if (state->next)
state->next->prevp = &state->next;
state->prevp = &rt->nativeEnumerators;
*state->prevp = state;
JS_UNLOCK_RUNTIME(rt);
*statep = PRIVATE_TO_JSVAL(state);
if (idp)
*idp = INT_TO_JSVAL(length);
if (!ne) {
JS_ASSERT(length == 0);
*statep = JSVAL_ZERO;
} else {
JS_ASSERT(length != 0);
JS_LOCK_GC(cx->runtime);
ne->next = cx->runtime->nativeEnumerators;
if (ne->next)
ne->next->prevp = &ne->next;
ne->prevp = &cx->runtime->nativeEnumerators;
*ne->prevp = ne;
JS_UNLOCK_GC(cx->runtime);
*statep = PRIVATE_TO_JSVAL(ne);
}
break;
case JSENUMERATE_NEXT:
state = (JSNativeEnumerator *) JSVAL_TO_PRIVATE(*statep);
ida = state->ida;
length = ida->length;
if (state->next_index != length) {
*idp = ida->vector[state->next_index++];
break;
}
/* FALL THROUGH */
case JSENUMERATE_DESTROY:
state = (JSNativeEnumerator *) JSVAL_TO_PRIVATE(*statep);
JS_LOCK_RUNTIME(rt);
JS_ASSERT(rt->nativeEnumerators);
JS_ASSERT(*state->prevp == state);
if (state->next) {
JS_ASSERT(state->next->prevp == &state->next);
state->next->prevp = state->prevp;
if (*statep != JSVAL_ZERO) {
ne = (JSNativeEnumerator *) JSVAL_TO_PRIVATE(*statep);
JS_ASSERT(ne->length >= 1);
if (ne->cursor != 0 && enum_op == JSENUMERATE_NEXT) {
*idp = ne->ids[--ne->cursor];
break;
}
JS_LOCK_GC(cx->runtime);
JS_ASSERT(cx->runtime->nativeEnumerators);
JS_ASSERT(*ne->prevp == ne);
if (ne->next) {
JS_ASSERT(ne->next->prevp == &ne->next);
ne->next->prevp = ne->prevp;
}
*ne->prevp = ne->next;
JS_UNLOCK_GC(cx->runtime);
JS_free(cx, ne);
}
*state->prevp = state->next;
JS_UNLOCK_RUNTIME(rt);
JS_DestroyIdArray(cx, state->ida);
JS_free(cx, state);
*statep = JSVAL_NULL;
break;
}
@ -4289,22 +4272,16 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
void
js_TraceNativeEnumerators(JSTracer *trc)
{
JSNativeEnumerator *state;
jsid *cursor, *end, id;
JSNativeEnumerator *ne;
jsid *cursor, *end;
state = trc->context->runtime->nativeEnumerators;
if (!state)
return;
do {
JS_ASSERT(*state->prevp == state);
cursor = state->ida->vector;
end = cursor + state->ida->length;
for (; cursor != end; ++cursor) {
id = *cursor;
TRACE_ID(trc, id);
}
} while ((state = state->next) != NULL);
for (ne = trc->context->runtime->nativeEnumerators; ne; ne = ne->next) {
JS_ASSERT(ne->length >= 1);
JS_ASSERT(*ne->prevp == ne);
end = ne->ids + ne->length;
for (cursor = ne->ids; cursor != end; ++cursor)
TRACE_ID(trc, *cursor);
}
}
JSBool

Просмотреть файл

@ -607,15 +607,6 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval);
extern JSBool
js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp);
extern JSIdArray *
js_NewIdArray(JSContext *cx, jsint length);
/*
* Unlike realloc(3), this function frees ida on failure.
*/
extern JSIdArray *
js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length);
extern JSBool
js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp);