1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include "mozilla/ArrayUtils.h"
6 #include "mozilla/UniquePtr.h"
7
8 #include "jsapi-tests/tests.h"
9
10 static unsigned gSliceCallbackCount = 0;
11
NonIncrementalGCSliceCallback(JSContext * cx,JS::GCProgress progress,const JS::GCDescription & desc)12 static void NonIncrementalGCSliceCallback(JSContext* cx,
13 JS::GCProgress progress,
14 const JS::GCDescription& desc) {
15 using namespace JS;
16 static GCProgress expect[] = {GC_CYCLE_BEGIN, GC_SLICE_BEGIN, GC_SLICE_END,
17 GC_CYCLE_END};
18
19 MOZ_RELEASE_ASSERT(gSliceCallbackCount < mozilla::ArrayLength(expect));
20 MOZ_RELEASE_ASSERT(progress == expect[gSliceCallbackCount++]);
21 MOZ_RELEASE_ASSERT(desc.isZone_ == false);
22 MOZ_RELEASE_ASSERT(desc.invocationKind_ == GC_NORMAL);
23 MOZ_RELEASE_ASSERT(desc.reason_ == JS::GCReason::API);
24 if (progress == GC_CYCLE_END) {
25 mozilla::UniquePtr<char16_t> summary(desc.formatSummaryMessage(cx));
26 mozilla::UniquePtr<char16_t> message(desc.formatSliceMessage(cx));
27 mozilla::UniquePtr<char16_t> json(desc.formatJSONTelemetry(cx, 0));
28 }
29 }
30
BEGIN_TEST(testGCSliceCallback)31 BEGIN_TEST(testGCSliceCallback) {
32 gSliceCallbackCount = 0;
33 JS::SetGCSliceCallback(cx, NonIncrementalGCSliceCallback);
34 JS_GC(cx);
35 JS::SetGCSliceCallback(cx, nullptr);
36 CHECK(gSliceCallbackCount == 4);
37 return true;
38 }
END_TEST(testGCSliceCallback)39 END_TEST(testGCSliceCallback)
40
41 static void RootsRemovedGCSliceCallback(JSContext* cx, JS::GCProgress progress,
42 const JS::GCDescription& desc) {
43 using namespace JS;
44
45 static GCProgress expectProgress[] = {
46 GC_CYCLE_BEGIN, GC_SLICE_BEGIN, GC_SLICE_END, GC_SLICE_BEGIN,
47 GC_SLICE_END, GC_CYCLE_END, GC_CYCLE_BEGIN, GC_SLICE_BEGIN,
48 GC_SLICE_END, GC_CYCLE_END};
49
50 static GCReason expectReasons[] = {
51 GCReason::DEBUG_GC, GCReason::DEBUG_GC, GCReason::DEBUG_GC,
52 GCReason::DEBUG_GC, GCReason::DEBUG_GC, GCReason::DEBUG_GC,
53 GCReason::ROOTS_REMOVED, GCReason::ROOTS_REMOVED, GCReason::ROOTS_REMOVED,
54 GCReason::ROOTS_REMOVED};
55
56 static_assert(
57 mozilla::ArrayLength(expectProgress) ==
58 mozilla::ArrayLength(expectReasons),
59 "expectProgress and expectReasons arrays should be the same length");
60
61 MOZ_RELEASE_ASSERT(gSliceCallbackCount <
62 mozilla::ArrayLength(expectProgress));
63 MOZ_RELEASE_ASSERT(progress == expectProgress[gSliceCallbackCount]);
64 MOZ_RELEASE_ASSERT(desc.isZone_ == false);
65 MOZ_RELEASE_ASSERT(desc.invocationKind_ == GC_SHRINK);
66 MOZ_RELEASE_ASSERT(desc.reason_ == expectReasons[gSliceCallbackCount]);
67 gSliceCallbackCount++;
68 }
69
BEGIN_TEST(testGCRootsRemoved)70 BEGIN_TEST(testGCRootsRemoved) {
71 #ifdef JS_GC_ZEAL
72 AutoLeaveZeal nozeal(cx);
73 #endif /* JS_GC_ZEAL */
74
75 JS_SetGCParameter(cx, JSGC_MODE, JSGC_MODE_ZONE_INCREMENTAL);
76
77 gSliceCallbackCount = 0;
78 JS::SetGCSliceCallback(cx, RootsRemovedGCSliceCallback);
79
80 JS::RootedObject obj(cx, JS_NewPlainObject(cx));
81 CHECK(obj);
82
83 JS::PrepareForFullGC(cx);
84 js::SliceBudget budget(js::WorkBudget(1));
85 cx->runtime()->gc.startDebugGC(GC_SHRINK, budget);
86 CHECK(JS::IsIncrementalGCInProgress(cx));
87
88 // Trigger another GC after the current one in shrinking / shutdown GCs.
89 cx->runtime()->gc.notifyRootsRemoved();
90
91 JS::FinishIncrementalGC(cx, JS::GCReason::DEBUG_GC);
92 CHECK(!JS::IsIncrementalGCInProgress(cx));
93
94 JS::SetGCSliceCallback(cx, nullptr);
95
96 JS_SetGCParameter(cx, JSGC_MODE, JSGC_MODE_GLOBAL);
97
98 return true;
99 }
100 END_TEST(testGCRootsRemoved)
101