Bug 710492 - add special cycle collector shape tracing path. r=bhackett

This commit is contained in:
Andrew McCreight 2011-12-19 10:24:56 -08:00
parent 87082c4df4
commit fd01649dbe
5 changed files with 74 additions and 41 deletions

View File

@ -143,10 +143,10 @@ JS_WrapPropertyDescriptor(JSContext *cx, js::PropertyDescriptor *desc)
return cx->compartment->wrap(cx, desc);
}
JS_FRIEND_API(void *)
JS_TraceShapeChildrenAcyclic(JSTracer *trc, void *shape)
JS_FRIEND_API(void)
JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape)
{
return (void *)MarkShapeChildrenAcyclic(trc, (const Shape *)shape);
MarkCycleCollectorChildren(trc, (const Shape *)shape);
}
AutoPreserveCompartment::AutoPreserveCompartment(JSContext *cx

View File

@ -86,11 +86,12 @@ extern JS_FRIEND_API(JSBool)
JS_NondeterministicGetWeakMapKeys(JSContext *cx, JSObject *obj, JSObject **ret);
/*
* Marks all the children of a shape except the parent, which avoids using
* unbounded stack space. Returns the parent.
* Used by the cycle collector to trace through the shape and all
* shapes it reaches, marking all non-shape children found in the
* process. Uses bounded stack space.
*/
extern JS_FRIEND_API(void *)
JS_TraceShapeChildrenAcyclic(JSTracer *trc, void *shape);
extern JS_FRIEND_API(void)
JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape);
enum {
JS_TELEMETRY_GC_REASON,

View File

@ -910,46 +910,28 @@ MarkChildren(JSTracer *trc, JSScript *script)
script->markTrapClosures(trc);
}
const Shape *
MarkShapeChildrenAcyclic(JSTracer *trc, const Shape *shape)
{
/*
* This function is used by the cycle collector to ensure that we use O(1)
* stack space when building the CC graph. It must avoid traversing through
* an unbounded number of shapes before reaching an object. (Objects are
* added to the CC graph, so reaching one terminates the recursion.)
*
* Traversing through shape->base() will use bounded space. All but one of
* the fields of BaseShape is an object, and objects terminate the
* recursion. An owned BaseShape may point to an unowned BaseShape, but
* unowned BaseShapes will not point to any other shapes. So the recursion
* is bounded.
*/
MarkBaseShapeUnbarriered(trc, shape->base(), "base");
MarkIdUnbarriered(trc, shape->maybePropid(), "propid");
return shape->previous();
}
void
MarkChildren(JSTracer *trc, const Shape *shape)
{
/*
* We ignore the return value of MarkShapeChildrenAcyclic and use
* shape->previous() instead so that the return value has MarkablePtr type.
*/
MarkShapeChildrenAcyclic(trc, shape);
MarkBaseShapeUnbarriered(trc, shape->base(), "base");
MarkIdUnbarriered(trc, shape->maybePropid(), "propid");
if (shape->previous())
MarkShape(trc, shape->previous(), "parent");
}
void
MarkChildren(JSTracer *trc, BaseShape *base)
inline void
MarkBaseShapeGetterSetter(JSTracer *trc, BaseShape *base)
{
if (base->hasGetterObject())
MarkObjectUnbarriered(trc, base->getterObject(), "getter");
if (base->hasSetterObject())
MarkObjectUnbarriered(trc, base->setterObject(), "setter");
}
void
MarkChildren(JSTracer *trc, BaseShape *base)
{
MarkBaseShapeGetterSetter(trc, base);
if (base->isOwned())
MarkBaseShapeUnbarriered(trc, base->baseUnowned(), "base");
@ -957,6 +939,58 @@ MarkChildren(JSTracer *trc, BaseShape *base)
MarkObjectUnbarriered(trc, parent, "parent");
}
/*
* This function is used by the cycle collector to trace through the
* children of a BaseShape (and its baseUnowned(), if any). The cycle
* collector does not directly care about BaseShapes, so only the
* getter, setter, and parent are marked. Furthermore, the parent is
* marked only if it isn't the same as prevParent, which will be
* updated to the current shape's parent.
*/
inline void
MarkCycleCollectorChildren(JSTracer *trc, BaseShape *base, JSObject **prevParent)
{
JS_ASSERT(base);
MarkBaseShapeGetterSetter(trc, base);
JSObject *parent = base->getObjectParent();
if (parent && parent != *prevParent) {
MarkObjectUnbarriered(trc, parent, "parent");
*prevParent = parent;
}
// An owned base shape has the same parent, getter and setter as
// its baseUnowned().
#ifdef DEBUG
if (base->isOwned()) {
UnownedBaseShape *unowned = base->baseUnowned();
JS_ASSERT_IF(base->hasGetterObject(), base->getterObject() == unowned->getterObject());
JS_ASSERT_IF(base->hasSetterObject(), base->setterObject() == unowned->setterObject());
JS_ASSERT(base->getObjectParent() == unowned->getObjectParent());
}
#endif
}
/*
* This function is used by the cycle collector to trace through a
* shape. The cycle collector does not care about shapes or base
* shapes, so those are not marked. Instead, any shapes or base shapes
* that are encountered have their children marked. Stack space is
* bounded. If two shapes in a row have the same parent pointer, the
* parent pointer will only be marked once.
*/
void
MarkCycleCollectorChildren(JSTracer *trc, const Shape *shape)
{
JSObject *prevParent = NULL;
do {
MarkCycleCollectorChildren(trc, shape->base(), &prevParent);
MarkIdUnbarriered(trc, shape->maybePropid(), "propid");
shape = shape->previous();
} while (shape);
}
static void
ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
{

View File

@ -197,11 +197,11 @@ void
MarkChildren(JSTracer *trc, JSXML *xml);
/*
* Marks all the children of a shape except the parent, which avoids using
* unbounded stack space. Returns the parent.
* Trace through the shape and any shapes it contains to mark
* non-shape children.
*/
const Shape *
MarkShapeChildrenAcyclic(JSTracer *trc, const Shape *shape);
void
MarkCycleCollectorChildren(JSTracer *trc, const Shape *shape);
/*
* Use function overloading to decide which function should be called based on

View File

@ -801,9 +801,7 @@ NoteJSChild(JSTracer *trc, void *thing, JSGCTraceKind kind)
#endif
tracer->cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, thing);
} else if (kind == JSTRACE_SHAPE) {
do {
thing = JS_TraceShapeChildrenAcyclic(trc, thing);
} while (thing);
JS_TraceShapeCycleCollectorChildren(trc, thing);
} else if (kind != JSTRACE_STRING) {
JS_TraceChildren(trc, thing, kind);
}