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