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/JitRealm.h"
19 #include "js/HeapAPI.h"
20 #include "js/Value.h"
21 #include "vm/HelperThreads.h"
22 #include "vm/Realm.h"
23 
24 #include "gc/Marking-inl.h"
25 #include "vm/GeckoProfiler-inl.h"
26 #include "vm/JSContext-inl.h"
27 
28 using namespace js;
29 using namespace js::gc;
30 
31 using mozilla::TimeStamp;
32 
AddRawValueRoot(JSContext * cx,Value * vp,const char * name)33 extern JS_PUBLIC_API bool js::AddRawValueRoot(JSContext* cx, Value* vp,
34                                               const char* name) {
35   MOZ_ASSERT(vp);
36   MOZ_ASSERT(name);
37   bool ok = cx->runtime()->gc.addRoot(vp, name);
38   if (!ok) {
39     JS_ReportOutOfMemory(cx);
40   }
41   return ok;
42 }
43 
RemoveRawValueRoot(JSContext * cx,Value * vp)44 extern JS_PUBLIC_API void js::RemoveRawValueRoot(JSContext* cx, Value* vp) {
45   cx->runtime()->gc.removeRoot(vp);
46 }
47 
RuntimeHeapState()48 JS_PUBLIC_API JS::HeapState JS::RuntimeHeapState() {
49   return TlsContext.get()->runtime()->gc.heapState();
50 }
51 
AutoDisableGenerationalGC(JSContext * cx)52 JS::AutoDisableGenerationalGC::AutoDisableGenerationalGC(JSContext* cx)
53     : cx(cx) {
54   if (!cx->generationalDisabled) {
55     cx->runtime()->gc.evictNursery(JS::GCReason::DISABLE_GENERATIONAL_GC);
56     cx->nursery().disable();
57   }
58   ++cx->generationalDisabled;
59 }
60 
~AutoDisableGenerationalGC()61 JS::AutoDisableGenerationalGC::~AutoDisableGenerationalGC() {
62   if (--cx->generationalDisabled == 0 &&
63       cx->runtime()->gc.tunables.gcMaxNurseryBytes() > 0) {
64     cx->nursery().enable();
65   }
66 }
67 
IsGenerationalGCEnabled(JSRuntime * rt)68 JS_PUBLIC_API bool JS::IsGenerationalGCEnabled(JSRuntime* rt) {
69   return !rt->mainContextFromOwnThread()->generationalDisabled;
70 }
71 
AutoDisableCompactingGC(JSContext * cx)72 AutoDisableCompactingGC::AutoDisableCompactingGC(JSContext* cx) : cx(cx) {
73   ++cx->compactingDisabledCount;
74   if (cx->runtime()->gc.isIncrementalGCInProgress() &&
75       cx->runtime()->gc.isCompactingGc()) {
76     FinishGC(cx);
77   }
78 }
79 
~AutoDisableCompactingGC()80 AutoDisableCompactingGC::~AutoDisableCompactingGC() {
81   MOZ_ASSERT(cx->compactingDisabledCount > 0);
82   --cx->compactingDisabledCount;
83 }
84 
85 #ifdef DEBUG
86 
87 /* Should only be called manually under gdb */
PreventGCDuringInteractiveDebug()88 void PreventGCDuringInteractiveDebug() { TlsContext.get()->suppressGC++; }
89 
90 #endif
91 
ReleaseAllJITCode(JSFreeOp * fop)92 void js::ReleaseAllJITCode(JSFreeOp* fop) {
93   js::CancelOffThreadIonCompile(fop->runtime());
94 
95   for (ZonesIter zone(fop->runtime(), SkipAtoms); !zone.done(); zone.next()) {
96     zone->setPreservingCode(false);
97     zone->discardJitCode(fop);
98   }
99 
100   for (RealmsIter realm(fop->runtime()); !realm.done(); realm.next()) {
101     if (jit::JitRealm* jitRealm = realm->jitRealm()) {
102       jitRealm->discardStubs();
103     }
104   }
105 }
106 
AutoSuppressGC(JSContext * cx)107 AutoSuppressGC::AutoSuppressGC(JSContext* cx)
108     : suppressGC_(cx->suppressGC.ref()) {
109   suppressGC_++;
110 }
111 
112 #ifdef DEBUG
AutoDisableProxyCheck()113 AutoDisableProxyCheck::AutoDisableProxyCheck() {
114   TlsContext.get()->disableStrictProxyChecking();
115 }
116 
~AutoDisableProxyCheck()117 AutoDisableProxyCheck::~AutoDisableProxyCheck() {
118   TlsContext.get()->enableStrictProxyChecking();
119 }
120 
AssertGCThingMustBeTenured(JSObject * obj)121 JS_PUBLIC_API void JS::AssertGCThingMustBeTenured(JSObject* obj) {
122   MOZ_ASSERT(obj->isTenured() &&
123              (!IsNurseryAllocable(obj->asTenured().getAllocKind()) ||
124               obj->getClass()->hasFinalize()));
125 }
126 
AssertGCThingIsNotNurseryAllocable(Cell * cell)127 JS_PUBLIC_API void JS::AssertGCThingIsNotNurseryAllocable(Cell* cell) {
128   MOZ_ASSERT(cell);
129   MOZ_ASSERT(!cell->is<JSObject>() && !cell->is<JSString>() &&
130              !cell->is<JS::BigInt>());
131 }
132 
AssertGCThingHasType(js::gc::Cell * cell,JS::TraceKind kind)133 JS_PUBLIC_API void js::gc::AssertGCThingHasType(js::gc::Cell* cell,
134                                                 JS::TraceKind kind) {
135   if (!cell) {
136     MOZ_ASSERT(kind == JS::TraceKind::Null);
137     return;
138   }
139 
140   MOZ_ASSERT(IsCellPointerValid(cell));
141   MOZ_ASSERT(cell->getTraceKind() == kind);
142 }
143 #endif
144 
145 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
146 
AutoAssertNoGC(JSContext * maybecx)147 JS::AutoAssertNoGC::AutoAssertNoGC(JSContext* maybecx)
148     : cx_(maybecx ? maybecx : TlsContext.get()) {
149   if (cx_) {
150     cx_->inUnsafeRegion++;
151   }
152 }
153 
~AutoAssertNoGC()154 JS::AutoAssertNoGC::~AutoAssertNoGC() {
155   if (cx_) {
156     MOZ_ASSERT(cx_->inUnsafeRegion > 0);
157     cx_->inUnsafeRegion--;
158   }
159 }
160 
161 #endif  // MOZ_DIAGNOSTIC_ASSERT_ENABLED
162 
163 #ifdef DEBUG
164 
AutoEnterCycleCollection(JSRuntime * rt)165 JS::AutoEnterCycleCollection::AutoEnterCycleCollection(JSRuntime* rt)
166     : runtime_(rt) {
167   MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
168   MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
169   runtime_->gc.heapState_ = HeapState::CycleCollecting;
170 }
171 
~AutoEnterCycleCollection()172 JS::AutoEnterCycleCollection::~AutoEnterCycleCollection() {
173   MOZ_ASSERT(JS::RuntimeHeapIsCycleCollecting());
174   runtime_->gc.heapState_ = HeapState::Idle;
175 }
176 
AutoAssertGCCallback()177 JS::AutoAssertGCCallback::AutoAssertGCCallback() : AutoSuppressGCAnalysis() {
178   MOZ_ASSERT(JS::RuntimeHeapIsCollecting());
179 }
180 
181 #endif  // DEBUG
182 
GCTraceKindToAscii(JS::TraceKind kind)183 JS_PUBLIC_API const char* JS::GCTraceKindToAscii(JS::TraceKind kind) {
184   switch (kind) {
185 #define MAP_NAME(name, _0, _1, _2) \
186   case JS::TraceKind::name:        \
187     return "JS " #name;
188     JS_FOR_EACH_TRACEKIND(MAP_NAME);
189 #undef MAP_NAME
190     default:
191       return "Invalid";
192   }
193 }
194 
GCTraceKindSize(JS::TraceKind kind)195 JS_PUBLIC_API size_t JS::GCTraceKindSize(JS::TraceKind kind) {
196   switch (kind) {
197 #define MAP_SIZE(name, type, _0, _1) \
198   case JS::TraceKind::name:          \
199     return sizeof(type);
200     JS_FOR_EACH_TRACEKIND(MAP_SIZE);
201 #undef MAP_SIZE
202     default:
203       return 0;
204   }
205 }
206 
GCCellPtr(const Value & v)207 JS::GCCellPtr::GCCellPtr(const Value& v)
208     : GCCellPtr(v.toGCThing(), v.traceKind()) {}
209 
outOfLineKind() const210 JS::TraceKind JS::GCCellPtr::outOfLineKind() const {
211   MOZ_ASSERT((ptr & OutOfLineTraceKindMask) == OutOfLineTraceKindMask);
212   MOZ_ASSERT(asCell()->isTenured());
213   return MapAllocToTraceKind(asCell()->asTenured().getAllocKind());
214 }
215 
PrepareZoneForGC(JSContext * cx,Zone * zone)216 JS_PUBLIC_API void JS::PrepareZoneForGC(JSContext* cx, Zone* zone) {
217   AssertHeapIsIdle();
218   CHECK_THREAD(cx);
219   MOZ_ASSERT(cx->runtime()->gc.hasZone(zone));
220 
221   zone->scheduleGC();
222 }
223 
PrepareForFullGC(JSContext * cx)224 JS_PUBLIC_API void JS::PrepareForFullGC(JSContext* cx) {
225   AssertHeapIsIdle();
226   CHECK_THREAD(cx);
227 
228   for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
229     zone->scheduleGC();
230   }
231 }
232 
PrepareForIncrementalGC(JSContext * cx)233 JS_PUBLIC_API void JS::PrepareForIncrementalGC(JSContext* cx) {
234   AssertHeapIsIdle();
235   CHECK_THREAD(cx);
236 
237   if (!JS::IsIncrementalGCInProgress(cx)) {
238     return;
239   }
240 
241   for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
242     if (zone->wasGCStarted()) {
243       zone->scheduleGC();
244     }
245   }
246 }
247 
IsGCScheduled(JSContext * cx)248 JS_PUBLIC_API bool JS::IsGCScheduled(JSContext* cx) {
249   AssertHeapIsIdle();
250   CHECK_THREAD(cx);
251 
252   for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
253     if (zone->isGCScheduled()) {
254       return true;
255     }
256   }
257 
258   return false;
259 }
260 
SkipZoneForGC(JSContext * cx,Zone * zone)261 JS_PUBLIC_API void JS::SkipZoneForGC(JSContext* cx, Zone* zone) {
262   AssertHeapIsIdle();
263   CHECK_THREAD(cx);
264   MOZ_ASSERT(cx->runtime()->gc.hasZone(zone));
265 
266   zone->unscheduleGC();
267 }
268 
CheckGCOptions(JS::GCOptions options)269 static inline void CheckGCOptions(JS::GCOptions options) {
270   MOZ_ASSERT(options == JS::GCOptions::Normal ||
271              options == JS::GCOptions::Shrink ||
272              options == JS::GCOptions::Shutdown);
273 }
274 
NonIncrementalGC(JSContext * cx,JS::GCOptions options,GCReason reason)275 JS_PUBLIC_API void JS::NonIncrementalGC(JSContext* cx, JS::GCOptions options,
276                                         GCReason reason) {
277   AssertHeapIsIdle();
278   CHECK_THREAD(cx);
279   CheckGCOptions(options);
280 
281   cx->runtime()->gc.gc(options, reason);
282 
283   MOZ_ASSERT(!IsIncrementalGCInProgress(cx));
284 }
285 
StartIncrementalGC(JSContext * cx,JS::GCOptions options,GCReason reason,const js::SliceBudget & budget)286 JS_PUBLIC_API void JS::StartIncrementalGC(JSContext* cx, JS::GCOptions options,
287                                           GCReason reason,
288                                           const js::SliceBudget& budget) {
289   AssertHeapIsIdle();
290   CHECK_THREAD(cx);
291   CheckGCOptions(options);
292 
293   cx->runtime()->gc.startGC(options, reason, budget);
294 }
295 
IncrementalGCSlice(JSContext * cx,GCReason reason,const js::SliceBudget & budget)296 JS_PUBLIC_API void JS::IncrementalGCSlice(JSContext* cx, GCReason reason,
297                                           const js::SliceBudget& budget) {
298   AssertHeapIsIdle();
299   CHECK_THREAD(cx);
300 
301   cx->runtime()->gc.gcSlice(reason, budget);
302 }
303 
IncrementalGCHasForegroundWork(JSContext * cx)304 JS_PUBLIC_API bool JS::IncrementalGCHasForegroundWork(JSContext* cx) {
305   AssertHeapIsIdle();
306   CHECK_THREAD(cx);
307 
308   return cx->runtime()->gc.hasForegroundWork();
309 }
310 
FinishIncrementalGC(JSContext * cx,GCReason reason)311 JS_PUBLIC_API void JS::FinishIncrementalGC(JSContext* cx, GCReason reason) {
312   AssertHeapIsIdle();
313   CHECK_THREAD(cx);
314 
315   cx->runtime()->gc.finishGC(reason);
316 }
317 
AbortIncrementalGC(JSContext * cx)318 JS_PUBLIC_API void JS::AbortIncrementalGC(JSContext* cx) {
319   AssertHeapIsIdle();
320   CHECK_THREAD(cx);
321 
322   if (IsIncrementalGCInProgress(cx)) {
323     cx->runtime()->gc.abortGC();
324   }
325 }
326 
formatSliceMessage(JSContext * cx) const327 char16_t* JS::GCDescription::formatSliceMessage(JSContext* cx) const {
328   UniqueChars cstr = cx->runtime()->gc.stats().formatCompactSliceMessage();
329 
330   size_t nchars = strlen(cstr.get());
331   UniqueTwoByteChars out(js_pod_malloc<char16_t>(nchars + 1));
332   if (!out) {
333     return nullptr;
334   }
335   out.get()[nchars] = 0;
336 
337   CopyAndInflateChars(out.get(), cstr.get(), nchars);
338   return out.release();
339 }
340 
formatSummaryMessage(JSContext * cx) const341 char16_t* JS::GCDescription::formatSummaryMessage(JSContext* cx) const {
342   UniqueChars cstr = cx->runtime()->gc.stats().formatCompactSummaryMessage();
343 
344   size_t nchars = strlen(cstr.get());
345   UniqueTwoByteChars out(js_pod_malloc<char16_t>(nchars + 1));
346   if (!out) {
347     return nullptr;
348   }
349   out.get()[nchars] = 0;
350 
351   CopyAndInflateChars(out.get(), cstr.get(), nchars);
352   return out.release();
353 }
354 
toGCEvent(JSContext * cx) const355 JS::dbg::GarbageCollectionEvent::Ptr JS::GCDescription::toGCEvent(
356     JSContext* cx) const {
357   return JS::dbg::GarbageCollectionEvent::Create(
358       cx->runtime(), cx->runtime()->gc.stats(),
359       cx->runtime()->gc.majorGCCount());
360 }
361 
startTime(JSContext * cx) const362 TimeStamp JS::GCDescription::startTime(JSContext* cx) const {
363   return cx->runtime()->gc.stats().start();
364 }
365 
endTime(JSContext * cx) const366 TimeStamp JS::GCDescription::endTime(JSContext* cx) const {
367   return cx->runtime()->gc.stats().end();
368 }
369 
lastSliceStart(JSContext * cx) const370 TimeStamp JS::GCDescription::lastSliceStart(JSContext* cx) const {
371   return cx->runtime()->gc.stats().slices().back().start;
372 }
373 
lastSliceEnd(JSContext * cx) const374 TimeStamp JS::GCDescription::lastSliceEnd(JSContext* cx) const {
375   return cx->runtime()->gc.stats().slices().back().end;
376 }
377 
sliceToJSONProfiler(JSContext * cx) const378 JS::UniqueChars JS::GCDescription::sliceToJSONProfiler(JSContext* cx) const {
379   size_t slices = cx->runtime()->gc.stats().slices().length();
380   MOZ_ASSERT(slices > 0);
381   return cx->runtime()->gc.stats().renderJsonSlice(slices - 1);
382 }
383 
formatJSONProfiler(JSContext * cx) const384 JS::UniqueChars JS::GCDescription::formatJSONProfiler(JSContext* cx) const {
385   return cx->runtime()->gc.stats().renderJsonMessage();
386 }
387 
MinorGcToJSON(JSContext * cx)388 JS_PUBLIC_API JS::UniqueChars JS::MinorGcToJSON(JSContext* cx) {
389   JSRuntime* rt = cx->runtime();
390   return rt->gc.stats().renderNurseryJson();
391 }
392 
SetGCSliceCallback(JSContext * cx,GCSliceCallback callback)393 JS_PUBLIC_API JS::GCSliceCallback JS::SetGCSliceCallback(
394     JSContext* cx, GCSliceCallback callback) {
395   return cx->runtime()->gc.setSliceCallback(callback);
396 }
397 
SetDoCycleCollectionCallback(JSContext * cx,JS::DoCycleCollectionCallback callback)398 JS_PUBLIC_API JS::DoCycleCollectionCallback JS::SetDoCycleCollectionCallback(
399     JSContext* cx, JS::DoCycleCollectionCallback callback) {
400   return cx->runtime()->gc.setDoCycleCollectionCallback(callback);
401 }
402 
403 JS_PUBLIC_API JS::GCNurseryCollectionCallback
SetGCNurseryCollectionCallback(JSContext * cx,GCNurseryCollectionCallback callback)404 JS::SetGCNurseryCollectionCallback(JSContext* cx,
405                                    GCNurseryCollectionCallback callback) {
406   return cx->runtime()->gc.setNurseryCollectionCallback(callback);
407 }
408 
SetLowMemoryState(JSContext * cx,bool newState)409 JS_PUBLIC_API void JS::SetLowMemoryState(JSContext* cx, bool newState) {
410   return cx->runtime()->gc.setLowMemoryState(newState);
411 }
412 
DisableIncrementalGC(JSContext * cx)413 JS_PUBLIC_API void JS::DisableIncrementalGC(JSContext* cx) {
414   cx->runtime()->gc.disallowIncrementalGC();
415 }
416 
IsIncrementalGCEnabled(JSContext * cx)417 JS_PUBLIC_API bool JS::IsIncrementalGCEnabled(JSContext* cx) {
418   GCRuntime& gc = cx->runtime()->gc;
419   return gc.isIncrementalGCEnabled() && gc.isIncrementalGCAllowed();
420 }
421 
IsIncrementalGCInProgress(JSContext * cx)422 JS_PUBLIC_API bool JS::IsIncrementalGCInProgress(JSContext* cx) {
423   return cx->runtime()->gc.isIncrementalGCInProgress();
424 }
425 
IsIncrementalGCInProgress(JSRuntime * rt)426 JS_PUBLIC_API bool JS::IsIncrementalGCInProgress(JSRuntime* rt) {
427   return rt->gc.isIncrementalGCInProgress() &&
428          !rt->gc.isVerifyPreBarriersEnabled();
429 }
430 
IsIncrementalBarrierNeeded(JSContext * cx)431 JS_PUBLIC_API bool JS::IsIncrementalBarrierNeeded(JSContext* cx) {
432   if (JS::RuntimeHeapIsBusy()) {
433     return false;
434   }
435 
436   auto state = cx->runtime()->gc.state();
437   return state != gc::State::NotActive && state <= gc::State::Sweep;
438 }
439 
IncrementalPreWriteBarrier(JSObject * obj)440 JS_PUBLIC_API void JS::IncrementalPreWriteBarrier(JSObject* obj) {
441   if (!obj) {
442     return;
443   }
444 
445   AutoGeckoProfilerEntry profilingStackFrame(
446       TlsContext.get(), "IncrementalPreWriteBarrier(JSObject*)",
447       JS::ProfilingCategoryPair::GCCC_Barrier);
448   PreWriteBarrier(obj);
449 }
450 
IncrementalPreWriteBarrier(GCCellPtr thing)451 JS_PUBLIC_API void JS::IncrementalPreWriteBarrier(GCCellPtr thing) {
452   if (!thing) {
453     return;
454   }
455 
456   AutoGeckoProfilerEntry profilingStackFrame(
457       TlsContext.get(), "IncrementalPreWriteBarrier(GCCellPtr)",
458       JS::ProfilingCategoryPair::GCCC_Barrier);
459   CellPtrPreWriteBarrier(thing);
460 }
461 
WasIncrementalGC(JSRuntime * rt)462 JS_PUBLIC_API bool JS::WasIncrementalGC(JSRuntime* rt) {
463   return rt->gc.isIncrementalGc();
464 }
465 
NextCellUniqueId(JSRuntime * rt)466 uint64_t js::gc::NextCellUniqueId(JSRuntime* rt) {
467   return rt->gc.nextCellUniqueId();
468 }
469 
470 namespace js {
471 namespace gc {
472 namespace MemInfo {
473 
GCBytesGetter(JSContext * cx,unsigned argc,Value * vp)474 static bool GCBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
475   CallArgs args = CallArgsFromVp(argc, vp);
476   args.rval().setNumber(double(cx->runtime()->gc.heapSize.bytes()));
477   return true;
478 }
479 
MallocBytesGetter(JSContext * cx,unsigned argc,Value * vp)480 static bool MallocBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
481   CallArgs args = CallArgsFromVp(argc, vp);
482   double bytes = 0;
483   for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
484     bytes += zone->mallocHeapSize.bytes();
485   }
486   args.rval().setNumber(bytes);
487   return true;
488 }
489 
GCMaxBytesGetter(JSContext * cx,unsigned argc,Value * vp)490 static bool GCMaxBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
491   CallArgs args = CallArgsFromVp(argc, vp);
492   args.rval().setNumber(double(cx->runtime()->gc.tunables.gcMaxBytes()));
493   return true;
494 }
495 
GCHighFreqGetter(JSContext * cx,unsigned argc,Value * vp)496 static bool GCHighFreqGetter(JSContext* cx, unsigned argc, Value* vp) {
497   CallArgs args = CallArgsFromVp(argc, vp);
498   args.rval().setBoolean(
499       cx->runtime()->gc.schedulingState.inHighFrequencyGCMode());
500   return true;
501 }
502 
GCNumberGetter(JSContext * cx,unsigned argc,Value * vp)503 static bool GCNumberGetter(JSContext* cx, unsigned argc, Value* vp) {
504   CallArgs args = CallArgsFromVp(argc, vp);
505   args.rval().setNumber(double(cx->runtime()->gc.gcNumber()));
506   return true;
507 }
508 
MajorGCCountGetter(JSContext * cx,unsigned argc,Value * vp)509 static bool MajorGCCountGetter(JSContext* cx, unsigned argc, Value* vp) {
510   CallArgs args = CallArgsFromVp(argc, vp);
511   args.rval().setNumber(double(cx->runtime()->gc.majorGCCount()));
512   return true;
513 }
514 
MinorGCCountGetter(JSContext * cx,unsigned argc,Value * vp)515 static bool MinorGCCountGetter(JSContext* cx, unsigned argc, Value* vp) {
516   CallArgs args = CallArgsFromVp(argc, vp);
517   args.rval().setNumber(double(cx->runtime()->gc.minorGCCount()));
518   return true;
519 }
520 
GCSliceCountGetter(JSContext * cx,unsigned argc,Value * vp)521 static bool GCSliceCountGetter(JSContext* cx, unsigned argc, Value* vp) {
522   CallArgs args = CallArgsFromVp(argc, vp);
523   args.rval().setNumber(double(cx->runtime()->gc.gcSliceCount()));
524   return true;
525 }
526 
GCCompartmentCount(JSContext * cx,unsigned argc,Value * vp)527 static bool GCCompartmentCount(JSContext* cx, unsigned argc, Value* vp) {
528   CallArgs args = CallArgsFromVp(argc, vp);
529   size_t count = 0;
530   for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
531     count += zone->compartments().length();
532   }
533 
534   args.rval().setNumber(double(count));
535   return true;
536 }
537 
GCLastStartReason(JSContext * cx,unsigned argc,Value * vp)538 static bool GCLastStartReason(JSContext* cx, unsigned argc, Value* vp) {
539   CallArgs args = CallArgsFromVp(argc, vp);
540   const char* reason = ExplainGCReason(cx->runtime()->gc.lastStartReason());
541   RootedString str(cx, JS_NewStringCopyZ(cx, reason));
542   if (!str) {
543     return false;
544   }
545 
546   args.rval().setString(str);
547   return true;
548 }
549 
ZoneGCBytesGetter(JSContext * cx,unsigned argc,Value * vp)550 static bool ZoneGCBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
551   CallArgs args = CallArgsFromVp(argc, vp);
552   args.rval().setNumber(double(cx->zone()->gcHeapSize.bytes()));
553   return true;
554 }
555 
ZoneGCTriggerBytesGetter(JSContext * cx,unsigned argc,Value * vp)556 static bool ZoneGCTriggerBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
557   CallArgs args = CallArgsFromVp(argc, vp);
558   args.rval().setNumber(double(cx->zone()->gcHeapThreshold.startBytes()));
559   return true;
560 }
561 
ZoneGCAllocTriggerGetter(JSContext * cx,unsigned argc,Value * vp)562 static bool ZoneGCAllocTriggerGetter(JSContext* cx, unsigned argc, Value* vp) {
563   CallArgs args = CallArgsFromVp(argc, vp);
564   bool highFrequency =
565       cx->runtime()->gc.schedulingState.inHighFrequencyGCMode();
566   args.rval().setNumber(
567       double(cx->zone()->gcHeapThreshold.eagerAllocTrigger(highFrequency)));
568   return true;
569 }
570 
ZoneMallocBytesGetter(JSContext * cx,unsigned argc,Value * vp)571 static bool ZoneMallocBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
572   CallArgs args = CallArgsFromVp(argc, vp);
573   args.rval().setNumber(double(cx->zone()->mallocHeapSize.bytes()));
574   return true;
575 }
576 
ZoneMallocTriggerBytesGetter(JSContext * cx,unsigned argc,Value * vp)577 static bool ZoneMallocTriggerBytesGetter(JSContext* cx, unsigned argc,
578                                          Value* vp) {
579   CallArgs args = CallArgsFromVp(argc, vp);
580   args.rval().setNumber(double(cx->zone()->mallocHeapThreshold.startBytes()));
581   return true;
582 }
583 
ZoneGCNumberGetter(JSContext * cx,unsigned argc,Value * vp)584 static bool ZoneGCNumberGetter(JSContext* cx, unsigned argc, Value* vp) {
585   CallArgs args = CallArgsFromVp(argc, vp);
586   args.rval().setNumber(double(cx->zone()->gcNumber()));
587   return true;
588 }
589 
590 #ifdef DEBUG
DummyGetter(JSContext * cx,unsigned argc,Value * vp)591 static bool DummyGetter(JSContext* cx, unsigned argc, Value* vp) {
592   CallArgs args = CallArgsFromVp(argc, vp);
593   args.rval().setUndefined();
594   return true;
595 }
596 #endif
597 
598 } /* namespace MemInfo */
599 
NewMemoryInfoObject(JSContext * cx)600 JSObject* NewMemoryInfoObject(JSContext* cx) {
601   RootedObject obj(cx, JS_NewObject(cx, nullptr));
602   if (!obj) {
603     return nullptr;
604   }
605 
606   using namespace MemInfo;
607   struct NamedGetter {
608     const char* name;
609     JSNative getter;
610   } getters[] = {{"gcBytes", GCBytesGetter},
611                  {"gcMaxBytes", GCMaxBytesGetter},
612                  {"mallocBytes", MallocBytesGetter},
613                  {"gcIsHighFrequencyMode", GCHighFreqGetter},
614                  {"gcNumber", GCNumberGetter},
615                  {"majorGCCount", MajorGCCountGetter},
616                  {"minorGCCount", MinorGCCountGetter},
617                  {"sliceCount", GCSliceCountGetter},
618                  {"compartmentCount", GCCompartmentCount},
619                  {"lastStartReason", GCLastStartReason}};
620 
621   for (auto pair : getters) {
622     JSNative getter = pair.getter;
623 
624 #ifdef DEBUG
625     if (js::SupportDifferentialTesting()) {
626       getter = DummyGetter;
627     }
628 #endif
629 
630     if (!JS_DefineProperty(cx, obj, pair.name, getter, nullptr,
631                            JSPROP_ENUMERATE)) {
632       return nullptr;
633     }
634   }
635 
636   RootedObject zoneObj(cx, JS_NewObject(cx, nullptr));
637   if (!zoneObj) {
638     return nullptr;
639   }
640 
641   if (!JS_DefineProperty(cx, obj, "zone", zoneObj, JSPROP_ENUMERATE)) {
642     return nullptr;
643   }
644 
645   struct NamedZoneGetter {
646     const char* name;
647     JSNative getter;
648   } zoneGetters[] = {{"gcBytes", ZoneGCBytesGetter},
649                      {"gcTriggerBytes", ZoneGCTriggerBytesGetter},
650                      {"gcAllocTrigger", ZoneGCAllocTriggerGetter},
651                      {"mallocBytes", ZoneMallocBytesGetter},
652                      {"mallocTriggerBytes", ZoneMallocTriggerBytesGetter},
653                      {"gcNumber", ZoneGCNumberGetter}};
654 
655   for (auto pair : zoneGetters) {
656     JSNative getter = pair.getter;
657 
658 #ifdef DEBUG
659     if (js::SupportDifferentialTesting()) {
660       getter = DummyGetter;
661     }
662 #endif
663 
664     if (!JS_DefineProperty(cx, zoneObj, pair.name, getter, nullptr,
665                            JSPROP_ENUMERATE)) {
666       return nullptr;
667     }
668   }
669 
670   return obj;
671 }
672 
StateName(State state)673 const char* StateName(State state) {
674   switch (state) {
675 #define MAKE_CASE(name) \
676   case State::name:     \
677     return #name;
678     GCSTATES(MAKE_CASE)
679 #undef MAKE_CASE
680   }
681   MOZ_CRASH("Invalid gc::State enum value");
682 }
683 
StateName(JS::Zone::GCState state)684 const char* StateName(JS::Zone::GCState state) {
685   switch (state) {
686     case JS::Zone::NoGC:
687       return "NoGC";
688     case JS::Zone::Prepare:
689       return "Prepare";
690     case JS::Zone::MarkBlackOnly:
691       return "MarkBlackOnly";
692     case JS::Zone::MarkBlackAndGray:
693       return "MarkBlackAndGray";
694     case JS::Zone::Sweep:
695       return "Sweep";
696     case JS::Zone::Finished:
697       return "Finished";
698     case JS::Zone::Compact:
699       return "Compact";
700   }
701   MOZ_CRASH("Invalid Zone::GCState enum value");
702 }
703 
704 } /* namespace gc */
705 } /* namespace js */
706 
FinalizeDeadNurseryObject(JSContext * cx,JSObject * obj)707 JS_PUBLIC_API void js::gc::FinalizeDeadNurseryObject(JSContext* cx,
708                                                      JSObject* obj) {
709   CHECK_THREAD(cx);
710   MOZ_ASSERT(JS::RuntimeHeapIsMinorCollecting());
711 
712   MOZ_ASSERT(obj);
713   MOZ_ASSERT(IsInsideNursery(obj));
714   MOZ_ASSERT(!IsForwarded(obj));
715 
716   const JSClass* jsClass = JS::GetClass(obj);
717   jsClass->doFinalize(cx->defaultFreeOp(), obj);
718 }
719 
SetPerformanceHint(JSContext * cx,PerformanceHint hint)720 JS_PUBLIC_API void js::gc::SetPerformanceHint(JSContext* cx,
721                                               PerformanceHint hint) {
722   CHECK_THREAD(cx);
723   MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
724 
725   cx->runtime()->gc.setPerformanceHint(hint);
726 }
727