tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

GCAPI.cpp (24518B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      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/. */
      6 
      7 /*
      8 * API functions and methods used by the rest of SpiderMonkey and by embeddings.
      9 */
     10 
     11 #include "mozilla/TimeStamp.h"
     12 
     13 #include "jsapi.h"
     14 #include "jsfriendapi.h"
     15 
     16 #include "gc/GC.h"
     17 #include "gc/PublicIterators.h"
     18 #include "jit/JitZone.h"
     19 #include "js/HeapAPI.h"
     20 #include "js/Value.h"
     21 #include "util/DifferentialTesting.h"
     22 #include "vm/HelperThreads.h"
     23 #include "vm/Realm.h"
     24 #include "vm/Scope.h"
     25 
     26 #include "gc/Marking-inl.h"
     27 #include "gc/StableCellHasher-inl.h"
     28 #include "vm/GeckoProfiler-inl.h"
     29 #include "vm/JSContext-inl.h"
     30 
     31 using namespace js;
     32 using namespace js::gc;
     33 
     34 using mozilla::TimeStamp;
     35 
     36 extern JS_PUBLIC_API bool js::AddRawValueRoot(JSContext* cx, Value* vp,
     37                                              const char* name) {
     38  MOZ_ASSERT(vp);
     39  MOZ_ASSERT(name);
     40  bool ok = cx->runtime()->gc.addRoot(vp, name);
     41  if (!ok) {
     42    JS_ReportOutOfMemory(cx);
     43  }
     44  return ok;
     45 }
     46 
     47 extern JS_PUBLIC_API void js::RemoveRawValueRoot(JSContext* cx, Value* vp) {
     48  cx->runtime()->gc.removeRoot(vp);
     49 }
     50 
     51 JS_PUBLIC_API JS::HeapState JS::RuntimeHeapState() {
     52  return TlsContext.get()->runtime()->gc.heapState();
     53 }
     54 
     55 JS::AutoDisableGenerationalGC::AutoDisableGenerationalGC(JSContext* cx)
     56    : cx(cx) {
     57  if (!cx->generationalDisabled) {
     58    cx->runtime()->gc.evictNursery(JS::GCReason::DISABLE_GENERATIONAL_GC);
     59    cx->nursery().disable();
     60  }
     61  ++cx->generationalDisabled;
     62  MOZ_ASSERT(cx->nursery().isEmpty());
     63 }
     64 
     65 JS::AutoDisableGenerationalGC::~AutoDisableGenerationalGC() {
     66  if (--cx->generationalDisabled == 0 &&
     67      cx->runtime()->gc.tunables.gcMaxNurseryBytes() > 0) {
     68    cx->nursery().enable();
     69  }
     70 }
     71 
     72 JS_PUBLIC_API bool JS::IsGenerationalGCEnabled(JSRuntime* rt) {
     73  return !rt->mainContextFromOwnThread()->generationalDisabled;
     74 }
     75 
     76 AutoDisableCompactingGC::AutoDisableCompactingGC(JSContext* cx) : cx(cx) {
     77  ++cx->compactingDisabledCount;
     78  if (cx->runtime()->gc.isIncrementalGCInProgress() &&
     79      cx->runtime()->gc.isCompactingGc()) {
     80    FinishGC(cx);
     81  }
     82 }
     83 
     84 AutoDisableCompactingGC::~AutoDisableCompactingGC() {
     85  MOZ_ASSERT(cx->compactingDisabledCount > 0);
     86  --cx->compactingDisabledCount;
     87 }
     88 
     89 #ifdef DEBUG
     90 
     91 /* Should only be called manually under gdb */
     92 void PreventGCDuringInteractiveDebug() { TlsContext.get()->suppressGC++; }
     93 
     94 #endif
     95 
     96 void js::ReleaseAllJITCode(JS::GCContext* gcx) {
     97  js::CancelOffThreadCompile(gcx->runtime());
     98 
     99  for (ZonesIter zone(gcx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
    100    zone->forceDiscardJitCode(gcx);
    101    if (jit::JitZone* jitZone = zone->jitZone()) {
    102      jitZone->discardStubs();
    103    }
    104  }
    105 
    106  gcx->runtime()->clearSelfHostedJitCache();
    107 }
    108 
    109 AutoSuppressGC::AutoSuppressGC(JSContext* cx)
    110    : suppressGC_(cx->suppressGC.ref()) {
    111  suppressGC_++;
    112 }
    113 
    114 #ifdef DEBUG
    115 AutoDisableProxyCheck::AutoDisableProxyCheck() {
    116  TlsContext.get()->disableStrictProxyChecking();
    117 }
    118 
    119 AutoDisableProxyCheck::~AutoDisableProxyCheck() {
    120  TlsContext.get()->enableStrictProxyChecking();
    121 }
    122 
    123 JS_PUBLIC_API void JS::AssertGCThingMustBeTenured(JSObject* obj) {
    124  MOZ_ASSERT(obj->isTenured());
    125  MOZ_ASSERT(obj->getClass()->hasFinalize() &&
    126             !(obj->getClass()->flags & JSCLASS_SKIP_NURSERY_FINALIZE));
    127 }
    128 
    129 JS_PUBLIC_API void JS::AssertGCThingIsNotNurseryAllocable(Cell* cell) {
    130  MOZ_ASSERT(cell);
    131  MOZ_ASSERT(!cell->is<JSObject>() && !cell->is<JSString>() &&
    132             !cell->is<JS::BigInt>() && !cell->is<GetterSetter>());
    133 }
    134 
    135 JS_PUBLIC_API void js::gc::AssertGCThingHasType(js::gc::Cell* cell,
    136                                                JS::TraceKind kind) {
    137  if (!cell) {
    138    MOZ_ASSERT(kind == JS::TraceKind::Null);
    139    return;
    140  }
    141 
    142  MOZ_ASSERT(IsCellPointerValid(cell));
    143  MOZ_ASSERT(cell->getTraceKind() == kind);
    144 }
    145 #endif
    146 
    147 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    148 
    149 JS::AutoAssertNoGC::AutoAssertNoGC(JSContext* maybecx) {
    150  if (maybecx) {
    151    cx_ = maybecx;
    152  } else if (TlsContext.initialized()) {
    153    cx_ = TlsContext.get();
    154  } else {
    155    cx_ = nullptr;
    156  }
    157  if (cx_) {
    158    cx_->inUnsafeRegion++;
    159  }
    160 }
    161 
    162 JS::AutoAssertNoGC::~AutoAssertNoGC() { reset(); }
    163 
    164 void JS::AutoAssertNoGC::reset() {
    165  if (cx_) {
    166    MOZ_ASSERT(cx_->inUnsafeRegion > 0);
    167    cx_->inUnsafeRegion--;
    168    cx_ = nullptr;
    169  }
    170 }
    171 
    172 #endif  // MOZ_DIAGNOSTIC_ASSERT_ENABLED
    173 
    174 #ifdef DEBUG
    175 
    176 JS::AutoEnterCycleCollection::AutoEnterCycleCollection(JSRuntime* rt)
    177    : runtime_(rt) {
    178  MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
    179  MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
    180  runtime_->gc.heapState_ = HeapState::CycleCollecting;
    181 }
    182 
    183 JS::AutoEnterCycleCollection::~AutoEnterCycleCollection() {
    184  MOZ_ASSERT(JS::RuntimeHeapIsCycleCollecting());
    185  runtime_->gc.heapState_ = HeapState::Idle;
    186 }
    187 
    188 JS::AutoAssertGCCallback::AutoAssertGCCallback() : AutoSuppressGCAnalysis() {
    189  MOZ_ASSERT(JS::RuntimeHeapIsCollecting());
    190 }
    191 
    192 #endif  // DEBUG
    193 
    194 JS_PUBLIC_API const char* JS::GCTraceKindToAscii(JS::TraceKind kind) {
    195  switch (kind) {
    196 #define MAP_NAME(name, _0, _1, _2) \
    197  case JS::TraceKind::name:        \
    198    return "JS " #name;
    199    JS_FOR_EACH_TRACEKIND(MAP_NAME);
    200 #undef MAP_NAME
    201    default:
    202      return "Invalid";
    203  }
    204 }
    205 
    206 JS_PUBLIC_API size_t JS::GCTraceKindSize(JS::TraceKind kind) {
    207  switch (kind) {
    208 #define MAP_SIZE(name, type, _0, _1) \
    209  case JS::TraceKind::name:          \
    210    return sizeof(type);
    211    JS_FOR_EACH_TRACEKIND(MAP_SIZE);
    212 #undef MAP_SIZE
    213    default:
    214      return 0;
    215  }
    216 }
    217 
    218 JS::GCCellPtr::GCCellPtr(const Value& v)
    219    : GCCellPtr(v.toGCThing(), v.traceKind()) {}
    220 
    221 JS::TraceKind JS::GCCellPtr::outOfLineKind() const {
    222  MOZ_ASSERT((ptr & OutOfLineTraceKindMask) == OutOfLineTraceKindMask);
    223  MOZ_ASSERT(asCell()->isTenured());
    224  return MapAllocToTraceKind(asCell()->asTenured().getAllocKind());
    225 }
    226 
    227 JS_PUBLIC_API void JS::PrepareZoneForGC(JSContext* cx, Zone* zone) {
    228  AssertHeapIsIdle();
    229  CHECK_THREAD(cx);
    230 
    231  // If we got the zone from a shared atom, we may have the wrong atoms zone
    232  // here.
    233  if (zone->isAtomsZone()) {
    234    zone = cx->runtime()->atomsZone();
    235  }
    236 
    237  MOZ_ASSERT(cx->runtime()->gc.hasZone(zone));
    238  zone->scheduleGC();
    239 }
    240 
    241 JS_PUBLIC_API void JS::PrepareForFullGC(JSContext* cx) {
    242  AssertHeapIsIdle();
    243  CHECK_THREAD(cx);
    244 
    245  cx->runtime()->gc.fullGCRequested = true;
    246  for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
    247    zone->scheduleGC();
    248  }
    249 }
    250 
    251 JS_PUBLIC_API void JS::PrepareForIncrementalGC(JSContext* cx) {
    252  AssertHeapIsIdle();
    253  CHECK_THREAD(cx);
    254 
    255  if (!JS::IsIncrementalGCInProgress(cx)) {
    256    return;
    257  }
    258 
    259  for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
    260    if (zone->wasGCStarted()) {
    261      zone->scheduleGC();
    262    }
    263  }
    264 }
    265 
    266 JS_PUBLIC_API bool JS::IsGCScheduled(JSContext* cx) {
    267  AssertHeapIsIdle();
    268  CHECK_THREAD(cx);
    269 
    270  for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
    271    if (zone->isGCScheduled()) {
    272      return true;
    273    }
    274  }
    275 
    276  return false;
    277 }
    278 
    279 JS_PUBLIC_API void JS::SkipZoneForGC(JSContext* cx, Zone* zone) {
    280  AssertHeapIsIdle();
    281  CHECK_THREAD(cx);
    282  MOZ_ASSERT(cx->runtime()->gc.hasZone(zone));
    283 
    284  cx->runtime()->gc.fullGCRequested = false;
    285  zone->unscheduleGC();
    286 }
    287 
    288 static inline void CheckGCOptions(JS::GCOptions options) {
    289  MOZ_ASSERT(options == JS::GCOptions::Normal ||
    290             options == JS::GCOptions::Shrink ||
    291             options == JS::GCOptions::Shutdown);
    292 }
    293 
    294 JS_PUBLIC_API void JS::NonIncrementalGC(JSContext* cx, JS::GCOptions options,
    295                                        GCReason reason) {
    296  AssertHeapIsIdle();
    297  CHECK_THREAD(cx);
    298  CheckGCOptions(options);
    299 
    300  cx->runtime()->gc.gc(options, reason);
    301 
    302  MOZ_ASSERT(!IsIncrementalGCInProgress(cx));
    303 }
    304 
    305 JS_PUBLIC_API void JS::StartIncrementalGC(JSContext* cx, JS::GCOptions options,
    306                                          GCReason reason,
    307                                          const JS::SliceBudget& budget) {
    308  AssertHeapIsIdle();
    309  CHECK_THREAD(cx);
    310  CheckGCOptions(options);
    311 
    312  cx->runtime()->gc.startGC(options, reason, budget);
    313 }
    314 
    315 JS_PUBLIC_API void JS::IncrementalGCSlice(JSContext* cx, GCReason reason,
    316                                          const JS::SliceBudget& budget) {
    317  AssertHeapIsIdle();
    318  CHECK_THREAD(cx);
    319 
    320  cx->runtime()->gc.gcSlice(reason, budget);
    321 }
    322 
    323 JS_PUBLIC_API bool JS::IncrementalGCHasForegroundWork(JSContext* cx) {
    324  AssertHeapIsIdle();
    325  CHECK_THREAD(cx);
    326 
    327  return cx->runtime()->gc.hasForegroundWork();
    328 }
    329 
    330 JS_PUBLIC_API void JS::FinishIncrementalGC(JSContext* cx, GCReason reason) {
    331  AssertHeapIsIdle();
    332  CHECK_THREAD(cx);
    333 
    334  cx->runtime()->gc.finishGC(reason);
    335 }
    336 
    337 JS_PUBLIC_API void JS::AbortIncrementalGC(JSContext* cx) {
    338  AssertHeapIsIdle();
    339  CHECK_THREAD(cx);
    340 
    341  if (IsIncrementalGCInProgress(cx)) {
    342    cx->runtime()->gc.abortGC();
    343  }
    344 }
    345 
    346 char16_t* JS::GCDescription::formatSliceMessage(JSContext* cx) const {
    347  UniqueChars cstr = cx->runtime()->gc.stats().formatCompactSliceMessage();
    348 
    349  size_t nchars = strlen(cstr.get());
    350  UniqueTwoByteChars out(js_pod_malloc<char16_t>(nchars + 1));
    351  if (!out) {
    352    return nullptr;
    353  }
    354  out.get()[nchars] = 0;
    355 
    356  CopyAndInflateChars(out.get(), cstr.get(), nchars);
    357  return out.release();
    358 }
    359 
    360 char16_t* JS::GCDescription::formatSummaryMessage(JSContext* cx) const {
    361  UniqueChars cstr = cx->runtime()->gc.stats().formatCompactSummaryMessage();
    362 
    363  size_t nchars = strlen(cstr.get());
    364  UniqueTwoByteChars out(js_pod_malloc<char16_t>(nchars + 1));
    365  if (!out) {
    366    return nullptr;
    367  }
    368  out.get()[nchars] = 0;
    369 
    370  CopyAndInflateChars(out.get(), cstr.get(), nchars);
    371  return out.release();
    372 }
    373 
    374 JS::dbg::GarbageCollectionEvent::Ptr JS::GCDescription::toGCEvent(
    375    JSContext* cx) const {
    376  return JS::dbg::GarbageCollectionEvent::Create(
    377      cx->runtime(), cx->runtime()->gc.stats(),
    378      cx->runtime()->gc.majorGCCount());
    379 }
    380 
    381 TimeStamp JS::GCDescription::startTime(JSContext* cx) const {
    382  return cx->runtime()->gc.stats().start();
    383 }
    384 
    385 TimeStamp JS::GCDescription::endTime(JSContext* cx) const {
    386  return cx->runtime()->gc.stats().end();
    387 }
    388 
    389 TimeStamp JS::GCDescription::lastSliceStart(JSContext* cx) const {
    390  return cx->runtime()->gc.stats().slices().back().start;
    391 }
    392 
    393 TimeStamp JS::GCDescription::lastSliceEnd(JSContext* cx) const {
    394  return cx->runtime()->gc.stats().slices().back().end;
    395 }
    396 
    397 JS::UniqueChars JS::GCDescription::sliceToJSONProfiler(JSContext* cx) const {
    398  size_t slices = cx->runtime()->gc.stats().slices().length();
    399  MOZ_ASSERT(slices > 0);
    400  return cx->runtime()->gc.stats().renderJsonSlice(slices - 1);
    401 }
    402 
    403 JS::UniqueChars JS::GCDescription::formatJSONProfiler(JSContext* cx) const {
    404  return cx->runtime()->gc.stats().renderJsonMessage();
    405 }
    406 
    407 JS_PUBLIC_API JS::UniqueChars JS::MinorGcToJSON(JSContext* cx) {
    408  JSRuntime* rt = cx->runtime();
    409  return rt->gc.stats().renderNurseryJson();
    410 }
    411 
    412 JS_PUBLIC_API JS::GCSliceCallback JS::SetGCSliceCallback(
    413    JSContext* cx, GCSliceCallback callback) {
    414  return cx->runtime()->gc.setSliceCallback(callback);
    415 }
    416 
    417 JS_PUBLIC_API JS::DoCycleCollectionCallback JS::SetDoCycleCollectionCallback(
    418    JSContext* cx, JS::DoCycleCollectionCallback callback) {
    419  return cx->runtime()->gc.setDoCycleCollectionCallback(callback);
    420 }
    421 
    422 JS_PUBLIC_API bool JS::AddGCNurseryCollectionCallback(
    423    JSContext* cx, GCNurseryCollectionCallback callback, void* data) {
    424  return cx->runtime()->gc.addNurseryCollectionCallback(callback, data);
    425 }
    426 
    427 JS_PUBLIC_API void JS::RemoveGCNurseryCollectionCallback(
    428    JSContext* cx, GCNurseryCollectionCallback callback, void* data) {
    429  return cx->runtime()->gc.removeNurseryCollectionCallback(callback, data);
    430 }
    431 
    432 JS_PUBLIC_API void JS::SetLowMemoryState(JSContext* cx, bool newState) {
    433  return cx->runtime()->gc.setLowMemoryState(newState);
    434 }
    435 
    436 JS_PUBLIC_API bool JS::IsIncrementalGCEnabled(JSContext* cx) {
    437  return cx->runtime()->gc.isIncrementalGCEnabled();
    438 }
    439 
    440 JS_PUBLIC_API bool JS::IsIncrementalGCInProgress(JSContext* cx) {
    441  return cx->runtime()->gc.isIncrementalGCInProgress();
    442 }
    443 
    444 JS_PUBLIC_API bool JS::IsIncrementalGCInProgress(JSRuntime* rt) {
    445  return rt->gc.isIncrementalGCInProgress() &&
    446         !rt->gc.isVerifyPreBarriersEnabled();
    447 }
    448 
    449 JS_PUBLIC_API bool JS::IsIncrementalBarrierNeeded(JSContext* cx) {
    450  if (JS::RuntimeHeapIsBusy()) {
    451    return false;
    452  }
    453 
    454  auto state = cx->runtime()->gc.state();
    455  return state != gc::State::NotActive && state <= gc::State::Sweep;
    456 }
    457 
    458 JS_PUBLIC_API void JS::IncrementalPreWriteBarrier(JSObject* obj) {
    459  if (!obj) {
    460    return;
    461  }
    462 
    463  AutoGeckoProfilerEntry profilingStackFrame(
    464      TlsContext.get(), "IncrementalPreWriteBarrier(JSObject*)",
    465      JS::ProfilingCategoryPair::GCCC_Barrier);
    466  PreWriteBarrier(obj);
    467 }
    468 
    469 JS_PUBLIC_API void JS::IncrementalPreWriteBarrier(GCCellPtr thing) {
    470  if (!thing) {
    471    return;
    472  }
    473 
    474  AutoGeckoProfilerEntry profilingStackFrame(
    475      TlsContext.get(), "IncrementalPreWriteBarrier(GCCellPtr)",
    476      JS::ProfilingCategoryPair::GCCC_Barrier);
    477  CellPtrPreWriteBarrier(thing);
    478 }
    479 
    480 JS_PUBLIC_API bool JS::WasIncrementalGC(JSRuntime* rt) {
    481  return rt->gc.isIncrementalGc();
    482 }
    483 
    484 bool js::gc::CreateUniqueIdForNativeObject(NativeObject* nobj, uint64_t* uidp) {
    485  JSRuntime* runtime = nobj->runtimeFromMainThread();
    486  *uidp = NextCellUniqueId(runtime);
    487  return nobj->setUniqueId(runtime, *uidp);
    488 }
    489 
    490 bool js::gc::CreateUniqueIdForNonNativeObject(Cell* cell,
    491                                              UniqueIdMap::AddPtr ptr,
    492                                              uint64_t* uidp) {
    493  // If the cell is in the nursery, hopefully unlikely, then we need to tell the
    494  // nursery about it so that it can sweep the uid if the thing does not get
    495  // tenured.
    496  JSRuntime* runtime = cell->runtimeFromMainThread();
    497  if (IsInsideNursery(cell) &&
    498      !runtime->gc.nursery().addedUniqueIdToCell(cell)) {
    499    return false;
    500  }
    501 
    502  // Set a new uid on the cell.
    503  *uidp = NextCellUniqueId(runtime);
    504  return cell->zone()->uniqueIds().add(ptr, cell, *uidp);
    505 }
    506 
    507 uint64_t js::gc::NextCellUniqueId(JSRuntime* rt) {
    508  return rt->gc.nextCellUniqueId();
    509 }
    510 
    511 namespace js {
    512 
    513 static const struct GCParamInfo {
    514  const char* name;
    515  JSGCParamKey key;
    516  bool writable;
    517 } GCParameters[] = {
    518 #define DEFINE_PARAM_INFO(name, key, writable) {name, key, writable},
    519    FOR_EACH_GC_PARAM(DEFINE_PARAM_INFO)
    520 #undef DEFINE_PARAM_INFO
    521 };
    522 
    523 bool GetGCParameterInfo(const char* name, JSGCParamKey* keyOut,
    524                        bool* writableOut) {
    525  MOZ_ASSERT(keyOut);
    526  MOZ_ASSERT(writableOut);
    527 
    528  for (const GCParamInfo& info : GCParameters) {
    529    if (strcmp(name, info.name) == 0) {
    530      *keyOut = info.key;
    531      *writableOut = info.writable;
    532      return true;
    533    }
    534  }
    535 
    536  return false;
    537 }
    538 
    539 namespace gc {
    540 namespace MemInfo {
    541 
    542 static bool GCBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
    543  CallArgs args = CallArgsFromVp(argc, vp);
    544  args.rval().setNumber(double(cx->runtime()->gc.heapSize.bytes()));
    545  return true;
    546 }
    547 
    548 static bool MallocBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
    549  CallArgs args = CallArgsFromVp(argc, vp);
    550  size_t bytes = 0;
    551  for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
    552    bytes += zone->mallocHeapSize.bytes();
    553  }
    554  args.rval().setNumber(bytes);
    555  return true;
    556 }
    557 
    558 static bool GCMaxBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
    559  CallArgs args = CallArgsFromVp(argc, vp);
    560  args.rval().setNumber(double(cx->runtime()->gc.tunables.gcMaxBytes()));
    561  return true;
    562 }
    563 
    564 static bool GCHighFreqGetter(JSContext* cx, unsigned argc, Value* vp) {
    565  CallArgs args = CallArgsFromVp(argc, vp);
    566  args.rval().setBoolean(
    567      cx->runtime()->gc.schedulingState.inHighFrequencyGCMode());
    568  return true;
    569 }
    570 
    571 static bool GCNumberGetter(JSContext* cx, unsigned argc, Value* vp) {
    572  CallArgs args = CallArgsFromVp(argc, vp);
    573  args.rval().setNumber(double(cx->runtime()->gc.gcNumber()));
    574  return true;
    575 }
    576 
    577 static bool MajorGCCountGetter(JSContext* cx, unsigned argc, Value* vp) {
    578  CallArgs args = CallArgsFromVp(argc, vp);
    579  args.rval().setNumber(double(cx->runtime()->gc.majorGCCount()));
    580  return true;
    581 }
    582 
    583 static bool MinorGCCountGetter(JSContext* cx, unsigned argc, Value* vp) {
    584  CallArgs args = CallArgsFromVp(argc, vp);
    585  args.rval().setNumber(double(cx->runtime()->gc.minorGCCount()));
    586  return true;
    587 }
    588 
    589 static bool GCSliceCountGetter(JSContext* cx, unsigned argc, Value* vp) {
    590  CallArgs args = CallArgsFromVp(argc, vp);
    591  args.rval().setNumber(double(cx->runtime()->gc.gcSliceCount()));
    592  return true;
    593 }
    594 
    595 static bool GCCompartmentCount(JSContext* cx, unsigned argc, Value* vp) {
    596  CallArgs args = CallArgsFromVp(argc, vp);
    597  size_t count = 0;
    598  for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
    599    count += zone->compartments().length();
    600  }
    601 
    602  args.rval().setNumber(double(count));
    603  return true;
    604 }
    605 
    606 static bool GCLastStartReason(JSContext* cx, unsigned argc, Value* vp) {
    607  CallArgs args = CallArgsFromVp(argc, vp);
    608  const char* reason = ExplainGCReason(cx->runtime()->gc.lastStartReason());
    609  RootedString str(cx, JS_NewStringCopyZ(cx, reason));
    610  if (!str) {
    611    return false;
    612  }
    613 
    614  args.rval().setString(str);
    615  return true;
    616 }
    617 
    618 static bool ZoneGCBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
    619  CallArgs args = CallArgsFromVp(argc, vp);
    620  args.rval().setNumber(double(cx->zone()->gcHeapSize.bytes()));
    621  return true;
    622 }
    623 
    624 static bool ZoneGCTriggerBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
    625  CallArgs args = CallArgsFromVp(argc, vp);
    626  args.rval().setNumber(double(cx->zone()->gcHeapThreshold.startBytes()));
    627  return true;
    628 }
    629 
    630 static bool ZoneGCAllocTriggerGetter(JSContext* cx, unsigned argc, Value* vp) {
    631  CallArgs args = CallArgsFromVp(argc, vp);
    632  bool highFrequency =
    633      cx->runtime()->gc.schedulingState.inHighFrequencyGCMode();
    634  args.rval().setNumber(
    635      double(cx->zone()->gcHeapThreshold.eagerAllocTrigger(highFrequency)));
    636  return true;
    637 }
    638 
    639 static bool ZoneMallocBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
    640  CallArgs args = CallArgsFromVp(argc, vp);
    641  args.rval().setNumber(double(cx->zone()->mallocHeapSize.bytes()));
    642  return true;
    643 }
    644 
    645 static bool ZoneMallocTriggerBytesGetter(JSContext* cx, unsigned argc,
    646                                         Value* vp) {
    647  CallArgs args = CallArgsFromVp(argc, vp);
    648  args.rval().setNumber(double(cx->zone()->mallocHeapThreshold.startBytes()));
    649  return true;
    650 }
    651 
    652 static bool ZoneGCNumberGetter(JSContext* cx, unsigned argc, Value* vp) {
    653  CallArgs args = CallArgsFromVp(argc, vp);
    654  args.rval().setNumber(double(cx->runtime()->gc.gcNumber()));
    655  return true;
    656 }
    657 
    658 #ifdef DEBUG
    659 static bool DummyGetter(JSContext* cx, unsigned argc, Value* vp) {
    660  CallArgs args = CallArgsFromVp(argc, vp);
    661  args.rval().setUndefined();
    662  return true;
    663 }
    664 #endif
    665 
    666 } /* namespace MemInfo */
    667 
    668 JSObject* NewMemoryInfoObject(JSContext* cx) {
    669  RootedObject obj(cx, JS_NewObject(cx, nullptr));
    670  if (!obj) {
    671    return nullptr;
    672  }
    673 
    674  using namespace MemInfo;
    675  struct NamedGetter {
    676    const char* name;
    677    JSNative getter;
    678  } getters[] = {{"gcBytes", GCBytesGetter},
    679                 {"gcMaxBytes", GCMaxBytesGetter},
    680                 {"mallocBytes", MallocBytesGetter},
    681                 {"gcIsHighFrequencyMode", GCHighFreqGetter},
    682                 {"gcNumber", GCNumberGetter},
    683                 {"majorGCCount", MajorGCCountGetter},
    684                 {"minorGCCount", MinorGCCountGetter},
    685                 {"sliceCount", GCSliceCountGetter},
    686                 {"compartmentCount", GCCompartmentCount},
    687                 {"lastStartReason", GCLastStartReason}};
    688 
    689  for (auto pair : getters) {
    690    JSNative getter = pair.getter;
    691 
    692 #ifdef DEBUG
    693    if (js::SupportDifferentialTesting()) {
    694      getter = DummyGetter;
    695    }
    696 #endif
    697 
    698    if (!JS_DefineProperty(cx, obj, pair.name, getter, nullptr,
    699                           JSPROP_ENUMERATE)) {
    700      return nullptr;
    701    }
    702  }
    703 
    704  RootedObject zoneObj(cx, JS_NewObject(cx, nullptr));
    705  if (!zoneObj) {
    706    return nullptr;
    707  }
    708 
    709  if (!JS_DefineProperty(cx, obj, "zone", zoneObj, JSPROP_ENUMERATE)) {
    710    return nullptr;
    711  }
    712 
    713  struct NamedZoneGetter {
    714    const char* name;
    715    JSNative getter;
    716  } zoneGetters[] = {{"gcBytes", ZoneGCBytesGetter},
    717                     {"gcTriggerBytes", ZoneGCTriggerBytesGetter},
    718                     {"gcAllocTrigger", ZoneGCAllocTriggerGetter},
    719                     {"mallocBytes", ZoneMallocBytesGetter},
    720                     {"mallocTriggerBytes", ZoneMallocTriggerBytesGetter},
    721                     {"gcNumber", ZoneGCNumberGetter}};
    722 
    723  for (auto pair : zoneGetters) {
    724    JSNative getter = pair.getter;
    725 
    726 #ifdef DEBUG
    727    if (js::SupportDifferentialTesting()) {
    728      getter = DummyGetter;
    729    }
    730 #endif
    731 
    732    if (!JS_DefineProperty(cx, zoneObj, pair.name, getter, nullptr,
    733                           JSPROP_ENUMERATE)) {
    734      return nullptr;
    735    }
    736  }
    737 
    738  return obj;
    739 }
    740 
    741 const char* StateName(State state) {
    742  switch (state) {
    743 #define MAKE_CASE(name) \
    744  case State::name:     \
    745    return #name;
    746    GCSTATES(MAKE_CASE)
    747 #undef MAKE_CASE
    748  }
    749  MOZ_CRASH("Invalid gc::State enum value");
    750 }
    751 
    752 const char* StateName(JS::Zone::GCState state) {
    753  switch (state) {
    754    case JS::Zone::NoGC:
    755      return "NoGC";
    756    case JS::Zone::Prepare:
    757      return "Prepare";
    758    case JS::Zone::MarkBlackOnly:
    759      return "MarkBlackOnly";
    760    case JS::Zone::MarkBlackAndGray:
    761      return "MarkBlackAndGray";
    762    case JS::Zone::Sweep:
    763      return "Sweep";
    764    case JS::Zone::Finished:
    765      return "Finished";
    766    case JS::Zone::Compact:
    767      return "Compact";
    768    case JS::Zone::VerifyPreBarriers:
    769      return "VerifyPreBarriers";
    770    case JS::Zone::Limit:
    771      break;
    772  }
    773  MOZ_CRASH("Invalid Zone::GCState enum value");
    774 }
    775 
    776 const char* CellColorName(CellColor color) {
    777  switch (color) {
    778    case CellColor::White:
    779      return "white";
    780    case CellColor::Black:
    781      return "black";
    782    case CellColor::Gray:
    783      return "gray";
    784    default:
    785      MOZ_CRASH("Unexpected cell color");
    786  }
    787 }
    788 
    789 } /* namespace gc */
    790 } /* namespace js */
    791 
    792 JS_PUBLIC_API JS::GCContext* js::gc::GetGCContext(JSContext* cx) {
    793  CHECK_THREAD(cx);
    794  return cx->gcContext();
    795 }
    796 
    797 JS_PUBLIC_API void js::gc::SetPerformanceHint(JSContext* cx,
    798                                              PerformanceHint hint) {
    799  CHECK_THREAD(cx);
    800  MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
    801 
    802  cx->runtime()->gc.setPerformanceHint(hint);
    803 }
    804 
    805 AutoSelectGCHeap::AutoSelectGCHeap(JSContext* cx,
    806                                   size_t allowedNurseryCollections)
    807    : cx_(cx), allowedNurseryCollections_(allowedNurseryCollections) {
    808  if (!JS::AddGCNurseryCollectionCallback(cx, &NurseryCollectionCallback,
    809                                          this)) {
    810    cx_ = nullptr;
    811  }
    812 }
    813 
    814 AutoSelectGCHeap::~AutoSelectGCHeap() {
    815  if (cx_) {
    816    JS::RemoveGCNurseryCollectionCallback(cx_, &NurseryCollectionCallback,
    817                                          this);
    818  }
    819 }
    820 
    821 /* static */
    822 void AutoSelectGCHeap::NurseryCollectionCallback(JSContext* cx,
    823                                                 JS::GCNurseryProgress progress,
    824                                                 JS::GCReason reason,
    825                                                 void* data) {
    826  if (progress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END) {
    827    static_cast<AutoSelectGCHeap*>(data)->onNurseryCollectionEnd();
    828  }
    829 }
    830 
    831 void AutoSelectGCHeap::onNurseryCollectionEnd() {
    832  if (allowedNurseryCollections_ != 0) {
    833    allowedNurseryCollections_--;
    834    return;
    835  }
    836 
    837  heap_ = gc::Heap::Tenured;
    838 }
    839 
    840 JS_PUBLIC_API void js::gc::LockStoreBuffer(JSRuntime* runtime) {
    841  MOZ_ASSERT(runtime);
    842  runtime->gc.lockStoreBuffer();
    843 }
    844 
    845 JS_PUBLIC_API void js::gc::UnlockStoreBuffer(JSRuntime* runtime) {
    846  MOZ_ASSERT(runtime);
    847  runtime->gc.unlockStoreBuffer();
    848 }
    849 
    850 #ifdef JS_GC_ZEAL
    851 JS_PUBLIC_API void JS::GetGCZealBits(JSContext* cx, uint32_t* zealBits,
    852                                     uint32_t* frequency,
    853                                     uint32_t* nextScheduled) {
    854  cx->runtime()->gc.getZealBits(zealBits, frequency, nextScheduled);
    855 }
    856 
    857 JS_PUBLIC_API void JS::SetGCZeal(JSContext* cx, uint8_t zeal,
    858                                 uint32_t frequency) {
    859  cx->runtime()->gc.setZeal(zeal, frequency);
    860 }
    861 
    862 JS_PUBLIC_API void JS::UnsetGCZeal(JSContext* cx, uint8_t zeal) {
    863  cx->runtime()->gc.unsetZeal(zeal);
    864 }
    865 
    866 JS_PUBLIC_API void JS::ScheduleGC(JSContext* cx, uint32_t count) {
    867  cx->runtime()->gc.setNextScheduled(count);
    868 }
    869 #endif