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/. */
6 
7 #ifndef js_GCAPI_h
8 #define js_GCAPI_h
9 
10 #include "mozilla/UniquePtr.h"
11 #include "mozilla/Vector.h"
12 
13 #include "js/HeapAPI.h"
14 
15 namespace js {
16 namespace gc {
17 class GCRuntime;
18 } // namespace gc
19 namespace gcstats {
20 struct Statistics;
21 } // namespace gcstats
22 } // namespace js
23 
24 typedef enum JSGCMode {
25     /** Perform only global GCs. */
26     JSGC_MODE_GLOBAL = 0,
27 
28     /** Perform per-compartment GCs until too much garbage has accumulated. */
29     JSGC_MODE_COMPARTMENT = 1,
30 
31     /**
32      * Collect in short time slices rather than all at once. Implies
33      * JSGC_MODE_COMPARTMENT.
34      */
35     JSGC_MODE_INCREMENTAL = 2
36 } JSGCMode;
37 
38 /**
39  * Kinds of js_GC invocation.
40  */
41 typedef enum JSGCInvocationKind {
42     /* Normal invocation. */
43     GC_NORMAL = 0,
44 
45     /* Minimize GC triggers and release empty GC chunks right away. */
46     GC_SHRINK = 1
47 } JSGCInvocationKind;
48 
49 namespace JS {
50 
51 using mozilla::UniquePtr;
52 
53 #define GCREASONS(D)                            \
54     /* Reasons internal to the JS engine */     \
55     D(API)                                      \
56     D(EAGER_ALLOC_TRIGGER)                      \
57     D(DESTROY_RUNTIME)                          \
58     D(DESTROY_CONTEXT)                          \
59     D(LAST_DITCH)                               \
60     D(TOO_MUCH_MALLOC)                          \
61     D(ALLOC_TRIGGER)                            \
62     D(DEBUG_GC)                                 \
63     D(COMPARTMENT_REVIVED)                      \
64     D(RESET)                                    \
65     D(OUT_OF_NURSERY)                           \
66     D(EVICT_NURSERY)                            \
67     D(FULL_STORE_BUFFER)                        \
68     D(SHARED_MEMORY_LIMIT)                      \
69     D(PERIODIC_FULL_GC)                         \
70     D(INCREMENTAL_TOO_SLOW)                     \
71     D(ABORT_GC)                                 \
72                                                 \
73     /* These are reserved for future use. */    \
74     D(RESERVED0)                                \
75     D(RESERVED1)                                \
76     D(RESERVED2)                                \
77     D(RESERVED3)                                \
78     D(RESERVED4)                                \
79     D(RESERVED5)                                \
80     D(RESERVED6)                                \
81     D(RESERVED7)                                \
82     D(RESERVED8)                                \
83     D(RESERVED9)                                \
84     D(RESERVED10)                               \
85     D(RESERVED11)                               \
86     D(RESERVED12)                               \
87     D(RESERVED13)                               \
88     D(RESERVED14)                               \
89     D(RESERVED15)                               \
90                                                 \
91     /* Reasons from Firefox */                  \
92     D(DOM_WINDOW_UTILS)                         \
93     D(COMPONENT_UTILS)                          \
94     D(MEM_PRESSURE)                             \
95     D(CC_WAITING)                               \
96     D(CC_FORCED)                                \
97     D(LOAD_END)                                 \
98     D(POST_COMPARTMENT)                         \
99     D(PAGE_HIDE)                                \
100     D(NSJSCONTEXT_DESTROY)                      \
101     D(SET_NEW_DOCUMENT)                         \
102     D(SET_DOC_SHELL)                            \
103     D(DOM_UTILS)                                \
104     D(DOM_IPC)                                  \
105     D(DOM_WORKER)                               \
106     D(INTER_SLICE_GC)                           \
107     D(REFRESH_FRAME)                            \
108     D(FULL_GC_TIMER)                            \
109     D(SHUTDOWN_CC)                              \
110     D(FINISH_LARGE_EVALUATE)                    \
111     D(USER_INACTIVE)                            \
112     D(XPCONNECT_SHUTDOWN)
113 
114 namespace gcreason {
115 
116 /* GCReasons will end up looking like JSGC_MAYBEGC */
117 enum Reason {
118 #define MAKE_REASON(name) name,
119     GCREASONS(MAKE_REASON)
120 #undef MAKE_REASON
121     NO_REASON,
122     NUM_REASONS,
123 
124     /*
125      * For telemetry, we want to keep a fixed max bucket size over time so we
126      * don't have to switch histograms. 100 is conservative; as of this writing
127      * there are 52. But the cost of extra buckets seems to be low while the
128      * cost of switching histograms is high.
129      */
130     NUM_TELEMETRY_REASONS = 100
131 };
132 
133 } /* namespace gcreason */
134 
135 /*
136  * Zone GC:
137  *
138  * SpiderMonkey's GC is capable of performing a collection on an arbitrary
139  * subset of the zones in the system. This allows an embedding to minimize
140  * collection time by only collecting zones that have run code recently,
141  * ignoring the parts of the heap that are unlikely to have changed.
142  *
143  * When triggering a GC using one of the functions below, it is first necessary
144  * to select the zones to be collected. To do this, you can call
145  * PrepareZoneForGC on each zone, or you can call PrepareForFullGC to select
146  * all zones. Failing to select any zone is an error.
147  */
148 
149 /**
150  * Schedule the given zone to be collected as part of the next GC.
151  */
152 extern JS_PUBLIC_API(void)
153 PrepareZoneForGC(Zone* zone);
154 
155 /**
156  * Schedule all zones to be collected in the next GC.
157  */
158 extern JS_PUBLIC_API(void)
159 PrepareForFullGC(JSRuntime* rt);
160 
161 /**
162  * When performing an incremental GC, the zones that were selected for the
163  * previous incremental slice must be selected in subsequent slices as well.
164  * This function selects those slices automatically.
165  */
166 extern JS_PUBLIC_API(void)
167 PrepareForIncrementalGC(JSRuntime* rt);
168 
169 /**
170  * Returns true if any zone in the system has been scheduled for GC with one of
171  * the functions above or by the JS engine.
172  */
173 extern JS_PUBLIC_API(bool)
174 IsGCScheduled(JSRuntime* rt);
175 
176 /**
177  * Undoes the effect of the Prepare methods above. The given zone will not be
178  * collected in the next GC.
179  */
180 extern JS_PUBLIC_API(void)
181 SkipZoneForGC(Zone* zone);
182 
183 /*
184  * Non-Incremental GC:
185  *
186  * The following functions perform a non-incremental GC.
187  */
188 
189 /**
190  * Performs a non-incremental collection of all selected zones.
191  *
192  * If the gckind argument is GC_NORMAL, then some objects that are unreachable
193  * from the program may still be alive afterwards because of internal
194  * references; if GC_SHRINK is passed then caches and other temporary references
195  * to objects will be cleared and all unreferenced objects will be removed from
196  * the system.
197  */
198 extern JS_PUBLIC_API(void)
199 GCForReason(JSRuntime* rt, JSGCInvocationKind gckind, gcreason::Reason reason);
200 
201 /*
202  * Incremental GC:
203  *
204  * Incremental GC divides the full mark-and-sweep collection into multiple
205  * slices, allowing client JavaScript code to run between each slice. This
206  * allows interactive apps to avoid long collection pauses. Incremental GC does
207  * not make collection take less time, it merely spreads that time out so that
208  * the pauses are less noticable.
209  *
210  * For a collection to be carried out incrementally the following conditions
211  * must be met:
212  *  - The collection must be run by calling JS::IncrementalGC() rather than
213  *    JS_GC().
214  *  - The GC mode must have been set to JSGC_MODE_INCREMENTAL with
215  *    JS_SetGCParameter().
216  *
217  * Note: Even if incremental GC is enabled and working correctly,
218  *       non-incremental collections can still happen when low on memory.
219  */
220 
221 /**
222  * Begin an incremental collection and perform one slice worth of work. When
223  * this function returns, the collection may not be complete.
224  * IncrementalGCSlice() must be called repeatedly until
225  * !IsIncrementalGCInProgress(rt).
226  *
227  * Note: SpiderMonkey's GC is not realtime. Slices in practice may be longer or
228  *       shorter than the requested interval.
229  */
230 extern JS_PUBLIC_API(void)
231 StartIncrementalGC(JSRuntime* rt, JSGCInvocationKind gckind, gcreason::Reason reason,
232                    int64_t millis = 0);
233 
234 /**
235  * Perform a slice of an ongoing incremental collection. When this function
236  * returns, the collection may not be complete. It must be called repeatedly
237  * until !IsIncrementalGCInProgress(rt).
238  *
239  * Note: SpiderMonkey's GC is not realtime. Slices in practice may be longer or
240  *       shorter than the requested interval.
241  */
242 extern JS_PUBLIC_API(void)
243 IncrementalGCSlice(JSRuntime* rt, gcreason::Reason reason, int64_t millis = 0);
244 
245 /**
246  * If IsIncrementalGCInProgress(rt), this call finishes the ongoing collection
247  * by performing an arbitrarily long slice. If !IsIncrementalGCInProgress(rt),
248  * this is equivalent to GCForReason. When this function returns,
249  * IsIncrementalGCInProgress(rt) will always be false.
250  */
251 extern JS_PUBLIC_API(void)
252 FinishIncrementalGC(JSRuntime* rt, gcreason::Reason reason);
253 
254 /**
255  * If IsIncrementalGCInProgress(rt), this call aborts the ongoing collection and
256  * performs whatever work needs to be done to return the collector to its idle
257  * state. This may take an arbitrarily long time. When this function returns,
258  * IsIncrementalGCInProgress(rt) will always be false.
259  */
260 extern JS_PUBLIC_API(void)
261 AbortIncrementalGC(JSRuntime* rt);
262 
263 namespace dbg {
264 
265 // The `JS::dbg::GarbageCollectionEvent` class is essentially a view of the
266 // `js::gcstats::Statistics` data without the uber implementation-specific bits.
267 // It should generally be palatable for web developers.
268 class GarbageCollectionEvent
269 {
270     // The major GC number of the GC cycle this data pertains to.
271     uint64_t majorGCNumber_;
272 
273     // Reference to a non-owned, statically allocated C string. This is a very
274     // short reason explaining why a GC was triggered.
275     const char* reason;
276 
277     // Reference to a nullable, non-owned, statically allocated C string. If the
278     // collection was forced to be non-incremental, this is a short reason of
279     // why the GC could not perform an incremental collection.
280     const char* nonincrementalReason;
281 
282     // Represents a single slice of a possibly multi-slice incremental garbage
283     // collection.
284     struct Collection {
285         double startTimestamp;
286         double endTimestamp;
287     };
288 
289     // The set of garbage collection slices that made up this GC cycle.
290     mozilla::Vector<Collection> collections;
291 
292     GarbageCollectionEvent(const GarbageCollectionEvent& rhs) = delete;
293     GarbageCollectionEvent& operator=(const GarbageCollectionEvent& rhs) = delete;
294 
295   public:
GarbageCollectionEvent(uint64_t majorGCNum)296     explicit GarbageCollectionEvent(uint64_t majorGCNum)
297         : majorGCNumber_(majorGCNum)
298         , reason(nullptr)
299         , nonincrementalReason(nullptr)
300         , collections()
301     { }
302 
303     using Ptr = UniquePtr<GarbageCollectionEvent, DeletePolicy<GarbageCollectionEvent>>;
304     static Ptr Create(JSRuntime* rt, ::js::gcstats::Statistics& stats, uint64_t majorGCNumber);
305 
306     JSObject* toJSObject(JSContext* cx) const;
307 
majorGCNumber()308     uint64_t majorGCNumber() const { return majorGCNumber_; }
309 };
310 
311 } // namespace dbg
312 
313 enum GCProgress {
314     /*
315      * During non-incremental GC, the GC is bracketed by JSGC_CYCLE_BEGIN/END
316      * callbacks. During an incremental GC, the sequence of callbacks is as
317      * follows:
318      *   JSGC_CYCLE_BEGIN, JSGC_SLICE_END  (first slice)
319      *   JSGC_SLICE_BEGIN, JSGC_SLICE_END  (second slice)
320      *   ...
321      *   JSGC_SLICE_BEGIN, JSGC_CYCLE_END  (last slice)
322      */
323 
324     GC_CYCLE_BEGIN,
325     GC_SLICE_BEGIN,
326     GC_SLICE_END,
327     GC_CYCLE_END
328 };
329 
JS_PUBLIC_API(GCDescription)330 struct JS_PUBLIC_API(GCDescription) {
331     bool isCompartment_;
332     JSGCInvocationKind invocationKind_;
333     gcreason::Reason reason_;
334 
335     GCDescription(bool isCompartment, JSGCInvocationKind kind, gcreason::Reason reason)
336       : isCompartment_(isCompartment), invocationKind_(kind), reason_(reason) {}
337 
338     char16_t* formatSliceMessage(JSRuntime* rt) const;
339     char16_t* formatSummaryMessage(JSRuntime* rt) const;
340     char16_t* formatJSON(JSRuntime* rt, uint64_t timestamp) const;
341 
342     JS::dbg::GarbageCollectionEvent::Ptr toGCEvent(JSRuntime* rt) const;
343 };
344 
345 typedef void
346 (* GCSliceCallback)(JSRuntime* rt, GCProgress progress, const GCDescription& desc);
347 
348 /**
349  * The GC slice callback is called at the beginning and end of each slice. This
350  * callback may be used for GC notifications as well as to perform additional
351  * marking.
352  */
353 extern JS_PUBLIC_API(GCSliceCallback)
354 SetGCSliceCallback(JSRuntime* rt, GCSliceCallback callback);
355 
356 /**
357  * Incremental GC defaults to enabled, but may be disabled for testing or in
358  * embeddings that have not yet implemented barriers on their native classes.
359  * There is not currently a way to re-enable incremental GC once it has been
360  * disabled on the runtime.
361  */
362 extern JS_PUBLIC_API(void)
363 DisableIncrementalGC(JSRuntime* rt);
364 
365 /**
366  * Returns true if incremental GC is enabled. Simply having incremental GC
367  * enabled is not sufficient to ensure incremental collections are happening.
368  * See the comment "Incremental GC" above for reasons why incremental GC may be
369  * suppressed. Inspection of the "nonincremental reason" field of the
370  * GCDescription returned by GCSliceCallback may help narrow down the cause if
371  * collections are not happening incrementally when expected.
372  */
373 extern JS_PUBLIC_API(bool)
374 IsIncrementalGCEnabled(JSRuntime* rt);
375 
376 /**
377  * Returns true while an incremental GC is ongoing, both when actively
378  * collecting and between slices.
379  */
380 extern JS_PUBLIC_API(bool)
381 IsIncrementalGCInProgress(JSRuntime* rt);
382 
383 /*
384  * Returns true when writes to GC things must call an incremental (pre) barrier.
385  * This is generally only true when running mutator code in-between GC slices.
386  * At other times, the barrier may be elided for performance.
387  */
388 extern JS_PUBLIC_API(bool)
389 IsIncrementalBarrierNeeded(JSRuntime* rt);
390 
391 extern JS_PUBLIC_API(bool)
392 IsIncrementalBarrierNeeded(JSContext* cx);
393 
394 /*
395  * Notify the GC that a reference to a GC thing is about to be overwritten.
396  * These methods must be called if IsIncrementalBarrierNeeded.
397  */
398 extern JS_PUBLIC_API(void)
399 IncrementalReferenceBarrier(GCCellPtr thing);
400 
401 extern JS_PUBLIC_API(void)
402 IncrementalValueBarrier(const Value& v);
403 
404 extern JS_PUBLIC_API(void)
405 IncrementalObjectBarrier(JSObject* obj);
406 
407 /**
408  * Returns true if the most recent GC ran incrementally.
409  */
410 extern JS_PUBLIC_API(bool)
411 WasIncrementalGC(JSRuntime* rt);
412 
413 /*
414  * Generational GC:
415  *
416  * Note: Generational GC is not yet enabled by default. The following class
417  *       is non-functional unless SpiderMonkey was configured with
418  *       --enable-gcgenerational.
419  */
420 
421 /** Ensure that generational GC is disabled within some scope. */
JS_PUBLIC_API(AutoDisableGenerationalGC)422 class JS_PUBLIC_API(AutoDisableGenerationalGC)
423 {
424     js::gc::GCRuntime* gc;
425 
426   public:
427     explicit AutoDisableGenerationalGC(JSRuntime* rt);
428     ~AutoDisableGenerationalGC();
429 };
430 
431 /**
432  * Returns true if generational allocation and collection is currently enabled
433  * on the given runtime.
434  */
435 extern JS_PUBLIC_API(bool)
436 IsGenerationalGCEnabled(JSRuntime* rt);
437 
438 /**
439  * Returns the GC's "number". This does not correspond directly to the number
440  * of GCs that have been run, but is guaranteed to be monotonically increasing
441  * with GC activity.
442  */
443 extern JS_PUBLIC_API(size_t)
444 GetGCNumber();
445 
446 /**
447  * The GC does not immediately return the unused memory freed by a collection
448  * back to the system incase it is needed soon afterwards. This call forces the
449  * GC to return this memory immediately.
450  */
451 extern JS_PUBLIC_API(void)
452 ShrinkGCBuffers(JSRuntime* rt);
453 
454 /**
455  * Assert if a GC occurs while this class is live. This class does not disable
456  * the static rooting hazard analysis.
457  */
JS_PUBLIC_API(AutoAssertOnGC)458 class JS_PUBLIC_API(AutoAssertOnGC)
459 {
460 #ifdef DEBUG
461     js::gc::GCRuntime* gc;
462     size_t gcNumber;
463 
464   public:
465     AutoAssertOnGC();
466     explicit AutoAssertOnGC(JSRuntime* rt);
467     ~AutoAssertOnGC();
468 
469     static void VerifyIsSafeToGC(JSRuntime* rt);
470 #else
471   public:
472     AutoAssertOnGC() {}
473     explicit AutoAssertOnGC(JSRuntime* rt) {}
474     ~AutoAssertOnGC() {}
475 
476     static void VerifyIsSafeToGC(JSRuntime* rt) {}
477 #endif
478 };
479 
480 /**
481  * Assert if an allocation of a GC thing occurs while this class is live. This
482  * class does not disable the static rooting hazard analysis.
483  */
JS_PUBLIC_API(AutoAssertNoAlloc)484 class JS_PUBLIC_API(AutoAssertNoAlloc)
485 {
486 #ifdef JS_DEBUG
487     js::gc::GCRuntime* gc;
488 
489   public:
490     AutoAssertNoAlloc() : gc(nullptr) {}
491     explicit AutoAssertNoAlloc(JSRuntime* rt);
492     void disallowAlloc(JSRuntime* rt);
493     ~AutoAssertNoAlloc();
494 #else
495   public:
496     AutoAssertNoAlloc() {}
497     explicit AutoAssertNoAlloc(JSRuntime* rt) {}
498     void disallowAlloc(JSRuntime* rt) {}
499 #endif
500 };
501 
502 /**
503  * Disable the static rooting hazard analysis in the live region and assert if
504  * any allocation that could potentially trigger a GC occurs while this guard
505  * object is live. This is most useful to help the exact rooting hazard analysis
506  * in complex regions, since it cannot understand dataflow.
507  *
508  * Note: GC behavior is unpredictable even when deterministic and is generally
509  *       non-deterministic in practice. The fact that this guard has not
510  *       asserted is not a guarantee that a GC cannot happen in the guarded
511  *       region. As a rule, anyone performing a GC unsafe action should
512  *       understand the GC properties of all code in that region and ensure
513  *       that the hazard analysis is correct for that code, rather than relying
514  *       on this class.
515  */
JS_PUBLIC_API(AutoSuppressGCAnalysis)516 class JS_PUBLIC_API(AutoSuppressGCAnalysis) : public AutoAssertNoAlloc
517 {
518   public:
519     AutoSuppressGCAnalysis() : AutoAssertNoAlloc() {}
520     explicit AutoSuppressGCAnalysis(JSRuntime* rt) : AutoAssertNoAlloc(rt) {}
521 };
522 
523 /**
524  * Assert that code is only ever called from a GC callback, disable the static
525  * rooting hazard analysis and assert if any allocation that could potentially
526  * trigger a GC occurs while this guard object is live.
527  *
528  * This is useful to make the static analysis ignore code that runs in GC
529  * callbacks.
530  */
JS_PUBLIC_API(AutoAssertGCCallback)531 class JS_PUBLIC_API(AutoAssertGCCallback) : public AutoSuppressGCAnalysis
532 {
533   public:
534     explicit AutoAssertGCCallback(JSObject* obj);
535 };
536 
537 /**
538  * Place AutoCheckCannotGC in scopes that you believe can never GC. These
539  * annotations will be verified both dynamically via AutoAssertOnGC, and
540  * statically with the rooting hazard analysis (implemented by making the
541  * analysis consider AutoCheckCannotGC to be a GC pointer, and therefore
542  * complain if it is live across a GC call.) It is useful when dealing with
543  * internal pointers to GC things where the GC thing itself may not be present
544  * for the static analysis: e.g. acquiring inline chars from a JSString* on the
545  * heap.
546  */
JS_PUBLIC_API(AutoCheckCannotGC)547 class JS_PUBLIC_API(AutoCheckCannotGC) : public AutoAssertOnGC
548 {
549   public:
550     AutoCheckCannotGC() : AutoAssertOnGC() {}
551     explicit AutoCheckCannotGC(JSRuntime* rt) : AutoAssertOnGC(rt) {}
552 };
553 
554 /**
555  * Unsets the gray bit for anything reachable from |thing|. |kind| should not be
556  * JS::TraceKind::Shape. |thing| should be non-null.
557  */
558 extern JS_FRIEND_API(bool)
559 UnmarkGrayGCThingRecursively(GCCellPtr thing);
560 
561 } /* namespace JS */
562 
563 namespace js {
564 namespace gc {
565 
566 static MOZ_ALWAYS_INLINE void
ExposeGCThingToActiveJS(JS::GCCellPtr thing)567 ExposeGCThingToActiveJS(JS::GCCellPtr thing)
568 {
569     /*
570      * GC things residing in the nursery cannot be gray: they have no mark bits.
571      * All live objects in the nursery are moved to tenured at the beginning of
572      * each GC slice, so the gray marker never sees nursery things.
573      */
574     if (IsInsideNursery(thing.asCell()))
575         return;
576     JS::shadow::Runtime* rt = detail::GetGCThingRuntime(thing.unsafeAsUIntPtr());
577     if (IsIncrementalBarrierNeededOnTenuredGCThing(rt, thing))
578         JS::IncrementalReferenceBarrier(thing);
579     else if (JS::GCThingIsMarkedGray(thing))
580         JS::UnmarkGrayGCThingRecursively(thing);
581 }
582 
583 static MOZ_ALWAYS_INLINE void
MarkGCThingAsLive(JSRuntime * aRt,JS::GCCellPtr thing)584 MarkGCThingAsLive(JSRuntime* aRt, JS::GCCellPtr thing)
585 {
586     JS::shadow::Runtime* rt = JS::shadow::Runtime::asShadowRuntime(aRt);
587     /*
588      * Any object in the nursery will not be freed during any GC running at that time.
589      */
590     if (IsInsideNursery(thing.asCell()))
591         return;
592     if (IsIncrementalBarrierNeededOnTenuredGCThing(rt, thing))
593         JS::IncrementalReferenceBarrier(thing);
594 }
595 
596 } /* namespace gc */
597 } /* namespace js */
598 
599 namespace JS {
600 
601 /*
602  * This should be called when an object that is marked gray is exposed to the JS
603  * engine (by handing it to running JS code or writing it into live JS
604  * data). During incremental GC, since the gray bits haven't been computed yet,
605  * we conservatively mark the object black.
606  */
607 static MOZ_ALWAYS_INLINE void
ExposeObjectToActiveJS(JSObject * obj)608 ExposeObjectToActiveJS(JSObject* obj)
609 {
610     js::gc::ExposeGCThingToActiveJS(GCCellPtr(obj));
611 }
612 
613 static MOZ_ALWAYS_INLINE void
ExposeScriptToActiveJS(JSScript * script)614 ExposeScriptToActiveJS(JSScript* script)
615 {
616     js::gc::ExposeGCThingToActiveJS(GCCellPtr(script));
617 }
618 
619 /*
620  * If a GC is currently marking, mark the string black.
621  */
622 static MOZ_ALWAYS_INLINE void
MarkStringAsLive(Zone * zone,JSString * string)623 MarkStringAsLive(Zone* zone, JSString* string)
624 {
625     JSRuntime* rt = JS::shadow::Zone::asShadowZone(zone)->runtimeFromMainThread();
626     js::gc::MarkGCThingAsLive(rt, GCCellPtr(string));
627 }
628 
629 /*
630  * Internal to Firefox.
631  *
632  * Note: this is not related to the PokeGC in nsJSEnvironment.
633  */
634 extern JS_FRIEND_API(void)
635 PokeGC(JSRuntime* rt);
636 
637 /*
638  * Internal to Firefox.
639  */
640 extern JS_FRIEND_API(void)
641 NotifyDidPaint(JSRuntime* rt);
642 
643 } /* namespace JS */
644 
645 #endif /* js_GCAPI_h */
646