diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 80bba66edda..b7310018f0f 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1275,11 +1275,24 @@ JSClass js_SlowArrayClass = { JSBool js_MakeArraySlow(JSContext *cx, JSObject *obj) { - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass); + JS_ASSERT(obj->getClass() == &js_ArrayClass); - /* Create a native scope. */ - JSScope *scope = JSScope::create(cx, &js_SlowArrayObjectOps, - &js_SlowArrayClass, obj); + /* + * Create a native scope. All slow arrays other than Array.prototype get + * the same initial shape. + */ + uint32 emptyShape; + JSObject *arrayProto = obj->getProto(); + if (arrayProto->getClass() == &js_ObjectClass) { + /* obj is Array.prototype. */ + emptyShape = js_GenerateShape(cx, false); + } else { + JS_ASSERT(arrayProto->getClass() == &js_SlowArrayClass); + if (!OBJ_SCOPE(arrayProto)->getEmptyScopeShape(cx, &js_SlowArrayClass, &emptyShape)) + return JS_FALSE; + } + JSScope *scope = JSScope::create(cx, &js_SlowArrayObjectOps, &js_SlowArrayClass, obj, + emptyShape); if (!scope) return JS_FALSE; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 1c38cdb7137..804fddaa051 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2025,7 +2025,7 @@ InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* o if (!scope) goto bad; } else { - scope = JSScope::create(cx, ops, clasp, obj); + scope = JSScope::create(cx, ops, clasp, obj, js_GenerateShape(cx, false)); if (!scope) goto bad; diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index 7f377fa63d4..c4964337234 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -99,7 +99,7 @@ js_GetMutableScope(JSContext *cx, JSObject *obj) * birth, and runtime clone of a block objects are never mutated. */ JS_ASSERT(STOBJ_GET_CLASS(obj) != &js_BlockClass); - newscope = JSScope::create(cx, scope->map.ops, obj->getClass(), obj); + newscope = JSScope::create(cx, scope->map.ops, obj->getClass(), obj, scope->shape); if (!newscope) return NULL; JS_LOCK_SCOPE(cx, newscope); @@ -132,9 +132,9 @@ js_GetMutableScope(JSContext *cx, JSObject *obj) #define SCOPE_TABLE_NBYTES(n) ((n) * sizeof(JSScopeProperty *)) void -JSScope::initMinimal(JSContext *cx) +JSScope::initMinimal(JSContext *cx, uint32 newShape) { - shape = js_GenerateShape(cx, false); + shape = newShape; emptyScope = NULL; hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2; entryCount = removedCount = 0; @@ -183,7 +183,7 @@ JSScope::createTable(JSContext *cx, bool report) } JSScope * -JSScope::create(JSContext *cx, JSObjectOps *ops, JSClass *clasp, JSObject *obj) +JSScope::create(JSContext *cx, JSObjectOps *ops, JSClass *clasp, JSObject *obj, uint32 shape) { JS_ASSERT(OPS_IS_NATIVE(ops)); JS_ASSERT(obj); @@ -198,7 +198,7 @@ JSScope::create(JSContext *cx, JSObjectOps *ops, JSClass *clasp, JSObject *obj) scope->freeslot = JSSLOT_FREE(clasp); scope->flags = cx->runtime->gcRegenShapesScopeFlag; js_LeaveTraceIfGlobalObject(cx, obj); - scope->initMinimal(cx); + scope->initMinimal(cx, shape); #ifdef JS_THREADSAFE js_InitTitle(cx, &scope->title); @@ -227,7 +227,7 @@ JSScope::createEmptyScope(JSContext *cx, JSClass *clasp) scope->nrefs = 2; scope->freeslot = JSSLOT_FREE(clasp); scope->flags = OWN_SHAPE | cx->runtime->gcRegenShapesScopeFlag; - scope->initMinimal(cx); + scope->initMinimal(cx, js_GenerateShape(cx, false)); #ifdef JS_THREADSAFE js_InitTitle(cx, &scope->title); @@ -1559,7 +1559,21 @@ JSScope::clear(JSContext *cx) js_free(table); clearMiddleDelete(); js_LeaveTraceIfGlobalObject(cx, object); - initMinimal(cx); + + JSClass *clasp = object->getClass(); + JSObject *proto = object->getProto(); + uint32 newShape; + if (proto && clasp == proto->getClass()) { +#ifdef DEBUG + bool ok = +#endif + OBJ_SCOPE(proto)->getEmptyScopeShape(cx, clasp, &newShape); + JS_ASSERT(ok); + } else { + newShape = js_GenerateShape(cx, false); + } + initMinimal(cx, newShape); + JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); } diff --git a/js/src/jsscope.h b/js/src/jsscope.h index 211b88c9014..6fc56a3ed4f 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -215,7 +215,7 @@ struct JSScope { JSScopeProperty *lastProp; /* pointer to last property added */ private: - void initMinimal(JSContext *cx); + void initMinimal(JSContext *cx, uint32 newShape); bool createTable(JSContext *cx, bool report); bool changeTable(JSContext *cx, int change); void reportReadOnlyScope(JSContext *cx); @@ -226,7 +226,8 @@ struct JSScope { public: /* Create a mutable, owned, empty scope. */ - static JSScope *create(JSContext *cx, JSObjectOps *ops, JSClass *clasp, JSObject *obj); + static JSScope *create(JSContext *cx, JSObjectOps *ops, JSClass *clasp, JSObject *obj, + uint32 shape); static void destroy(JSContext *cx, JSScope *scope); @@ -245,6 +246,19 @@ struct JSScope { return createEmptyScope(cx, clasp); } + bool getEmptyScopeShape(JSContext *cx, JSClass *clasp, uint32 *shapep) { + if (emptyScope) { + *shapep = emptyScope->shape; + return true; + } + JSScope *e = getEmptyScope(cx, clasp); + if (!e) + return false; + *shapep = e->shape; + e->drop(cx, NULL); + return true; + } + inline void hold(); inline bool drop(JSContext *cx, JSObject *obj);