/src/mozilla-central/js/src/gc/Zone.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "gc/Zone-inl.h" |
8 | | |
9 | | #include "gc/FreeOp.h" |
10 | | #include "gc/Policy.h" |
11 | | #include "gc/PublicIterators.h" |
12 | | #include "jit/BaselineJIT.h" |
13 | | #include "jit/Ion.h" |
14 | | #include "jit/JitRealm.h" |
15 | | #include "vm/Debugger.h" |
16 | | #include "vm/Runtime.h" |
17 | | |
18 | | #include "gc/GC-inl.h" |
19 | | #include "gc/Marking-inl.h" |
20 | | #include "vm/Realm-inl.h" |
21 | | |
22 | | using namespace js; |
23 | | using namespace js::gc; |
24 | | |
25 | | Zone * const Zone::NotOnList = reinterpret_cast<Zone*>(1); |
26 | | |
27 | | JS::Zone::Zone(JSRuntime* rt) |
28 | | : JS::shadow::Zone(rt, &rt->gc.marker), |
29 | | // Note: don't use |this| before initializing helperThreadUse_! |
30 | | // ProtectedData checks in CheckZone::check may read this field. |
31 | | helperThreadUse_(HelperThreadUse::None), |
32 | | helperThreadOwnerContext_(nullptr), |
33 | | debuggers(this, nullptr), |
34 | | uniqueIds_(this), |
35 | | suppressAllocationMetadataBuilder(this, false), |
36 | | arenas(this), |
37 | | tenuredAllocsSinceMinorGC_(0), |
38 | | types(this), |
39 | | gcWeakMapList_(this), |
40 | | compartments_(), |
41 | | gcGrayRoots_(this), |
42 | | gcWeakRefs_(this), |
43 | | weakCaches_(this), |
44 | | gcWeakKeys_(this, SystemAllocPolicy(), rt->randomHashCodeScrambler()), |
45 | | typeDescrObjects_(this, this), |
46 | | markedAtoms_(this), |
47 | | atomCache_(this), |
48 | | externalStringCache_(this), |
49 | | functionToStringCache_(this), |
50 | | keepAtomsCount(this, 0), |
51 | | purgeAtomsDeferred(this, 0), |
52 | | usage(&rt->gc.usage), |
53 | | threshold(), |
54 | | gcDelayBytes(0), |
55 | | tenuredStrings(this, 0), |
56 | | allocNurseryStrings(this, true), |
57 | | propertyTree_(this, this), |
58 | | baseShapes_(this, this), |
59 | | initialShapes_(this, this), |
60 | | nurseryShapes_(this), |
61 | | data(this, nullptr), |
62 | | isSystem(this, false), |
63 | | #ifdef DEBUG |
64 | | gcLastSweepGroupIndex(0), |
65 | | #endif |
66 | | jitZone_(this, nullptr), |
67 | | gcScheduled_(false), |
68 | | gcScheduledSaved_(false), |
69 | | gcPreserveCode_(false), |
70 | | keepShapeTables_(this, false), |
71 | | listNext_(NotOnList) |
72 | 9 | { |
73 | 9 | /* Ensure that there are no vtables to mess us up here. */ |
74 | 9 | MOZ_ASSERT(reinterpret_cast<JS::shadow::Zone*>(this) == |
75 | 9 | static_cast<JS::shadow::Zone*>(this)); |
76 | 9 | |
77 | 9 | AutoLockGC lock(rt); |
78 | 9 | threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables, rt->gc.schedulingState, lock); |
79 | 9 | setGCMaxMallocBytes(rt->gc.tunables.maxMallocBytes(), lock); |
80 | 9 | jitCodeCounter.setMax(jit::MaxCodeBytesPerProcess * 0.8, lock); |
81 | 9 | } |
82 | | |
83 | | Zone::~Zone() |
84 | 0 | { |
85 | 0 | MOZ_ASSERT(helperThreadUse_ == HelperThreadUse::None); |
86 | 0 |
|
87 | 0 | JSRuntime* rt = runtimeFromAnyThread(); |
88 | 0 | if (this == rt->gc.systemZone) { |
89 | 0 | rt->gc.systemZone = nullptr; |
90 | 0 | } |
91 | 0 |
|
92 | 0 | js_delete(debuggers.ref()); |
93 | 0 | js_delete(jitZone_.ref()); |
94 | 0 |
|
95 | | #ifdef DEBUG |
96 | | // Avoid assertions failures warning that not everything has been destroyed |
97 | | // if the embedding leaked GC things. |
98 | | if (!rt->gc.shutdownCollectedEverything()) { |
99 | | gcWeakMapList().clear(); |
100 | | regExps().clear(); |
101 | | } |
102 | | #endif |
103 | | } |
104 | | |
105 | | bool |
106 | | Zone::init(bool isSystemArg) |
107 | 9 | { |
108 | 9 | isSystem = isSystemArg; |
109 | 9 | regExps_.ref() = make_unique<RegExpZone>(this); |
110 | 9 | return regExps_.ref() && gcWeakKeys().init(); |
111 | 9 | } |
112 | | |
113 | | void |
114 | | Zone::setNeedsIncrementalBarrier(bool needs) |
115 | 72 | { |
116 | 72 | MOZ_ASSERT_IF(needs, canCollect()); |
117 | 72 | needsIncrementalBarrier_ = needs; |
118 | 72 | } |
119 | | |
120 | | void |
121 | | Zone::beginSweepTypes(bool releaseTypes) |
122 | 18 | { |
123 | 18 | types.beginSweep(releaseTypes); |
124 | 18 | } |
125 | | |
126 | | Zone::DebuggerVector* |
127 | | Zone::getOrCreateDebuggers(JSContext* cx) |
128 | 0 | { |
129 | 0 | if (debuggers) { |
130 | 0 | return debuggers; |
131 | 0 | } |
132 | 0 | |
133 | 0 | debuggers = js_new<DebuggerVector>(); |
134 | 0 | if (!debuggers) { |
135 | 0 | ReportOutOfMemory(cx); |
136 | 0 | } |
137 | 0 | return debuggers; |
138 | 0 | } |
139 | | |
140 | | void |
141 | | Zone::sweepBreakpoints(FreeOp* fop) |
142 | 18 | { |
143 | 18 | if (fop->runtime()->debuggerList().isEmpty()) { |
144 | 18 | return; |
145 | 18 | } |
146 | 0 | |
147 | 0 | /* |
148 | 0 | * Sweep all compartments in a zone at the same time, since there is no way |
149 | 0 | * to iterate over the scripts belonging to a single compartment in a zone. |
150 | 0 | */ |
151 | 0 | |
152 | 0 | MOZ_ASSERT(isGCSweepingOrCompacting()); |
153 | 0 | for (auto iter = cellIter<JSScript>(); !iter.done(); iter.next()) { |
154 | 0 | JSScript* script = iter; |
155 | 0 | if (!script->hasAnyBreakpointsOrStepMode()) { |
156 | 0 | continue; |
157 | 0 | } |
158 | 0 | |
159 | 0 | bool scriptGone = IsAboutToBeFinalizedUnbarriered(&script); |
160 | 0 | MOZ_ASSERT(script == iter); |
161 | 0 | for (unsigned i = 0; i < script->length(); i++) { |
162 | 0 | BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i)); |
163 | 0 | if (!site) { |
164 | 0 | continue; |
165 | 0 | } |
166 | 0 | |
167 | 0 | Breakpoint* nextbp; |
168 | 0 | for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) { |
169 | 0 | nextbp = bp->nextInSite(); |
170 | 0 | GCPtrNativeObject& dbgobj = bp->debugger->toJSObjectRef(); |
171 | 0 |
|
172 | 0 | // If we are sweeping, then we expect the script and the |
173 | 0 | // debugger object to be swept in the same sweep group, except |
174 | 0 | // if the breakpoint was added after we computed the sweep |
175 | 0 | // groups. In this case both script and debugger object must be |
176 | 0 | // live. |
177 | 0 | MOZ_ASSERT_IF(isGCSweeping() && dbgobj->zone()->isCollecting(), |
178 | 0 | dbgobj->zone()->isGCSweeping() || |
179 | 0 | (!scriptGone && dbgobj->asTenured().isMarkedAny())); |
180 | 0 |
|
181 | 0 | bool dying = scriptGone || IsAboutToBeFinalized(&dbgobj); |
182 | 0 | MOZ_ASSERT_IF(!dying, !IsAboutToBeFinalized(&bp->getHandlerRef())); |
183 | 0 | if (dying) { |
184 | 0 | bp->destroy(fop); |
185 | 0 | } |
186 | 0 | } |
187 | 0 | } |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | | void |
192 | | Zone::sweepWeakMaps() |
193 | 18 | { |
194 | 18 | /* Finalize unreachable (key,value) pairs in all weak maps. */ |
195 | 18 | WeakMapBase::sweepZone(this); |
196 | 18 | } |
197 | | |
198 | | void |
199 | | Zone::discardJitCode(FreeOp* fop, bool discardBaselineCode) |
200 | 36 | { |
201 | 36 | if (!jitZone()) { |
202 | 0 | return; |
203 | 0 | } |
204 | 36 | |
205 | 36 | if (isPreservingCode()) { |
206 | 28 | return; |
207 | 28 | } |
208 | 8 | |
209 | 8 | if (discardBaselineCode) { |
210 | | #ifdef DEBUG |
211 | | /* Assert no baseline scripts are marked as active. */ |
212 | | for (auto script = cellIter<JSScript>(); !script.done(); script.next()) { |
213 | | MOZ_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active()); |
214 | | } |
215 | | #endif |
216 | | |
217 | 8 | /* Mark baseline scripts on the stack as active. */ |
218 | 8 | jit::MarkActiveBaselineScripts(this); |
219 | 8 | } |
220 | 8 | |
221 | 8 | /* Only mark OSI points if code is being discarded. */ |
222 | 8 | jit::InvalidateAll(fop, this); |
223 | 8 | |
224 | 632 | for (auto script = cellIter<JSScript>(); !script.done(); script.next()) { |
225 | 624 | jit::FinishInvalidation(fop, script); |
226 | 624 | |
227 | 624 | /* |
228 | 624 | * Discard baseline script if it's not marked as active. Note that |
229 | 624 | * this also resets the active flag. |
230 | 624 | */ |
231 | 624 | if (discardBaselineCode) { |
232 | 624 | jit::FinishDiscardBaselineScript(fop, script); |
233 | 624 | } |
234 | 624 | |
235 | 624 | /* |
236 | 624 | * Warm-up counter for scripts are reset on GC. After discarding code we |
237 | 624 | * need to let it warm back up to get information such as which |
238 | 624 | * opcodes are setting array holes or accessing getter properties. |
239 | 624 | */ |
240 | 624 | script->resetWarmUpCounter(); |
241 | 624 | |
242 | 624 | /* |
243 | 624 | * Make it impossible to use the control flow graphs cached on the |
244 | 624 | * BaselineScript. They get deleted. |
245 | 624 | */ |
246 | 624 | if (script->hasBaselineScript()) { |
247 | 4 | script->baselineScript()->setControlFlowGraph(nullptr); |
248 | 4 | } |
249 | 624 | } |
250 | 8 | |
251 | 8 | /* |
252 | 8 | * When scripts contains pointers to nursery things, the store buffer |
253 | 8 | * can contain entries that point into the optimized stub space. Since |
254 | 8 | * this method can be called outside the context of a GC, this situation |
255 | 8 | * could result in us trying to mark invalid store buffer entries. |
256 | 8 | * |
257 | 8 | * Defer freeing any allocated blocks until after the next minor GC. |
258 | 8 | */ |
259 | 8 | if (discardBaselineCode) { |
260 | 8 | jitZone()->optimizedStubSpace()->freeAllAfterMinorGC(this); |
261 | 8 | jitZone()->purgeIonCacheIRStubInfo(); |
262 | 8 | } |
263 | 8 | |
264 | 8 | /* |
265 | 8 | * Free all control flow graphs that are cached on BaselineScripts. |
266 | 8 | * Assuming this happens on the main thread and all control flow |
267 | 8 | * graph reads happen on the main thread, this is safe. |
268 | 8 | */ |
269 | 8 | jitZone()->cfgSpace()->lifoAlloc().freeAll(); |
270 | 8 | } |
271 | | |
272 | | #ifdef JSGC_HASH_TABLE_CHECKS |
273 | | void |
274 | | JS::Zone::checkUniqueIdTableAfterMovingGC() |
275 | | { |
276 | | for (auto r = uniqueIds().all(); !r.empty(); r.popFront()) { |
277 | | js::gc::CheckGCThingAfterMovingGC(r.front().key()); |
278 | | } |
279 | | } |
280 | | #endif |
281 | | |
282 | | uint64_t |
283 | | Zone::gcNumber() |
284 | 11.4M | { |
285 | 11.4M | // Zones in use by exclusive threads are not collected, and threads using |
286 | 11.4M | // them cannot access the main runtime's gcNumber without racing. |
287 | 11.4M | return usedByHelperThread() ? 0 : runtimeFromMainThread()->gc.gcNumber(); |
288 | 11.4M | } |
289 | | |
290 | | js::jit::JitZone* |
291 | | Zone::createJitZone(JSContext* cx) |
292 | 4 | { |
293 | 4 | MOZ_ASSERT(!jitZone_); |
294 | 4 | |
295 | 4 | if (!cx->runtime()->getJitRuntime(cx)) { |
296 | 0 | return nullptr; |
297 | 0 | } |
298 | 4 | |
299 | 4 | UniquePtr<jit::JitZone> jitZone(cx->new_<js::jit::JitZone>()); |
300 | 4 | if (!jitZone) { |
301 | 0 | return nullptr; |
302 | 0 | } |
303 | 4 | |
304 | 4 | jitZone_ = jitZone.release(); |
305 | 4 | return jitZone_; |
306 | 4 | } |
307 | | |
308 | | bool |
309 | | Zone::hasMarkedRealms() |
310 | 0 | { |
311 | 0 | for (RealmsInZoneIter realm(this); !realm.done(); realm.next()) { |
312 | 0 | if (realm->marked()) { |
313 | 0 | return true; |
314 | 0 | } |
315 | 0 | } |
316 | 0 | return false; |
317 | 0 | } |
318 | | |
319 | | bool |
320 | | Zone::canCollect() |
321 | 918 | { |
322 | 918 | // The atoms zone cannot be collected while off-thread parsing is taking |
323 | 918 | // place. |
324 | 918 | if (isAtomsZone()) { |
325 | 300 | return !runtimeFromAnyThread()->hasHelperThreadZones(); |
326 | 300 | } |
327 | 618 | |
328 | 618 | // Zones that will be or are currently used by other threads cannot be |
329 | 618 | // collected. |
330 | 618 | return !createdForHelperThread(); |
331 | 618 | } |
332 | | |
333 | | void |
334 | | Zone::notifyObservingDebuggers() |
335 | 18 | { |
336 | 18 | JSRuntime* rt = runtimeFromMainThread(); |
337 | 18 | JSContext* cx = rt->mainContextFromOwnThread(); |
338 | 18 | |
339 | 54 | for (RealmsInZoneIter realms(this); !realms.done(); realms.next()) { |
340 | 36 | RootedGlobalObject global(cx, realms->unsafeUnbarrieredMaybeGlobal()); |
341 | 36 | if (!global) { |
342 | 0 | continue; |
343 | 0 | } |
344 | 36 | |
345 | 36 | GlobalObject::DebuggerVector* dbgs = global->getDebuggers(); |
346 | 36 | if (!dbgs) { |
347 | 36 | continue; |
348 | 36 | } |
349 | 0 | |
350 | 0 | for (GlobalObject::DebuggerVector::Range r = dbgs->all(); !r.empty(); r.popFront()) { |
351 | 0 | if (!r.front()->debuggeeIsBeingCollected(rt->gc.majorGCCount())) { |
352 | | #ifdef DEBUG |
353 | | fprintf(stderr, |
354 | | "OOM while notifying observing Debuggers of a GC: The onGarbageCollection\n" |
355 | | "hook will not be fired for this GC for some Debuggers!\n"); |
356 | | #endif |
357 | | return; |
358 | 0 | } |
359 | 0 | } |
360 | 0 | } |
361 | 18 | } |
362 | | |
363 | | bool |
364 | | Zone::isOnList() const |
365 | 18 | { |
366 | 18 | return listNext_ != NotOnList; |
367 | 18 | } |
368 | | |
369 | | Zone* |
370 | | Zone::nextZone() const |
371 | 0 | { |
372 | 0 | MOZ_ASSERT(isOnList()); |
373 | 0 | return listNext_; |
374 | 0 | } |
375 | | |
376 | | void |
377 | | Zone::clearTables() |
378 | 0 | { |
379 | 0 | MOZ_ASSERT(regExps().empty()); |
380 | 0 |
|
381 | 0 | baseShapes().clear(); |
382 | 0 | initialShapes().clear(); |
383 | 0 | } |
384 | | |
385 | | void |
386 | | Zone::fixupAfterMovingGC() |
387 | 0 | { |
388 | 0 | fixupInitialShapeTable(); |
389 | 0 | } |
390 | | |
391 | | bool |
392 | | Zone::addTypeDescrObject(JSContext* cx, HandleObject obj) |
393 | 0 | { |
394 | 0 | // Type descriptor objects are always tenured so we don't need post barriers |
395 | 0 | // on the set. |
396 | 0 | MOZ_ASSERT(!IsInsideNursery(obj)); |
397 | 0 |
|
398 | 0 | if (!typeDescrObjects().put(obj)) { |
399 | 0 | ReportOutOfMemory(cx); |
400 | 0 | return false; |
401 | 0 | } |
402 | 0 | |
403 | 0 | return true; |
404 | 0 | } |
405 | | |
406 | | void |
407 | | Zone::deleteEmptyCompartment(JS::Compartment* comp) |
408 | 0 | { |
409 | 0 | MOZ_ASSERT(comp->zone() == this); |
410 | 0 | MOZ_ASSERT(arenas.checkEmptyArenaLists()); |
411 | 0 |
|
412 | 0 | MOZ_ASSERT(compartments().length() == 1); |
413 | 0 | MOZ_ASSERT(compartments()[0] == comp); |
414 | 0 | MOZ_ASSERT(comp->realms().length() == 1); |
415 | 0 |
|
416 | 0 | Realm* realm = comp->realms()[0]; |
417 | 0 | FreeOp* fop = runtimeFromMainThread()->defaultFreeOp(); |
418 | 0 | realm->destroy(fop); |
419 | 0 | comp->destroy(fop); |
420 | 0 |
|
421 | 0 | compartments().clear(); |
422 | 0 | } |
423 | | |
424 | | void |
425 | | Zone::setHelperThreadOwnerContext(JSContext* cx) |
426 | 0 | { |
427 | 0 | MOZ_ASSERT_IF(cx, TlsContext.get() == cx); |
428 | 0 | helperThreadOwnerContext_ = cx; |
429 | 0 | } |
430 | | |
431 | | bool |
432 | | Zone::ownedByCurrentHelperThread() |
433 | 0 | { |
434 | 0 | MOZ_ASSERT(usedByHelperThread()); |
435 | 0 | MOZ_ASSERT(TlsContext.get()); |
436 | 0 | return helperThreadOwnerContext_ == TlsContext.get(); |
437 | 0 | } |
438 | | |
439 | | void Zone::releaseAtoms() |
440 | 16 | { |
441 | 16 | MOZ_ASSERT(hasKeptAtoms()); |
442 | 16 | |
443 | 16 | keepAtomsCount--; |
444 | 16 | |
445 | 16 | if (!hasKeptAtoms() && purgeAtomsDeferred) { |
446 | 0 | purgeAtomsDeferred = false; |
447 | 0 | purgeAtomCache(); |
448 | 0 | } |
449 | 16 | } |
450 | | |
451 | | void |
452 | | Zone::purgeAtomCacheOrDefer() |
453 | 18 | { |
454 | 18 | if (hasKeptAtoms()) { |
455 | 0 | purgeAtomsDeferred = true; |
456 | 0 | return; |
457 | 0 | } |
458 | 18 | |
459 | 18 | purgeAtomCache(); |
460 | 18 | } |
461 | | |
462 | | void |
463 | | Zone::purgeAtomCache() |
464 | 18 | { |
465 | 18 | MOZ_ASSERT(!hasKeptAtoms()); |
466 | 18 | MOZ_ASSERT(!purgeAtomsDeferred); |
467 | 18 | |
468 | 18 | atomCache().clearAndCompact(); |
469 | 18 | |
470 | 18 | // Also purge the dtoa caches so that subsequent lookups populate atom |
471 | 18 | // cache too. |
472 | 54 | for (RealmsInZoneIter r(this); !r.done(); r.next()) { |
473 | 36 | r->dtoaCache.purge(); |
474 | 36 | } |
475 | 18 | } |
476 | | |
477 | | void |
478 | | Zone::traceAtomCache(JSTracer* trc) |
479 | 0 | { |
480 | 0 | MOZ_ASSERT(hasKeptAtoms()); |
481 | 0 | for (auto r = atomCache().all(); !r.empty(); r.popFront()) { |
482 | 0 | JSAtom* atom = r.front().asPtrUnbarriered(); |
483 | 0 | TraceRoot(trc, &atom, "kept atom"); |
484 | 0 | MOZ_ASSERT(r.front().asPtrUnbarriered() == atom); |
485 | 0 | } |
486 | 0 | } |
487 | | |
488 | | void* |
489 | | Zone::onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes, void* reallocPtr) |
490 | 0 | { |
491 | 0 | if (!js::CurrentThreadCanAccessRuntime(runtime_)) { |
492 | 0 | return nullptr; |
493 | 0 | } |
494 | 0 | return runtimeFromMainThread()->onOutOfMemory(allocFunc, nbytes, reallocPtr); |
495 | 0 | } |
496 | | |
497 | | void |
498 | | Zone::reportAllocationOverflow() |
499 | 0 | { |
500 | 0 | js::ReportAllocationOverflow(nullptr); |
501 | 0 | } |
502 | | |
503 | | void |
504 | | JS::Zone::maybeTriggerGCForTooMuchMalloc(js::gc::MemoryCounter& counter, TriggerKind trigger) |
505 | 0 | { |
506 | 0 | JSRuntime* rt = runtimeFromAnyThread(); |
507 | 0 |
|
508 | 0 | if (!js::CurrentThreadCanAccessRuntime(rt)) { |
509 | 0 | return; |
510 | 0 | } |
511 | 0 | |
512 | 0 | bool wouldInterruptGC = rt->gc.isIncrementalGCInProgress() && !isCollecting(); |
513 | 0 | if (wouldInterruptGC && !counter.shouldResetIncrementalGC(rt->gc.tunables)) { |
514 | 0 | return; |
515 | 0 | } |
516 | 0 | |
517 | 0 | if (!rt->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC, |
518 | 0 | counter.bytes(), counter.maxBytes())) |
519 | 0 | { |
520 | 0 | return; |
521 | 0 | } |
522 | 0 | |
523 | 0 | counter.recordTrigger(trigger); |
524 | 0 | } |
525 | | |
526 | | ZoneList::ZoneList() |
527 | | : head(nullptr), tail(nullptr) |
528 | 42 | {} |
529 | | |
530 | | ZoneList::ZoneList(Zone* zone) |
531 | | : head(zone), tail(zone) |
532 | 18 | { |
533 | 18 | MOZ_RELEASE_ASSERT(!zone->isOnList()); |
534 | 18 | zone->listNext_ = nullptr; |
535 | 18 | } |
536 | | |
537 | | ZoneList::~ZoneList() |
538 | 54 | { |
539 | 54 | MOZ_ASSERT(isEmpty()); |
540 | 54 | } |
541 | | |
542 | | void |
543 | | ZoneList::check() const |
544 | 126 | { |
545 | | #ifdef DEBUG |
546 | | MOZ_ASSERT((head == nullptr) == (tail == nullptr)); |
547 | | if (!head) { |
548 | | return; |
549 | | } |
550 | | |
551 | | Zone* zone = head; |
552 | | for (;;) { |
553 | | MOZ_ASSERT(zone && zone->isOnList()); |
554 | | if (zone == tail) |
555 | | break; |
556 | | zone = zone->listNext_; |
557 | | } |
558 | | MOZ_ASSERT(!zone->listNext_); |
559 | | #endif |
560 | | } |
561 | | |
562 | | bool |
563 | | ZoneList::isEmpty() const |
564 | 72 | { |
565 | 72 | return head == nullptr; |
566 | 72 | } |
567 | | |
568 | | Zone* |
569 | | ZoneList::front() const |
570 | 0 | { |
571 | 0 | MOZ_ASSERT(!isEmpty()); |
572 | 0 | MOZ_ASSERT(head->isOnList()); |
573 | 0 | return head; |
574 | 0 | } |
575 | | |
576 | | void |
577 | | ZoneList::append(Zone* zone) |
578 | 18 | { |
579 | 18 | ZoneList singleZone(zone); |
580 | 18 | transferFrom(singleZone); |
581 | 18 | } |
582 | | |
583 | | void |
584 | | ZoneList::transferFrom(ZoneList& other) |
585 | 54 | { |
586 | 54 | check(); |
587 | 54 | other.check(); |
588 | 54 | MOZ_ASSERT(tail != other.tail); |
589 | 54 | |
590 | 54 | if (tail) { |
591 | 0 | tail->listNext_ = other.head; |
592 | 54 | } else { |
593 | 54 | head = other.head; |
594 | 54 | } |
595 | 54 | tail = other.tail; |
596 | 54 | |
597 | 54 | other.head = nullptr; |
598 | 54 | other.tail = nullptr; |
599 | 54 | } |
600 | | |
601 | | Zone* |
602 | | ZoneList::removeFront() |
603 | 18 | { |
604 | 18 | MOZ_ASSERT(!isEmpty()); |
605 | 18 | check(); |
606 | 18 | |
607 | 18 | Zone* front = head; |
608 | 18 | head = head->listNext_; |
609 | 18 | if (!head) { |
610 | 18 | tail = nullptr; |
611 | 18 | } |
612 | 18 | |
613 | 18 | front->listNext_ = Zone::NotOnList; |
614 | 18 | |
615 | 18 | return front; |
616 | 18 | } |
617 | | |
618 | | void |
619 | | ZoneList::clear() |
620 | 0 | { |
621 | 0 | while (!isEmpty()) { |
622 | 0 | removeFront(); |
623 | 0 | } |
624 | 0 | } |
625 | | |
626 | | JS_PUBLIC_API(void) |
627 | | JS::shadow::RegisterWeakCache(JS::Zone* zone, detail::WeakCacheBase* cachep) |
628 | 67 | { |
629 | 67 | zone->registerWeakCache(cachep); |
630 | 67 | } |