diff -r 000000000000 -r 6474c204b198 js/src/gc/RootMarking.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/js/src/gc/RootMarking.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,812 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#ifdef MOZ_VALGRIND +# include +#endif + +#include "jscntxt.h" +#include "jsgc.h" +#include "jsonparser.h" +#include "jsprf.h" +#include "jstypes.h" +#include "jswatchpoint.h" + +#include "builtin/MapObject.h" +#include "frontend/BytecodeCompiler.h" +#include "gc/GCInternals.h" +#include "gc/Marking.h" +#ifdef JS_ION +# include "jit/IonMacroAssembler.h" +#endif +#include "js/HashTable.h" +#include "vm/Debugger.h" + +#include "jsgcinlines.h" +#include "jsobjinlines.h" + +using namespace js; +using namespace js::gc; + +using mozilla::ArrayEnd; + +typedef RootedValueMap::Range RootRange; +typedef RootedValueMap::Entry RootEntry; +typedef RootedValueMap::Enum RootEnum; + +#ifdef JSGC_USE_EXACT_ROOTING +static inline void +MarkExactStackRoot(JSTracer *trc, Rooted *rooter, ThingRootKind kind) +{ + void **addr = (void **)rooter->address(); + if (IsNullTaggedPointer(*addr)) + return; + + if (kind == THING_ROOT_OBJECT && *addr == TaggedProto::LazyProto) + return; + + switch (kind) { + case THING_ROOT_OBJECT: MarkObjectRoot(trc, (JSObject **)addr, "exact-object"); break; + case THING_ROOT_STRING: MarkStringRoot(trc, (JSString **)addr, "exact-string"); break; + case THING_ROOT_SCRIPT: MarkScriptRoot(trc, (JSScript **)addr, "exact-script"); break; + case THING_ROOT_LAZY_SCRIPT: MarkLazyScriptRoot(trc, (LazyScript **)addr, "exact-lazy-script"); break; + case THING_ROOT_SHAPE: MarkShapeRoot(trc, (Shape **)addr, "exact-shape"); break; + case THING_ROOT_BASE_SHAPE: MarkBaseShapeRoot(trc, (BaseShape **)addr, "exact-baseshape"); break; + case THING_ROOT_TYPE: MarkTypeRoot(trc, (types::Type *)addr, "exact-type"); break; + case THING_ROOT_TYPE_OBJECT: MarkTypeObjectRoot(trc, (types::TypeObject **)addr, "exact-typeobject"); break; + case THING_ROOT_JIT_CODE: MarkJitCodeRoot(trc, (jit::JitCode **)addr, "exact-jitcode"); break; + case THING_ROOT_VALUE: MarkValueRoot(trc, (Value *)addr, "exact-value"); break; + case THING_ROOT_ID: MarkIdRoot(trc, (jsid *)addr, "exact-id"); break; + case THING_ROOT_BINDINGS: ((Bindings *)addr)->trace(trc); break; + case THING_ROOT_PROPERTY_DESCRIPTOR: ((JSPropertyDescriptor *)addr)->trace(trc); break; + case THING_ROOT_CUSTOM: { + // 'rooter' is a member within a class containing a vtable. Back up + // to the vtable and call trace() through it. + const size_t rooterOffset = offsetof(RootedGeneric, rooter); + reinterpret_cast< RootedGeneric* >(uintptr_t(rooter) - rooterOffset)->trace(trc); + break; + } + default: MOZ_ASSUME_UNREACHABLE("Invalid THING_ROOT kind"); break; + } +} + +static inline void +MarkExactStackRootList(JSTracer *trc, Rooted *rooter, ThingRootKind kind) +{ + while (rooter) { + MarkExactStackRoot(trc, rooter, kind); + rooter = rooter->previous(); + } +} + +static void +MarkExactStackRoots(JSTracer *trc) +{ + for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) { + for (ContextIter cx(trc->runtime()); !cx.done(); cx.next()) + MarkExactStackRootList(trc, cx->thingGCRooters[i], ThingRootKind(i)); + + MarkExactStackRootList(trc, trc->runtime()->mainThread.thingGCRooters[i], ThingRootKind(i)); + } +} +#endif /* JSGC_USE_EXACT_ROOTING */ + +enum ConservativeGCTest +{ + CGCT_VALID, + CGCT_LOWBITSET, /* excluded because one of the low bits was set */ + CGCT_NOTARENA, /* not within arena range in a chunk */ + CGCT_OTHERCOMPARTMENT, /* in another compartment */ + CGCT_NOTCHUNK, /* not within a valid chunk */ + CGCT_FREEARENA, /* within arena containing only free things */ + CGCT_NOTLIVE, /* gcthing is not allocated */ + CGCT_END +}; + +/* + * Tests whether w is a (possibly dead) GC thing. Returns CGCT_VALID and + * details about the thing if so. On failure, returns the reason for rejection. + */ +static inline ConservativeGCTest +IsAddressableGCThing(JSRuntime *rt, uintptr_t w, + bool skipUncollectedCompartments, + gc::AllocKind *thingKindPtr, + ArenaHeader **arenaHeader, + void **thing) +{ + /* + * We assume that the compiler never uses sub-word alignment to store + * pointers and does not tag pointers on its own. Additionally, the value + * representation for all values and the jsid representation for GC-things + * do not touch the low two bits. Thus any word with the low two bits set + * is not a valid GC-thing. + */ + JS_STATIC_ASSERT(JSID_TYPE_STRING == 0 && JSID_TYPE_OBJECT == 4); + if (w & 0x3) + return CGCT_LOWBITSET; + + /* + * An object jsid has its low bits tagged. In the value representation on + * 64-bit, the high bits are tagged. + */ + const uintptr_t JSID_PAYLOAD_MASK = ~uintptr_t(JSID_TYPE_MASK); +#if JS_BITS_PER_WORD == 32 + uintptr_t addr = w & JSID_PAYLOAD_MASK; +#elif JS_BITS_PER_WORD == 64 + uintptr_t addr = w & JSID_PAYLOAD_MASK & JSVAL_PAYLOAD_MASK; +#endif + + Chunk *chunk = Chunk::fromAddress(addr); + + if (!rt->gcChunkSet.has(chunk)) + return CGCT_NOTCHUNK; + + /* + * We query for pointers outside the arena array after checking for an + * allocated chunk. Such pointers are rare and we want to reject them + * after doing more likely rejections. + */ + if (!Chunk::withinArenasRange(addr)) + return CGCT_NOTARENA; + + /* If the arena is not currently allocated, don't access the header. */ + size_t arenaOffset = Chunk::arenaIndex(addr); + if (chunk->decommittedArenas.get(arenaOffset)) + return CGCT_FREEARENA; + + ArenaHeader *aheader = &chunk->arenas[arenaOffset].aheader; + + if (!aheader->allocated()) + return CGCT_FREEARENA; + + if (skipUncollectedCompartments && !aheader->zone->isCollecting()) + return CGCT_OTHERCOMPARTMENT; + + AllocKind thingKind = aheader->getAllocKind(); + uintptr_t offset = addr & ArenaMask; + uintptr_t minOffset = Arena::firstThingOffset(thingKind); + if (offset < minOffset) + return CGCT_NOTARENA; + + /* addr can point inside the thing so we must align the address. */ + uintptr_t shift = (offset - minOffset) % Arena::thingSize(thingKind); + addr -= shift; + + if (thing) + *thing = reinterpret_cast(addr); + if (arenaHeader) + *arenaHeader = aheader; + if (thingKindPtr) + *thingKindPtr = thingKind; + return CGCT_VALID; +} + +/* + * Returns CGCT_VALID and mark it if the w can be a live GC thing and sets + * thingKind accordingly. Otherwise returns the reason for rejection. + */ +static inline ConservativeGCTest +MarkIfGCThingWord(JSTracer *trc, uintptr_t w) +{ + void *thing; + ArenaHeader *aheader; + AllocKind thingKind; + ConservativeGCTest status = + IsAddressableGCThing(trc->runtime(), w, IS_GC_MARKING_TRACER(trc), + &thingKind, &aheader, &thing); + if (status != CGCT_VALID) + return status; + + /* + * Check if the thing is free. We must use the list of free spans as at + * this point we no longer have the mark bits from the previous GC run and + * we must account for newly allocated things. + */ + if (InFreeList(aheader, thing)) + return CGCT_NOTLIVE; + + JSGCTraceKind traceKind = MapAllocToTraceKind(thingKind); +#ifdef DEBUG + const char pattern[] = "machine_stack %p"; + char nameBuf[sizeof(pattern) - 2 + sizeof(thing) * 2]; + JS_snprintf(nameBuf, sizeof(nameBuf), pattern, thing); + trc->setTracingName(nameBuf); +#endif + trc->setTracingLocation((void *)w); + void *tmp = thing; + MarkKind(trc, &tmp, traceKind); + JS_ASSERT(tmp == thing); + +#ifdef DEBUG + if (trc->runtime()->gcIncrementalState == MARK_ROOTS) + trc->runtime()->mainThread.gcSavedRoots.append( + PerThreadData::SavedGCRoot(thing, traceKind)); +#endif + + return CGCT_VALID; +} + +#ifndef JSGC_USE_EXACT_ROOTING +static void +MarkWordConservatively(JSTracer *trc, uintptr_t w) +{ + /* + * The conservative scanner may access words that valgrind considers as + * undefined. To avoid false positives and not to alter valgrind view of + * the memory we make as memcheck-defined the argument, a copy of the + * original word. See bug 572678. + */ +#ifdef MOZ_VALGRIND + JS_SILENCE_UNUSED_VALUE_IN_EXPR(VALGRIND_MAKE_MEM_DEFINED(&w, sizeof(w))); +#endif + + MarkIfGCThingWord(trc, w); +} + +MOZ_ASAN_BLACKLIST +static void +MarkRangeConservatively(JSTracer *trc, const uintptr_t *begin, const uintptr_t *end) +{ + JS_ASSERT(begin <= end); + for (const uintptr_t *i = begin; i < end; ++i) + MarkWordConservatively(trc, *i); +} + +static void +MarkRangeConservativelyAndSkipIon(JSTracer *trc, JSRuntime *rt, const uintptr_t *begin, const uintptr_t *end) +{ + const uintptr_t *i = begin; + +#if JS_STACK_GROWTH_DIRECTION < 0 && defined(JS_ION) && !defined(JS_ARM_SIMULATOR) + // Walk only regions in between JIT activations. Note that non-volatile + // registers are spilled to the stack before the entry frame, ensuring + // that the conservative scanner will still see them. + // + // If the ARM simulator is enabled, JIT activations are not on the native + // stack but on the simulator stack, so we don't have to skip JIT regions + // in this case. + for (jit::JitActivationIterator iter(rt); !iter.done(); ++iter) { + uintptr_t *jitMin, *jitEnd; + iter.jitStackRange(jitMin, jitEnd); + + MarkRangeConservatively(trc, i, jitMin); + i = jitEnd; + } +#endif + + // Mark everything after the most recent Ion activation. + MarkRangeConservatively(trc, i, end); +} + +static MOZ_NEVER_INLINE void +MarkConservativeStackRoots(JSTracer *trc, bool useSavedRoots) +{ + JSRuntime *rt = trc->runtime(); + +#ifdef DEBUG + if (useSavedRoots) { + for (PerThreadData::SavedGCRoot *root = rt->mainThread.gcSavedRoots.begin(); + root != rt->mainThread.gcSavedRoots.end(); + root++) + { + trc->setTracingName("cstack"); + MarkKind(trc, &root->thing, root->kind); + } + return; + } + + if (rt->gcIncrementalState == MARK_ROOTS) + rt->mainThread.gcSavedRoots.clearAndFree(); +#endif + + ConservativeGCData *cgcd = &rt->conservativeGC; + if (!cgcd->hasStackToScan()) { +#ifdef JS_THREADSAFE + JS_ASSERT(!rt->requestDepth); +#endif + return; + } + + uintptr_t *stackMin, *stackEnd; +#if JS_STACK_GROWTH_DIRECTION > 0 + stackMin = reinterpret_cast(rt->nativeStackBase); + stackEnd = cgcd->nativeStackTop; +#else + stackMin = cgcd->nativeStackTop + 1; + stackEnd = reinterpret_cast(rt->nativeStackBase); +#endif + + JS_ASSERT(stackMin <= stackEnd); + MarkRangeConservativelyAndSkipIon(trc, rt, stackMin, stackEnd); + MarkRangeConservatively(trc, cgcd->registerSnapshot.words, + ArrayEnd(cgcd->registerSnapshot.words)); +} + +void +js::MarkStackRangeConservatively(JSTracer *trc, Value *beginv, Value *endv) +{ + const uintptr_t *begin = beginv->payloadUIntPtr(); + const uintptr_t *end = endv->payloadUIntPtr(); +#ifdef JS_NUNBOX32 + /* + * With 64-bit jsvals on 32-bit systems, we can optimize a bit by + * scanning only the payloads. + */ + JS_ASSERT(begin <= end); + for (const uintptr_t *i = begin; i < end; i += sizeof(Value) / sizeof(uintptr_t)) + MarkWordConservatively(trc, *i); +#else + MarkRangeConservatively(trc, begin, end); +#endif +} + +#endif /* JSGC_USE_EXACT_ROOTING */ + +MOZ_NEVER_INLINE void +ConservativeGCData::recordStackTop() +{ + /* Update the native stack pointer if it points to a bigger stack. */ + uintptr_t dummy; + nativeStackTop = &dummy; + + /* + * To record and update the register snapshot for the conservative scanning + * with the latest values we use setjmp. + */ +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4611) +#endif + (void) setjmp(registerSnapshot.jmpbuf); +#if defined(_MSC_VER) +# pragma warning(pop) +#endif +} + +void +JS::AutoIdArray::trace(JSTracer *trc) +{ + JS_ASSERT(tag_ == IDARRAY); + gc::MarkIdRange(trc, idArray->length, idArray->vector, "JSAutoIdArray.idArray"); +} + +inline void +AutoGCRooter::trace(JSTracer *trc) +{ + switch (tag_) { + case PARSER: + frontend::MarkParser(trc, this); + return; + + case IDARRAY: { + JSIdArray *ida = static_cast(this)->idArray; + MarkIdRange(trc, ida->length, ida->vector, "JS::AutoIdArray.idArray"); + return; + } + + case DESCRIPTORS: { + PropDescArray &descriptors = + static_cast(this)->descriptors; + for (size_t i = 0, len = descriptors.length(); i < len; i++) { + PropDesc &desc = descriptors[i]; + MarkValueRoot(trc, &desc.pd_, "PropDesc::pd_"); + MarkValueRoot(trc, &desc.value_, "PropDesc::value_"); + MarkValueRoot(trc, &desc.get_, "PropDesc::get_"); + MarkValueRoot(trc, &desc.set_, "PropDesc::set_"); + } + return; + } + + case ID: + MarkIdRoot(trc, &static_cast(this)->id_, "JS::AutoIdRooter.id_"); + return; + + case VALVECTOR: { + AutoValueVector::VectorImpl &vector = static_cast(this)->vector; + MarkValueRootRange(trc, vector.length(), vector.begin(), "js::AutoValueVector.vector"); + return; + } + + case IDVECTOR: { + AutoIdVector::VectorImpl &vector = static_cast(this)->vector; + MarkIdRootRange(trc, vector.length(), vector.begin(), "js::AutoIdVector.vector"); + return; + } + + case SHAPEVECTOR: { + AutoShapeVector::VectorImpl &vector = static_cast(this)->vector; + MarkShapeRootRange(trc, vector.length(), const_cast(vector.begin()), + "js::AutoShapeVector.vector"); + return; + } + + case OBJVECTOR: { + AutoObjectVector::VectorImpl &vector = static_cast(this)->vector; + MarkObjectRootRange(trc, vector.length(), vector.begin(), "js::AutoObjectVector.vector"); + return; + } + + case FUNVECTOR: { + AutoFunctionVector::VectorImpl &vector = static_cast(this)->vector; + MarkObjectRootRange(trc, vector.length(), vector.begin(), "js::AutoFunctionVector.vector"); + return; + } + + case STRINGVECTOR: { + AutoStringVector::VectorImpl &vector = static_cast(this)->vector; + MarkStringRootRange(trc, vector.length(), vector.begin(), "js::AutoStringVector.vector"); + return; + } + + case NAMEVECTOR: { + AutoNameVector::VectorImpl &vector = static_cast(this)->vector; + MarkStringRootRange(trc, vector.length(), vector.begin(), "js::AutoNameVector.vector"); + return; + } + + case VALARRAY: { + /* + * We don't know the template size parameter, but we can safely treat it + * as an AutoValueArray<1> because the length is stored separately. + */ + AutoValueArray<1> *array = static_cast *>(this); + MarkValueRootRange(trc, array->length(), array->begin(), "js::AutoValueArray"); + return; + } + + case SCRIPTVECTOR: { + AutoScriptVector::VectorImpl &vector = static_cast(this)->vector; + MarkScriptRootRange(trc, vector.length(), vector.begin(), "js::AutoScriptVector.vector"); + return; + } + + case OBJOBJHASHMAP: { + AutoObjectObjectHashMap::HashMapImpl &map = static_cast(this)->map; + for (AutoObjectObjectHashMap::Enum e(map); !e.empty(); e.popFront()) { + MarkObjectRoot(trc, &e.front().value(), "AutoObjectObjectHashMap value"); + trc->setTracingLocation((void *)&e.front().key()); + JSObject *key = e.front().key(); + MarkObjectRoot(trc, &key, "AutoObjectObjectHashMap key"); + if (key != e.front().key()) + e.rekeyFront(key); + } + return; + } + + case OBJU32HASHMAP: { + AutoObjectUnsigned32HashMap *self = static_cast(this); + AutoObjectUnsigned32HashMap::HashMapImpl &map = self->map; + for (AutoObjectUnsigned32HashMap::Enum e(map); !e.empty(); e.popFront()) { + JSObject *key = e.front().key(); + MarkObjectRoot(trc, &key, "AutoObjectUnsignedHashMap key"); + if (key != e.front().key()) + e.rekeyFront(key); + } + return; + } + + case OBJHASHSET: { + AutoObjectHashSet *self = static_cast(this); + AutoObjectHashSet::HashSetImpl &set = self->set; + for (AutoObjectHashSet::Enum e(set); !e.empty(); e.popFront()) { + JSObject *obj = e.front(); + MarkObjectRoot(trc, &obj, "AutoObjectHashSet value"); + if (obj != e.front()) + e.rekeyFront(obj); + } + return; + } + + case HASHABLEVALUE: { + AutoHashableValueRooter *rooter = static_cast(this); + rooter->trace(trc); + return; + } + + case IONMASM: { +#ifdef JS_ION + static_cast(this)->masm()->trace(trc); +#endif + return; + } + + case IONALLOC: { +#ifdef JS_ION + static_cast(this)->trace(trc); +#endif + return; + } + + case WRAPPER: { + /* + * We need to use MarkValueUnbarriered here because we mark wrapper + * roots in every slice. This is because of some rule-breaking in + * RemapAllWrappersForObject; see comment there. + */ + MarkValueUnbarriered(trc, &static_cast(this)->value.get(), + "JS::AutoWrapperRooter.value"); + return; + } + + case WRAPVECTOR: { + AutoWrapperVector::VectorImpl &vector = static_cast(this)->vector; + /* + * We need to use MarkValueUnbarriered here because we mark wrapper + * roots in every slice. This is because of some rule-breaking in + * RemapAllWrappersForObject; see comment there. + */ + for (WrapperValue *p = vector.begin(); p < vector.end(); p++) + MarkValueUnbarriered(trc, &p->get(), "js::AutoWrapperVector.vector"); + return; + } + + case JSONPARSER: + static_cast(this)->trace(trc); + return; + + case CUSTOM: + static_cast(this)->trace(trc); + return; + } + + JS_ASSERT(tag_ >= 0); + if (Value *vp = static_cast(this)->array) + MarkValueRootRange(trc, tag_, vp, "JS::AutoArrayRooter.array"); +} + +/* static */ void +AutoGCRooter::traceAll(JSTracer *trc) +{ + for (ContextIter cx(trc->runtime()); !cx.done(); cx.next()) { + for (js::AutoGCRooter *gcr = cx->autoGCRooters; gcr; gcr = gcr->down) + gcr->trace(trc); + } +} + +/* static */ void +AutoGCRooter::traceAllWrappers(JSTracer *trc) +{ + for (ContextIter cx(trc->runtime()); !cx.done(); cx.next()) { + for (js::AutoGCRooter *gcr = cx->autoGCRooters; gcr; gcr = gcr->down) { + if (gcr->tag_ == WRAPVECTOR || gcr->tag_ == WRAPPER) + gcr->trace(trc); + } + } +} + +void +AutoHashableValueRooter::trace(JSTracer *trc) +{ + MarkValueRoot(trc, reinterpret_cast(&value), "AutoHashableValueRooter"); +} + +void +StackShape::trace(JSTracer *trc) +{ + if (base) + MarkBaseShapeRoot(trc, (BaseShape**) &base, "StackShape base"); + MarkIdRoot(trc, (jsid*) &propid, "StackShape id"); +} + +void +JSPropertyDescriptor::trace(JSTracer *trc) +{ + if (obj) + MarkObjectRoot(trc, &obj, "Descriptor::obj"); + MarkValueRoot(trc, &value, "Descriptor::value"); + if ((attrs & JSPROP_GETTER) && getter) { + JSObject *tmp = JS_FUNC_TO_DATA_PTR(JSObject *, getter); + MarkObjectRoot(trc, &tmp, "Descriptor::get"); + getter = JS_DATA_TO_FUNC_PTR(JSPropertyOp, tmp); + } + if ((attrs & JSPROP_SETTER) && setter) { + JSObject *tmp = JS_FUNC_TO_DATA_PTR(JSObject *, setter); + MarkObjectRoot(trc, &tmp, "Descriptor::set"); + setter = JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, tmp); + } +} + +namespace js { +namespace gc { + +template +struct PersistentRootedMarker +{ + typedef PersistentRooted Element; + typedef mozilla::LinkedList List; + typedef void (*MarkFunc)(JSTracer *trc, T *ref, const char *name); + + template + static void + markChainIfNotNull(JSTracer *trc, List &list, const char *name) + { + for (Element *r = list.getFirst(); r; r = r->getNext()) { + if (r->get()) + Mark(trc, r->address(), name); + } + } + + template + static void + markChain(JSTracer *trc, List &list, const char *name) + { + for (Element *r = list.getFirst(); r; r = r->getNext()) + Mark(trc, r->address(), name); + } +}; +} +} + +void +js::gc::MarkPersistentRootedChains(JSTracer *trc) +{ + JSRuntime *rt = trc->runtime(); + + // Mark the PersistentRooted chains of types that may be null. + PersistentRootedMarker::markChainIfNotNull( + trc, rt->functionPersistentRooteds, "PersistentRooted"); + PersistentRootedMarker::markChainIfNotNull( + trc, rt->objectPersistentRooteds, "PersistentRooted"); + PersistentRootedMarker::markChainIfNotNull( + trc, rt->scriptPersistentRooteds, "PersistentRooted"); + PersistentRootedMarker::markChainIfNotNull( + trc, rt->stringPersistentRooteds, "PersistentRooted"); + + // Mark the PersistentRooted chains of types that are never null. + PersistentRootedMarker::markChain(trc, rt->idPersistentRooteds, + "PersistentRooted"); + PersistentRootedMarker::markChain(trc, rt->valuePersistentRooteds, + "PersistentRooted"); +} + +void +js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots) +{ + JSRuntime *rt = trc->runtime(); + JS_ASSERT(trc->callback != GCMarker::GrayCallback); + + JS_ASSERT(!rt->mainThread.suppressGC); + + if (IS_GC_MARKING_TRACER(trc)) { + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { + if (!c->zone()->isCollecting()) + c->markCrossCompartmentWrappers(trc); + } + Debugger::markCrossCompartmentDebuggerObjectReferents(trc); + } + + AutoGCRooter::traceAll(trc); + + if (!rt->isBeingDestroyed()) { +#ifdef JSGC_USE_EXACT_ROOTING + MarkExactStackRoots(trc); +#else + MarkConservativeStackRoots(trc, useSavedRoots); +#endif + rt->markSelfHostingGlobal(trc); + } + + for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront()) { + const RootEntry &entry = r.front(); + const char *name = entry.value().name ? entry.value().name : "root"; + JSGCRootType type = entry.value().type; + void *key = entry.key(); + if (type == JS_GC_ROOT_VALUE_PTR) { + MarkValueRoot(trc, reinterpret_cast(key), name); + } else if (*reinterpret_cast(key)){ + if (type == JS_GC_ROOT_STRING_PTR) + MarkStringRoot(trc, reinterpret_cast(key), name); + else if (type == JS_GC_ROOT_OBJECT_PTR) + MarkObjectRoot(trc, reinterpret_cast(key), name); + else if (type == JS_GC_ROOT_SCRIPT_PTR) + MarkScriptRoot(trc, reinterpret_cast(key), name); + else + MOZ_ASSUME_UNREACHABLE("unexpected js::RootInfo::type value"); + } + } + + MarkPersistentRootedChains(trc); + + if (rt->scriptAndCountsVector) { + ScriptAndCountsVector &vec = *rt->scriptAndCountsVector; + for (size_t i = 0; i < vec.length(); i++) + MarkScriptRoot(trc, &vec[i].script, "scriptAndCountsVector"); + } + + if (!rt->isBeingDestroyed() && !trc->runtime()->isHeapMinorCollecting()) { + if (!IS_GC_MARKING_TRACER(trc) || rt->atomsCompartment()->zone()->isCollecting()) { + MarkPermanentAtoms(trc); + MarkAtoms(trc); +#ifdef JS_ION + jit::JitRuntime::Mark(trc); +#endif + } + } + + for (ContextIter acx(rt); !acx.done(); acx.next()) + acx->mark(trc); + + for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { + if (IS_GC_MARKING_TRACER(trc) && !zone->isCollecting()) + continue; + + /* Do not discard scripts with counts while profiling. */ + if (rt->profilingScripts && !rt->isHeapMinorCollecting()) { + for (CellIterUnderGC i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + if (script->hasScriptCounts()) { + MarkScriptRoot(trc, &script, "profilingScripts"); + JS_ASSERT(script == i.get()); + } + } + } + } + + /* We can't use GCCompartmentsIter if we're called from TraceRuntime. */ + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { + if (trc->runtime()->isHeapMinorCollecting()) + c->globalWriteBarriered = false; + + if (IS_GC_MARKING_TRACER(trc) && !c->zone()->isCollecting()) + continue; + + /* During a GC, these are treated as weak pointers. */ + if (!IS_GC_MARKING_TRACER(trc)) { + if (c->watchpointMap) + c->watchpointMap->markAll(trc); + } + + /* Mark debug scopes, if present */ + if (c->debugScopes) + c->debugScopes->mark(trc); + } + + MarkInterpreterActivations(rt, trc); + +#ifdef JS_ION + jit::MarkJitActivations(rt, trc); +#endif + + if (!rt->isHeapMinorCollecting()) { + /* + * All JSCompartment::mark does is mark the globals for compartments + * which have been entered. Globals aren't nursery allocated so there's + * no need to do this for minor GCs. + */ + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) + c->markRoots(trc); + + /* + * The embedding can register additional roots here. + * + * We don't need to trace these in a minor GC because all pointers into + * the nursery should be in the store buffer, and we want to avoid the + * time taken to trace all these roots. + */ + for (size_t i = 0; i < rt->gcBlackRootTracers.length(); i++) { + const JSRuntime::ExtraTracer &e = rt->gcBlackRootTracers[i]; + (*e.op)(trc, e.data); + } + + /* During GC, we don't mark gray roots at this stage. */ + if (JSTraceDataOp op = rt->gcGrayRootTracer.op) { + if (!IS_GC_MARKING_TRACER(trc)) + (*op)(trc, rt->gcGrayRootTracer.data); + } + } +} + +void +js::gc::BufferGrayRoots(GCMarker *gcmarker) +{ + JSRuntime *rt = gcmarker->runtime(); + gcmarker->startBufferingGrayRoots(); + if (JSTraceDataOp op = rt->gcGrayRootTracer.op) + (*op)(gcmarker, rt->gcGrayRootTracer.data); + gcmarker->endBufferingGrayRoots(); +}