Line data Source code
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 : #ifndef gc_Zone_h
8 : #define gc_Zone_h
9 :
10 : #include "mozilla/Atomics.h"
11 : #include "mozilla/HashFunctions.h"
12 :
13 : #include "gc/FindSCCs.h"
14 : #include "gc/GCRuntime.h"
15 : #include "js/GCHashTable.h"
16 : #include "vm/MallocProvider.h"
17 : #include "vm/RegExpShared.h"
18 : #include "vm/Runtime.h"
19 :
20 : namespace js {
21 :
22 : class Debugger;
23 :
24 : namespace jit {
25 : class JitZone;
26 : } // namespace jit
27 :
28 : namespace gc {
29 :
30 0 : struct ZoneComponentFinder : public ComponentFinder<JS::Zone, ZoneComponentFinder>
31 : {
32 : ZoneComponentFinder(uintptr_t sl, JS::Zone* maybeAtomsZone)
33 0 : : ComponentFinder<JS::Zone, ZoneComponentFinder>(sl), maybeAtomsZone(maybeAtomsZone)
34 : {}
35 :
36 : JS::Zone* maybeAtomsZone;
37 : };
38 :
39 : struct UniqueIdGCPolicy {
40 : static bool needsSweep(Cell** cell, uint64_t* value);
41 : };
42 :
43 : // Maps a Cell* to a unique, 64bit id.
44 : using UniqueIdMap = GCHashMap<Cell*,
45 : uint64_t,
46 : PointerHasher<Cell*>,
47 : SystemAllocPolicy,
48 : UniqueIdGCPolicy>;
49 :
50 : extern uint64_t NextCellUniqueId(JSRuntime* rt);
51 :
52 : template <typename T>
53 : class ZoneCellIter;
54 :
55 : } // namespace gc
56 :
57 : class MOZ_NON_TEMPORARY_CLASS ExternalStringCache
58 : {
59 : static const size_t NumEntries = 4;
60 : mozilla::Array<JSString*, NumEntries> entries_;
61 :
62 : ExternalStringCache(const ExternalStringCache&) = delete;
63 : void operator=(const ExternalStringCache&) = delete;
64 :
65 : public:
66 0 : ExternalStringCache() { purge(); }
67 0 : void purge() { mozilla::PodArrayZero(entries_); }
68 :
69 : MOZ_ALWAYS_INLINE JSString* lookup(const char16_t* chars, size_t len) const;
70 : MOZ_ALWAYS_INLINE void put(JSString* s);
71 : };
72 :
73 : class MOZ_NON_TEMPORARY_CLASS FunctionToStringCache
74 : {
75 : struct Entry {
76 : JSScript* script;
77 : JSString* string;
78 :
79 : void set(JSScript* scriptArg, JSString* stringArg) {
80 0 : script = scriptArg;
81 0 : string = stringArg;
82 : }
83 : };
84 : static const size_t NumEntries = 2;
85 : mozilla::Array<Entry, NumEntries> entries_;
86 :
87 : FunctionToStringCache(const FunctionToStringCache&) = delete;
88 : void operator=(const FunctionToStringCache&) = delete;
89 :
90 : public:
91 0 : FunctionToStringCache() { purge(); }
92 0 : void purge() { mozilla::PodArrayZero(entries_); }
93 :
94 : MOZ_ALWAYS_INLINE JSString* lookup(JSScript* script) const;
95 : MOZ_ALWAYS_INLINE void put(JSScript* script, JSString* string);
96 : };
97 :
98 : } // namespace js
99 :
100 : namespace JS {
101 :
102 : // A zone is a collection of compartments. Every compartment belongs to exactly
103 : // one zone. In Firefox, there is roughly one zone per tab along with a system
104 : // zone for everything else. Zones mainly serve as boundaries for garbage
105 : // collection. Unlike compartments, they have no special security properties.
106 : //
107 : // Every GC thing belongs to exactly one zone. GC things from the same zone but
108 : // different compartments can share an arena (4k page). GC things from different
109 : // zones cannot be stored in the same arena. The garbage collector is capable of
110 : // collecting one zone at a time; it cannot collect at the granularity of
111 : // compartments.
112 : //
113 : // GC things are tied to zones and compartments as follows:
114 : //
115 : // - JSObjects belong to a compartment and cannot be shared between
116 : // compartments. If an object needs to point to a JSObject in a different
117 : // compartment, regardless of zone, it must go through a cross-compartment
118 : // wrapper. Each compartment keeps track of its outgoing wrappers in a table.
119 : // JSObjects find their compartment via their ObjectGroup.
120 : //
121 : // - JSStrings do not belong to any particular compartment, but they do belong
122 : // to a zone. Thus, two different compartments in the same zone can point to a
123 : // JSString. When a string needs to be wrapped, we copy it if it's in a
124 : // different zone and do nothing if it's in the same zone. Thus, transferring
125 : // strings within a zone is very efficient.
126 : //
127 : // - Shapes and base shapes belong to a zone and are shared between compartments
128 : // in that zone where possible. Accessor shapes store getter and setter
129 : // JSObjects which belong to a single compartment, so these shapes and all
130 : // their descendants can't be shared with other compartments.
131 : //
132 : // - Scripts are also compartment-local and cannot be shared. A script points to
133 : // its compartment.
134 : //
135 : // - ObjectGroup and JitCode objects belong to a compartment and cannot be
136 : // shared. There is no mechanism to obtain the compartment from a JitCode
137 : // object.
138 : //
139 : // A zone remains alive as long as any GC things in the zone are alive. A
140 : // compartment remains alive as long as any JSObjects, scripts, shapes, or base
141 : // shapes within it are alive.
142 : //
143 : // We always guarantee that a zone has at least one live compartment by refusing
144 : // to delete the last compartment in a live zone.
145 : class Zone : public JS::shadow::Zone,
146 : public js::gc::GraphNodeBase<JS::Zone>,
147 : public js::MallocProvider<JS::Zone>
148 : {
149 : public:
150 : explicit Zone(JSRuntime* rt);
151 : ~Zone();
152 : MOZ_MUST_USE bool init(bool isSystem);
153 : void destroy(js::FreeOp *fop);
154 :
155 : private:
156 : enum class HelperThreadUse : uint32_t {
157 : None,
158 : Pending,
159 : Active
160 : };
161 : mozilla::Atomic<HelperThreadUse> helperThreadUse_;
162 :
163 : // The helper thread context with exclusive access to this zone, if
164 : // usedByHelperThread(), or nullptr when on the main thread.
165 : js::UnprotectedData<JSContext*> helperThreadOwnerContext_;
166 :
167 : public:
168 : bool ownedByCurrentHelperThread();
169 : void setHelperThreadOwnerContext(JSContext* cx);
170 :
171 : // Whether this zone was created for use by a helper thread.
172 : bool createdForHelperThread() const {
173 1500 : return helperThreadUse_ != HelperThreadUse::None;
174 : }
175 : // Whether this zone is currently in use by a helper thread.
176 0 : bool usedByHelperThread() {
177 0 : MOZ_ASSERT_IF(isAtomsZone(), helperThreadUse_ == HelperThreadUse::None);
178 21603992 : return helperThreadUse_ == HelperThreadUse::Active;
179 : }
180 0 : void setCreatedForHelperThread() {
181 0 : MOZ_ASSERT(helperThreadUse_ == HelperThreadUse::None);
182 0 : helperThreadUse_ = HelperThreadUse::Pending;
183 0 : }
184 0 : void setUsedByHelperThread() {
185 0 : MOZ_ASSERT(helperThreadUse_ == HelperThreadUse::Pending);
186 0 : helperThreadUse_ = HelperThreadUse::Active;
187 0 : }
188 0 : void clearUsedByHelperThread() {
189 0 : MOZ_ASSERT(helperThreadUse_ != HelperThreadUse::None);
190 0 : helperThreadUse_ = HelperThreadUse::None;
191 5 : }
192 :
193 : void findOutgoingEdges(js::gc::ZoneComponentFinder& finder);
194 :
195 : void discardJitCode(js::FreeOp* fop, bool discardBaselineCode = true);
196 :
197 : void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
198 : size_t* typePool,
199 : size_t* regexpZone,
200 : size_t* jitZone,
201 : size_t* baselineStubsOptimized,
202 : size_t* cachedCFG,
203 : size_t* uniqueIdMap,
204 : size_t* shapeTables,
205 : size_t* atomsMarkBitmaps,
206 : size_t* compartmentObjects,
207 : size_t* crossCompartmentWrappersTables,
208 : size_t* compartmentsPrivateData);
209 :
210 : // Iterate over all cells in the zone. See the definition of ZoneCellIter
211 : // in gc/GC-inl.h for the possible arguments and documentation.
212 : template <typename T, typename... Args>
213 : js::gc::ZoneCellIter<T> cellIter(Args&&... args) {
214 11 : return js::gc::ZoneCellIter<T>(const_cast<Zone*>(this), std::forward<Args>(args)...);
215 : }
216 :
217 0 : MOZ_MUST_USE void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes,
218 : void* reallocPtr = nullptr) {
219 0 : if (!js::CurrentThreadCanAccessRuntime(runtime_))
220 : return nullptr;
221 0 : return runtimeFromMainThread()->onOutOfMemory(allocFunc, nbytes, reallocPtr);
222 : }
223 0 : void reportAllocationOverflow() { js::ReportAllocationOverflow(nullptr); }
224 :
225 : void beginSweepTypes(bool releaseTypes);
226 :
227 : bool hasMarkedRealms();
228 :
229 0 : void scheduleGC() { MOZ_ASSERT(!RuntimeHeapIsBusy()); gcScheduled_ = true; }
230 0 : void unscheduleGC() { gcScheduled_ = false; }
231 0 : bool isGCScheduled() { return gcScheduled_; }
232 :
233 0 : void setPreservingCode(bool preserving) { gcPreserveCode_ = preserving; }
234 0 : bool isPreservingCode() const { return gcPreserveCode_; }
235 :
236 : // Whether this zone can currently be collected. This doesn't take account
237 : // of AutoKeepAtoms for the atoms zone.
238 : bool canCollect();
239 :
240 0 : void changeGCState(GCState prev, GCState next) {
241 0 : MOZ_ASSERT(RuntimeHeapIsBusy());
242 0 : MOZ_ASSERT(gcState() == prev);
243 0 : MOZ_ASSERT_IF(next != NoGC, canCollect());
244 0 : gcState_ = next;
245 0 : }
246 :
247 0 : bool isCollecting() const {
248 0 : MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtimeFromMainThread()));
249 0 : return isCollectingFromAnyThread();
250 : }
251 :
252 0 : bool isCollectingFromAnyThread() const {
253 0 : if (RuntimeHeapIsCollecting())
254 0 : return gcState_ != NoGC;
255 : else
256 0 : return needsIncrementalBarrier();
257 : }
258 :
259 : // If this returns true, all object tracing must be done with a GC marking
260 : // tracer.
261 72 : bool requireGCTracer() const {
262 72 : JSRuntime* rt = runtimeFromAnyThread();
263 0 : return RuntimeHeapIsMajorCollecting() && !rt->gc.isHeapCompacting() && gcState_ != NoGC;
264 : }
265 :
266 : bool shouldMarkInZone() const {
267 0 : return needsIncrementalBarrier() || isGCMarking();
268 : }
269 :
270 : // Get a number that is incremented whenever this zone is collected, and
271 : // possibly at other times too.
272 : uint64_t gcNumber();
273 :
274 : bool compileBarriers() const { return compileBarriers(needsIncrementalBarrier()); }
275 : bool compileBarriers(bool needsIncrementalBarrier) const {
276 : return needsIncrementalBarrier ||
277 : runtimeFromMainThread()->hasZealMode(js::gc::ZealMode::VerifierPre);
278 : }
279 :
280 : void setNeedsIncrementalBarrier(bool needs);
281 957 : const uint32_t* addressOfNeedsIncrementalBarrier() const { return &needsIncrementalBarrier_; }
282 :
283 0 : js::jit::JitZone* getJitZone(JSContext* cx) { return jitZone_ ? jitZone_ : createJitZone(cx); }
284 74470 : js::jit::JitZone* jitZone() { return jitZone_; }
285 :
286 40195840 : bool isAtomsZone() const { return runtimeFromAnyThread()->isAtomsZone(this); }
287 0 : bool isSelfHostingZone() const { return runtimeFromAnyThread()->isSelfHostingZone(this); }
288 :
289 : void prepareForCompacting();
290 :
291 : #ifdef DEBUG
292 : // For testing purposes, return the index of the sweep group which this zone
293 : // was swept in in the last GC.
294 : unsigned lastSweepGroupIndex() { return gcLastSweepGroupIndex; }
295 : #endif
296 :
297 : void sweepBreakpoints(js::FreeOp* fop);
298 : void sweepUniqueIds();
299 : void sweepWeakMaps();
300 : void sweepCompartments(js::FreeOp* fop, bool keepAtleastOne, bool lastGC);
301 :
302 : using DebuggerVector = js::Vector<js::Debugger*, 0, js::SystemAllocPolicy>;
303 :
304 : private:
305 : js::ZoneData<DebuggerVector*> debuggers;
306 :
307 : js::jit::JitZone* createJitZone(JSContext* cx);
308 :
309 : bool isQueuedForBackgroundSweep() {
310 0 : return isOnList();
311 : }
312 :
313 : // Side map for storing a unique ids for cells, independent of address.
314 : js::ZoneOrGCTaskData<js::gc::UniqueIdMap> uniqueIds_;
315 :
316 0 : js::gc::UniqueIdMap& uniqueIds() { return uniqueIds_.ref(); }
317 :
318 : public:
319 : bool hasDebuggers() const { return debuggers && debuggers->length(); }
320 0 : DebuggerVector* getDebuggers() const { return debuggers; }
321 : DebuggerVector* getOrCreateDebuggers(JSContext* cx);
322 :
323 : void notifyObservingDebuggers();
324 :
325 : void clearTables();
326 :
327 : /*
328 : * When true, skip calling the metadata callback. We use this:
329 : * - to avoid invoking the callback recursively;
330 : * - to avoid observing lazy prototype setup (which confuses callbacks that
331 : * want to use the types being set up!);
332 : * - to avoid attaching allocation stacks to allocation stack nodes, which
333 : * is silly
334 : * And so on.
335 : */
336 : js::ZoneData<bool> suppressAllocationMetadataBuilder;
337 :
338 : js::gc::ArenaLists arenas;
339 :
340 : js::TypeZone types;
341 :
342 : private:
343 : /* Live weakmaps in this zone. */
344 : js::ZoneOrGCTaskData<mozilla::LinkedList<js::WeakMapBase>> gcWeakMapList_;
345 : public:
346 34 : mozilla::LinkedList<js::WeakMapBase>& gcWeakMapList() { return gcWeakMapList_.ref(); }
347 :
348 : typedef js::Vector<JS::Compartment*, 1, js::SystemAllocPolicy> CompartmentVector;
349 :
350 : private:
351 : // The set of compartments in this zone.
352 : js::MainThreadOrGCTaskData<CompartmentVector> compartments_;
353 : public:
354 6043 : CompartmentVector& compartments() { return compartments_.ref(); }
355 :
356 : // This zone's gray roots.
357 : typedef js::Vector<js::gc::Cell*, 0, js::SystemAllocPolicy> GrayRootVector;
358 : private:
359 : js::ZoneOrGCTaskData<GrayRootVector> gcGrayRoots_;
360 : public:
361 0 : GrayRootVector& gcGrayRoots() { return gcGrayRoots_.ref(); }
362 :
363 : // This zone's weak edges found via graph traversal during marking,
364 : // preserved for re-scanning during sweeping.
365 : using WeakEdges = js::Vector<js::gc::TenuredCell**, 0, js::SystemAllocPolicy>;
366 : private:
367 : js::ZoneOrGCTaskData<WeakEdges> gcWeakRefs_;
368 : public:
369 0 : WeakEdges& gcWeakRefs() { return gcWeakRefs_.ref(); }
370 :
371 : private:
372 : // List of non-ephemeron weak containers to sweep during beginSweepingSweepGroup.
373 : js::ZoneOrGCTaskData<mozilla::LinkedList<detail::WeakCacheBase>> weakCaches_;
374 : public:
375 249 : mozilla::LinkedList<detail::WeakCacheBase>& weakCaches() { return weakCaches_.ref(); }
376 : void registerWeakCache(detail::WeakCacheBase* cachep) {
377 498 : weakCaches().insertBack(cachep);
378 : }
379 :
380 : private:
381 : /*
382 : * Mapping from not yet marked keys to a vector of all values that the key
383 : * maps to in any live weak map.
384 : */
385 : js::ZoneOrGCTaskData<js::gc::WeakKeyTable> gcWeakKeys_;
386 : public:
387 21 : js::gc::WeakKeyTable& gcWeakKeys() { return gcWeakKeys_.ref(); }
388 :
389 : private:
390 : // A set of edges from this zone to other zones.
391 : //
392 : // This is used during GC while calculating sweep groups to record edges
393 : // that can't be determined by examining this zone by itself.
394 : js::MainThreadData<ZoneSet> gcSweepGroupEdges_;
395 :
396 : public:
397 21 : ZoneSet& gcSweepGroupEdges() { return gcSweepGroupEdges_.ref(); }
398 :
399 : // Keep track of all TypeDescr and related objects in this compartment.
400 : // This is used by the GC to trace them all first when compacting, since the
401 : // TypedObject trace hook may access these objects.
402 : //
403 : // There are no barriers here - the set contains only tenured objects so no
404 : // post-barrier is required, and these are weak references so no pre-barrier
405 : // is required.
406 : using TypeDescrObjectSet = js::GCHashSet<JSObject*,
407 : js::MovableCellHasher<JSObject*>,
408 : js::SystemAllocPolicy>;
409 : private:
410 : js::ZoneData<JS::WeakCache<TypeDescrObjectSet>> typeDescrObjects_;
411 :
412 : // Malloc counter to measure memory pressure for GC scheduling. This
413 : // counter should be used only when it's not possible to know the size of
414 : // a free.
415 : js::gc::MemoryCounter gcMallocCounter;
416 :
417 : // Counter of JIT code executable memory for GC scheduling. Also imprecise,
418 : // since wasm can generate code that outlives a zone.
419 : js::gc::MemoryCounter jitCodeCounter;
420 :
421 0 : void updateMemoryCounter(js::gc::MemoryCounter& counter, size_t nbytes) {
422 0 : JSRuntime* rt = runtimeFromAnyThread();
423 :
424 143711 : counter.update(nbytes);
425 0 : auto trigger = counter.shouldTriggerGC(rt->gc.tunables);
426 143712 : if (MOZ_LIKELY(trigger == js::gc::NoTrigger) || trigger <= counter.triggered())
427 : return;
428 :
429 0 : if (!js::CurrentThreadCanAccessRuntime(rt))
430 : return;
431 :
432 0 : bool wouldInterruptGC = rt->gc.isIncrementalGCInProgress() && !isCollecting();
433 0 : if (wouldInterruptGC && !counter.shouldResetIncrementalGC(rt->gc.tunables))
434 : return;
435 :
436 0 : if (!rt->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC,
437 : counter.bytes(), counter.maxBytes()))
438 : {
439 : return;
440 : }
441 :
442 0 : counter.recordTrigger(trigger);
443 : }
444 :
445 : public:
446 : js::RegExpZone regExps;
447 :
448 105 : JS::WeakCache<TypeDescrObjectSet>& typeDescrObjects() { return typeDescrObjects_.ref(); }
449 :
450 : bool addTypeDescrObject(JSContext* cx, HandleObject obj);
451 :
452 : void setGCMaxMallocBytes(size_t value, const js::AutoLockGC& lock) {
453 33 : gcMallocCounter.setMax(value, lock);
454 : }
455 : void updateMallocCounter(size_t nbytes) {
456 140903 : updateMemoryCounter(gcMallocCounter, nbytes);
457 : }
458 : void adoptMallocBytes(Zone* other) {
459 5 : gcMallocCounter.adopt(other->gcMallocCounter);
460 : }
461 0 : size_t GCMaxMallocBytes() const { return gcMallocCounter.maxBytes(); }
462 0 : size_t GCMallocBytes() const { return gcMallocCounter.bytes(); }
463 :
464 : void updateJitCodeMallocBytes(size_t nbytes) {
465 0 : updateMemoryCounter(jitCodeCounter, nbytes);
466 : }
467 :
468 : void updateAllGCMallocCountersOnGCStart() {
469 0 : gcMallocCounter.updateOnGCStart();
470 0 : jitCodeCounter.updateOnGCStart();
471 : }
472 0 : void updateAllGCMallocCountersOnGCEnd(const js::AutoLockGC& lock) {
473 0 : auto& gc = runtimeFromAnyThread()->gc;
474 0 : gcMallocCounter.updateOnGCEnd(gc.tunables, lock);
475 0 : jitCodeCounter.updateOnGCEnd(gc.tunables, lock);
476 0 : }
477 0 : js::gc::TriggerKind shouldTriggerGCForTooMuchMalloc() {
478 0 : auto& gc = runtimeFromAnyThread()->gc;
479 0 : return std::max(gcMallocCounter.shouldTriggerGC(gc.tunables),
480 0 : jitCodeCounter.shouldTriggerGC(gc.tunables));
481 : }
482 :
483 : void keepAtoms() {
484 4900 : keepAtomsCount++;
485 : }
486 : void releaseAtoms();
487 : bool hasKeptAtoms() const {
488 125754 : return keepAtomsCount;
489 : }
490 :
491 : private:
492 : // Bitmap of atoms marked by this zone.
493 : js::ZoneOrGCTaskData<js::SparseBitmap> markedAtoms_;
494 :
495 : // Set of atoms recently used by this Zone. Purged on GC unless
496 : // keepAtomsCount is non-zero.
497 : js::ZoneOrGCTaskData<js::AtomSet> atomCache_;
498 :
499 : // Cache storing allocated external strings. Purged on GC.
500 : js::ZoneOrGCTaskData<js::ExternalStringCache> externalStringCache_;
501 :
502 : // Cache for Function.prototype.toString. Purged on GC.
503 : js::ZoneOrGCTaskData<js::FunctionToStringCache> functionToStringCache_;
504 :
505 : // Count of AutoKeepAtoms instances for this zone. When any instances exist,
506 : // atoms in the runtime will be marked from this zone's atom mark bitmap,
507 : // rather than when traced in the normal way. Threads parsing off the main
508 : // thread do not increment this value, but the presence of any such threads
509 : // also inhibits collection of atoms. We don't scan the stacks of exclusive
510 : // threads, so we need to avoid collecting their objects in another way. The
511 : // only GC thing pointers they have are to their exclusive compartment
512 : // (which is not collected) or to the atoms compartment. Therefore, we avoid
513 : // collecting the atoms zone when exclusive threads are running.
514 : js::ZoneOrGCTaskData<unsigned> keepAtomsCount;
515 :
516 : // Whether purging atoms was deferred due to keepAtoms being set. If this
517 : // happen then the cache will be purged when keepAtoms drops to zero.
518 : js::ZoneOrGCTaskData<bool> purgeAtomsDeferred;
519 :
520 : public:
521 477418 : js::SparseBitmap& markedAtoms() { return markedAtoms_.ref(); }
522 :
523 406812 : js::AtomSet& atomCache() { return atomCache_.ref(); }
524 :
525 : void traceAtomCache(JSTracer* trc);
526 : void purgeAtomCacheOrDefer();
527 :
528 2398 : js::ExternalStringCache& externalStringCache() { return externalStringCache_.ref(); };
529 :
530 19 : js::FunctionToStringCache& functionToStringCache() { return functionToStringCache_.ref(); }
531 :
532 : // Track heap usage under this Zone.
533 : js::gc::HeapUsage usage;
534 :
535 : // Thresholds used to trigger GC.
536 : js::gc::ZoneHeapThreshold threshold;
537 :
538 : // Amount of data to allocate before triggering a new incremental slice for
539 : // the current GC.
540 : js::UnprotectedData<size_t> gcDelayBytes;
541 :
542 : js::ZoneData<uint32_t> tenuredStrings;
543 : js::ZoneData<bool> allocNurseryStrings;
544 :
545 : private:
546 : // Shared Shape property tree.
547 : js::ZoneData<js::PropertyTree> propertyTree_;
548 : public:
549 104074 : js::PropertyTree& propertyTree() { return propertyTree_.ref(); }
550 :
551 : private:
552 : // Set of all unowned base shapes in the Zone.
553 : js::ZoneData<js::BaseShapeSet> baseShapes_;
554 : public:
555 13385 : js::BaseShapeSet& baseShapes() { return baseShapes_.ref(); }
556 :
557 : private:
558 : // Set of initial shapes in the Zone. For certain prototypes -- namely,
559 : // those of various builtin classes -- there are two entries: one for a
560 : // lookup via TaggedProto, and one for a lookup via JSProtoKey. See
561 : // InitialShapeProto.
562 : js::ZoneData<js::InitialShapeSet> initialShapes_;
563 : public:
564 96179 : js::InitialShapeSet& initialShapes() { return initialShapes_.ref(); }
565 :
566 : private:
567 : // List of shapes that may contain nursery pointers.
568 : using NurseryShapeVector = js::Vector<js::AccessorShape*, 0, js::SystemAllocPolicy>;
569 : js::ZoneData<NurseryShapeVector> nurseryShapes_;
570 : public:
571 2659 : NurseryShapeVector& nurseryShapes() { return nurseryShapes_.ref(); }
572 :
573 : #ifdef JSGC_HASH_TABLE_CHECKS
574 : void checkInitialShapesTableAfterMovingGC();
575 : void checkBaseShapeTableAfterMovingGC();
576 : #endif
577 : void fixupInitialShapeTable();
578 : void fixupAfterMovingGC();
579 :
580 : // Per-zone data for use by an embedder.
581 : js::ZoneData<void*> data;
582 :
583 : js::ZoneData<bool> isSystem;
584 :
585 : #ifdef DEBUG
586 : js::ZoneData<unsigned> gcLastSweepGroupIndex;
587 : #endif
588 :
589 : static js::HashNumber UniqueIdToHash(uint64_t uid) {
590 288925 : return mozilla::HashGeneric(uid);
591 : }
592 :
593 : // Creates a HashNumber based on getUniqueId. Returns false on OOM.
594 : MOZ_MUST_USE bool getHashCode(js::gc::Cell* cell, js::HashNumber* hashp) {
595 : uint64_t uid;
596 : if (!getOrCreateUniqueId(cell, &uid))
597 : return false;
598 : *hashp = UniqueIdToHash(uid);
599 : return true;
600 : }
601 :
602 : // Gets an existing UID in |uidp| if one exists.
603 253831 : MOZ_MUST_USE bool maybeGetUniqueId(js::gc::Cell* cell, uint64_t* uidp) {
604 253831 : MOZ_ASSERT(uidp);
605 253831 : MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
606 :
607 : // Get an existing uid, if one has been set.
608 0 : auto p = uniqueIds().lookup(cell);
609 253826 : if (p)
610 0 : *uidp = p->value();
611 :
612 253822 : return p.found();
613 : }
614 :
615 : // Puts an existing UID in |uidp|, or creates a new UID for this Cell and
616 : // puts that into |uidp|. Returns false on OOM.
617 621347 : MOZ_MUST_USE bool getOrCreateUniqueId(js::gc::Cell* cell, uint64_t* uidp) {
618 0 : MOZ_ASSERT(uidp);
619 621347 : MOZ_ASSERT(js::CurrentThreadCanAccessZone(this) || js::CurrentThreadIsPerformingGC());
620 :
621 : // Get an existing uid, if one has been set.
622 1242775 : auto p = uniqueIds().lookupForAdd(cell);
623 0 : if (p) {
624 0 : *uidp = p->value();
625 1 : return true;
626 : }
627 :
628 11624 : MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
629 :
630 : // Set a new uid on the cell.
631 0 : *uidp = js::gc::NextCellUniqueId(runtimeFromAnyThread());
632 0 : if (!uniqueIds().add(p, cell, *uidp))
633 : return false;
634 :
635 : // If the cell was in the nursery, hopefully unlikely, then we need to
636 : // tell the nursery about it so that it can sweep the uid if the thing
637 : // does not get tenured.
638 0 : if (IsInsideNursery(cell) &&
639 0 : !runtimeFromMainThread()->gc.nursery().addedUniqueIdToCell(cell))
640 : {
641 0 : uniqueIds().remove(cell);
642 0 : return false;
643 : }
644 :
645 : return true;
646 : }
647 :
648 : js::HashNumber getHashCodeInfallible(js::gc::Cell* cell) {
649 0 : return UniqueIdToHash(getUniqueIdInfallible(cell));
650 : }
651 :
652 542699 : uint64_t getUniqueIdInfallible(js::gc::Cell* cell) {
653 : uint64_t uid;
654 0 : js::AutoEnterOOMUnsafeRegion oomUnsafe;
655 0 : if (!getOrCreateUniqueId(cell, &uid))
656 0 : oomUnsafe.crash("failed to allocate uid");
657 0 : return uid;
658 : }
659 :
660 : // Return true if this cell has a UID associated with it.
661 0 : MOZ_MUST_USE bool hasUniqueId(js::gc::Cell* cell) {
662 0 : MOZ_ASSERT(js::CurrentThreadCanAccessZone(this) || js::CurrentThreadIsPerformingGC());
663 618132 : return uniqueIds().has(cell);
664 : }
665 :
666 : // Transfer an id from another cell. This must only be called on behalf of a
667 : // moving GC. This method is infallible.
668 5389 : void transferUniqueId(js::gc::Cell* tgt, js::gc::Cell* src) {
669 5389 : MOZ_ASSERT(src != tgt);
670 0 : MOZ_ASSERT(!IsInsideNursery(tgt));
671 5389 : MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtimeFromMainThread()));
672 5389 : MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
673 0 : MOZ_ASSERT(!uniqueIds().has(tgt));
674 10778 : uniqueIds().rekeyIfMoved(src, tgt);
675 5389 : }
676 :
677 : // Remove any unique id associated with this Cell.
678 174 : void removeUniqueId(js::gc::Cell* cell) {
679 174 : MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
680 174 : uniqueIds().remove(cell);
681 174 : }
682 :
683 : // When finished parsing off-thread, transfer any UIDs we created in the
684 : // off-thread zone into the target zone.
685 0 : void adoptUniqueIds(JS::Zone* source) {
686 0 : js::AutoEnterOOMUnsafeRegion oomUnsafe;
687 44 : for (js::gc::UniqueIdMap::Enum e(source->uniqueIds()); !e.empty(); e.popFront()) {
688 51 : MOZ_ASSERT(!uniqueIds().has(e.front().key()));
689 1 : if (!uniqueIds().put(e.front().key(), e.front().value()))
690 0 : oomUnsafe.crash("failed to transfer unique ids from off-thread");
691 : }
692 10 : source->uniqueIds().clear();
693 1 : }
694 :
695 : #ifdef JSGC_HASH_TABLE_CHECKS
696 : // Assert that the UniqueId table has been redirected successfully.
697 : void checkUniqueIdTableAfterMovingGC();
698 : #endif
699 :
700 : bool keepShapeTables() const {
701 200332 : return keepShapeTables_;
702 : }
703 : void setKeepShapeTables(bool b) {
704 400658 : keepShapeTables_ = b;
705 : }
706 :
707 : // Delete an empty compartment after its contents have been merged.
708 : void deleteEmptyCompartment(JS::Compartment* comp);
709 :
710 : /*
711 : * This variation of calloc will call the large-allocation-failure callback
712 : * on OOM and retry the allocation.
713 : */
714 : template <typename T>
715 8 : T* pod_callocCanGC(size_t numElems) {
716 8 : T* p = pod_calloc<T>(numElems);
717 8 : if (MOZ_LIKELY(!!p))
718 : return p;
719 : size_t bytes;
720 0 : if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
721 : reportAllocationOverflow();
722 : return nullptr;
723 : }
724 0 : JSRuntime* rt = runtimeFromMainThread();
725 0 : p = static_cast<T*>(rt->onOutOfMemoryCanGC(js::AllocFunction::Calloc, bytes));
726 0 : if (!p)
727 : return nullptr;
728 0 : updateMallocCounter(bytes);
729 0 : return p;
730 : }
731 :
732 : // Non-zero if the storage underlying any typed object in this zone might
733 : // be detached. This is stored in Zone because IC stubs bake in a pointer
734 : // to this field and Baseline IC code is shared across realms within a
735 : // Zone. Furthermore, it's not entirely clear if this flag is ever set to
736 : // a non-zero value since bug 1458011.
737 : uint32_t detachedTypedObjects = 0;
738 :
739 : private:
740 : js::ZoneData<js::jit::JitZone*> jitZone_;
741 :
742 : js::MainThreadData<bool> gcScheduled_;
743 : js::MainThreadData<bool> gcScheduledSaved_;
744 : js::ZoneData<bool> gcPreserveCode_;
745 : js::ZoneData<bool> keepShapeTables_;
746 :
747 : // Allow zones to be linked into a list
748 : friend class js::gc::ZoneList;
749 : static Zone * const NotOnList;
750 : js::MainThreadOrGCTaskData<Zone*> listNext_;
751 : bool isOnList() const;
752 : Zone* nextZone() const;
753 :
754 : friend bool js::CurrentThreadCanAccessZone(Zone* zone);
755 : friend class js::gc::GCRuntime;
756 : };
757 :
758 : } // namespace JS
759 :
760 : namespace js {
761 :
762 : template <typename T>
763 : inline T*
764 : ZoneAllocPolicy::maybe_pod_malloc(size_t numElems)
765 : {
766 : return zone->maybe_pod_malloc<T>(numElems);
767 : }
768 :
769 : template <typename T>
770 : inline T*
771 : ZoneAllocPolicy::maybe_pod_calloc(size_t numElems)
772 : {
773 : return zone->maybe_pod_calloc<T>(numElems);
774 : }
775 :
776 : template <typename T>
777 : inline T*
778 : ZoneAllocPolicy::maybe_pod_realloc(T* p, size_t oldSize, size_t newSize)
779 : {
780 : return zone->maybe_pod_realloc<T>(p, oldSize, newSize);
781 : }
782 :
783 : template <typename T>
784 : inline T*
785 : ZoneAllocPolicy::pod_malloc(size_t numElems)
786 : {
787 : return zone->pod_malloc<T>(numElems);
788 : }
789 :
790 : template <typename T>
791 : inline T*
792 : ZoneAllocPolicy::pod_calloc(size_t numElems)
793 : {
794 : return zone->pod_calloc<T>(numElems);
795 : }
796 :
797 : template <typename T>
798 : inline T*
799 : ZoneAllocPolicy::pod_realloc(T* p, size_t oldSize, size_t newSize)
800 : {
801 : return zone->pod_realloc<T>(p, oldSize, newSize);
802 : }
803 :
804 : } // namespace js
805 :
806 : #endif // gc_Zone_h
|