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