diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index cc87e3b3b385..e9379d92fcf9 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -104,9 +104,20 @@ FinishThreadData(JSThreadData *data) static void PurgeThreadData(JSContext *cx, JSThreadData *data) { + js_PurgeGSNCache(&data->gsnCache); + + if (cx->runtime->gcRegenShapes) + js_PurgePropertyCache(cx, &data->propertyCache); + # ifdef JS_TRACER JSTraceMonitor *tm = &data->traceMonitor; tm->reservedDoublePoolPtr = tm->reservedDoublePool; + + /* + * FIXME: bug 506117. We should flush only if (cx->runtime->gcRegenShapes), + * but we can't yet, because traces may embed sprop and object references, + * and we don't yet mark such embedded refs. + */ tm->needFlush = JS_TRUE; if (tm->recorder) @@ -123,9 +134,6 @@ PurgeThreadData(JSContext *cx, JSThreadData *data) /* Destroy eval'ed scripts. */ js_DestroyScriptsToGC(cx, data); - - js_PurgeGSNCache(&data->gsnCache); - js_PurgePropertyCache(cx, &data->propertyCache); } #ifdef JS_THREADSAFE diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index a7c81a697c16..2a6c2cc6817f 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -389,7 +389,8 @@ struct JSRuntime { */ JSPackedBool gcPoke; JSPackedBool gcRunning; - uint16 gcPadding; + JSPackedBool gcRegenShapes; + uint8 gcPadding; #ifdef JS_GC_ZEAL jsrefcount gcZeal; #endif @@ -1539,6 +1540,7 @@ static JS_INLINE uint32 js_RegenerateShapeForGC(JSContext *cx) { JS_ASSERT(cx->runtime->gcRunning); + JS_ASSERT(cx->runtime->gcRegenShapes); /* * Under the GC, compared with js_GenerateShape, we don't need to use diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 45fe5352a14c..40ee8971bfc0 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3599,7 +3599,6 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) #ifdef JS_TRACER js_PurgeJITOracle(); #endif - js_PurgeThreads(cx); restart: rt->gcNumber++; @@ -3611,8 +3610,13 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) * Same for the protoHazardShape proxy-shape standing in for all object * prototypes having readonly or setter properties. */ - rt->shapeGen = 0; - rt->protoHazardShape = 0; + if (rt->shapeGen & SHAPE_OVERFLOW_BIT) { + rt->gcRegenShapes = true; + rt->shapeGen = 0; + rt->protoHazardShape = 0; + } + + js_PurgeThreads(cx); /* * Mark phase. @@ -3887,7 +3891,7 @@ out: rt->setGCLastBytes(rt->gcBytes); done_running: rt->gcLevel = 0; - rt->gcRunning = JS_FALSE; + rt->gcRunning = rt->gcRegenShapes = false; #ifdef JS_THREADSAFE rt->gcThread = NULL; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 80bf3a89e960..c14b66e837aa 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -5060,11 +5060,15 @@ js_TraceNativeEnumerators(JSTracer *trc) jsid *cursor, *end; /* - * Purge native enumerators cached by shape id, which we are about to - * re-number completely when tracing is done for the GC. + * Purge native enumerators cached by shape id when the GC is about to + * re-number shapes due to shape generation overflow. Do this also when + * shutting down the runtime. */ rt = trc->context->runtime; - if (IS_GC_MARKING_TRACER(trc)) { + bool doGC = IS_GC_MARKING_TRACER(trc) && + (rt->gcRegenShapes || rt->state == JSRTS_LANDING); + + if (doGC) { memset(&rt->nativeEnumCache, 0, sizeof rt->nativeEnumCache); #ifdef JS_DUMP_ENUM_CACHE_STATS printf("nativeEnumCache hit rate %g%%\n", @@ -5083,7 +5087,7 @@ js_TraceNativeEnumerators(JSTracer *trc) do { TRACE_ID(trc, *cursor); } while (++cursor != end); - } else if (IS_GC_MARKING_TRACER(trc)) { + } else if (doGC) { js_RemoveAsGCBytes(rt, NativeEnumeratorSize(ne->length)); *nep = ne->next; JS_free(trc->context, ne); @@ -5768,23 +5772,22 @@ js_TraceObject(JSTracer *trc, JSObject *obj) MeterEntryCount(scope->entryCount); #endif - sprop = SCOPE_LAST_PROP(scope); + sprop = scope->lastProp; if (sprop) { JS_ASSERT(scope->has(sprop)); /* Regenerate property cache shape ids if GC'ing. */ - if (IS_GC_MARKING_TRACER(trc)) { - uint32 shape, oldshape; - - shape = js_RegenerateShapeForGC(cx); - if (!(sprop->flags & SPROP_MARK)) { - oldshape = sprop->shape; - sprop->shape = shape; + if (IS_GC_MARKING_TRACER(trc) && cx->runtime->gcRegenShapes) { + if (!(sprop->flags & SPROP_FLAG_SHAPE_REGEN)) { + sprop->shape = js_RegenerateShapeForGC(cx); sprop->flags |= SPROP_FLAG_SHAPE_REGEN; - if (scope->shape != oldshape) - shape = js_RegenerateShapeForGC(cx); } + uint32 shape = sprop->shape; + if (scope->hasOwnShape()) { + shape = js_RegenerateShapeForGC(cx); + JS_ASSERT(shape != sprop->shape); + } scope->shape = shape; } diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index a9689fcfe02b..c343533dde35 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -1032,12 +1032,14 @@ JSScope::reportReadOnlyScope(JSContext *cx) JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, bytes); } -static inline void -js_MakeScopeShapeUnique(JSContext *cx, JSScope *scope) +void +JSScope::generateOwnShape(JSContext *cx) { - if (scope->object) - js_LeaveTraceIfGlobalObject(cx, scope->object); - scope->shape = js_GenerateShape(cx, false); + if (object) + js_LeaveTraceIfGlobalObject(cx, object); + + shape = js_GenerateShape(cx, false); + setOwnShape(); } JSScopeProperty * @@ -1154,7 +1156,6 @@ JSScope::add(JSContext *cx, jsid id, } setMiddleDelete(); } - js_MakeScopeShapeUnique(cx, this); /* * If we fail later on trying to find or create a new sprop, we will @@ -1537,7 +1538,7 @@ JSScope::remove(JSContext *cx, jsid id) } else if (!hadMiddleDelete()) { setMiddleDelete(); } - js_MakeScopeShapeUnique(cx, this); + generateOwnShape(cx); CHECK_ANCESTOR_LINE(this, true); /* Last, consider shrinking this->table if its load factor is <= .25. */ @@ -1567,25 +1568,25 @@ JSScope::clear(JSContext *cx) void JSScope::brandingShapeChange(JSContext *cx, uint32 slot, jsval v) { - js_MakeScopeShapeUnique(cx, this); + generateOwnShape(cx); } void JSScope::deletingShapeChange(JSContext *cx, JSScopeProperty *sprop) { - js_MakeScopeShapeUnique(cx, this); + generateOwnShape(cx); } void JSScope::methodShapeChange(JSContext *cx, uint32 slot, jsval toval) { - js_MakeScopeShapeUnique(cx, this); + generateOwnShape(cx); } void JSScope::protoShapeChange(JSContext *cx) { - js_MakeScopeShapeUnique(cx, this); + generateOwnShape(cx); } void @@ -1594,19 +1595,19 @@ JSScope::replacingShapeChange(JSContext *cx, JSScopeProperty *sprop, JSScopeProp if (shape == sprop->shape) shape = newsprop->shape; else - js_MakeScopeShapeUnique(cx, this); + generateOwnShape(cx); } void JSScope::sealingShapeChange(JSContext *cx) { - js_MakeScopeShapeUnique(cx, this); + generateOwnShape(cx); } void JSScope::shadowingShapeChange(JSContext *cx, JSScopeProperty *sprop) { - js_MakeScopeShapeUnique(cx, this); + generateOwnShape(cx); } void @@ -1826,10 +1827,12 @@ js_SweepScopeProperties(JSContext *cx) */ if (sprop->flags & SPROP_MARK) { sprop->flags &= ~SPROP_MARK; - if (sprop->flags & SPROP_FLAG_SHAPE_REGEN) - sprop->flags &= ~SPROP_FLAG_SHAPE_REGEN; - else - sprop->shape = js_RegenerateShapeForGC(cx); + if (rt->gcRegenShapes) { + if (sprop->flags & SPROP_FLAG_SHAPE_REGEN) + sprop->flags &= ~SPROP_FLAG_SHAPE_REGEN; + else + sprop->shape = js_RegenerateShapeForGC(cx); + } liveCount++; continue; } @@ -1869,7 +1872,7 @@ js_SweepScopeProperties(JSContext *cx) sprop->kids = NULL; parent = sprop->parent; - /* Assert that grandparent has no kids or chunky kids. */ + /* The grandparent must have either no kids or chunky kids. */ JS_ASSERT(!parent || !parent->kids || KIDS_IS_CHUNKY(parent->kids)); if (KIDS_IS_CHUNKY(kids)) { @@ -1888,8 +1891,7 @@ js_SweepScopeProperties(JSContext *cx) * re-use by InsertPropertyTreeChild. */ chunk->kids[i] = NULL; - if (!InsertPropertyTreeChild(rt, parent, kid, - chunk)) { + if (!InsertPropertyTreeChild(rt, parent, kid, chunk)) { /* * This can happen only if we failed to add an * entry to the root property hash table. diff --git a/js/src/jsscope.h b/js/src/jsscope.h index c501317cfad2..481da6a4a5b3 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -219,6 +219,7 @@ struct JSScope { bool createTable(JSContext *cx, bool report); bool changeTable(JSContext *cx, int change); void reportReadOnlyScope(JSContext *cx); + void generateOwnShape(JSContext *cx); JSScopeProperty **searchTable(jsid id, bool adding); inline JSScopeProperty **search(jsid id, bool adding); JSScope *createEmptyScope(JSContext *cx, JSClass *clasp); @@ -281,16 +282,14 @@ struct JSScope { MIDDLE_DELETE = 0x0001, SEALED = 0x0002, BRANDED = 0x0004, - INDEXED_PROPERTIES = 0x0008 + INDEXED_PROPERTIES = 0x0008, + OWN_SHAPE = 0x0010 }; bool hadMiddleDelete() { return flags & MIDDLE_DELETE; } void setMiddleDelete() { flags |= MIDDLE_DELETE; } void clearMiddleDelete() { flags &= ~MIDDLE_DELETE; } - bool hadIndexedProperties() { return flags & INDEXED_PROPERTIES; } - void setIndexedProperties() { flags |= INDEXED_PROPERTIES; } - /* * Don't define clearSealed, as it can't be done safely because JS_LOCK_OBJ * will avoid taking the lock if the object owns its scope and the scope is @@ -306,7 +305,12 @@ struct JSScope { */ bool branded() { return flags & BRANDED; } void setBranded() { flags |= BRANDED; } - void clearBranded() { flags &= ~BRANDED; } + + bool hadIndexedProperties() { return flags & INDEXED_PROPERTIES; } + void setIndexedProperties() { flags |= INDEXED_PROPERTIES; } + + bool hasOwnShape() { return flags & OWN_SHAPE; } + void setOwnShape() { flags |= OWN_SHAPE; } bool owned() { return object != NULL; } }; diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index ff817993e96b..03e88f8d791c 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1610,6 +1610,14 @@ js_DestroyScript(JSContext *cx, JSScript *script) * to its bytecode if this script is being destroyed from JS_DestroyScript * or equivalent according to a mandatory "New/Destroy" protocol. * + * The GC purges all property caches when regenerating shapes upon shape + * generator overflow, so no need in that event to purge just the entries + * for this script. + * + * The GC purges trace-JITted code on every GC activation, not just when + * regenerating shapes, so we don't have to purge fragments if the GC is + * currently running. + * * JS_THREADSAFE note: js_PurgePropertyCacheForScript purges only the * current thread's property cache, so a script not owned by a function * or object, which hands off lifetime management for that script to the @@ -1624,7 +1632,7 @@ js_DestroyScript(JSContext *cx, JSScript *script) JS_ASSERT_IF(cx->runtime->gcRunning, !script->owner); #endif - if (!cx->runtime->gcRunning) { + if (!cx->runtime->gcRegenShapes) { JSStackFrame *fp = js_GetTopStackFrame(cx); if (!(fp && (fp->flags & JSFRAME_EVAL))) {