HeapAPI.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #ifndef js_HeapAPI_h
  6. #define js_HeapAPI_h
  7. #include <limits.h>
  8. #include "jspubtd.h"
  9. #include "js/TraceKind.h"
  10. #include "js/Utility.h"
  11. /* These values are private to the JS engine. */
  12. namespace js {
  13. JS_FRIEND_API(bool)
  14. CurrentThreadCanAccessZone(JS::Zone* zone);
  15. namespace gc {
  16. struct Cell;
  17. const size_t ArenaShift = 12;
  18. const size_t ArenaSize = size_t(1) << ArenaShift;
  19. const size_t ArenaMask = ArenaSize - 1;
  20. #ifdef JS_GC_SMALL_CHUNK_SIZE
  21. const size_t ChunkShift = 18;
  22. #else
  23. const size_t ChunkShift = 20;
  24. #endif
  25. const size_t ChunkSize = size_t(1) << ChunkShift;
  26. const size_t ChunkMask = ChunkSize - 1;
  27. const size_t CellShift = 3;
  28. const size_t CellSize = size_t(1) << CellShift;
  29. const size_t CellMask = CellSize - 1;
  30. /* These are magic constants derived from actual offsets in gc/Heap.h. */
  31. #ifdef JS_GC_SMALL_CHUNK_SIZE
  32. const size_t ChunkMarkBitmapOffset = 258104;
  33. const size_t ChunkMarkBitmapBits = 31744;
  34. #else
  35. const size_t ChunkMarkBitmapOffset = 1032352;
  36. const size_t ChunkMarkBitmapBits = 129024;
  37. #endif
  38. const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*);
  39. const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t);
  40. const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize;
  41. const size_t ChunkStoreBufferOffset = ChunkSize - ChunkTrailerSize + sizeof(uint64_t);
  42. const size_t ArenaZoneOffset = sizeof(size_t);
  43. const size_t ArenaHeaderSize = sizeof(size_t) + 2 * sizeof(uintptr_t) +
  44. sizeof(size_t) + sizeof(uintptr_t);
  45. /*
  46. * Live objects are marked black. How many other additional colors are available
  47. * depends on the size of the GCThing. Objects marked gray are eligible for
  48. * cycle collection.
  49. */
  50. static const uint32_t BLACK = 0;
  51. static const uint32_t GRAY = 1;
  52. /*
  53. * The "location" field in the Chunk trailer is a enum indicating various roles
  54. * of the chunk.
  55. */
  56. enum class ChunkLocation : uint32_t
  57. {
  58. Invalid = 0,
  59. Nursery = 1,
  60. TenuredHeap = 2
  61. };
  62. #ifdef JS_DEBUG
  63. /* When downcasting, ensure we are actually the right type. */
  64. extern JS_FRIEND_API(void)
  65. AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind);
  66. #else
  67. inline void
  68. AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind) {}
  69. #endif
  70. MOZ_ALWAYS_INLINE bool IsInsideNursery(const js::gc::Cell* cell);
  71. } /* namespace gc */
  72. } /* namespace js */
  73. namespace JS {
  74. struct Zone;
  75. /* Default size for the generational nursery in bytes. */
  76. const uint32_t DefaultNurseryBytes = 16 * js::gc::ChunkSize;
  77. /* Default maximum heap size in bytes to pass to JS_NewRuntime(). */
  78. const uint32_t DefaultHeapMaxBytes = 32 * 1024 * 1024;
  79. namespace shadow {
  80. struct Zone
  81. {
  82. protected:
  83. JSRuntime* const runtime_;
  84. JSTracer* const barrierTracer_; // A pointer to the JSRuntime's |gcMarker|.
  85. public:
  86. // Stack GC roots for Rooted GC pointers.
  87. js::RootedListHeads stackRoots_;
  88. template <typename T> friend class JS::Rooted;
  89. bool needsIncrementalBarrier_;
  90. Zone(JSRuntime* runtime, JSTracer* barrierTracerArg)
  91. : runtime_(runtime),
  92. barrierTracer_(barrierTracerArg),
  93. needsIncrementalBarrier_(false)
  94. {
  95. for (auto& stackRootPtr : stackRoots_)
  96. stackRootPtr = nullptr;
  97. }
  98. bool needsIncrementalBarrier() const {
  99. return needsIncrementalBarrier_;
  100. }
  101. JSTracer* barrierTracer() {
  102. MOZ_ASSERT(needsIncrementalBarrier_);
  103. MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
  104. return barrierTracer_;
  105. }
  106. JSRuntime* runtimeFromMainThread() const {
  107. MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
  108. return runtime_;
  109. }
  110. // Note: Unrestricted access to the zone's runtime from an arbitrary
  111. // thread can easily lead to races. Use this method very carefully.
  112. JSRuntime* runtimeFromAnyThread() const {
  113. return runtime_;
  114. }
  115. static MOZ_ALWAYS_INLINE JS::shadow::Zone* asShadowZone(JS::Zone* zone) {
  116. return reinterpret_cast<JS::shadow::Zone*>(zone);
  117. }
  118. };
  119. } /* namespace shadow */
  120. /**
  121. * A GC pointer, tagged with the trace kind.
  122. *
  123. * In general, a GC pointer should be stored with an exact type. This class
  124. * is for use when that is not possible because a single pointer must point
  125. * to several kinds of GC thing.
  126. */
  127. class JS_FRIEND_API(GCCellPtr)
  128. {
  129. public:
  130. // Construction from a void* and trace kind.
  131. GCCellPtr(void* gcthing, JS::TraceKind traceKind) : ptr(checkedCast(gcthing, traceKind)) {}
  132. // Automatically construct a null GCCellPtr from nullptr.
  133. MOZ_IMPLICIT GCCellPtr(decltype(nullptr)) : ptr(checkedCast(nullptr, JS::TraceKind::Null)) {}
  134. // Construction from an explicit type.
  135. template <typename T>
  136. explicit GCCellPtr(T* p) : ptr(checkedCast(p, JS::MapTypeToTraceKind<T>::kind)) { }
  137. explicit GCCellPtr(JSFunction* p) : ptr(checkedCast(p, JS::TraceKind::Object)) { }
  138. explicit GCCellPtr(JSFlatString* str) : ptr(checkedCast(str, JS::TraceKind::String)) { }
  139. explicit GCCellPtr(const Value& v);
  140. JS::TraceKind kind() const {
  141. JS::TraceKind traceKind = JS::TraceKind(ptr & OutOfLineTraceKindMask);
  142. if (uintptr_t(traceKind) != OutOfLineTraceKindMask)
  143. return traceKind;
  144. return outOfLineKind();
  145. }
  146. // Allow GCCellPtr to be used in a boolean context.
  147. explicit operator bool() const {
  148. MOZ_ASSERT(bool(asCell()) == (kind() != JS::TraceKind::Null));
  149. return asCell();
  150. }
  151. // Simplify checks to the kind.
  152. template <typename T>
  153. bool is() const { return kind() == JS::MapTypeToTraceKind<T>::kind; }
  154. // Conversions to more specific types must match the kind. Access to
  155. // further refined types is not allowed directly from a GCCellPtr.
  156. template <typename T>
  157. T& as() const {
  158. MOZ_ASSERT(kind() == JS::MapTypeToTraceKind<T>::kind);
  159. // We can't use static_cast here, because the fact that JSObject
  160. // inherits from js::gc::Cell is not part of the public API.
  161. return *reinterpret_cast<T*>(asCell());
  162. }
  163. // Return a pointer to the cell this |GCCellPtr| refers to, or |nullptr|.
  164. // (It would be more symmetrical with |to| for this to return a |Cell&|, but
  165. // the result can be |nullptr|, and null references are undefined behavior.)
  166. js::gc::Cell* asCell() const {
  167. return reinterpret_cast<js::gc::Cell*>(ptr & ~OutOfLineTraceKindMask);
  168. }
  169. // The CC's trace logger needs an identity that is XPIDL serializable.
  170. uint64_t unsafeAsInteger() const {
  171. return static_cast<uint64_t>(unsafeAsUIntPtr());
  172. }
  173. // Inline mark bitmap access requires direct pointer arithmetic.
  174. uintptr_t unsafeAsUIntPtr() const {
  175. MOZ_ASSERT(asCell());
  176. MOZ_ASSERT(!js::gc::IsInsideNursery(asCell()));
  177. return reinterpret_cast<uintptr_t>(asCell());
  178. }
  179. bool mayBeOwnedByOtherRuntime() const;
  180. private:
  181. static uintptr_t checkedCast(void* p, JS::TraceKind traceKind) {
  182. js::gc::Cell* cell = static_cast<js::gc::Cell*>(p);
  183. MOZ_ASSERT((uintptr_t(p) & OutOfLineTraceKindMask) == 0);
  184. AssertGCThingHasType(cell, traceKind);
  185. // Note: the OutOfLineTraceKindMask bits are set on all out-of-line kinds
  186. // so that we can mask instead of branching.
  187. MOZ_ASSERT_IF(uintptr_t(traceKind) >= OutOfLineTraceKindMask,
  188. (uintptr_t(traceKind) & OutOfLineTraceKindMask) == OutOfLineTraceKindMask);
  189. return uintptr_t(p) | (uintptr_t(traceKind) & OutOfLineTraceKindMask);
  190. }
  191. JS::TraceKind outOfLineKind() const;
  192. uintptr_t ptr;
  193. };
  194. inline bool
  195. operator==(const GCCellPtr& ptr1, const GCCellPtr& ptr2)
  196. {
  197. return ptr1.asCell() == ptr2.asCell();
  198. }
  199. inline bool
  200. operator!=(const GCCellPtr& ptr1, const GCCellPtr& ptr2)
  201. {
  202. return !(ptr1 == ptr2);
  203. }
  204. // Unwraps the given GCCellPtr and calls the given functor with a template
  205. // argument of the actual type of the pointer.
  206. template <typename F, typename... Args>
  207. auto
  208. DispatchTyped(F f, GCCellPtr thing, Args&&... args)
  209. -> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
  210. {
  211. switch (thing.kind()) {
  212. #define JS_EXPAND_DEF(name, type, _) \
  213. case JS::TraceKind::name: \
  214. return f(&thing.as<type>(), mozilla::Forward<Args>(args)...);
  215. JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
  216. #undef JS_EXPAND_DEF
  217. default:
  218. MOZ_CRASH("Invalid trace kind in DispatchTyped for GCCellPtr.");
  219. }
  220. }
  221. } /* namespace JS */
  222. namespace js {
  223. namespace gc {
  224. namespace detail {
  225. static MOZ_ALWAYS_INLINE uintptr_t*
  226. GetGCThingMarkBitmap(const uintptr_t addr)
  227. {
  228. MOZ_ASSERT(addr);
  229. const uintptr_t bmap_addr = (addr & ~ChunkMask) | ChunkMarkBitmapOffset;
  230. return reinterpret_cast<uintptr_t*>(bmap_addr);
  231. }
  232. static MOZ_ALWAYS_INLINE void
  233. GetGCThingMarkWordAndMask(const uintptr_t addr, uint32_t color,
  234. uintptr_t** wordp, uintptr_t* maskp)
  235. {
  236. MOZ_ASSERT(addr);
  237. const size_t bit = (addr & js::gc::ChunkMask) / js::gc::CellSize + color;
  238. MOZ_ASSERT(bit < js::gc::ChunkMarkBitmapBits);
  239. uintptr_t* bitmap = GetGCThingMarkBitmap(addr);
  240. const uintptr_t nbits = sizeof(*bitmap) * CHAR_BIT;
  241. *maskp = uintptr_t(1) << (bit % nbits);
  242. *wordp = &bitmap[bit / nbits];
  243. }
  244. static MOZ_ALWAYS_INLINE JS::Zone*
  245. GetGCThingZone(const uintptr_t addr)
  246. {
  247. MOZ_ASSERT(addr);
  248. const uintptr_t zone_addr = (addr & ~ArenaMask) | ArenaZoneOffset;
  249. return *reinterpret_cast<JS::Zone**>(zone_addr);
  250. }
  251. static MOZ_ALWAYS_INLINE JS::shadow::Runtime*
  252. GetCellRuntime(const Cell* cell)
  253. {
  254. MOZ_ASSERT(cell);
  255. const uintptr_t addr = uintptr_t(cell);
  256. const uintptr_t rt_addr = (addr & ~ChunkMask) | ChunkRuntimeOffset;
  257. return *reinterpret_cast<JS::shadow::Runtime**>(rt_addr);
  258. }
  259. static MOZ_ALWAYS_INLINE bool
  260. CellIsMarkedGray(const Cell* cell)
  261. {
  262. MOZ_ASSERT(cell);
  263. if (js::gc::IsInsideNursery(cell))
  264. return false;
  265. uintptr_t* word, mask;
  266. js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::GRAY, &word, &mask);
  267. return *word & mask;
  268. }
  269. extern JS_PUBLIC_API(bool)
  270. CellIsMarkedGrayIfKnown(const Cell* cell);
  271. MOZ_ALWAYS_INLINE ChunkLocation GetCellLocation(const void* cell) {
  272. uintptr_t addr = uintptr_t(cell);
  273. addr &= ~js::gc::ChunkMask;
  274. addr |= js::gc::ChunkLocationOffset;
  275. return *reinterpret_cast<ChunkLocation*>(addr);
  276. }
  277. MOZ_ALWAYS_INLINE bool NurseryCellHasStoreBuffer(const void* cell) {
  278. uintptr_t addr = uintptr_t(cell);
  279. addr &= ~js::gc::ChunkMask;
  280. addr |= js::gc::ChunkStoreBufferOffset;
  281. return *reinterpret_cast<void**>(addr) != nullptr;
  282. }
  283. } /* namespace detail */
  284. MOZ_ALWAYS_INLINE bool
  285. IsInsideNursery(const js::gc::Cell* cell)
  286. {
  287. if (!cell)
  288. return false;
  289. uintptr_t addr = uintptr_t(cell);
  290. addr &= ~js::gc::ChunkMask;
  291. addr |= js::gc::ChunkLocationOffset;
  292. auto location = *reinterpret_cast<ChunkLocation*>(addr);
  293. MOZ_ASSERT(location == ChunkLocation::Nursery || location == ChunkLocation::TenuredHeap);
  294. return location == ChunkLocation::Nursery;
  295. }
  296. MOZ_ALWAYS_INLINE bool IsCellPointerValid(const void* cell) {
  297. auto addr = uintptr_t(cell);
  298. if (addr < ChunkSize || addr % CellSize != 0) {
  299. return false;
  300. }
  301. auto location = detail::GetCellLocation(cell);
  302. if (location == ChunkLocation::TenuredHeap) {
  303. return !!detail::GetGCThingZone(addr);
  304. }
  305. if (location == ChunkLocation::Nursery) {
  306. return detail::NurseryCellHasStoreBuffer(cell);
  307. }
  308. return false;
  309. }
  310. MOZ_ALWAYS_INLINE bool IsCellPointerValidOrNull(const void* cell) {
  311. if (!cell) {
  312. return true;
  313. }
  314. return IsCellPointerValid(cell);
  315. }
  316. } /* namespace gc */
  317. } /* namespace js */
  318. namespace JS {
  319. static MOZ_ALWAYS_INLINE Zone*
  320. GetTenuredGCThingZone(GCCellPtr thing)
  321. {
  322. MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell()));
  323. return js::gc::detail::GetGCThingZone(thing.unsafeAsUIntPtr());
  324. }
  325. static MOZ_ALWAYS_INLINE Zone*
  326. GetStringZone(JSString* str)
  327. {
  328. return js::gc::detail::GetGCThingZone(uintptr_t(str));
  329. }
  330. extern JS_PUBLIC_API(Zone*)
  331. GetObjectZone(JSObject* obj);
  332. static MOZ_ALWAYS_INLINE bool
  333. GCThingIsMarkedGray(GCCellPtr thing)
  334. {
  335. if (thing.mayBeOwnedByOtherRuntime())
  336. return false;
  337. return js::gc::detail::CellIsMarkedGrayIfKnown(thing.asCell());
  338. }
  339. extern JS_PUBLIC_API(JS::TraceKind)
  340. GCThingTraceKind(void* thing);
  341. } /* namespace JS */
  342. namespace js {
  343. namespace gc {
  344. static MOZ_ALWAYS_INLINE bool
  345. IsIncrementalBarrierNeededOnTenuredGCThing(JS::shadow::Runtime* rt, const JS::GCCellPtr thing)
  346. {
  347. MOZ_ASSERT(thing);
  348. MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell()));
  349. // TODO: I'd like to assert !isHeapBusy() here but this gets called while we
  350. // are tracing the heap, e.g. during memory reporting (see bug 1313318).
  351. MOZ_ASSERT(!rt->isHeapCollecting());
  352. JS::Zone* zone = JS::GetTenuredGCThingZone(thing);
  353. return JS::shadow::Zone::asShadowZone(zone)->needsIncrementalBarrier();
  354. }
  355. #ifdef MOZ_DEVTOOLS_SERVER
  356. /**
  357. * Create an object providing access to the garbage collector's internal notion
  358. * of the current state of memory (both GC heap memory and GCthing-controlled
  359. * malloc memory.
  360. */
  361. extern JS_PUBLIC_API(JSObject*)
  362. NewMemoryInfoObject(JSContext* cx);
  363. #endif
  364. } /* namespace gc */
  365. } /* namespace js */
  366. #endif /* js_HeapAPI_h */