js/src/gc/Marking.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "gc/Marking.h"
     9 #include "mozilla/DebugOnly.h"
    11 #include "jit/IonCode.h"
    12 #include "js/SliceBudget.h"
    13 #include "vm/ArgumentsObject.h"
    14 #include "vm/ScopeObject.h"
    15 #include "vm/Shape.h"
    16 #include "vm/TypedArrayObject.h"
    18 #include "jscompartmentinlines.h"
    19 #include "jsinferinlines.h"
    20 #include "jsobjinlines.h"
    22 #ifdef JSGC_GENERATIONAL
    23 # include "gc/Nursery-inl.h"
    24 #endif
    25 #include "vm/String-inl.h"
    27 using namespace js;
    28 using namespace js::gc;
    30 using mozilla::DebugOnly;
    32 void * const js::NullPtr::constNullValue = nullptr;
    34 JS_PUBLIC_DATA(void * const) JS::NullPtr::constNullValue = nullptr;
    36 /*
    37  * There are two mostly separate mark paths. The first is a fast path used
    38  * internally in the GC. The second is a slow path used for root marking and
    39  * for API consumers like the cycle collector or Class::trace implementations.
    40  *
    41  * The fast path uses explicit stacks. The basic marking process during a GC is
    42  * that all roots are pushed on to a mark stack, and then each item on the
    43  * stack is scanned (possibly pushing more stuff) until the stack is empty.
    44  *
    45  * PushMarkStack pushes a GC thing onto the mark stack. In some cases (shapes
    46  * or strings) it eagerly marks the object rather than pushing it. Popping and
    47  * scanning is done by the processMarkStackTop method. For efficiency reasons
    48  * like tail recursion elimination that method also implements the scanning of
    49  * objects. For other GC things it uses helper methods.
    50  *
    51  * Most of the marking code outside Marking.cpp uses functions like MarkObject,
    52  * MarkString, etc. These functions check if an object is in the compartment
    53  * currently being GCed. If it is, they call PushMarkStack. Roots are pushed
    54  * this way as well as pointers traversed inside trace hooks (for things like
    55  * PropertyIteratorObjects). It is always valid to call a MarkX function
    56  * instead of PushMarkStack, although it may be slower.
    57  *
    58  * The MarkX functions also handle non-GC object traversal. In this case, they
    59  * call a callback for each object visited. This is a recursive process; the
    60  * mark stacks are not involved. These callbacks may ask for the outgoing
    61  * pointers to be visited. Eventually, this leads to the MarkChildren functions
    62  * being called. These functions duplicate much of the functionality of
    63  * scanning functions, but they don't push onto an explicit stack.
    64  */
    66 static inline void
    67 PushMarkStack(GCMarker *gcmarker, ObjectImpl *thing);
    69 static inline void
    70 PushMarkStack(GCMarker *gcmarker, JSFunction *thing);
    72 static inline void
    73 PushMarkStack(GCMarker *gcmarker, JSScript *thing);
    75 static inline void
    76 PushMarkStack(GCMarker *gcmarker, Shape *thing);
    78 static inline void
    79 PushMarkStack(GCMarker *gcmarker, JSString *thing);
    81 static inline void
    82 PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing);
    84 namespace js {
    85 namespace gc {
    87 static void MarkChildren(JSTracer *trc, JSString *str);
    88 static void MarkChildren(JSTracer *trc, JSScript *script);
    89 static void MarkChildren(JSTracer *trc, LazyScript *lazy);
    90 static void MarkChildren(JSTracer *trc, Shape *shape);
    91 static void MarkChildren(JSTracer *trc, BaseShape *base);
    92 static void MarkChildren(JSTracer *trc, types::TypeObject *type);
    93 static void MarkChildren(JSTracer *trc, jit::JitCode *code);
    95 } /* namespace gc */
    96 } /* namespace js */
    98 /*** Object Marking ***/
   100 #if defined(DEBUG)
   101 template<typename T>
   102 static inline bool
   103 IsThingPoisoned(T *thing)
   104 {
   105     static_assert(sizeof(T) >= sizeof(FreeSpan) + sizeof(uint32_t),
   106                   "Ensure it is well defined to look past any free span that "
   107                   "may be embedded in the thing's header when freed.");
   108     const uint8_t poisonBytes[] = {
   109         JS_FRESH_NURSERY_PATTERN,
   110         JS_SWEPT_NURSERY_PATTERN,
   111         JS_ALLOCATED_NURSERY_PATTERN,
   112         JS_FRESH_TENURED_PATTERN,
   113         JS_SWEPT_TENURED_PATTERN,
   114         JS_ALLOCATED_TENURED_PATTERN,
   115         JS_SWEPT_CODE_PATTERN,
   116         JS_SWEPT_FRAME_PATTERN
   117     };
   118     const int numPoisonBytes = sizeof(poisonBytes) / sizeof(poisonBytes[0]);
   119     uint32_t *p = reinterpret_cast<uint32_t *>(reinterpret_cast<FreeSpan *>(thing) + 1);
   120     // Note: all free patterns are odd to make the common, not-poisoned case a single test.
   121     if ((*p & 1) == 0)
   122         return false;
   123     for (int i = 0; i < numPoisonBytes; ++i) {
   124         const uint8_t pb = poisonBytes[i];
   125         const uint32_t pw = pb | (pb << 8) | (pb << 16) | (pb << 24);
   126         if (*p == pw)
   127             return true;
   128     }
   129     return false;
   130 }
   131 #endif
   133 static GCMarker *
   134 AsGCMarker(JSTracer *trc)
   135 {
   136     JS_ASSERT(IS_GC_MARKING_TRACER(trc));
   137     return static_cast<GCMarker *>(trc);
   138 }
   140 template <typename T> bool ThingIsPermanentAtom(T *thing) { return false; }
   141 template <> bool ThingIsPermanentAtom<JSString>(JSString *str) { return str->isPermanentAtom(); }
   142 template <> bool ThingIsPermanentAtom<JSFlatString>(JSFlatString *str) { return str->isPermanentAtom(); }
   143 template <> bool ThingIsPermanentAtom<JSLinearString>(JSLinearString *str) { return str->isPermanentAtom(); }
   144 template <> bool ThingIsPermanentAtom<JSAtom>(JSAtom *atom) { return atom->isPermanent(); }
   145 template <> bool ThingIsPermanentAtom<PropertyName>(PropertyName *name) { return name->isPermanent(); }
   147 template<typename T>
   148 static inline void
   149 CheckMarkedThing(JSTracer *trc, T *thing)
   150 {
   151 #ifdef DEBUG
   152     JS_ASSERT(trc);
   153     JS_ASSERT(thing);
   155     /* This function uses data that's not available in the nursery. */
   156     if (IsInsideNursery(trc->runtime(), thing))
   157         return;
   159     /*
   160      * Permanent atoms are not associated with this runtime, but will be ignored
   161      * during marking.
   162      */
   163     if (ThingIsPermanentAtom(thing))
   164         return;
   166     JS_ASSERT(thing->zone());
   167     JS_ASSERT(thing->zone()->runtimeFromMainThread() == trc->runtime());
   168     JS_ASSERT(trc->hasTracingDetails());
   170     DebugOnly<JSRuntime *> rt = trc->runtime();
   172     JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && rt->gcManipulatingDeadZones,
   173                  !thing->zone()->scheduledForDestruction);
   175     JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
   177     JS_ASSERT_IF(thing->zone()->requireGCTracer(),
   178                  IS_GC_MARKING_TRACER(trc));
   180     JS_ASSERT(thing->isAligned());
   182     JS_ASSERT(MapTypeToTraceKind<T>::kind == GetGCThingTraceKind(thing));
   184     JS_ASSERT_IF(rt->gcStrictCompartmentChecking,
   185                  thing->zone()->isCollecting() || rt->isAtomsZone(thing->zone()));
   187     JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && AsGCMarker(trc)->getMarkColor() == GRAY,
   188                  !thing->zone()->isGCMarkingBlack() || rt->isAtomsZone(thing->zone()));
   190     JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc),
   191                  !(thing->zone()->isGCSweeping() || thing->zone()->isGCFinished()));
   193     /*
   194      * Try to assert that the thing is allocated.  This is complicated by the
   195      * fact that allocated things may still contain the poison pattern if that
   196      * part has not been overwritten, and that the free span list head in the
   197      * ArenaHeader may not be synced with the real one in ArenaLists.
   198      */
   199     JS_ASSERT_IF(IsThingPoisoned(thing) && rt->isHeapBusy(),
   200                  !InFreeList(thing->arenaHeader(), thing));
   201 #endif
   202 }
   204 template<typename T>
   205 static void
   206 MarkInternal(JSTracer *trc, T **thingp)
   207 {
   208     JS_ASSERT(thingp);
   209     T *thing = *thingp;
   211     CheckMarkedThing(trc, thing);
   213     if (!trc->callback) {
   214         /*
   215          * We may mark a Nursery thing outside the context of the
   216          * MinorCollectionTracer because of a pre-barrier. The pre-barrier is
   217          * not needed in this case because we perform a minor collection before
   218          * each incremental slice.
   219          */
   220         if (IsInsideNursery(trc->runtime(), thing))
   221             return;
   223         /*
   224          * Don't mark permanent atoms, as they may be associated with another
   225          * runtime. Note that PushMarkStack() also checks this, but the tests
   226          * and maybeAlive write below should only be done on the main thread.
   227          */
   228         if (ThingIsPermanentAtom(thing))
   229             return;
   231         /*
   232          * Don't mark things outside a compartment if we are in a
   233          * per-compartment GC.
   234          */
   235         if (!thing->zone()->isGCMarking())
   236             return;
   238         PushMarkStack(AsGCMarker(trc), thing);
   239         thing->zone()->maybeAlive = true;
   240     } else {
   241         trc->callback(trc, (void **)thingp, MapTypeToTraceKind<T>::kind);
   242         trc->unsetTracingLocation();
   243     }
   245     trc->clearTracingDetails();
   246 }
   248 #define JS_ROOT_MARKING_ASSERT(trc)                                     \
   249     JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc),                             \
   250                  trc->runtime()->gcIncrementalState == NO_INCREMENTAL ||  \
   251                  trc->runtime()->gcIncrementalState == MARK_ROOTS);
   253 namespace js {
   254 namespace gc {
   256 template <typename T>
   257 void
   258 MarkUnbarriered(JSTracer *trc, T **thingp, const char *name)
   259 {
   260     trc->setTracingName(name);
   261     MarkInternal(trc, thingp);
   262 }
   264 template <typename T>
   265 static void
   266 Mark(JSTracer *trc, BarrieredPtr<T> *thing, const char *name)
   267 {
   268     trc->setTracingName(name);
   269     MarkInternal(trc, thing->unsafeGet());
   270 }
   272 void
   273 MarkPermanentAtom(JSTracer *trc, JSAtom *atom, const char *name)
   274 {
   275     trc->setTracingName(name);
   277     JS_ASSERT(atom->isPermanent());
   279     CheckMarkedThing(trc, atom);
   281     if (!trc->callback) {
   282         // Atoms do not refer to other GC things so don't need to go on the mark stack.
   283         // Additionally, PushMarkStack will ignore permanent atoms.
   284         atom->markIfUnmarked();
   285     } else {
   286         void *thing = atom;
   287         trc->callback(trc, &thing, JSTRACE_STRING);
   288         JS_ASSERT(thing == atom);
   289         trc->unsetTracingLocation();
   290     }
   292     trc->clearTracingDetails();
   293 }
   295 } /* namespace gc */
   296 } /* namespace js */
   298 template <typename T>
   299 static void
   300 MarkRoot(JSTracer *trc, T **thingp, const char *name)
   301 {
   302     JS_ROOT_MARKING_ASSERT(trc);
   303     trc->setTracingName(name);
   304     MarkInternal(trc, thingp);
   305 }
   307 template <typename T>
   308 static void
   309 MarkRange(JSTracer *trc, size_t len, HeapPtr<T> *vec, const char *name)
   310 {
   311     for (size_t i = 0; i < len; ++i) {
   312         if (vec[i].get()) {
   313             trc->setTracingIndex(name, i);
   314             MarkInternal(trc, vec[i].unsafeGet());
   315         }
   316     }
   317 }
   319 template <typename T>
   320 static void
   321 MarkRootRange(JSTracer *trc, size_t len, T **vec, const char *name)
   322 {
   323     JS_ROOT_MARKING_ASSERT(trc);
   324     for (size_t i = 0; i < len; ++i) {
   325         if (vec[i]) {
   326             trc->setTracingIndex(name, i);
   327             MarkInternal(trc, &vec[i]);
   328         }
   329     }
   330 }
   332 namespace js {
   333 namespace gc {
   335 template <typename T>
   336 static bool
   337 IsMarked(T **thingp)
   338 {
   339     JS_ASSERT(thingp);
   340     JS_ASSERT(*thingp);
   341 #ifdef JSGC_GENERATIONAL
   342     Nursery &nursery = (*thingp)->runtimeFromMainThread()->gcNursery;
   343     if (nursery.isInside(*thingp))
   344         return nursery.getForwardedPointer(thingp);
   345 #endif
   346     Zone *zone = (*thingp)->tenuredZone();
   347     if (!zone->isCollecting() || zone->isGCFinished())
   348         return true;
   349     return (*thingp)->isMarked();
   350 }
   352 template <typename T>
   353 static bool
   354 IsAboutToBeFinalized(T **thingp)
   355 {
   356     JS_ASSERT(thingp);
   357     JS_ASSERT(*thingp);
   359     T *thing = *thingp;
   360     JSRuntime *rt = thing->runtimeFromAnyThread();
   362     /* Permanent atoms are never finalized by non-owning runtimes. */
   363     if (ThingIsPermanentAtom(thing) && !TlsPerThreadData.get()->associatedWith(rt))
   364         return false;
   366 #ifdef JSGC_GENERATIONAL
   367     Nursery &nursery = rt->gcNursery;
   368     JS_ASSERT_IF(!rt->isHeapMinorCollecting(), !nursery.isInside(thing));
   369     if (rt->isHeapMinorCollecting()) {
   370         if (nursery.isInside(thing))
   371             return !nursery.getForwardedPointer(thingp);
   372         return false;
   373     }
   374 #endif
   376     if (!thing->tenuredZone()->isGCSweeping())
   377         return false;
   379     /*
   380      * We should return false for things that have been allocated during
   381      * incremental sweeping, but this possibility doesn't occur at the moment
   382      * because this function is only called at the very start of the sweeping a
   383      * compartment group and during minor gc. Rather than do the extra check,
   384      * we just assert that it's not necessary.
   385      */
   386     JS_ASSERT_IF(!rt->isHeapMinorCollecting(), !thing->arenaHeader()->allocatedDuringIncremental);
   388     return !thing->isMarked();
   389 }
   391 template <typename T>
   392 T *
   393 UpdateIfRelocated(JSRuntime *rt, T **thingp)
   394 {
   395     JS_ASSERT(thingp);
   396 #ifdef JSGC_GENERATIONAL
   397     if (*thingp && rt->isHeapMinorCollecting() && rt->gcNursery.isInside(*thingp))
   398         rt->gcNursery.getForwardedPointer(thingp);
   399 #endif
   400     return *thingp;
   401 }
   403 #define DeclMarkerImpl(base, type)                                                                \
   404 void                                                                                              \
   405 Mark##base(JSTracer *trc, BarrieredPtr<type> *thing, const char *name)                            \
   406 {                                                                                                 \
   407     Mark<type>(trc, thing, name);                                                                 \
   408 }                                                                                                 \
   409                                                                                                   \
   410 void                                                                                              \
   411 Mark##base##Root(JSTracer *trc, type **thingp, const char *name)                                  \
   412 {                                                                                                 \
   413     MarkRoot<type>(trc, thingp, name);                                                            \
   414 }                                                                                                 \
   415                                                                                                   \
   416 void                                                                                              \
   417 Mark##base##Unbarriered(JSTracer *trc, type **thingp, const char *name)                           \
   418 {                                                                                                 \
   419     MarkUnbarriered<type>(trc, thingp, name);                                                     \
   420 }                                                                                                 \
   421                                                                                                   \
   422 /* Explicitly instantiate MarkUnbarriered<type>. It is referenced from */                         \
   423 /* other translation units and the instantiation might otherwise get */                           \
   424 /* inlined away. */                                                                               \
   425 template void MarkUnbarriered<type>(JSTracer *, type **, const char *);                           \
   426                                                                                                   \
   427 void                                                                                              \
   428 Mark##base##Range(JSTracer *trc, size_t len, HeapPtr<type> *vec, const char *name)                \
   429 {                                                                                                 \
   430     MarkRange<type>(trc, len, vec, name);                                                         \
   431 }                                                                                                 \
   432                                                                                                   \
   433 void                                                                                              \
   434 Mark##base##RootRange(JSTracer *trc, size_t len, type **vec, const char *name)                    \
   435 {                                                                                                 \
   436     MarkRootRange<type>(trc, len, vec, name);                                                     \
   437 }                                                                                                 \
   438                                                                                                   \
   439 bool                                                                                              \
   440 Is##base##Marked(type **thingp)                                                                   \
   441 {                                                                                                 \
   442     return IsMarked<type>(thingp);                                                                \
   443 }                                                                                                 \
   444                                                                                                   \
   445 bool                                                                                              \
   446 Is##base##Marked(BarrieredPtr<type> *thingp)                                                      \
   447 {                                                                                                 \
   448     return IsMarked<type>(thingp->unsafeGet());                                                   \
   449 }                                                                                                 \
   450                                                                                                   \
   451 bool                                                                                              \
   452 Is##base##AboutToBeFinalized(type **thingp)                                                       \
   453 {                                                                                                 \
   454     return IsAboutToBeFinalized<type>(thingp);                                                    \
   455 }                                                                                                 \
   456                                                                                                   \
   457 bool                                                                                              \
   458 Is##base##AboutToBeFinalized(BarrieredPtr<type> *thingp)                                          \
   459 {                                                                                                 \
   460     return IsAboutToBeFinalized<type>(thingp->unsafeGet());                                       \
   461 }                                                                                                 \
   462                                                                                                   \
   463 type *                                                                                            \
   464 Update##base##IfRelocated(JSRuntime *rt, BarrieredPtr<type> *thingp)                              \
   465 {                                                                                                 \
   466     return UpdateIfRelocated<type>(rt, thingp->unsafeGet());                                      \
   467 }                                                                                                 \
   468                                                                                                   \
   469 type *                                                                                            \
   470 Update##base##IfRelocated(JSRuntime *rt, type **thingp)                                           \
   471 {                                                                                                 \
   472     return UpdateIfRelocated<type>(rt, thingp);                                                   \
   473 }
   476 DeclMarkerImpl(BaseShape, BaseShape)
   477 DeclMarkerImpl(BaseShape, UnownedBaseShape)
   478 DeclMarkerImpl(JitCode, jit::JitCode)
   479 DeclMarkerImpl(Object, ArgumentsObject)
   480 DeclMarkerImpl(Object, ArrayBufferObject)
   481 DeclMarkerImpl(Object, ArrayBufferViewObject)
   482 DeclMarkerImpl(Object, SharedArrayBufferObject)
   483 DeclMarkerImpl(Object, DebugScopeObject)
   484 DeclMarkerImpl(Object, GlobalObject)
   485 DeclMarkerImpl(Object, JSObject)
   486 DeclMarkerImpl(Object, JSFunction)
   487 DeclMarkerImpl(Object, ObjectImpl)
   488 DeclMarkerImpl(Object, ScopeObject)
   489 DeclMarkerImpl(Script, JSScript)
   490 DeclMarkerImpl(LazyScript, LazyScript)
   491 DeclMarkerImpl(Shape, Shape)
   492 DeclMarkerImpl(String, JSAtom)
   493 DeclMarkerImpl(String, JSString)
   494 DeclMarkerImpl(String, JSFlatString)
   495 DeclMarkerImpl(String, JSLinearString)
   496 DeclMarkerImpl(String, PropertyName)
   497 DeclMarkerImpl(TypeObject, js::types::TypeObject)
   499 } /* namespace gc */
   500 } /* namespace js */
   502 /*** Externally Typed Marking ***/
   504 void
   505 gc::MarkKind(JSTracer *trc, void **thingp, JSGCTraceKind kind)
   506 {
   507     JS_ASSERT(thingp);
   508     JS_ASSERT(*thingp);
   509     DebugOnly<Cell *> cell = static_cast<Cell *>(*thingp);
   510     JS_ASSERT_IF(cell->isTenured(), kind == MapAllocToTraceKind(cell->tenuredGetAllocKind()));
   511     switch (kind) {
   512       case JSTRACE_OBJECT:
   513         MarkInternal(trc, reinterpret_cast<JSObject **>(thingp));
   514         break;
   515       case JSTRACE_STRING:
   516         MarkInternal(trc, reinterpret_cast<JSString **>(thingp));
   517         break;
   518       case JSTRACE_SCRIPT:
   519         MarkInternal(trc, reinterpret_cast<JSScript **>(thingp));
   520         break;
   521       case JSTRACE_LAZY_SCRIPT:
   522         MarkInternal(trc, reinterpret_cast<LazyScript **>(thingp));
   523         break;
   524       case JSTRACE_SHAPE:
   525         MarkInternal(trc, reinterpret_cast<Shape **>(thingp));
   526         break;
   527       case JSTRACE_BASE_SHAPE:
   528         MarkInternal(trc, reinterpret_cast<BaseShape **>(thingp));
   529         break;
   530       case JSTRACE_TYPE_OBJECT:
   531         MarkInternal(trc, reinterpret_cast<types::TypeObject **>(thingp));
   532         break;
   533       case JSTRACE_JITCODE:
   534         MarkInternal(trc, reinterpret_cast<jit::JitCode **>(thingp));
   535         break;
   536     }
   537 }
   539 static void
   540 MarkGCThingInternal(JSTracer *trc, void **thingp, const char *name)
   541 {
   542     trc->setTracingName(name);
   543     JS_ASSERT(thingp);
   544     if (!*thingp)
   545         return;
   546     MarkKind(trc, thingp, GetGCThingTraceKind(*thingp));
   547 }
   549 void
   550 gc::MarkGCThingRoot(JSTracer *trc, void **thingp, const char *name)
   551 {
   552     JS_ROOT_MARKING_ASSERT(trc);
   553     MarkGCThingInternal(trc, thingp, name);
   554 }
   556 void
   557 gc::MarkGCThingUnbarriered(JSTracer *trc, void **thingp, const char *name)
   558 {
   559     MarkGCThingInternal(trc, thingp, name);
   560 }
   562 /*** ID Marking ***/
   564 static inline void
   565 MarkIdInternal(JSTracer *trc, jsid *id)
   566 {
   567     if (JSID_IS_STRING(*id)) {
   568         JSString *str = JSID_TO_STRING(*id);
   569         trc->setTracingLocation((void *)id);
   570         MarkInternal(trc, &str);
   571         *id = NON_INTEGER_ATOM_TO_JSID(reinterpret_cast<JSAtom *>(str));
   572     } else if (MOZ_UNLIKELY(JSID_IS_OBJECT(*id))) {
   573         JSObject *obj = JSID_TO_OBJECT(*id);
   574         trc->setTracingLocation((void *)id);
   575         MarkInternal(trc, &obj);
   576         *id = OBJECT_TO_JSID(obj);
   577     } else {
   578         /* Unset realLocation manually if we do not call MarkInternal. */
   579         trc->unsetTracingLocation();
   580     }
   581 }
   583 void
   584 gc::MarkId(JSTracer *trc, BarrieredId *id, const char *name)
   585 {
   586     trc->setTracingName(name);
   587     MarkIdInternal(trc, id->unsafeGet());
   588 }
   590 void
   591 gc::MarkIdRoot(JSTracer *trc, jsid *id, const char *name)
   592 {
   593     JS_ROOT_MARKING_ASSERT(trc);
   594     trc->setTracingName(name);
   595     MarkIdInternal(trc, id);
   596 }
   598 void
   599 gc::MarkIdUnbarriered(JSTracer *trc, jsid *id, const char *name)
   600 {
   601     trc->setTracingName(name);
   602     MarkIdInternal(trc, id);
   603 }
   605 void
   606 gc::MarkIdRange(JSTracer *trc, size_t len, HeapId *vec, const char *name)
   607 {
   608     for (size_t i = 0; i < len; ++i) {
   609         trc->setTracingIndex(name, i);
   610         MarkIdInternal(trc, vec[i].unsafeGet());
   611     }
   612 }
   614 void
   615 gc::MarkIdRootRange(JSTracer *trc, size_t len, jsid *vec, const char *name)
   616 {
   617     JS_ROOT_MARKING_ASSERT(trc);
   618     for (size_t i = 0; i < len; ++i) {
   619         trc->setTracingIndex(name, i);
   620         MarkIdInternal(trc, &vec[i]);
   621     }
   622 }
   624 /*** Value Marking ***/
   626 static inline void
   627 MarkValueInternal(JSTracer *trc, Value *v)
   628 {
   629     if (v->isMarkable()) {
   630         JS_ASSERT(v->toGCThing());
   631         void *thing = v->toGCThing();
   632         trc->setTracingLocation((void *)v);
   633         MarkKind(trc, &thing, v->gcKind());
   634         if (v->isString())
   635             v->setString((JSString *)thing);
   636         else
   637             v->setObjectOrNull((JSObject *)thing);
   638     } else {
   639         /* Unset realLocation manually if we do not call MarkInternal. */
   640         trc->unsetTracingLocation();
   641     }
   642 }
   644 void
   645 gc::MarkValue(JSTracer *trc, BarrieredValue *v, const char *name)
   646 {
   647     trc->setTracingName(name);
   648     MarkValueInternal(trc, v->unsafeGet());
   649 }
   651 void
   652 gc::MarkValueRoot(JSTracer *trc, Value *v, const char *name)
   653 {
   654     JS_ROOT_MARKING_ASSERT(trc);
   655     trc->setTracingName(name);
   656     MarkValueInternal(trc, v);
   657 }
   659 void
   660 gc::MarkTypeRoot(JSTracer *trc, types::Type *v, const char *name)
   661 {
   662     JS_ROOT_MARKING_ASSERT(trc);
   663     trc->setTracingName(name);
   664     if (v->isSingleObject()) {
   665         JSObject *obj = v->singleObject();
   666         MarkInternal(trc, &obj);
   667         *v = types::Type::ObjectType(obj);
   668     } else if (v->isTypeObject()) {
   669         types::TypeObject *typeObj = v->typeObject();
   670         MarkInternal(trc, &typeObj);
   671         *v = types::Type::ObjectType(typeObj);
   672     }
   673 }
   675 void
   676 gc::MarkValueRange(JSTracer *trc, size_t len, BarrieredValue *vec, const char *name)
   677 {
   678     for (size_t i = 0; i < len; ++i) {
   679         trc->setTracingIndex(name, i);
   680         MarkValueInternal(trc, vec[i].unsafeGet());
   681     }
   682 }
   684 void
   685 gc::MarkValueRootRange(JSTracer *trc, size_t len, Value *vec, const char *name)
   686 {
   687     JS_ROOT_MARKING_ASSERT(trc);
   688     for (size_t i = 0; i < len; ++i) {
   689         trc->setTracingIndex(name, i);
   690         MarkValueInternal(trc, &vec[i]);
   691     }
   692 }
   694 bool
   695 gc::IsValueMarked(Value *v)
   696 {
   697     JS_ASSERT(v->isMarkable());
   698     bool rv;
   699     if (v->isString()) {
   700         JSString *str = (JSString *)v->toGCThing();
   701         rv = IsMarked<JSString>(&str);
   702         v->setString(str);
   703     } else {
   704         JSObject *obj = (JSObject *)v->toGCThing();
   705         rv = IsMarked<JSObject>(&obj);
   706         v->setObject(*obj);
   707     }
   708     return rv;
   709 }
   711 bool
   712 gc::IsValueAboutToBeFinalized(Value *v)
   713 {
   714     JS_ASSERT(v->isMarkable());
   715     bool rv;
   716     if (v->isString()) {
   717         JSString *str = (JSString *)v->toGCThing();
   718         rv = IsAboutToBeFinalized<JSString>(&str);
   719         v->setString(str);
   720     } else {
   721         JSObject *obj = (JSObject *)v->toGCThing();
   722         rv = IsAboutToBeFinalized<JSObject>(&obj);
   723         v->setObject(*obj);
   724     }
   725     return rv;
   726 }
   728 /*** Slot Marking ***/
   730 bool
   731 gc::IsSlotMarked(HeapSlot *s)
   732 {
   733     return IsMarked(s);
   734 }
   736 void
   737 gc::MarkSlot(JSTracer *trc, HeapSlot *s, const char *name)
   738 {
   739     trc->setTracingName(name);
   740     MarkValueInternal(trc, s->unsafeGet());
   741 }
   743 void
   744 gc::MarkArraySlots(JSTracer *trc, size_t len, HeapSlot *vec, const char *name)
   745 {
   746     for (size_t i = 0; i < len; ++i) {
   747         trc->setTracingIndex(name, i);
   748         MarkValueInternal(trc, vec[i].unsafeGet());
   749     }
   750 }
   752 void
   753 gc::MarkObjectSlots(JSTracer *trc, JSObject *obj, uint32_t start, uint32_t nslots)
   754 {
   755     JS_ASSERT(obj->isNative());
   756     for (uint32_t i = start; i < (start + nslots); ++i) {
   757         trc->setTracingDetails(js_GetObjectSlotName, obj, i);
   758         MarkValueInternal(trc, obj->nativeGetSlotRef(i).unsafeGet());
   759     }
   760 }
   762 static bool
   763 ShouldMarkCrossCompartment(JSTracer *trc, JSObject *src, Cell *cell)
   764 {
   765     if (!IS_GC_MARKING_TRACER(trc))
   766         return true;
   768     uint32_t color = AsGCMarker(trc)->getMarkColor();
   769     JS_ASSERT(color == BLACK || color == GRAY);
   771     if (IsInsideNursery(trc->runtime(), cell)) {
   772         JS_ASSERT(color == BLACK);
   773         return false;
   774     }
   776     JS::Zone *zone = cell->tenuredZone();
   777     if (color == BLACK) {
   778         /*
   779          * Having black->gray edges violates our promise to the cycle
   780          * collector. This can happen if we're collecting a compartment and it
   781          * has an edge to an uncollected compartment: it's possible that the
   782          * source and destination of the cross-compartment edge should be gray,
   783          * but the source was marked black by the conservative scanner.
   784          */
   785         if (cell->isMarked(GRAY)) {
   786             JS_ASSERT(!zone->isCollecting());
   787             trc->runtime()->gcFoundBlackGrayEdges = true;
   788         }
   789         return zone->isGCMarking();
   790     } else {
   791         if (zone->isGCMarkingBlack()) {
   792             /*
   793              * The destination compartment is being not being marked gray now,
   794              * but it will be later, so record the cell so it can be marked gray
   795              * at the appropriate time.
   796              */
   797             if (!cell->isMarked())
   798                 DelayCrossCompartmentGrayMarking(src);
   799             return false;
   800         }
   801         return zone->isGCMarkingGray();
   802     }
   803 }
   805 void
   806 gc::MarkCrossCompartmentObjectUnbarriered(JSTracer *trc, JSObject *src, JSObject **dst, const char *name)
   807 {
   808     if (ShouldMarkCrossCompartment(trc, src, *dst))
   809         MarkObjectUnbarriered(trc, dst, name);
   810 }
   812 void
   813 gc::MarkCrossCompartmentScriptUnbarriered(JSTracer *trc, JSObject *src, JSScript **dst,
   814                                           const char *name)
   815 {
   816     if (ShouldMarkCrossCompartment(trc, src, *dst))
   817         MarkScriptUnbarriered(trc, dst, name);
   818 }
   820 void
   821 gc::MarkCrossCompartmentSlot(JSTracer *trc, JSObject *src, HeapSlot *dst, const char *name)
   822 {
   823     if (dst->isMarkable() && ShouldMarkCrossCompartment(trc, src, (Cell *)dst->toGCThing()))
   824         MarkSlot(trc, dst, name);
   825 }
   827 /*** Special Marking ***/
   829 void
   830 gc::MarkObject(JSTracer *trc, HeapPtr<GlobalObject, JSScript *> *thingp, const char *name)
   831 {
   832     trc->setTracingName(name);
   833     MarkInternal(trc, thingp->unsafeGet());
   834 }
   836 void
   837 gc::MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name)
   838 {
   839     trc->setTracingName(name);
   840     MarkValueInternal(trc, v);
   841 }
   843 bool
   844 gc::IsCellMarked(Cell **thingp)
   845 {
   846     return IsMarked<Cell>(thingp);
   847 }
   849 bool
   850 gc::IsCellAboutToBeFinalized(Cell **thingp)
   851 {
   852     return IsAboutToBeFinalized<Cell>(thingp);
   853 }
   855 /*** Push Mark Stack ***/
   857 #define JS_COMPARTMENT_ASSERT(rt, thing)                                \
   858     JS_ASSERT((thing)->zone()->isGCMarking())
   860 #define JS_COMPARTMENT_ASSERT_STR(rt, thing)                            \
   861     JS_ASSERT((thing)->zone()->isGCMarking() ||                         \
   862               (rt)->isAtomsZone((thing)->zone()));
   864 static void
   865 PushMarkStack(GCMarker *gcmarker, ObjectImpl *thing)
   866 {
   867     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
   868     JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
   870     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
   871         gcmarker->pushObject(thing);
   872 }
   874 /*
   875  * PushMarkStack for BaseShape unpacks its children directly onto the mark
   876  * stack. For a pre-barrier between incremental slices, this may result in
   877  * objects in the nursery getting pushed onto the mark stack. It is safe to
   878  * ignore these objects because they will be marked by the matching
   879  * post-barrier during the minor GC at the start of each incremental slice.
   880  */
   881 static void
   882 MaybePushMarkStackBetweenSlices(GCMarker *gcmarker, JSObject *thing)
   883 {
   884     JSRuntime *rt = gcmarker->runtime();
   885     JS_COMPARTMENT_ASSERT(rt, thing);
   886     JS_ASSERT_IF(rt->isHeapBusy(), !IsInsideNursery(rt, thing));
   888     if (!IsInsideNursery(rt, thing) && thing->markIfUnmarked(gcmarker->getMarkColor()))
   889         gcmarker->pushObject(thing);
   890 }
   892 static void
   893 PushMarkStack(GCMarker *gcmarker, JSFunction *thing)
   894 {
   895     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
   896     JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
   898     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
   899         gcmarker->pushObject(thing);
   900 }
   902 static void
   903 PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing)
   904 {
   905     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
   906     JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
   908     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
   909         gcmarker->pushType(thing);
   910 }
   912 static void
   913 PushMarkStack(GCMarker *gcmarker, JSScript *thing)
   914 {
   915     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
   916     JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
   918     /*
   919      * We mark scripts directly rather than pushing on the stack as they can
   920      * refer to other scripts only indirectly (like via nested functions) and
   921      * we cannot get to deep recursion.
   922      */
   923     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
   924         MarkChildren(gcmarker, thing);
   925 }
   927 static void
   928 PushMarkStack(GCMarker *gcmarker, LazyScript *thing)
   929 {
   930     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
   931     JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
   933     /*
   934      * We mark lazy scripts directly rather than pushing on the stack as they
   935      * only refer to normal scripts and to strings, and cannot recurse.
   936      */
   937     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
   938         MarkChildren(gcmarker, thing);
   939 }
   941 static void
   942 ScanShape(GCMarker *gcmarker, Shape *shape);
   944 static void
   945 PushMarkStack(GCMarker *gcmarker, Shape *thing)
   946 {
   947     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
   948     JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
   950     /* We mark shapes directly rather than pushing on the stack. */
   951     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
   952         ScanShape(gcmarker, thing);
   953 }
   955 static void
   956 PushMarkStack(GCMarker *gcmarker, jit::JitCode *thing)
   957 {
   958     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
   959     JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
   961     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
   962         gcmarker->pushJitCode(thing);
   963 }
   965 static inline void
   966 ScanBaseShape(GCMarker *gcmarker, BaseShape *base);
   968 static void
   969 PushMarkStack(GCMarker *gcmarker, BaseShape *thing)
   970 {
   971     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
   972     JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
   974     /* We mark base shapes directly rather than pushing on the stack. */
   975     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
   976         ScanBaseShape(gcmarker, thing);
   977 }
   979 static void
   980 ScanShape(GCMarker *gcmarker, Shape *shape)
   981 {
   982   restart:
   983     PushMarkStack(gcmarker, shape->base());
   985     const BarrieredId &id = shape->propidRef();
   986     if (JSID_IS_STRING(id))
   987         PushMarkStack(gcmarker, JSID_TO_STRING(id));
   988     else if (MOZ_UNLIKELY(JSID_IS_OBJECT(id)))
   989         PushMarkStack(gcmarker, JSID_TO_OBJECT(id));
   991     shape = shape->previous();
   992     if (shape && shape->markIfUnmarked(gcmarker->getMarkColor()))
   993         goto restart;
   994 }
   996 static inline void
   997 ScanBaseShape(GCMarker *gcmarker, BaseShape *base)
   998 {
   999     base->assertConsistency();
  1001     base->compartment()->mark();
  1003     if (base->hasGetterObject())
  1004         MaybePushMarkStackBetweenSlices(gcmarker, base->getterObject());
  1006     if (base->hasSetterObject())
  1007         MaybePushMarkStackBetweenSlices(gcmarker, base->setterObject());
  1009     if (JSObject *parent = base->getObjectParent()) {
  1010         MaybePushMarkStackBetweenSlices(gcmarker, parent);
  1011     } else if (GlobalObject *global = base->compartment()->maybeGlobal()) {
  1012         PushMarkStack(gcmarker, global);
  1015     if (JSObject *metadata = base->getObjectMetadata())
  1016         MaybePushMarkStackBetweenSlices(gcmarker, metadata);
  1018     /*
  1019      * All children of the owned base shape are consistent with its
  1020      * unowned one, thus we do not need to trace through children of the
  1021      * unowned base shape.
  1022      */
  1023     if (base->isOwned()) {
  1024         UnownedBaseShape *unowned = base->baseUnowned();
  1025         JS_ASSERT(base->compartment() == unowned->compartment());
  1026         unowned->markIfUnmarked(gcmarker->getMarkColor());
  1030 static inline void
  1031 ScanLinearString(GCMarker *gcmarker, JSLinearString *str)
  1033     JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime(), str);
  1034     JS_ASSERT(str->isMarked());
  1036     /*
  1037      * Add extra asserts to confirm the static type to detect incorrect string
  1038      * mutations.
  1039      */
  1040     JS_ASSERT(str->JSString::isLinear());
  1041     while (str->hasBase()) {
  1042         str = str->base();
  1043         JS_ASSERT(str->JSString::isLinear());
  1044         if (str->isPermanentAtom())
  1045             break;
  1046         JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime(), str);
  1047         if (!str->markIfUnmarked())
  1048             break;
  1052 /*
  1053  * The function tries to scan the whole rope tree using the marking stack as
  1054  * temporary storage. If that becomes full, the unscanned ropes are added to
  1055  * the delayed marking list. When the function returns, the marking stack is
  1056  * at the same depth as it was on entry. This way we avoid using tags when
  1057  * pushing ropes to the stack as ropes never leaks to other users of the
  1058  * stack. This also assumes that a rope can only point to other ropes or
  1059  * linear strings, it cannot refer to GC things of other types.
  1060  */
  1061 static void
  1062 ScanRope(GCMarker *gcmarker, JSRope *rope)
  1064     ptrdiff_t savedPos = gcmarker->stack.position();
  1065     JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
  1066     for (;;) {
  1067         JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
  1068         JS_DIAGNOSTICS_ASSERT(rope->JSString::isRope());
  1069         JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime(), rope);
  1070         JS_ASSERT(rope->isMarked());
  1071         JSRope *next = nullptr;
  1073         JSString *right = rope->rightChild();
  1074         if (!right->isPermanentAtom() && right->markIfUnmarked()) {
  1075             if (right->isLinear())
  1076                 ScanLinearString(gcmarker, &right->asLinear());
  1077             else
  1078                 next = &right->asRope();
  1081         JSString *left = rope->leftChild();
  1082         if (!left->isPermanentAtom() && left->markIfUnmarked()) {
  1083             if (left->isLinear()) {
  1084                 ScanLinearString(gcmarker, &left->asLinear());
  1085             } else {
  1086                 /*
  1087                  * When both children are ropes, set aside the right one to
  1088                  * scan it later.
  1089                  */
  1090                 if (next && !gcmarker->stack.push(reinterpret_cast<uintptr_t>(next)))
  1091                     gcmarker->delayMarkingChildren(next);
  1092                 next = &left->asRope();
  1095         if (next) {
  1096             rope = next;
  1097         } else if (savedPos != gcmarker->stack.position()) {
  1098             JS_ASSERT(savedPos < gcmarker->stack.position());
  1099             rope = reinterpret_cast<JSRope *>(gcmarker->stack.pop());
  1100         } else {
  1101             break;
  1104     JS_ASSERT(savedPos == gcmarker->stack.position());
  1107 static inline void
  1108 ScanString(GCMarker *gcmarker, JSString *str)
  1110     if (str->isLinear())
  1111         ScanLinearString(gcmarker, &str->asLinear());
  1112     else
  1113         ScanRope(gcmarker, &str->asRope());
  1116 static inline void
  1117 PushMarkStack(GCMarker *gcmarker, JSString *str)
  1119     // Permanent atoms might not be associated with this runtime.
  1120     if (str->isPermanentAtom())
  1121         return;
  1123     JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime(), str);
  1125     /*
  1126      * As string can only refer to other strings we fully scan its GC graph
  1127      * using the explicit stack when navigating the rope tree to avoid
  1128      * dealing with strings on the stack in drainMarkStack.
  1129      */
  1130     if (str->markIfUnmarked())
  1131         ScanString(gcmarker, str);
  1134 void
  1135 gc::MarkChildren(JSTracer *trc, JSObject *obj)
  1137     obj->markChildren(trc);
  1140 static void
  1141 gc::MarkChildren(JSTracer *trc, JSString *str)
  1143     if (str->hasBase())
  1144         str->markBase(trc);
  1145     else if (str->isRope())
  1146         str->asRope().markChildren(trc);
  1149 static void
  1150 gc::MarkChildren(JSTracer *trc, JSScript *script)
  1152     script->markChildren(trc);
  1155 static void
  1156 gc::MarkChildren(JSTracer *trc, LazyScript *lazy)
  1158     lazy->markChildren(trc);
  1161 static void
  1162 gc::MarkChildren(JSTracer *trc, Shape *shape)
  1164     shape->markChildren(trc);
  1167 static void
  1168 gc::MarkChildren(JSTracer *trc, BaseShape *base)
  1170     base->markChildren(trc);
  1173 /*
  1174  * This function is used by the cycle collector to trace through the
  1175  * children of a BaseShape (and its baseUnowned(), if any). The cycle
  1176  * collector does not directly care about BaseShapes, so only the
  1177  * getter, setter, and parent are marked. Furthermore, the parent is
  1178  * marked only if it isn't the same as prevParent, which will be
  1179  * updated to the current shape's parent.
  1180  */
  1181 static inline void
  1182 MarkCycleCollectorChildren(JSTracer *trc, BaseShape *base, JSObject **prevParent)
  1184     JS_ASSERT(base);
  1186     /*
  1187      * The cycle collector does not need to trace unowned base shapes,
  1188      * as they have the same getter, setter and parent as the original
  1189      * base shape.
  1190      */
  1191     base->assertConsistency();
  1193     if (base->hasGetterObject()) {
  1194         JSObject *tmp = base->getterObject();
  1195         MarkObjectUnbarriered(trc, &tmp, "getter");
  1196         JS_ASSERT(tmp == base->getterObject());
  1199     if (base->hasSetterObject()) {
  1200         JSObject *tmp = base->setterObject();
  1201         MarkObjectUnbarriered(trc, &tmp, "setter");
  1202         JS_ASSERT(tmp == base->setterObject());
  1205     JSObject *parent = base->getObjectParent();
  1206     if (parent && parent != *prevParent) {
  1207         MarkObjectUnbarriered(trc, &parent, "parent");
  1208         JS_ASSERT(parent == base->getObjectParent());
  1209         *prevParent = parent;
  1213 /*
  1214  * This function is used by the cycle collector to trace through a
  1215  * shape. The cycle collector does not care about shapes or base
  1216  * shapes, so those are not marked. Instead, any shapes or base shapes
  1217  * that are encountered have their children marked. Stack space is
  1218  * bounded. If two shapes in a row have the same parent pointer, the
  1219  * parent pointer will only be marked once.
  1220  */
  1221 void
  1222 gc::MarkCycleCollectorChildren(JSTracer *trc, Shape *shape)
  1224     JSObject *prevParent = nullptr;
  1225     do {
  1226         MarkCycleCollectorChildren(trc, shape->base(), &prevParent);
  1227         MarkId(trc, &shape->propidRef(), "propid");
  1228         shape = shape->previous();
  1229     } while (shape);
  1232 static void
  1233 ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
  1235     unsigned count = type->getPropertyCount();
  1236     for (unsigned i = 0; i < count; i++) {
  1237         types::Property *prop = type->getProperty(i);
  1238         if (prop && JSID_IS_STRING(prop->id))
  1239             PushMarkStack(gcmarker, JSID_TO_STRING(prop->id));
  1242     if (type->proto().isObject())
  1243         PushMarkStack(gcmarker, type->proto().toObject());
  1245     if (type->singleton() && !type->lazy())
  1246         PushMarkStack(gcmarker, type->singleton());
  1248     if (type->hasNewScript()) {
  1249         PushMarkStack(gcmarker, type->newScript()->fun);
  1250         PushMarkStack(gcmarker, type->newScript()->templateObject);
  1251     } else if (type->hasTypedObject()) {
  1252         PushMarkStack(gcmarker, type->typedObject()->descrHeapPtr());
  1255     if (type->interpretedFunction)
  1256         PushMarkStack(gcmarker, type->interpretedFunction);
  1259 static void
  1260 gc::MarkChildren(JSTracer *trc, types::TypeObject *type)
  1262     unsigned count = type->getPropertyCount();
  1263     for (unsigned i = 0; i < count; i++) {
  1264         types::Property *prop = type->getProperty(i);
  1265         if (prop)
  1266             MarkId(trc, &prop->id, "type_prop");
  1269     if (type->proto().isObject())
  1270         MarkObject(trc, &type->protoRaw(), "type_proto");
  1272     if (type->singleton() && !type->lazy())
  1273         MarkObject(trc, &type->singletonRaw(), "type_singleton");
  1275     if (type->hasNewScript()) {
  1276         MarkObject(trc, &type->newScript()->fun, "type_new_function");
  1277         MarkObject(trc, &type->newScript()->templateObject, "type_new_template");
  1278     } else if (type->hasTypedObject()) {
  1279         MarkObject(trc, &type->typedObject()->descrHeapPtr(), "type_heap_ptr");
  1282     if (type->interpretedFunction)
  1283         MarkObject(trc, &type->interpretedFunction, "type_function");
  1286 static void
  1287 gc::MarkChildren(JSTracer *trc, jit::JitCode *code)
  1289 #ifdef JS_ION
  1290     code->trace(trc);
  1291 #endif
  1294 template<typename T>
  1295 static void
  1296 PushArenaTyped(GCMarker *gcmarker, ArenaHeader *aheader)
  1298     for (CellIterUnderGC i(aheader); !i.done(); i.next())
  1299         PushMarkStack(gcmarker, i.get<T>());
  1302 void
  1303 gc::PushArena(GCMarker *gcmarker, ArenaHeader *aheader)
  1305     switch (MapAllocToTraceKind(aheader->getAllocKind())) {
  1306       case JSTRACE_OBJECT:
  1307         PushArenaTyped<JSObject>(gcmarker, aheader);
  1308         break;
  1310       case JSTRACE_STRING:
  1311         PushArenaTyped<JSString>(gcmarker, aheader);
  1312         break;
  1314       case JSTRACE_SCRIPT:
  1315         PushArenaTyped<JSScript>(gcmarker, aheader);
  1316         break;
  1318       case JSTRACE_LAZY_SCRIPT:
  1319         PushArenaTyped<LazyScript>(gcmarker, aheader);
  1320         break;
  1322       case JSTRACE_SHAPE:
  1323         PushArenaTyped<js::Shape>(gcmarker, aheader);
  1324         break;
  1326       case JSTRACE_BASE_SHAPE:
  1327         PushArenaTyped<js::BaseShape>(gcmarker, aheader);
  1328         break;
  1330       case JSTRACE_TYPE_OBJECT:
  1331         PushArenaTyped<js::types::TypeObject>(gcmarker, aheader);
  1332         break;
  1334       case JSTRACE_JITCODE:
  1335         PushArenaTyped<js::jit::JitCode>(gcmarker, aheader);
  1336         break;
  1340 struct SlotArrayLayout
  1342     union {
  1343         HeapSlot *end;
  1344         uintptr_t kind;
  1345     };
  1346     union {
  1347         HeapSlot *start;
  1348         uintptr_t index;
  1349     };
  1350     JSObject *obj;
  1352     static void staticAsserts() {
  1353         /* This should have the same layout as three mark stack items. */
  1354         JS_STATIC_ASSERT(sizeof(SlotArrayLayout) == 3 * sizeof(uintptr_t));
  1356 };
  1358 /*
  1359  * During incremental GC, we return from drainMarkStack without having processed
  1360  * the entire stack. At that point, JS code can run and reallocate slot arrays
  1361  * that are stored on the stack. To prevent this from happening, we replace all
  1362  * ValueArrayTag stack items with SavedValueArrayTag. In the latter, slots
  1363  * pointers are replaced with slot indexes, and slot array end pointers are
  1364  * replaced with the kind of index (properties vs. elements).
  1365  */
  1366 void
  1367 GCMarker::saveValueRanges()
  1369     for (uintptr_t *p = stack.tos_; p > stack.stack_; ) {
  1370         uintptr_t tag = *--p & StackTagMask;
  1371         if (tag == ValueArrayTag) {
  1372             *p &= ~StackTagMask;
  1373             p -= 2;
  1374             SlotArrayLayout *arr = reinterpret_cast<SlotArrayLayout *>(p);
  1375             JSObject *obj = arr->obj;
  1376             JS_ASSERT(obj->isNative());
  1378             HeapSlot *vp = obj->getDenseElements();
  1379             if (arr->end == vp + obj->getDenseInitializedLength()) {
  1380                 JS_ASSERT(arr->start >= vp);
  1381                 arr->index = arr->start - vp;
  1382                 arr->kind = HeapSlot::Element;
  1383             } else {
  1384                 HeapSlot *vp = obj->fixedSlots();
  1385                 unsigned nfixed = obj->numFixedSlots();
  1386                 if (arr->start == arr->end) {
  1387                     arr->index = obj->slotSpan();
  1388                 } else if (arr->start >= vp && arr->start < vp + nfixed) {
  1389                     JS_ASSERT(arr->end == vp + Min(nfixed, obj->slotSpan()));
  1390                     arr->index = arr->start - vp;
  1391                 } else {
  1392                     JS_ASSERT(arr->start >= obj->slots &&
  1393                               arr->end == obj->slots + obj->slotSpan() - nfixed);
  1394                     arr->index = (arr->start - obj->slots) + nfixed;
  1396                 arr->kind = HeapSlot::Slot;
  1398             p[2] |= SavedValueArrayTag;
  1399         } else if (tag == SavedValueArrayTag) {
  1400             p -= 2;
  1405 bool
  1406 GCMarker::restoreValueArray(JSObject *obj, void **vpp, void **endp)
  1408     uintptr_t start = stack.pop();
  1409     HeapSlot::Kind kind = (HeapSlot::Kind) stack.pop();
  1411     if (kind == HeapSlot::Element) {
  1412         if (!obj->is<ArrayObject>())
  1413             return false;
  1415         uint32_t initlen = obj->getDenseInitializedLength();
  1416         HeapSlot *vp = obj->getDenseElements();
  1417         if (start < initlen) {
  1418             *vpp = vp + start;
  1419             *endp = vp + initlen;
  1420         } else {
  1421             /* The object shrunk, in which case no scanning is needed. */
  1422             *vpp = *endp = vp;
  1424     } else {
  1425         JS_ASSERT(kind == HeapSlot::Slot);
  1426         HeapSlot *vp = obj->fixedSlots();
  1427         unsigned nfixed = obj->numFixedSlots();
  1428         unsigned nslots = obj->slotSpan();
  1429         if (start < nslots) {
  1430             if (start < nfixed) {
  1431                 *vpp = vp + start;
  1432                 *endp = vp + Min(nfixed, nslots);
  1433             } else {
  1434                 *vpp = obj->slots + start - nfixed;
  1435                 *endp = obj->slots + nslots - nfixed;
  1437         } else {
  1438             /* The object shrunk, in which case no scanning is needed. */
  1439             *vpp = *endp = vp;
  1443     JS_ASSERT(*vpp <= *endp);
  1444     return true;
  1447 void
  1448 GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr)
  1450     if (tag == TypeTag) {
  1451         ScanTypeObject(this, reinterpret_cast<types::TypeObject *>(addr));
  1452     } else if (tag == SavedValueArrayTag) {
  1453         JS_ASSERT(!(addr & CellMask));
  1454         JSObject *obj = reinterpret_cast<JSObject *>(addr);
  1455         HeapValue *vp, *end;
  1456         if (restoreValueArray(obj, (void **)&vp, (void **)&end))
  1457             pushValueArray(obj, vp, end);
  1458         else
  1459             pushObject(obj);
  1460     } else if (tag == JitCodeTag) {
  1461         MarkChildren(this, reinterpret_cast<jit::JitCode *>(addr));
  1465 inline void
  1466 GCMarker::processMarkStackTop(SliceBudget &budget)
  1468     /*
  1469      * The function uses explicit goto and implements the scanning of the
  1470      * object directly. It allows to eliminate the tail recursion and
  1471      * significantly improve the marking performance, see bug 641025.
  1472      */
  1473     HeapSlot *vp, *end;
  1474     JSObject *obj;
  1476     uintptr_t addr = stack.pop();
  1477     uintptr_t tag = addr & StackTagMask;
  1478     addr &= ~StackTagMask;
  1480     if (tag == ValueArrayTag) {
  1481         JS_STATIC_ASSERT(ValueArrayTag == 0);
  1482         JS_ASSERT(!(addr & CellMask));
  1483         obj = reinterpret_cast<JSObject *>(addr);
  1484         uintptr_t addr2 = stack.pop();
  1485         uintptr_t addr3 = stack.pop();
  1486         JS_ASSERT(addr2 <= addr3);
  1487         JS_ASSERT((addr3 - addr2) % sizeof(Value) == 0);
  1488         vp = reinterpret_cast<HeapSlot *>(addr2);
  1489         end = reinterpret_cast<HeapSlot *>(addr3);
  1490         goto scan_value_array;
  1493     if (tag == ObjectTag) {
  1494         obj = reinterpret_cast<JSObject *>(addr);
  1495         JS_COMPARTMENT_ASSERT(runtime(), obj);
  1496         goto scan_obj;
  1499     processMarkStackOther(tag, addr);
  1500     return;
  1502   scan_value_array:
  1503     JS_ASSERT(vp <= end);
  1504     while (vp != end) {
  1505         const Value &v = *vp++;
  1506         if (v.isString()) {
  1507             JSString *str = v.toString();
  1508             if (!str->isPermanentAtom()) {
  1509                 JS_COMPARTMENT_ASSERT_STR(runtime(), str);
  1510                 JS_ASSERT(runtime()->isAtomsZone(str->zone()) || str->zone() == obj->zone());
  1511                 if (str->markIfUnmarked())
  1512                     ScanString(this, str);
  1514         } else if (v.isObject()) {
  1515             JSObject *obj2 = &v.toObject();
  1516             JS_COMPARTMENT_ASSERT(runtime(), obj2);
  1517             JS_ASSERT(obj->compartment() == obj2->compartment());
  1518             if (obj2->markIfUnmarked(getMarkColor())) {
  1519                 pushValueArray(obj, vp, end);
  1520                 obj = obj2;
  1521                 goto scan_obj;
  1525     return;
  1527   scan_obj:
  1529         JS_COMPARTMENT_ASSERT(runtime(), obj);
  1531         budget.step();
  1532         if (budget.isOverBudget()) {
  1533             pushObject(obj);
  1534             return;
  1537         types::TypeObject *type = obj->typeFromGC();
  1538         PushMarkStack(this, type);
  1540         Shape *shape = obj->lastProperty();
  1541         PushMarkStack(this, shape);
  1543         /* Call the trace hook if necessary. */
  1544         const Class *clasp = type->clasp();
  1545         if (clasp->trace) {
  1546             // Global objects all have the same trace hook. That hook is safe without barriers
  1547             // if the gloal has no custom trace hook of it's own, or has been moved to a different
  1548             // compartment, and so can't have one.
  1549             JS_ASSERT_IF(runtime()->gcMode() == JSGC_MODE_INCREMENTAL &&
  1550                          runtime()->gcIncrementalEnabled &&
  1551                          !(clasp->trace == JS_GlobalObjectTraceHook &&
  1552                            (!obj->compartment()->options().getTrace() ||
  1553                             !obj->isOwnGlobal())),
  1554                          clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS);
  1555             clasp->trace(this, obj);
  1558         if (!shape->isNative())
  1559             return;
  1561         unsigned nslots = obj->slotSpan();
  1563         if (!obj->hasEmptyElements()) {
  1564             vp = obj->getDenseElements();
  1565             end = vp + obj->getDenseInitializedLength();
  1566             if (!nslots)
  1567                 goto scan_value_array;
  1568             pushValueArray(obj, vp, end);
  1571         vp = obj->fixedSlots();
  1572         if (obj->slots) {
  1573             unsigned nfixed = obj->numFixedSlots();
  1574             if (nslots > nfixed) {
  1575                 pushValueArray(obj, vp, vp + nfixed);
  1576                 vp = obj->slots;
  1577                 end = vp + (nslots - nfixed);
  1578                 goto scan_value_array;
  1581         JS_ASSERT(nslots <= obj->numFixedSlots());
  1582         end = vp + nslots;
  1583         goto scan_value_array;
  1587 bool
  1588 GCMarker::drainMarkStack(SliceBudget &budget)
  1590 #ifdef DEBUG
  1591     JSRuntime *rt = runtime();
  1593     struct AutoCheckCompartment {
  1594         JSRuntime *runtime;
  1595         AutoCheckCompartment(JSRuntime *rt) : runtime(rt) {
  1596             JS_ASSERT(!rt->gcStrictCompartmentChecking);
  1597             runtime->gcStrictCompartmentChecking = true;
  1599         ~AutoCheckCompartment() { runtime->gcStrictCompartmentChecking = false; }
  1600     } acc(rt);
  1601 #endif
  1603     if (budget.isOverBudget())
  1604         return false;
  1606     for (;;) {
  1607         while (!stack.isEmpty()) {
  1608             processMarkStackTop(budget);
  1609             if (budget.isOverBudget()) {
  1610                 saveValueRanges();
  1611                 return false;
  1615         if (!hasDelayedChildren())
  1616             break;
  1618         /*
  1619          * Mark children of things that caused too deep recursion during the
  1620          * above tracing. Don't do this until we're done with everything
  1621          * else.
  1622          */
  1623         if (!markDelayedChildren(budget)) {
  1624             saveValueRanges();
  1625             return false;
  1629     return true;
  1632 void
  1633 js::TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
  1635     switch (kind) {
  1636       case JSTRACE_OBJECT:
  1637         MarkChildren(trc, static_cast<JSObject *>(thing));
  1638         break;
  1640       case JSTRACE_STRING:
  1641         MarkChildren(trc, static_cast<JSString *>(thing));
  1642         break;
  1644       case JSTRACE_SCRIPT:
  1645         MarkChildren(trc, static_cast<JSScript *>(thing));
  1646         break;
  1648       case JSTRACE_LAZY_SCRIPT:
  1649         MarkChildren(trc, static_cast<LazyScript *>(thing));
  1650         break;
  1652       case JSTRACE_SHAPE:
  1653         MarkChildren(trc, static_cast<Shape *>(thing));
  1654         break;
  1656       case JSTRACE_JITCODE:
  1657         MarkChildren(trc, (js::jit::JitCode *)thing);
  1658         break;
  1660       case JSTRACE_BASE_SHAPE:
  1661         MarkChildren(trc, static_cast<BaseShape *>(thing));
  1662         break;
  1664       case JSTRACE_TYPE_OBJECT:
  1665         MarkChildren(trc, (types::TypeObject *)thing);
  1666         break;
  1670 static void
  1671 UnmarkGrayGCThing(void *thing)
  1673     static_cast<js::gc::Cell *>(thing)->unmark(js::gc::GRAY);
  1676 static void
  1677 UnmarkGrayChildren(JSTracer *trc, void **thingp, JSGCTraceKind kind);
  1679 struct UnmarkGrayTracer : public JSTracer
  1681     /*
  1682      * We set eagerlyTraceWeakMaps to false because the cycle collector will fix
  1683      * up any color mismatches involving weakmaps when it runs.
  1684      */
  1685     UnmarkGrayTracer(JSRuntime *rt)
  1686       : JSTracer(rt, UnmarkGrayChildren, DoNotTraceWeakMaps),
  1687         tracingShape(false),
  1688         previousShape(nullptr),
  1689         unmarkedAny(false)
  1690     {}
  1692     UnmarkGrayTracer(JSTracer *trc, bool tracingShape)
  1693       : JSTracer(trc->runtime(), UnmarkGrayChildren, DoNotTraceWeakMaps),
  1694         tracingShape(tracingShape),
  1695         previousShape(nullptr),
  1696         unmarkedAny(false)
  1697     {}
  1699     /* True iff we are tracing the immediate children of a shape. */
  1700     bool tracingShape;
  1702     /* If tracingShape, shape child or nullptr. Otherwise, nullptr. */
  1703     void *previousShape;
  1705     /* Whether we unmarked anything. */
  1706     bool unmarkedAny;
  1707 };
  1709 /*
  1710  * The GC and CC are run independently. Consequently, the following sequence of
  1711  * events can occur:
  1712  * 1. GC runs and marks an object gray.
  1713  * 2. Some JS code runs that creates a pointer from a JS root to the gray
  1714  *    object. If we re-ran a GC at this point, the object would now be black.
  1715  * 3. Now we run the CC. It may think it can collect the gray object, even
  1716  *    though it's reachable from the JS heap.
  1718  * To prevent this badness, we unmark the gray bit of an object when it is
  1719  * accessed by callers outside XPConnect. This would cause the object to go
  1720  * black in step 2 above. This must be done on everything reachable from the
  1721  * object being returned. The following code takes care of the recursive
  1722  * re-coloring.
  1724  * There is an additional complication for certain kinds of edges that are not
  1725  * contained explicitly in the source object itself, such as from a weakmap key
  1726  * to its value, and from an object being watched by a watchpoint to the
  1727  * watchpoint's closure. These "implicit edges" are represented in some other
  1728  * container object, such as the weakmap or the watchpoint itself. In these
  1729  * cases, calling unmark gray on an object won't find all of its children.
  1731  * Handling these implicit edges has two parts:
  1732  * - A special pass enumerating all of the containers that know about the
  1733  *   implicit edges to fix any black-gray edges that have been created. This
  1734  *   is implemented in nsXPConnect::FixWeakMappingGrayBits.
  1735  * - To prevent any incorrectly gray objects from escaping to live JS outside
  1736  *   of the containers, we must add unmark-graying read barriers to these
  1737  *   containers.
  1738  */
  1739 static void
  1740 UnmarkGrayChildren(JSTracer *trc, void **thingp, JSGCTraceKind kind)
  1742     void *thing = *thingp;
  1743     int stackDummy;
  1744     if (!JS_CHECK_STACK_SIZE(trc->runtime()->mainThread.nativeStackLimit[StackForSystemCode], &stackDummy)) {
  1745         /*
  1746          * If we run out of stack, we take a more drastic measure: require that
  1747          * we GC again before the next CC.
  1748          */
  1749         trc->runtime()->gcGrayBitsValid = false;
  1750         return;
  1753     UnmarkGrayTracer *tracer = static_cast<UnmarkGrayTracer *>(trc);
  1754     if (!IsInsideNursery(trc->runtime(), thing)) {
  1755         if (!JS::GCThingIsMarkedGray(thing))
  1756             return;
  1758         UnmarkGrayGCThing(thing);
  1759         tracer->unmarkedAny = true;
  1762     /*
  1763      * Trace children of |thing|. If |thing| and its parent are both shapes,
  1764      * |thing| will get saved to mPreviousShape without being traced. The parent
  1765      * will later trace |thing|. This is done to avoid increasing the stack
  1766      * depth during shape tracing. It is safe to do because a shape can only
  1767      * have one child that is a shape.
  1768      */
  1769     UnmarkGrayTracer childTracer(tracer, kind == JSTRACE_SHAPE);
  1771     if (kind != JSTRACE_SHAPE) {
  1772         JS_TraceChildren(&childTracer, thing, kind);
  1773         JS_ASSERT(!childTracer.previousShape);
  1774         tracer->unmarkedAny |= childTracer.unmarkedAny;
  1775         return;
  1778     if (tracer->tracingShape) {
  1779         JS_ASSERT(!tracer->previousShape);
  1780         tracer->previousShape = thing;
  1781         return;
  1784     do {
  1785         JS_ASSERT(!JS::GCThingIsMarkedGray(thing));
  1786         JS_TraceChildren(&childTracer, thing, JSTRACE_SHAPE);
  1787         thing = childTracer.previousShape;
  1788         childTracer.previousShape = nullptr;
  1789     } while (thing);
  1790     tracer->unmarkedAny |= childTracer.unmarkedAny;
  1793 JS_FRIEND_API(bool)
  1794 JS::UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind)
  1796     JS_ASSERT(kind != JSTRACE_SHAPE);
  1798     JSRuntime *rt = static_cast<Cell *>(thing)->runtimeFromMainThread();
  1800     bool unmarkedArg = false;
  1801     if (!IsInsideNursery(rt, thing)) {
  1802         if (!JS::GCThingIsMarkedGray(thing))
  1803             return false;
  1805         UnmarkGrayGCThing(thing);
  1806         unmarkedArg = true;
  1809     UnmarkGrayTracer trc(rt);
  1810     JS_TraceChildren(&trc, thing, kind);
  1812     return unmarkedArg || trc.unmarkedAny;

mercurial