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 #include "mozilla/DebugOnly.h"
8
9 #include "gc/GCInternals.h"
10 #include "gc/GCLock.h"
11 #include "js/HashTable.h"
12 #include "vm/Realm.h"
13 #include "vm/Runtime.h"
14
15 #include "gc/PrivateIterators-inl.h"
16 #include "vm/JSContext-inl.h"
17
18 using namespace js;
19 using namespace js::gc;
20
IterateRealmsArenasCellsUnbarriered(JSContext * cx,Zone * zone,void * data,JS::IterateRealmCallback realmCallback,IterateArenaCallback arenaCallback,IterateCellCallback cellCallback)21 static void IterateRealmsArenasCellsUnbarriered(
22 JSContext* cx, Zone* zone, void* data,
23 JS::IterateRealmCallback realmCallback, IterateArenaCallback arenaCallback,
24 IterateCellCallback cellCallback) {
25 {
26 Rooted<Realm*> realm(cx);
27 for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
28 realm = r;
29 (*realmCallback)(cx, data, realm);
30 }
31 }
32
33 for (auto thingKind : AllAllocKinds()) {
34 JS::TraceKind traceKind = MapAllocToTraceKind(thingKind);
35 size_t thingSize = Arena::thingSize(thingKind);
36
37 for (ArenaIter aiter(zone, thingKind); !aiter.done(); aiter.next()) {
38 Arena* arena = aiter.get();
39 (*arenaCallback)(cx->runtime(), data, arena, traceKind, thingSize);
40 for (ArenaCellIter iter(arena); !iter.done(); iter.next()) {
41 (*cellCallback)(cx->runtime(), data,
42 JS::GCCellPtr(iter.getCell(), traceKind), thingSize);
43 }
44 }
45 }
46 }
47
IterateHeapUnbarriered(JSContext * cx,void * data,IterateZoneCallback zoneCallback,JS::IterateRealmCallback realmCallback,IterateArenaCallback arenaCallback,IterateCellCallback cellCallback)48 void js::IterateHeapUnbarriered(JSContext* cx, void* data,
49 IterateZoneCallback zoneCallback,
50 JS::IterateRealmCallback realmCallback,
51 IterateArenaCallback arenaCallback,
52 IterateCellCallback cellCallback) {
53 AutoPrepareForTracing prep(cx);
54
55 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
56 (*zoneCallback)(cx->runtime(), data, zone);
57 IterateRealmsArenasCellsUnbarriered(cx, zone, data, realmCallback,
58 arenaCallback, cellCallback);
59 }
60 }
61
IterateHeapUnbarrieredForZone(JSContext * cx,Zone * zone,void * data,IterateZoneCallback zoneCallback,JS::IterateRealmCallback realmCallback,IterateArenaCallback arenaCallback,IterateCellCallback cellCallback)62 void js::IterateHeapUnbarrieredForZone(JSContext* cx, Zone* zone, void* data,
63 IterateZoneCallback zoneCallback,
64 JS::IterateRealmCallback realmCallback,
65 IterateArenaCallback arenaCallback,
66 IterateCellCallback cellCallback) {
67 AutoPrepareForTracing prep(cx);
68
69 (*zoneCallback)(cx->runtime(), data, zone);
70 IterateRealmsArenasCellsUnbarriered(cx, zone, data, realmCallback,
71 arenaCallback, cellCallback);
72 }
73
IterateChunks(JSContext * cx,void * data,IterateChunkCallback chunkCallback)74 void js::IterateChunks(JSContext* cx, void* data,
75 IterateChunkCallback chunkCallback) {
76 AutoPrepareForTracing prep(cx);
77 AutoLockGC lock(cx->runtime());
78
79 for (auto chunk = cx->runtime()->gc.allNonEmptyChunks(lock); !chunk.done();
80 chunk.next()) {
81 chunkCallback(cx->runtime(), data, chunk);
82 }
83 }
84
TraverseInnerLazyScriptsForLazyScript(JSContext * cx,void * data,BaseScript * enclosingScript,IterateScriptCallback lazyScriptCallback,const JS::AutoRequireNoGC & nogc)85 static void TraverseInnerLazyScriptsForLazyScript(
86 JSContext* cx, void* data, BaseScript* enclosingScript,
87 IterateScriptCallback lazyScriptCallback, const JS::AutoRequireNoGC& nogc) {
88 for (JS::GCCellPtr gcThing : enclosingScript->gcthings()) {
89 if (!gcThing.is<JSObject>()) {
90 continue;
91 }
92 JSObject* obj = &gcThing.as<JSObject>();
93
94 MOZ_ASSERT(obj->is<JSFunction>(),
95 "All objects in lazy scripts should be functions");
96 JSFunction* fun = &obj->as<JSFunction>();
97
98 if (!fun->hasBaseScript() || fun->hasBytecode()) {
99 continue;
100 }
101
102 BaseScript* script = fun->baseScript();
103 MOZ_ASSERT_IF(script->hasEnclosingScript(),
104 script->enclosingScript() == enclosingScript);
105
106 lazyScriptCallback(cx->runtime(), data, script, nogc);
107
108 TraverseInnerLazyScriptsForLazyScript(cx, data, script, lazyScriptCallback,
109 nogc);
110 }
111 }
112
DoScriptCallback(JSContext * cx,void * data,BaseScript * script,IterateScriptCallback callback,const JS::AutoRequireNoGC & nogc)113 static inline void DoScriptCallback(JSContext* cx, void* data,
114 BaseScript* script,
115 IterateScriptCallback callback,
116 const JS::AutoRequireNoGC& nogc) {
117 // Exclude any scripts that may be the result of a failed compile. Check that
118 // script either has bytecode or is ready to delazify.
119 //
120 // This excludes lazy scripts that do not have an enclosing scope because we
121 // cannot distinguish a failed compile fragment from a lazy script with a lazy
122 // parent.
123 if (!script->hasBytecode() && !script->isReadyForDelazification()) {
124 return;
125 }
126
127 // Invoke callback.
128 callback(cx->runtime(), data, script, nogc);
129
130 // The check above excluded lazy scripts with lazy parents, so explicitly
131 // visit inner scripts now if we are lazy with a successfully compiled parent.
132 if (!script->hasBytecode()) {
133 TraverseInnerLazyScriptsForLazyScript(cx, data, script, callback, nogc);
134 }
135 }
136
137 template <bool HasBytecode>
IterateScriptsImpl(JSContext * cx,Realm * realm,void * data,IterateScriptCallback scriptCallback)138 static void IterateScriptsImpl(JSContext* cx, Realm* realm, void* data,
139 IterateScriptCallback scriptCallback) {
140 MOZ_ASSERT(!cx->suppressGC);
141 AutoEmptyNurseryAndPrepareForTracing prep(cx);
142 JS::AutoSuppressGCAnalysis nogc;
143
144 if (realm) {
145 Zone* zone = realm->zone();
146 for (auto iter = zone->cellIter<BaseScript>(prep); !iter.done();
147 iter.next()) {
148 if (iter->realm() != realm) {
149 continue;
150 }
151 if (HasBytecode != iter->hasBytecode()) {
152 continue;
153 }
154 DoScriptCallback(cx, data, iter.get(), scriptCallback, nogc);
155 }
156 } else {
157 for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
158 for (auto iter = zone->cellIter<BaseScript>(prep); !iter.done();
159 iter.next()) {
160 if (HasBytecode != iter->hasBytecode()) {
161 continue;
162 }
163 DoScriptCallback(cx, data, iter.get(), scriptCallback, nogc);
164 }
165 }
166 }
167 }
168
IterateScripts(JSContext * cx,Realm * realm,void * data,IterateScriptCallback scriptCallback)169 void js::IterateScripts(JSContext* cx, Realm* realm, void* data,
170 IterateScriptCallback scriptCallback) {
171 IterateScriptsImpl</*HasBytecode = */ true>(cx, realm, data, scriptCallback);
172 }
173
IterateLazyScripts(JSContext * cx,Realm * realm,void * data,IterateScriptCallback scriptCallback)174 void js::IterateLazyScripts(JSContext* cx, Realm* realm, void* data,
175 IterateScriptCallback scriptCallback) {
176 IterateScriptsImpl</*HasBytecode = */ false>(cx, realm, data, scriptCallback);
177 }
178
IterateGrayObjects(Zone * zone,GCThingCallback cellCallback,void * data)179 void js::IterateGrayObjects(Zone* zone, GCThingCallback cellCallback,
180 void* data) {
181 MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
182
183 AutoPrepareForTracing prep(TlsContext.get());
184 for (auto kind : ObjectAllocKinds()) {
185 for (GrayObjectIter obj(zone, kind); !obj.done(); obj.next()) {
186 if (obj->asTenured().isMarkedGray()) {
187 cellCallback(data, JS::GCCellPtr(obj.get()));
188 }
189 }
190 }
191 }
192
JS_IterateCompartments(JSContext * cx,void * data,JSIterateCompartmentCallback compartmentCallback)193 JS_PUBLIC_API void JS_IterateCompartments(
194 JSContext* cx, void* data,
195 JSIterateCompartmentCallback compartmentCallback) {
196 AutoTraceSession session(cx->runtime());
197
198 for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
199 if ((*compartmentCallback)(cx, data, c) ==
200 JS::CompartmentIterResult::Stop) {
201 break;
202 }
203 }
204 }
205
JS_IterateCompartmentsInZone(JSContext * cx,JS::Zone * zone,void * data,JSIterateCompartmentCallback compartmentCallback)206 JS_PUBLIC_API void JS_IterateCompartmentsInZone(
207 JSContext* cx, JS::Zone* zone, void* data,
208 JSIterateCompartmentCallback compartmentCallback) {
209 AutoTraceSession session(cx->runtime());
210
211 for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
212 if ((*compartmentCallback)(cx, data, c) ==
213 JS::CompartmentIterResult::Stop) {
214 break;
215 }
216 }
217 }
218
IterateRealms(JSContext * cx,void * data,JS::IterateRealmCallback realmCallback)219 JS_PUBLIC_API void JS::IterateRealms(JSContext* cx, void* data,
220 JS::IterateRealmCallback realmCallback) {
221 AutoTraceSession session(cx->runtime());
222
223 Rooted<Realm*> realm(cx);
224 for (RealmsIter r(cx->runtime()); !r.done(); r.next()) {
225 realm = r;
226 (*realmCallback)(cx, data, realm);
227 }
228 }
229
IterateRealmsWithPrincipals(JSContext * cx,JSPrincipals * principals,void * data,JS::IterateRealmCallback realmCallback)230 JS_PUBLIC_API void JS::IterateRealmsWithPrincipals(
231 JSContext* cx, JSPrincipals* principals, void* data,
232 JS::IterateRealmCallback realmCallback) {
233 MOZ_ASSERT(principals);
234
235 AutoTraceSession session(cx->runtime());
236
237 Rooted<Realm*> realm(cx);
238 for (RealmsIter r(cx->runtime()); !r.done(); r.next()) {
239 if (r->principals() != principals) {
240 continue;
241 }
242 realm = r;
243 (*realmCallback)(cx, data, realm);
244 }
245 }
246
IterateRealmsInCompartment(JSContext * cx,JS::Compartment * compartment,void * data,JS::IterateRealmCallback realmCallback)247 JS_PUBLIC_API void JS::IterateRealmsInCompartment(
248 JSContext* cx, JS::Compartment* compartment, void* data,
249 JS::IterateRealmCallback realmCallback) {
250 AutoTraceSession session(cx->runtime());
251
252 Rooted<Realm*> realm(cx);
253 for (RealmsInCompartmentIter r(compartment); !r.done(); r.next()) {
254 realm = r;
255 (*realmCallback)(cx, data, realm);
256 }
257 }
258