зеркало из https://github.com/mozilla/gecko-dev.git
Bug 708382 - GC marking - one common stack and tail recurssion elimination. r=wmccloskey. a=ms2geronirc
--HG-- extra : rebase_source : 580b30f289f4e1b1a1980bb000fc25e6e3cf27e7
This commit is contained in:
Родитель
dcc0c4884c
Коммит
8f52fc5c88
|
@ -87,7 +87,7 @@ template <size_t i> struct CeilingLog2 {
|
|||
|
||||
/* Round up to the nearest power of 2. */
|
||||
template <size_t i> struct RoundUpPow2 {
|
||||
static const size_t result = 1u << CeilingLog2<i>::result;
|
||||
static const size_t result = size_t(1) << CeilingLog2<i>::result;
|
||||
};
|
||||
template <> struct RoundUpPow2<0> {
|
||||
static const size_t result = 1;
|
||||
|
|
|
@ -5,7 +5,7 @@ function build_getter(i) {
|
|||
|
||||
function test()
|
||||
{
|
||||
var N = internalConst("OBJECT_MARK_STACK_LENGTH") + 2;
|
||||
var N = internalConst("MARK_STACK_LENGTH") + 2;
|
||||
var o = {};
|
||||
var descriptor = { enumerable: true};
|
||||
for (var i = 0; i != N; ++i) {
|
||||
|
|
|
@ -437,11 +437,8 @@ struct JSRuntime
|
|||
/* The reason that an interrupt-triggered GC should be called. */
|
||||
js::gcstats::Reason gcTriggerReason;
|
||||
|
||||
/* Pre-allocated space for the GC mark stacks. Pointer type ensures alignment. */
|
||||
void *gcMarkStackObjs[js::OBJECT_MARK_STACK_SIZE / sizeof(void *)];
|
||||
void *gcMarkStackTypes[js::TYPE_MARK_STACK_SIZE / sizeof(void *)];
|
||||
void *gcMarkStackXMLs[js::XML_MARK_STACK_SIZE / sizeof(void *)];
|
||||
void *gcMarkStackLarges[js::LARGE_MARK_STACK_SIZE / sizeof(void *)];
|
||||
/* Pre-allocated space for the GC mark stack. */
|
||||
uintptr_t gcMarkStackArray[js::MARK_STACK_LENGTH];
|
||||
|
||||
/*
|
||||
* Compartment that triggered GC. If more than one Compatment need GC,
|
||||
|
|
|
@ -1735,10 +1735,7 @@ namespace js {
|
|||
GCMarker::GCMarker(JSContext *cx)
|
||||
: color(BLACK),
|
||||
unmarkedArenaStackTop(NULL),
|
||||
objStack(cx->runtime->gcMarkStackObjs, sizeof(cx->runtime->gcMarkStackObjs)),
|
||||
typeStack(cx->runtime->gcMarkStackTypes, sizeof(cx->runtime->gcMarkStackTypes)),
|
||||
xmlStack(cx->runtime->gcMarkStackXMLs, sizeof(cx->runtime->gcMarkStackXMLs)),
|
||||
largeStack(cx->runtime->gcMarkStackLarges, sizeof(cx->runtime->gcMarkStackLarges))
|
||||
stack(cx->runtime->gcMarkStackArray)
|
||||
{
|
||||
JS_TRACER_INIT(this, cx, NULL);
|
||||
markLaterArenas = 0;
|
||||
|
@ -1792,7 +1789,8 @@ MarkDelayedChildren(GCMarker *trc, Arena *a)
|
|||
void
|
||||
GCMarker::markDelayedChildren()
|
||||
{
|
||||
while (unmarkedArenaStackTop) {
|
||||
JS_ASSERT(unmarkedArenaStackTop);
|
||||
do {
|
||||
/*
|
||||
* If marking gets delayed at the same arena again, we must repeat
|
||||
* marking of its things. For that we pop arena from the stack and
|
||||
|
@ -1805,7 +1803,7 @@ GCMarker::markDelayedChildren()
|
|||
a->aheader.hasDelayedMarking = 0;
|
||||
markLaterArenas--;
|
||||
MarkDelayedChildren(this, a);
|
||||
}
|
||||
} while (unmarkedArenaStackTop);
|
||||
JS_ASSERT(!markLaterArenas);
|
||||
}
|
||||
|
||||
|
@ -3496,7 +3494,8 @@ EndVerifyBarriers(JSContext *cx)
|
|||
|
||||
JS_ASSERT(trc->number == rt->gcNumber);
|
||||
|
||||
rt->gcIncrementalTracer->markDelayedChildren();
|
||||
if (rt->gcIncrementalTracer->hasDelayedChildren())
|
||||
rt->gcIncrementalTracer->markDelayedChildren();
|
||||
|
||||
rt->gcVerifyData = NULL;
|
||||
rt->gcIncrementalTracer = NULL;
|
||||
|
|
116
js/src/jsgc.h
116
js/src/jsgc.h
|
@ -63,6 +63,7 @@
|
|||
#include "gc/Statistics.h"
|
||||
#include "js/HashTable.h"
|
||||
#include "js/Vector.h"
|
||||
#include "js/TemplateLib.h"
|
||||
|
||||
struct JSCompartment;
|
||||
|
||||
|
@ -1593,49 +1594,68 @@ struct ConservativeGCThreadData {
|
|||
template<class T>
|
||||
struct MarkStack {
|
||||
T *stack;
|
||||
uintN tos, limit;
|
||||
T *tos;
|
||||
T *limit;
|
||||
|
||||
bool push(T item) {
|
||||
if (tos == limit)
|
||||
return false;
|
||||
stack[tos++] = item;
|
||||
*tos++ = item;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isEmpty() { return tos == 0; }
|
||||
bool push(T item1, T item2) {
|
||||
T *nextTos = tos + 2;
|
||||
if (nextTos > limit)
|
||||
return false;
|
||||
tos[0] = item1;
|
||||
tos[1] = item2;
|
||||
tos = nextTos;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
return tos == stack;
|
||||
}
|
||||
|
||||
T pop() {
|
||||
JS_ASSERT(!isEmpty());
|
||||
return stack[--tos];
|
||||
return *--tos;
|
||||
}
|
||||
|
||||
T &peek() {
|
||||
JS_ASSERT(!isEmpty());
|
||||
return stack[tos-1];
|
||||
}
|
||||
|
||||
MarkStack(void **buffer, size_t size)
|
||||
{
|
||||
tos = 0;
|
||||
limit = size / sizeof(T) - 1;
|
||||
stack = (T *)buffer;
|
||||
}
|
||||
template<size_t N>
|
||||
MarkStack(T (&buffer)[N])
|
||||
: stack(buffer),
|
||||
tos(buffer),
|
||||
limit(buffer + N) { }
|
||||
};
|
||||
|
||||
struct LargeMarkItem
|
||||
{
|
||||
JSObject *obj;
|
||||
uintN markpos;
|
||||
|
||||
LargeMarkItem(JSObject *obj) : obj(obj), markpos(0) {}
|
||||
};
|
||||
|
||||
static const size_t OBJECT_MARK_STACK_SIZE = 32768 * sizeof(JSObject *);
|
||||
static const size_t XML_MARK_STACK_SIZE = 1024 * sizeof(JSXML *);
|
||||
static const size_t TYPE_MARK_STACK_SIZE = 1024 * sizeof(types::TypeObject *);
|
||||
static const size_t LARGE_MARK_STACK_SIZE = 64 * sizeof(LargeMarkItem);
|
||||
static const size_t MARK_STACK_LENGTH = 32768;
|
||||
|
||||
struct GCMarker : public JSTracer {
|
||||
/*
|
||||
* We use a common mark stack to mark GC things of different types and use
|
||||
* the explicit tags to distinguish them when it cannot be deduced from
|
||||
* the context of push or pop operation.
|
||||
*
|
||||
* Currently we need only 4 tags. However that can be extended to 8 if
|
||||
* necessary. We tag either pointers to GC things or pointers to Value
|
||||
* arrays. So the pointers are always at least 8-byte aligned.
|
||||
*/
|
||||
enum StackTag {
|
||||
ValueArrayTag,
|
||||
ObjectTag,
|
||||
TypeTag,
|
||||
XmlTag,
|
||||
LastTag = XmlTag
|
||||
};
|
||||
|
||||
static const uintptr_t StackTagMask = 3;
|
||||
|
||||
static void staticAsserts() {
|
||||
JS_STATIC_ASSERT(StackTagMask >= uintptr_t(LastTag));
|
||||
}
|
||||
|
||||
private:
|
||||
/* The color is only applied to objects, functions and xml. */
|
||||
uint32 color;
|
||||
|
@ -1653,10 +1673,7 @@ struct GCMarker : public JSTracer {
|
|||
void dumpConservativeRoots();
|
||||
#endif
|
||||
|
||||
MarkStack<void *> objStack;
|
||||
MarkStack<types::TypeObject *> typeStack;
|
||||
MarkStack<JSXML *> xmlStack;
|
||||
MarkStack<LargeMarkItem> largeStack;
|
||||
MarkStack<uintptr_t> stack;
|
||||
|
||||
public:
|
||||
explicit GCMarker(JSContext *cx);
|
||||
|
@ -1681,30 +1698,47 @@ struct GCMarker : public JSTracer {
|
|||
|
||||
void delayMarkingChildren(const void *thing);
|
||||
|
||||
bool hasDelayedChildren() const {
|
||||
return !!unmarkedArenaStackTop;
|
||||
}
|
||||
|
||||
void markDelayedChildren();
|
||||
|
||||
bool isMarkStackEmpty() {
|
||||
return objStack.isEmpty() &&
|
||||
typeStack.isEmpty() &&
|
||||
xmlStack.isEmpty() &&
|
||||
largeStack.isEmpty();
|
||||
return stack.isEmpty();
|
||||
}
|
||||
|
||||
void drainMarkStack();
|
||||
|
||||
inline void processMarkStackTop();
|
||||
|
||||
void pushObject(JSObject *obj) {
|
||||
if (!objStack.push(obj))
|
||||
delayMarkingChildren(obj);
|
||||
pushTaggedPtr(ObjectTag, obj);
|
||||
}
|
||||
|
||||
void pushType(types::TypeObject *type) {
|
||||
if (!typeStack.push(type))
|
||||
delayMarkingChildren(type);
|
||||
pushTaggedPtr(TypeTag, type);
|
||||
}
|
||||
|
||||
void pushXML(JSXML *xml) {
|
||||
if (!xmlStack.push(xml))
|
||||
delayMarkingChildren(xml);
|
||||
pushTaggedPtr(XmlTag, xml);
|
||||
}
|
||||
|
||||
void pushTaggedPtr(StackTag tag, void *ptr) {
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
|
||||
JS_ASSERT(!(addr & StackTagMask));
|
||||
if (!stack.push(addr | uintptr_t(tag)))
|
||||
delayMarkingChildren(ptr);
|
||||
}
|
||||
|
||||
bool pushValueArray(void *start, void *end) {
|
||||
JS_STATIC_ASSERT(ValueArrayTag == 0);
|
||||
JS_ASSERT(start < end);
|
||||
uintptr_t startAddr = reinterpret_cast<uintptr_t>(start);
|
||||
uintptr_t endAddr = reinterpret_cast<uintptr_t>(end);
|
||||
JS_ASSERT(!(startAddr & StackTagMask));
|
||||
JS_ASSERT(!(endAddr & StackTagMask));
|
||||
return stack.push(endAddr, startAddr);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -280,6 +280,9 @@ MarkXML(JSTracer *trc, const MarkablePtr<JSXML> &xml, const char *name)
|
|||
}
|
||||
#endif
|
||||
|
||||
#define JS_SAME_COMPARTMENT_ASSERT(thing1, thing2) \
|
||||
JS_ASSERT((thing1)->compartment() == (thing2)->compartment())
|
||||
|
||||
#define JS_COMPARTMENT_ASSERT(rt, thing) \
|
||||
JS_ASSERT_IF((rt)->gcCurrentCompartment, \
|
||||
(thing)->compartment() == (rt)->gcCurrentCompartment);
|
||||
|
@ -352,14 +355,13 @@ PushMarkStack(GCMarker *gcmarker, const Shape *thing)
|
|||
ScanShape(gcmarker, thing);
|
||||
}
|
||||
|
||||
static void
|
||||
static inline void
|
||||
ScanBaseShape(GCMarker *gcmarker, BaseShape *base);
|
||||
|
||||
void
|
||||
PushMarkStack(GCMarker *gcmarker, BaseShape *thing)
|
||||
{
|
||||
JS_OPT_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment,
|
||||
thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment);
|
||||
JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
|
||||
|
||||
/* We mark base shapes directly rather than pushing on the stack. */
|
||||
if (thing->markIfUnmarked(gcmarker->getMarkColor()))
|
||||
|
@ -665,24 +667,10 @@ MarkRootRange(JSTracer *trc, size_t len, jsid *vec, const char *name)
|
|||
MarkIdRangeUnbarriered(trc, len, vec, name);
|
||||
}
|
||||
|
||||
static inline void
|
||||
ScanValue(GCMarker *gcmarker, const Value &v)
|
||||
{
|
||||
if (v.isMarkable()) {
|
||||
JSGCTraceKind kind = v.gcKind();
|
||||
if (kind == JSTRACE_STRING) {
|
||||
PushMarkStack(gcmarker, v.toString());
|
||||
} else {
|
||||
JS_ASSERT(kind == JSTRACE_OBJECT);
|
||||
PushMarkStack(gcmarker, &v.toObject());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ScanShape(GCMarker *gcmarker, const Shape *shape)
|
||||
{
|
||||
restart:
|
||||
restart:
|
||||
PushMarkStack(gcmarker, shape->base());
|
||||
|
||||
jsid id = shape->maybePropid();
|
||||
|
@ -696,20 +684,33 @@ restart:
|
|||
goto restart;
|
||||
}
|
||||
|
||||
static void
|
||||
static inline void
|
||||
ScanBaseShape(GCMarker *gcmarker, BaseShape *base)
|
||||
{
|
||||
if (base->hasGetterObject())
|
||||
PushMarkStack(gcmarker, base->getterObject());
|
||||
for (;;) {
|
||||
if (base->hasGetterObject())
|
||||
PushMarkStack(gcmarker, base->getterObject());
|
||||
|
||||
if (base->hasSetterObject())
|
||||
PushMarkStack(gcmarker, base->setterObject());
|
||||
if (base->hasSetterObject())
|
||||
PushMarkStack(gcmarker, base->setterObject());
|
||||
|
||||
if (base->isOwned())
|
||||
PushMarkStack(gcmarker, base->baseUnowned());
|
||||
if (JSObject *parent = base->getObjectParent())
|
||||
PushMarkStack(gcmarker, parent);
|
||||
|
||||
if (JSObject *parent = base->getObjectParent())
|
||||
PushMarkStack(gcmarker, parent);
|
||||
if (base->isOwned()) {
|
||||
/*
|
||||
* Make sure that ScanBaseShape is not recursive so its inlining
|
||||
* is possible.
|
||||
*/
|
||||
UnownedBaseShape *unowned = base->baseUnowned();
|
||||
JS_SAME_COMPARTMENT_ASSERT(base, unowned);
|
||||
if (unowned->markIfUnmarked(gcmarker->getMarkColor())) {
|
||||
base = unowned;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -736,15 +737,15 @@ ScanLinearString(GCMarker *gcmarker, JSLinearString *str)
|
|||
* The function tries to scan the whole rope tree using the marking stack as
|
||||
* temporary storage. If that becomes full, the unscanned ropes are added to
|
||||
* the delayed marking list. When the function returns, the marking stack is
|
||||
* at the same depth as it was on entry.
|
||||
*
|
||||
* The function relies on the fact that a rope can only point to other ropes or
|
||||
* linear strings, it cannot refer to other GC things of other types.
|
||||
* at the same depth as it was on entry. This way we avoid using tags when
|
||||
* pushing ropes to the stack as ropes never leaks to other users of the
|
||||
* stack. This also assumes that a rope can only point to other ropes or
|
||||
* linear strings, it cannot refer to GC things of other types.
|
||||
*/
|
||||
static void
|
||||
ScanRope(GCMarker *gcmarker, JSRope *rope)
|
||||
{
|
||||
unsigned savedTos = gcmarker->objStack.tos;
|
||||
uintptr_t *savedTos = gcmarker->stack.tos;
|
||||
for (;;) {
|
||||
JS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
|
||||
JS_ASSERT(rope->JSString::isRope());
|
||||
|
@ -769,21 +770,21 @@ ScanRope(GCMarker *gcmarker, JSRope *rope)
|
|||
* When both children are ropes, set aside the right one to
|
||||
* scan it later.
|
||||
*/
|
||||
if (next && !gcmarker->objStack.push(next))
|
||||
if (next && !gcmarker->stack.push(reinterpret_cast<uintptr_t>(next)))
|
||||
gcmarker->delayMarkingChildren(next);
|
||||
next = &left->asRope();
|
||||
}
|
||||
}
|
||||
if (next) {
|
||||
rope = next;
|
||||
} else if (savedTos != gcmarker->objStack.tos) {
|
||||
JS_ASSERT(savedTos < gcmarker->objStack.tos);
|
||||
rope = static_cast<JSRope *>(gcmarker->objStack.pop());
|
||||
} else if (savedTos != gcmarker->stack.tos) {
|
||||
JS_ASSERT(savedTos < gcmarker->stack.tos);
|
||||
rope = reinterpret_cast<JSRope *>(gcmarker->stack.pop());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
JS_ASSERT(savedTos == gcmarker->objStack.tos);
|
||||
JS_ASSERT(savedTos == gcmarker->stack.tos);
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -809,10 +810,40 @@ PushMarkStack(GCMarker *gcmarker, JSString *str)
|
|||
ScanString(gcmarker, str);
|
||||
}
|
||||
|
||||
static const uintN LARGE_OBJECT_CHUNK_SIZE = 2048;
|
||||
static JS_NEVER_INLINE void
|
||||
DelayMarkingValueArray(GCMarker *gcmarker, HeapValue *begin, HeapValue *end)
|
||||
{
|
||||
for (HeapValue *vp = begin; vp != end; ++vp) {
|
||||
const Value &v = *vp;
|
||||
Cell *cell;
|
||||
uint32 color;
|
||||
if (v.isString()) {
|
||||
cell = v.toString();
|
||||
color = BLACK;
|
||||
} else if (v.isObject()) {
|
||||
cell = &v.toObject();
|
||||
color = gcmarker->getMarkColor();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (cell->markIfUnmarked(color))
|
||||
gcmarker->delayMarkingChildren(cell);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ScanObject(GCMarker *gcmarker, JSObject *obj)
|
||||
static inline void
|
||||
PushValueArray(GCMarker *gcmarker, HeapValue *array, size_t size)
|
||||
{
|
||||
if (size != 0) {
|
||||
JS_ASSERT(array);
|
||||
HeapValue *end = array + size;
|
||||
if (!gcmarker->pushValueArray(array, end))
|
||||
DelayMarkingValueArray(gcmarker, array, end);
|
||||
}
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
ScanObjectWithoutSlots(GCMarker *gcmarker, JSObject *obj)
|
||||
{
|
||||
types::TypeObject *type = obj->typeFromGC();
|
||||
PushMarkStack(gcmarker, type);
|
||||
|
@ -824,54 +855,15 @@ ScanObject(GCMarker *gcmarker, JSObject *obj)
|
|||
Class *clasp = shape->getObjectClass();
|
||||
if (clasp->trace) {
|
||||
if (clasp == &ArrayClass) {
|
||||
if (obj->getDenseArrayInitializedLength() > LARGE_OBJECT_CHUNK_SIZE) {
|
||||
if (!gcmarker->largeStack.push(LargeMarkItem(obj)))
|
||||
clasp->trace(gcmarker, obj);
|
||||
} else {
|
||||
clasp->trace(gcmarker, obj);
|
||||
}
|
||||
PushValueArray(gcmarker,
|
||||
obj->getDenseArrayElements(),
|
||||
obj->getDenseArrayInitializedLength());
|
||||
} else {
|
||||
clasp->trace(gcmarker, obj);
|
||||
}
|
||||
}
|
||||
|
||||
if (shape->isNative()) {
|
||||
uint32 nslots = obj->slotSpan();
|
||||
if (nslots > LARGE_OBJECT_CHUNK_SIZE) {
|
||||
if (gcmarker->largeStack.push(LargeMarkItem(obj)))
|
||||
return;
|
||||
}
|
||||
|
||||
obj->scanSlots(gcmarker);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
ScanLargeObject(GCMarker *gcmarker, LargeMarkItem &item)
|
||||
{
|
||||
JSObject *obj = item.obj;
|
||||
|
||||
uintN start = item.markpos;
|
||||
uintN stop;
|
||||
uint32 capacity;
|
||||
if (obj->isDenseArray()) {
|
||||
capacity = obj->getDenseArrayInitializedLength();
|
||||
stop = JS_MIN(start + LARGE_OBJECT_CHUNK_SIZE, capacity);
|
||||
for (uintN i=stop; i>start; i--)
|
||||
ScanValue(gcmarker, obj->getDenseArrayElement(i-1));
|
||||
} else {
|
||||
JS_ASSERT(obj->isNative());
|
||||
capacity = obj->slotSpan();
|
||||
stop = JS_MIN(start + LARGE_OBJECT_CHUNK_SIZE, capacity);
|
||||
for (uintN i=stop; i>start; i--)
|
||||
ScanValue(gcmarker, obj->nativeGetSlot(i-1));
|
||||
}
|
||||
|
||||
if (stop == capacity)
|
||||
return true;
|
||||
|
||||
item.markpos += LARGE_OBJECT_CHUNK_SIZE;
|
||||
return false;
|
||||
return shape->isNative();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1071,35 +1063,104 @@ MarkChildren(JSTracer *trc, JSXML *xml)
|
|||
|
||||
} /* namespace gc */
|
||||
|
||||
inline void
|
||||
GCMarker::processMarkStackTop()
|
||||
{
|
||||
/*
|
||||
* The code uses explicit goto to eliminate the tail recursion that
|
||||
* compilers cannot optimize on their own.
|
||||
*/
|
||||
HeapValue *vp, *end;
|
||||
JSObject *obj;
|
||||
|
||||
uintptr_t addr = stack.pop();
|
||||
uintptr_t tag = addr & StackTagMask;
|
||||
if (tag == ValueArrayTag) {
|
||||
/*
|
||||
* We set ValueArrayTag to zero to avoid bit setting and clearing when
|
||||
* pushing and poping tagged value array pointers. This is the most
|
||||
* common stack operation as we push the array on the stack again when
|
||||
* we find the next unmarked object in the array.
|
||||
*/
|
||||
JS_STATIC_ASSERT(ValueArrayTag == 0);
|
||||
uintptr_t addr2 = stack.pop();
|
||||
JS_ASSERT(addr <= addr2);
|
||||
JS_ASSERT((addr2 - addr) % sizeof(Value) == 0);
|
||||
vp = reinterpret_cast<HeapValue *>(addr);
|
||||
end = reinterpret_cast<HeapValue *>(addr2);
|
||||
goto scan_value_array;
|
||||
}
|
||||
|
||||
addr &= ~StackTagMask;
|
||||
if (tag == ObjectTag) {
|
||||
obj = reinterpret_cast<JSObject *>(addr);
|
||||
goto scan_obj;
|
||||
} else if (tag == TypeTag) {
|
||||
ScanTypeObject(this, reinterpret_cast<types::TypeObject *>(addr));
|
||||
} else {
|
||||
JS_ASSERT(tag == XmlTag);
|
||||
MarkChildren(this, reinterpret_cast<JSXML *>(addr));
|
||||
}
|
||||
return;
|
||||
|
||||
scan_value_array:
|
||||
JS_ASSERT(vp < end);
|
||||
do {
|
||||
const Value &v = *vp++;
|
||||
if (v.isString()) {
|
||||
JSString *str = v.toString();
|
||||
if (str->markIfUnmarked())
|
||||
ScanString(this, str);
|
||||
} else if (v.isObject()) {
|
||||
obj = &v.toObject();
|
||||
if (obj->markIfUnmarked(getMarkColor())) {
|
||||
if (vp != end && !pushValueArray(vp, end))
|
||||
DelayMarkingValueArray(this, vp, end);
|
||||
goto scan_obj;
|
||||
}
|
||||
}
|
||||
} while (vp != end);
|
||||
return;
|
||||
|
||||
scan_obj:
|
||||
if (ScanObjectWithoutSlots(this, obj)) {
|
||||
unsigned nslots = obj->slotSpan();
|
||||
vp = obj->fixedSlots();
|
||||
if (obj->slots) {
|
||||
unsigned nfixed = obj->numFixedSlots();
|
||||
if (nslots > nfixed) {
|
||||
PushValueArray(this, vp, nfixed);
|
||||
vp = obj->slots;
|
||||
end = vp + (nslots - nfixed);
|
||||
goto scan_value_array;
|
||||
}
|
||||
}
|
||||
if (nslots) {
|
||||
end = vp + nslots;
|
||||
goto scan_value_array;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::drainMarkStack()
|
||||
{
|
||||
JSRuntime *rt = runtime;
|
||||
rt->gcCheckCompartment = rt->gcCurrentCompartment;
|
||||
|
||||
while (!isMarkStackEmpty()) {
|
||||
while (!objStack.isEmpty())
|
||||
ScanObject(this, static_cast<JSObject *>(objStack.pop()));
|
||||
for (;;) {
|
||||
while (!stack.isEmpty())
|
||||
processMarkStackTop();
|
||||
if (!hasDelayedChildren())
|
||||
break;
|
||||
|
||||
while (!typeStack.isEmpty())
|
||||
ScanTypeObject(this, typeStack.pop());
|
||||
|
||||
while (!xmlStack.isEmpty())
|
||||
MarkChildren(this, xmlStack.pop());
|
||||
|
||||
if (!largeStack.isEmpty()) {
|
||||
LargeMarkItem &item = largeStack.peek();
|
||||
if (ScanLargeObject(this, item))
|
||||
largeStack.pop();
|
||||
}
|
||||
|
||||
if (isMarkStackEmpty()) {
|
||||
/*
|
||||
* Mark children of things that caused too deep recursion during the above
|
||||
* tracing. Don't do this until we're done with everything else.
|
||||
*/
|
||||
markDelayedChildren();
|
||||
}
|
||||
/*
|
||||
* Mark children of things that caused too deep recursion during the
|
||||
* above tracing. Don't do this until we're done with everything
|
||||
* else.
|
||||
*/
|
||||
markDelayedChildren();
|
||||
}
|
||||
|
||||
rt->gcCheckCompartment = NULL;
|
||||
|
@ -1149,29 +1210,3 @@ CallTracer(JSTracer *trc, void *thing, JSGCTraceKind kind)
|
|||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
inline void
|
||||
JSObject::scanSlots(GCMarker *gcmarker)
|
||||
{
|
||||
/*
|
||||
* Scan the fixed slots and the dynamic slots separately, to avoid
|
||||
* branching inside nativeGetSlot().
|
||||
*/
|
||||
unsigned i, nslots = slotSpan();
|
||||
if (slots) {
|
||||
unsigned nfixed = numFixedSlots();
|
||||
if (nslots > nfixed) {
|
||||
HeapValue *vp = fixedSlots();
|
||||
for (i = 0; i < nfixed; i++, vp++)
|
||||
ScanValue(gcmarker, *vp);
|
||||
vp = slots;
|
||||
for (; i < nslots; i++, vp++)
|
||||
ScanValue(gcmarker, *vp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
JS_ASSERT(nslots <= numFixedSlots());
|
||||
HeapValue *vp = fixedSlots();
|
||||
for (i = 0; i < nslots; i++, vp++)
|
||||
ScanValue(gcmarker, *vp);
|
||||
}
|
||||
|
|
|
@ -457,6 +457,7 @@ struct JSObject : js::gc::Cell
|
|||
{
|
||||
private:
|
||||
friend struct js::Shape;
|
||||
friend struct js::GCMarker;
|
||||
|
||||
/*
|
||||
* Shape of the object, encodes the layout of the object's properties and
|
||||
|
@ -546,8 +547,6 @@ struct JSObject : js::gc::Cell
|
|||
inline bool hasClass(const js::Class *c) const;
|
||||
inline const js::ObjectOps *getOps() const;
|
||||
|
||||
inline void scanSlots(js::GCMarker *gcmarker);
|
||||
|
||||
/*
|
||||
* An object is a delegate if it is on another object's prototype or scope
|
||||
* chain, and therefore the delegate might be asked implicitly to get or
|
||||
|
|
|
@ -1309,8 +1309,8 @@ InternalConst(JSContext *cx, uintN argc, jsval *vp)
|
|||
if (!flat)
|
||||
return false;
|
||||
|
||||
if (JS_FlatStringEqualsAscii(flat, "OBJECT_MARK_STACK_LENGTH")) {
|
||||
vp[0] = UINT_TO_JSVAL(js::OBJECT_MARK_STACK_SIZE / sizeof(JSObject *));
|
||||
if (JS_FlatStringEqualsAscii(flat, "MARK_STACK_LENGTH")) {
|
||||
vp[0] = UINT_TO_JSVAL(js::MARK_STACK_LENGTH);
|
||||
} else {
|
||||
JS_ReportError(cx, "unknown const name");
|
||||
return false;
|
||||
|
|
Загрузка…
Ссылка в новой задаче