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 js_HeapAPI_h
8 : #define js_HeapAPI_h
9 :
10 : #include <limits.h>
11 :
12 : #include "jspubtd.h"
13 :
14 : #include "js/TraceKind.h"
15 : #include "js/Utility.h"
16 :
17 : /* These values are private to the JS engine. */
18 : namespace js {
19 :
20 : JS_FRIEND_API(bool)
21 : CurrentThreadCanAccessZone(JS::Zone* zone);
22 :
23 : namespace gc {
24 :
25 : struct Cell;
26 :
27 : const size_t ArenaShift = 12;
28 : const size_t ArenaSize = size_t(1) << ArenaShift;
29 : const size_t ArenaMask = ArenaSize - 1;
30 :
31 : #ifdef JS_GC_SMALL_CHUNK_SIZE
32 : const size_t ChunkShift = 18;
33 : #else
34 : const size_t ChunkShift = 20;
35 : #endif
36 : const size_t ChunkSize = size_t(1) << ChunkShift;
37 : const size_t ChunkMask = ChunkSize - 1;
38 :
39 : const size_t CellAlignShift = 3;
40 : const size_t CellAlignBytes = size_t(1) << CellAlignShift;
41 : const size_t CellAlignMask = CellAlignBytes - 1;
42 :
43 : const size_t CellBytesPerMarkBit = CellAlignBytes;
44 :
45 : /* These are magic constants derived from actual offsets in gc/Heap.h. */
46 : #ifdef JS_GC_SMALL_CHUNK_SIZE
47 : const size_t ChunkMarkBitmapOffset = 258104;
48 : const size_t ChunkMarkBitmapBits = 31744;
49 : #else
50 : const size_t ChunkMarkBitmapOffset = 1032352;
51 : const size_t ChunkMarkBitmapBits = 129024;
52 : #endif
53 : const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*);
54 : const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t);
55 : const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize;
56 : const size_t ArenaZoneOffset = sizeof(size_t);
57 : const size_t ArenaHeaderSize = sizeof(size_t) + 2 * sizeof(uintptr_t) +
58 : sizeof(size_t) + sizeof(uintptr_t);
59 :
60 : /*
61 : * Live objects are marked black or gray. Everything reachable from a JS root is
62 : * marked black. Objects marked gray are eligible for cycle collection.
63 : *
64 : * BlackBit: GrayOrBlackBit: Color:
65 : * 0 0 white
66 : * 0 1 gray
67 : * 1 0 black
68 : * 1 1 black
69 : */
70 : enum class ColorBit : uint32_t
71 : {
72 : BlackBit = 0,
73 : GrayOrBlackBit = 1
74 : };
75 :
76 : /*
77 : * The "location" field in the Chunk trailer is a enum indicating various roles
78 : * of the chunk.
79 : */
80 : enum class ChunkLocation : uint32_t
81 : {
82 : Invalid = 0,
83 : Nursery = 1,
84 : TenuredHeap = 2
85 : };
86 :
87 : #ifdef JS_DEBUG
88 : /* When downcasting, ensure we are actually the right type. */
89 : extern JS_FRIEND_API(void)
90 : AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind);
91 : #else
92 : inline void
93 : AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind) {}
94 : #endif
95 :
96 : MOZ_ALWAYS_INLINE bool IsInsideNursery(const js::gc::Cell* cell);
97 :
98 : } /* namespace gc */
99 : } /* namespace js */
100 :
101 : namespace JS {
102 : struct Zone;
103 :
104 : /* Default size for the generational nursery in bytes. */
105 : const uint32_t DefaultNurseryBytes = 16 * js::gc::ChunkSize;
106 :
107 : /* Default maximum heap size in bytes to pass to JS_NewRuntime(). */
108 : const uint32_t DefaultHeapMaxBytes = 32 * 1024 * 1024;
109 :
110 : namespace shadow {
111 :
112 : struct Zone
113 : {
114 : enum GCState : uint8_t {
115 : NoGC,
116 : Mark,
117 : MarkGray,
118 : Sweep,
119 : Finished,
120 : Compact
121 : };
122 :
123 : protected:
124 : JSRuntime* const runtime_;
125 : JSTracer* const barrierTracer_; // A pointer to the JSRuntime's |gcMarker|.
126 : uint32_t needsIncrementalBarrier_;
127 : GCState gcState_;
128 :
129 31 : Zone(JSRuntime* runtime, JSTracer* barrierTracerArg)
130 31 : : runtime_(runtime),
131 : barrierTracer_(barrierTracerArg),
132 : needsIncrementalBarrier_(0),
133 31 : gcState_(NoGC)
134 31 : {}
135 :
136 : public:
137 1436221 : bool needsIncrementalBarrier() const {
138 1436221 : return needsIncrementalBarrier_;
139 : }
140 :
141 21808 : JSTracer* barrierTracer() {
142 21808 : MOZ_ASSERT(needsIncrementalBarrier_);
143 21808 : MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
144 21808 : return barrierTracer_;
145 : }
146 :
147 237409 : JSRuntime* runtimeFromActiveCooperatingThread() const {
148 237409 : MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
149 237409 : return runtime_;
150 : }
151 :
152 : // Note: Unrestricted access to the zone's runtime from an arbitrary
153 : // thread can easily lead to races. Use this method very carefully.
154 5709184 : JSRuntime* runtimeFromAnyThread() const {
155 5709184 : return runtime_;
156 : }
157 :
158 0 : GCState gcState() const { return gcState_; }
159 407984 : bool wasGCStarted() const { return gcState_ != NoGC; }
160 0 : bool isGCMarkingBlack() const { return gcState_ == Mark; }
161 0 : bool isGCMarkingGray() const { return gcState_ == MarkGray; }
162 573607 : bool isGCSweeping() const { return gcState_ == Sweep; }
163 41601 : bool isGCFinished() const { return gcState_ == Finished; }
164 598153 : bool isGCCompacting() const { return gcState_ == Compact; }
165 44963 : bool isGCMarking() const { return gcState_ == Mark || gcState_ == MarkGray; }
166 102730 : bool isGCSweepingOrCompacting() const { return gcState_ == Sweep || gcState_ == Compact; }
167 :
168 1275370 : static MOZ_ALWAYS_INLINE JS::shadow::Zone* asShadowZone(JS::Zone* zone) {
169 1275370 : return reinterpret_cast<JS::shadow::Zone*>(zone);
170 : }
171 : };
172 :
173 : } /* namespace shadow */
174 :
175 : /**
176 : * A GC pointer, tagged with the trace kind.
177 : *
178 : * In general, a GC pointer should be stored with an exact type. This class
179 : * is for use when that is not possible because a single pointer must point
180 : * to several kinds of GC thing.
181 : */
182 : class JS_FRIEND_API(GCCellPtr)
183 : {
184 : public:
185 : // Construction from a void* and trace kind.
186 55251 : GCCellPtr(void* gcthing, JS::TraceKind traceKind) : ptr(checkedCast(gcthing, traceKind)) {}
187 :
188 : // Automatically construct a null GCCellPtr from nullptr.
189 0 : MOZ_IMPLICIT GCCellPtr(decltype(nullptr)) : ptr(checkedCast(nullptr, JS::TraceKind::Null)) {}
190 :
191 : // Construction from an explicit type.
192 : template <typename T>
193 205950 : explicit GCCellPtr(T* p) : ptr(checkedCast(p, JS::MapTypeToTraceKind<T>::kind)) { }
194 0 : explicit GCCellPtr(JSFunction* p) : ptr(checkedCast(p, JS::TraceKind::Object)) { }
195 : explicit GCCellPtr(JSFlatString* str) : ptr(checkedCast(str, JS::TraceKind::String)) { }
196 : explicit GCCellPtr(const Value& v);
197 :
198 724368 : JS::TraceKind kind() const {
199 724368 : JS::TraceKind traceKind = JS::TraceKind(ptr & OutOfLineTraceKindMask);
200 724368 : if (uintptr_t(traceKind) != OutOfLineTraceKindMask)
201 722080 : return traceKind;
202 2288 : return outOfLineKind();
203 : }
204 :
205 : // Allow GCCellPtr to be used in a boolean context.
206 238925 : explicit operator bool() const {
207 238925 : MOZ_ASSERT(bool(asCell()) == (kind() != JS::TraceKind::Null));
208 238925 : return asCell();
209 : }
210 :
211 : // Simplify checks to the kind.
212 : template <typename T>
213 473894 : bool is() const { return kind() == JS::MapTypeToTraceKind<T>::kind; }
214 :
215 : // Conversions to more specific types must match the kind. Access to
216 : // further refined types is not allowed directly from a GCCellPtr.
217 : template <typename T>
218 4515 : T& as() const {
219 4515 : MOZ_ASSERT(kind() == JS::MapTypeToTraceKind<T>::kind);
220 : // We can't use static_cast here, because the fact that JSObject
221 : // inherits from js::gc::Cell is not part of the public API.
222 4515 : return *reinterpret_cast<T*>(asCell());
223 : }
224 :
225 : // Return a pointer to the cell this |GCCellPtr| refers to, or |nullptr|.
226 : // (It would be more symmetrical with |to| for this to return a |Cell&|, but
227 : // the result can be |nullptr|, and null references are undefined behavior.)
228 2400448 : js::gc::Cell* asCell() const {
229 2400448 : return reinterpret_cast<js::gc::Cell*>(ptr & ~OutOfLineTraceKindMask);
230 : }
231 :
232 : // The CC's trace logger needs an identity that is XPIDL serializable.
233 0 : uint64_t unsafeAsInteger() const {
234 0 : return static_cast<uint64_t>(unsafeAsUIntPtr());
235 : }
236 : // Inline mark bitmap access requires direct pointer arithmetic.
237 235649 : uintptr_t unsafeAsUIntPtr() const {
238 235649 : MOZ_ASSERT(asCell());
239 235649 : MOZ_ASSERT(!js::gc::IsInsideNursery(asCell()));
240 235649 : return reinterpret_cast<uintptr_t>(asCell());
241 : }
242 :
243 235651 : MOZ_ALWAYS_INLINE bool mayBeOwnedByOtherRuntime() const {
244 235651 : if (is<JSString>() || is<JS::Symbol>())
245 94 : return mayBeOwnedByOtherRuntimeSlow();
246 235557 : return false;
247 : }
248 :
249 : private:
250 265916 : static uintptr_t checkedCast(void* p, JS::TraceKind traceKind) {
251 265916 : js::gc::Cell* cell = static_cast<js::gc::Cell*>(p);
252 265916 : MOZ_ASSERT((uintptr_t(p) & OutOfLineTraceKindMask) == 0);
253 265916 : AssertGCThingHasType(cell, traceKind);
254 : // Note: the OutOfLineTraceKindMask bits are set on all out-of-line kinds
255 : // so that we can mask instead of branching.
256 265916 : MOZ_ASSERT_IF(uintptr_t(traceKind) >= OutOfLineTraceKindMask,
257 : (uintptr_t(traceKind) & OutOfLineTraceKindMask) == OutOfLineTraceKindMask);
258 265916 : return uintptr_t(p) | (uintptr_t(traceKind) & OutOfLineTraceKindMask);
259 : }
260 :
261 : bool mayBeOwnedByOtherRuntimeSlow() const;
262 :
263 : JS::TraceKind outOfLineKind() const;
264 :
265 : uintptr_t ptr;
266 : };
267 :
268 : inline bool
269 5184 : operator==(const GCCellPtr& ptr1, const GCCellPtr& ptr2)
270 : {
271 5184 : return ptr1.asCell() == ptr2.asCell();
272 : }
273 :
274 : inline bool
275 : operator!=(const GCCellPtr& ptr1, const GCCellPtr& ptr2)
276 : {
277 : return !(ptr1 == ptr2);
278 : }
279 :
280 : // Unwraps the given GCCellPtr and calls the given functor with a template
281 : // argument of the actual type of the pointer.
282 : template <typename F, typename... Args>
283 : auto
284 4421 : DispatchTyped(F f, GCCellPtr thing, Args&&... args)
285 : -> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
286 : {
287 4421 : switch (thing.kind()) {
288 : #define JS_EXPAND_DEF(name, type, _) \
289 : case JS::TraceKind::name: \
290 : return f(&thing.as<type>(), mozilla::Forward<Args>(args)...);
291 0 : JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
292 : #undef JS_EXPAND_DEF
293 : default:
294 0 : MOZ_CRASH("Invalid trace kind in DispatchTyped for GCCellPtr.");
295 : }
296 : }
297 :
298 : } /* namespace JS */
299 :
300 : namespace js {
301 : namespace gc {
302 : namespace detail {
303 :
304 : static MOZ_ALWAYS_INLINE uintptr_t*
305 2927838 : GetGCThingMarkBitmap(const uintptr_t addr)
306 : {
307 2927838 : MOZ_ASSERT(addr);
308 2927838 : const uintptr_t bmap_addr = (addr & ~ChunkMask) | ChunkMarkBitmapOffset;
309 2927838 : return reinterpret_cast<uintptr_t*>(bmap_addr);
310 : }
311 :
312 : static MOZ_ALWAYS_INLINE void
313 2927835 : GetGCThingMarkWordAndMask(const uintptr_t addr, ColorBit colorBit,
314 : uintptr_t** wordp, uintptr_t* maskp)
315 : {
316 2927835 : MOZ_ASSERT(addr);
317 2927835 : const size_t bit = (addr & js::gc::ChunkMask) / js::gc::CellBytesPerMarkBit +
318 2927835 : static_cast<uint32_t>(colorBit);
319 2927835 : MOZ_ASSERT(bit < js::gc::ChunkMarkBitmapBits);
320 2927835 : uintptr_t* bitmap = GetGCThingMarkBitmap(addr);
321 2927848 : const uintptr_t nbits = sizeof(*bitmap) * CHAR_BIT;
322 2927848 : *maskp = uintptr_t(1) << (bit % nbits);
323 2927848 : *wordp = &bitmap[bit / nbits];
324 2927848 : }
325 :
326 : static MOZ_ALWAYS_INLINE JS::Zone*
327 260581 : GetGCThingZone(const uintptr_t addr)
328 : {
329 260581 : MOZ_ASSERT(addr);
330 260581 : const uintptr_t zone_addr = (addr & ~ArenaMask) | ArenaZoneOffset;
331 260581 : return *reinterpret_cast<JS::Zone**>(zone_addr);
332 :
333 : }
334 :
335 : static MOZ_ALWAYS_INLINE bool
336 462658 : TenuredCellIsMarkedGray(const Cell* cell)
337 : {
338 : // Return true if GrayOrBlackBit is set and BlackBit is not set.
339 462658 : MOZ_ASSERT(cell);
340 462658 : MOZ_ASSERT(!js::gc::IsInsideNursery(cell));
341 :
342 : uintptr_t* grayWord, grayMask;
343 462658 : js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::ColorBit::GrayOrBlackBit,
344 462658 : &grayWord, &grayMask);
345 462658 : if (!(*grayWord & grayMask))
346 462658 : return false;
347 :
348 : uintptr_t* blackWord, blackMask;
349 0 : js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::ColorBit::BlackBit,
350 0 : &blackWord, &blackMask);
351 0 : return !(*blackWord & blackMask);
352 : }
353 :
354 : static MOZ_ALWAYS_INLINE bool
355 0 : CellIsMarkedGray(const Cell* cell)
356 : {
357 0 : MOZ_ASSERT(cell);
358 0 : if (js::gc::IsInsideNursery(cell))
359 0 : return false;
360 0 : return TenuredCellIsMarkedGray(cell);
361 : }
362 :
363 : extern JS_PUBLIC_API(bool)
364 : CellIsMarkedGrayIfKnown(const Cell* cell);
365 :
366 : #ifdef DEBUG
367 : extern JS_PUBLIC_API(bool)
368 : CellIsNotGray(const Cell* cell);
369 : #endif
370 :
371 : } /* namespace detail */
372 :
373 : MOZ_ALWAYS_INLINE bool
374 25978824 : IsInsideNursery(const js::gc::Cell* cell)
375 : {
376 25978824 : if (!cell)
377 365692 : return false;
378 25613132 : uintptr_t addr = uintptr_t(cell);
379 25613132 : addr &= ~js::gc::ChunkMask;
380 25613132 : addr |= js::gc::ChunkLocationOffset;
381 25613132 : auto location = *reinterpret_cast<ChunkLocation*>(addr);
382 25613132 : MOZ_ASSERT(location == ChunkLocation::Nursery || location == ChunkLocation::TenuredHeap);
383 25613132 : return location == ChunkLocation::Nursery;
384 : }
385 :
386 : } /* namespace gc */
387 : } /* namespace js */
388 :
389 : namespace JS {
390 :
391 : static MOZ_ALWAYS_INLINE Zone*
392 232967 : GetTenuredGCThingZone(GCCellPtr thing)
393 : {
394 232967 : MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell()));
395 232968 : return js::gc::detail::GetGCThingZone(thing.unsafeAsUIntPtr());
396 : }
397 :
398 : static MOZ_ALWAYS_INLINE Zone*
399 5 : GetStringZone(JSString* str)
400 : {
401 5 : return js::gc::detail::GetGCThingZone(uintptr_t(str));
402 : }
403 :
404 : extern JS_PUBLIC_API(Zone*)
405 : GetObjectZone(JSObject* obj);
406 :
407 : extern JS_PUBLIC_API(Zone*)
408 : GetValueZone(const Value& value);
409 :
410 : static MOZ_ALWAYS_INLINE bool
411 0 : GCThingIsMarkedGray(GCCellPtr thing)
412 : {
413 0 : if (thing.mayBeOwnedByOtherRuntime())
414 0 : return false;
415 0 : return js::gc::detail::CellIsMarkedGrayIfKnown(thing.asCell());
416 : }
417 :
418 : extern JS_PUBLIC_API(JS::TraceKind)
419 : GCThingTraceKind(void* thing);
420 :
421 : } /* namespace JS */
422 :
423 : namespace js {
424 : namespace gc {
425 :
426 : static MOZ_ALWAYS_INLINE bool
427 232967 : IsIncrementalBarrierNeededOnTenuredGCThing(const JS::GCCellPtr thing)
428 : {
429 232967 : MOZ_ASSERT(thing);
430 232967 : MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell()));
431 :
432 : // TODO: I'd like to assert !CurrentThreadIsHeapBusy() here but this gets
433 : // called while we are tracing the heap, e.g. during memory reporting
434 : // (see bug 1313318).
435 232967 : MOZ_ASSERT(!JS::CurrentThreadIsHeapCollecting());
436 :
437 232967 : JS::Zone* zone = JS::GetTenuredGCThingZone(thing);
438 232968 : return JS::shadow::Zone::asShadowZone(zone)->needsIncrementalBarrier();
439 : }
440 :
441 : /**
442 : * Create an object providing access to the garbage collector's internal notion
443 : * of the current state of memory (both GC heap memory and GCthing-controlled
444 : * malloc memory.
445 : */
446 : extern JS_PUBLIC_API(JSObject*)
447 : NewMemoryInfoObject(JSContext* cx);
448 :
449 : } /* namespace gc */
450 : } /* namespace js */
451 :
452 : #endif /* js_HeapAPI_h */
|