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