Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / js / public / UbiNode.h
blob15edea070e4af05bb3311fe74e90f2f6ebb931d1
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/. */
7 #ifndef js_UbiNode_h
8 #define js_UbiNode_h
10 #include "mozilla/Alignment.h"
11 #include "mozilla/Assertions.h"
12 #include "mozilla/Attributes.h"
13 #include "mozilla/HashFunctions.h"
14 #include "mozilla/Maybe.h"
15 #include "mozilla/MemoryReporting.h"
16 #include "mozilla/RangedPtr.h"
17 #include "mozilla/Variant.h"
18 #include "mozilla/Vector.h"
20 #include <utility>
22 #include "jspubtd.h"
24 #include "js/AllocPolicy.h"
25 #include "js/ColumnNumber.h" // JS::TaggedColumnNumberOneOrigin
26 #include "js/HashTable.h"
27 #include "js/RootingAPI.h"
28 #include "js/TypeDecls.h"
29 #include "js/UniquePtr.h"
30 #include "js/Value.h"
32 // [SMDOC] ubi::Node (Heap Analysis framework)
34 // JS::ubi::Node is a pointer-like type designed for internal use by heap
35 // analysis tools. A ubi::Node can refer to:
37 // - a JS value, like a string, object, or symbol;
38 // - an internal SpiderMonkey structure, like a shape or a scope chain object
39 // - an instance of some embedding-provided type: in Firefox, an XPCOM
40 // object, or an internal DOM node class instance
42 // A ubi::Node instance provides metadata about its referent, and can
43 // enumerate its referent's outgoing edges, so you can implement heap analysis
44 // algorithms that walk the graph - finding paths between objects, or
45 // computing heap dominator trees, say - using ubi::Node, while remaining
46 // ignorant of the details of the types you're operating on.
48 // Of course, when it comes to presenting the results in a developer-facing
49 // tool, you'll need to stop being ignorant of those details, because you have
50 // to discuss the ubi::Nodes' referents with the developer. Here, ubi::Node
51 // can hand you dynamically checked, properly typed pointers to the original
52 // objects via the as<T> method, or generate descriptions of the referent
53 // itself.
55 // ubi::Node instances are lightweight (two-word) value types. Instances:
56 // - compare equal if and only if they refer to the same object;
57 // - have hash values that respect their equality relation; and
58 // - have serializations that are only equal if the ubi::Nodes are equal.
60 // A ubi::Node is only valid for as long as its referent is alive; if its
61 // referent goes away, the ubi::Node becomes a dangling pointer. A ubi::Node
62 // that refers to a GC-managed object is not automatically a GC root; if the
63 // GC frees or relocates its referent, the ubi::Node becomes invalid. A
64 // ubi::Node that refers to a reference-counted object does not bump the
65 // reference count.
67 // ubi::Node values require no supporting data structures, making them
68 // feasible for use in memory-constrained devices --- ideally, the memory
69 // requirements of the algorithm which uses them will be the limiting factor,
70 // not the demands of ubi::Node itself.
72 // One can construct a ubi::Node value given a pointer to a type that ubi::Node
73 // supports. In the other direction, one can convert a ubi::Node back to a
74 // pointer; these downcasts are checked dynamically. In particular, one can
75 // convert a 'JSContext*' to a ubi::Node, yielding a node with an outgoing edge
76 // for every root registered with the runtime; starting from this, one can walk
77 // the entire heap. (Of course, one could also start traversal at any other kind
78 // of type to which one has a pointer.)
81 // Extending ubi::Node To Handle Your Embedding's Types
83 // To add support for a new ubi::Node referent type R, you must define a
84 // specialization of the ubi::Concrete template, ubi::Concrete<R>, which
85 // inherits from ubi::Base. ubi::Node itself uses the specialization for
86 // compile-time information (i.e. the checked conversions between R * and
87 // ubi::Node), and the inheritance for run-time dispatching.
90 // ubi::Node Exposes Implementation Details
92 // In many cases, a JavaScript developer's view of their data differs
93 // substantially from its actual implementation. For example, while the
94 // ECMAScript specification describes objects as maps from property names to
95 // sets of attributes (like ECMAScript's [[Value]]), in practice many objects
96 // have only a pointer to a shape, shared with other similar objects, and
97 // indexed slots that contain the [[Value]] attributes. As another example, a
98 // string produced by concatenating two other strings may sometimes be
99 // represented by a "rope", a structure that points to the two original
100 // strings.
102 // We intend to use ubi::Node to write tools that report memory usage, so it's
103 // important that ubi::Node accurately portray how much memory nodes consume.
104 // Thus, for example, when data that apparently belongs to multiple nodes is
105 // in fact shared in a common structure, ubi::Node's graph uses a separate
106 // node for that shared structure, and presents edges to it from the data's
107 // apparent owners. For example, ubi::Node exposes SpiderMonkey objects'
108 // shapes and base shapes, and exposes rope string and substring structure,
109 // because these optimizations become visible when a tool reports how much
110 // memory a structure consumes.
112 // However, fine granularity is not a goal. When a particular object is the
113 // exclusive owner of a separate block of memory, ubi::Node may present the
114 // object and its block as a single node, and add their sizes together when
115 // reporting the node's size, as there is no meaningful loss of data in this
116 // case. Thus, for example, a ubi::Node referring to a JavaScript object, when
117 // asked for the object's size in bytes, includes the object's slot and
118 // element arrays' sizes in the total. There is no separate ubi::Node value
119 // representing the slot and element arrays, since they are owned exclusively
120 // by the object.
123 // Presenting Analysis Results To JavaScript Developers
125 // If an analysis provides its results in terms of ubi::Node values, a user
126 // interface presenting those results will generally need to clean them up
127 // before they can be understood by JavaScript developers. For example,
128 // JavaScript developers should not need to understand shapes, only JavaScript
129 // objects. Similarly, they should not need to understand the distinction
130 // between DOM nodes and the JavaScript shadow objects that represent them.
133 // Rooting Restrictions
135 // At present there is no way to root ubi::Node instances, so instances can't be
136 // live across any operation that might GC. Analyses using ubi::Node must either
137 // run to completion and convert their results to some other rootable type, or
138 // save their intermediate state in some rooted structure if they must GC before
139 // they complete. (For algorithms like path-finding and dominator tree
140 // computation, we implement the algorithm avoiding any operation that could
141 // cause a GC --- and use AutoCheckCannotGC to verify this.)
143 // If this restriction prevents us from implementing interesting tools, we may
144 // teach the GC how to root ubi::Nodes, fix up hash tables that use them as
145 // keys, etc.
148 // Hostile Graph Structure
150 // Analyses consuming ubi::Node graphs must be robust when presented with graphs
151 // that are deliberately constructed to exploit their weaknesses. When operating
152 // on live graphs, web content has control over the object graph, and less
153 // direct control over shape and string structure, and analyses should be
154 // prepared to handle extreme cases gracefully. For example, if an analysis were
155 // to use the C++ stack in a depth-first traversal, carefully constructed
156 // content could cause the analysis to overflow the stack.
158 // When ubi::Nodes refer to nodes deserialized from a heap snapshot, analyses
159 // must be even more careful: since snapshots often come from potentially
160 // compromised e10s content processes, even properties normally guaranteed by
161 // the platform (the proper linking of DOM nodes, for example) might be
162 // corrupted. While it is the deserializer's responsibility to check the basic
163 // structure of the snapshot file, the analyses should be prepared for ubi::Node
164 // graphs constructed from snapshots to be even more bizarre.
166 namespace js {
167 class BaseScript;
168 } // namespace js
170 namespace JS {
172 class JS_PUBLIC_API AutoCheckCannotGC;
174 using ZoneSet =
175 js::HashSet<Zone*, js::DefaultHasher<Zone*>, js::SystemAllocPolicy>;
177 using CompartmentSet =
178 js::HashSet<Compartment*, js::DefaultHasher<Compartment*>,
179 js::SystemAllocPolicy>;
181 namespace ubi {
183 class Edge;
184 class EdgeRange;
185 class StackFrame;
187 using mozilla::Maybe;
188 using mozilla::RangedPtr;
189 using mozilla::Variant;
191 template <typename T>
192 using Vector = mozilla::Vector<T, 0, js::SystemAllocPolicy>;
194 /*** ubi::StackFrame **********************************************************/
196 // Concrete JS::ubi::StackFrame instances backed by a live SavedFrame object
197 // store their strings as JSAtom*, while deserialized stack frames from offline
198 // heap snapshots store their strings as const char16_t*. In order to provide
199 // zero-cost accessors to these strings in a single interface that works with
200 // both cases, we use this variant type.
201 class JS_PUBLIC_API AtomOrTwoByteChars
202 : public Variant<JSAtom*, const char16_t*> {
203 using Base = Variant<JSAtom*, const char16_t*>;
205 public:
206 template <typename T>
207 MOZ_IMPLICIT AtomOrTwoByteChars(T&& rhs) : Base(std::forward<T>(rhs)) {}
209 template <typename T>
210 AtomOrTwoByteChars& operator=(T&& rhs) {
211 MOZ_ASSERT(this != &rhs, "self-move disallowed");
212 this->~AtomOrTwoByteChars();
213 new (this) AtomOrTwoByteChars(std::forward<T>(rhs));
214 return *this;
217 // Return the length of the given AtomOrTwoByteChars string.
218 size_t length();
220 // Copy the given AtomOrTwoByteChars string into the destination buffer,
221 // inflating if necessary. Does NOT null terminate. Returns the number of
222 // characters written to destination.
223 size_t copyToBuffer(RangedPtr<char16_t> destination, size_t length);
226 // The base class implemented by each ConcreteStackFrame<T> type. Subclasses
227 // must not add data members to this class.
228 class BaseStackFrame {
229 friend class StackFrame;
231 BaseStackFrame(const StackFrame&) = delete;
232 BaseStackFrame& operator=(const StackFrame&) = delete;
234 protected:
235 void* ptr;
236 explicit BaseStackFrame(void* ptr) : ptr(ptr) {}
238 public:
239 // This is a value type that should not have a virtual destructor. Don't add
240 // destructors in subclasses!
242 // Get a unique identifier for this StackFrame. The identifier is not valid
243 // across garbage collections.
244 virtual uint64_t identifier() const { return uint64_t(uintptr_t(ptr)); }
246 // Get this frame's parent frame.
247 virtual StackFrame parent() const = 0;
249 // Get this frame's line number (1-origin).
250 virtual uint32_t line() const = 0;
252 // Get this frame's column number in UTF-16 code units.
253 virtual JS::TaggedColumnNumberOneOrigin column() const = 0;
255 // Get this frame's source name. Never null.
256 virtual AtomOrTwoByteChars source() const = 0;
258 // Get a unique per-process ID for this frame's source. Defaults to zero.
259 virtual uint32_t sourceId() const = 0;
261 // Return this frame's function name if named, otherwise the inferred
262 // display name. Can be null.
263 virtual AtomOrTwoByteChars functionDisplayName() const = 0;
265 // Returns true if this frame's function is system JavaScript running with
266 // trusted principals, false otherwise.
267 virtual bool isSystem() const = 0;
269 // Return true if this frame's function is a self-hosted JavaScript builtin,
270 // false otherwise.
271 virtual bool isSelfHosted(JSContext* cx) const = 0;
273 // Construct a SavedFrame stack for the stack starting with this frame and
274 // containing all of its parents. The SavedFrame objects will be placed into
275 // cx's current compartment.
277 // Note that the process of
279 // SavedFrame
280 // |
281 // V
282 // JS::ubi::StackFrame
283 // |
284 // V
285 // offline heap snapshot
286 // |
287 // V
288 // JS::ubi::StackFrame
289 // |
290 // V
291 // SavedFrame
293 // is lossy because we cannot serialize and deserialize the SavedFrame's
294 // principals in the offline heap snapshot, so JS::ubi::StackFrame
295 // simplifies the principals check into the boolean isSystem() state. This
296 // is fine because we only expose JS::ubi::Stack to devtools and chrome
297 // code, and not to the web platform.
298 [[nodiscard]] virtual bool constructSavedFrameStack(
299 JSContext* cx, MutableHandleObject outSavedFrameStack) const = 0;
301 // Trace the concrete implementation of JS::ubi::StackFrame.
302 virtual void trace(JSTracer* trc) = 0;
305 // A traits template with a specialization for each backing type that implements
306 // the ubi::BaseStackFrame interface. Each specialization must be the a subclass
307 // of ubi::BaseStackFrame.
308 template <typename T>
309 class ConcreteStackFrame;
311 // A JS::ubi::StackFrame represents a frame in a recorded stack. It can be
312 // backed either by a live SavedFrame object or by a structure deserialized from
313 // an offline heap snapshot.
315 // It is a value type that may be memcpy'd hither and thither without worrying
316 // about constructors or destructors, similar to POD types.
318 // Its lifetime is the same as the lifetime of the graph that is being analyzed
319 // by the JS::ubi::Node that the JS::ubi::StackFrame came from. That is, if the
320 // graph being analyzed is the live heap graph, the JS::ubi::StackFrame is only
321 // valid within the scope of an AutoCheckCannotGC; if the graph being analyzed
322 // is an offline heap snapshot, the JS::ubi::StackFrame is valid as long as the
323 // offline heap snapshot is alive.
324 class StackFrame {
325 // Storage in which we allocate BaseStackFrame subclasses.
326 mozilla::AlignedStorage2<BaseStackFrame> storage;
328 BaseStackFrame* base() { return storage.addr(); }
329 const BaseStackFrame* base() const { return storage.addr(); }
331 template <typename T>
332 void construct(T* ptr) {
333 static_assert(std::is_base_of_v<BaseStackFrame, ConcreteStackFrame<T>>,
334 "ConcreteStackFrame<T> must inherit from BaseStackFrame");
335 static_assert(
336 sizeof(ConcreteStackFrame<T>) == sizeof(*base()),
337 "ubi::ConcreteStackFrame<T> specializations must be the same size as "
338 "ubi::BaseStackFrame");
339 ConcreteStackFrame<T>::construct(base(), ptr);
341 struct ConstructFunctor;
343 public:
344 StackFrame() { construct<void>(nullptr); }
346 template <typename T>
347 MOZ_IMPLICIT StackFrame(T* ptr) {
348 construct(ptr);
351 template <typename T>
352 StackFrame& operator=(T* ptr) {
353 construct(ptr);
354 return *this;
357 // Constructors accepting SpiderMonkey's generic-pointer-ish types.
359 template <typename T>
360 explicit StackFrame(const JS::Handle<T*>& handle) {
361 construct(handle.get());
364 template <typename T>
365 StackFrame& operator=(const JS::Handle<T*>& handle) {
366 construct(handle.get());
367 return *this;
370 template <typename T>
371 explicit StackFrame(const JS::Rooted<T*>& root) {
372 construct(root.get());
375 template <typename T>
376 StackFrame& operator=(const JS::Rooted<T*>& root) {
377 construct(root.get());
378 return *this;
381 // Because StackFrame is just a vtable pointer and an instance pointer, we
382 // can memcpy everything around instead of making concrete classes define
383 // virtual constructors. See the comment above Node's copy constructor for
384 // more details; that comment applies here as well.
385 StackFrame(const StackFrame& rhs) {
386 memcpy(storage.u.mBytes, rhs.storage.u.mBytes, sizeof(storage.u));
389 StackFrame& operator=(const StackFrame& rhs) {
390 memcpy(storage.u.mBytes, rhs.storage.u.mBytes, sizeof(storage.u));
391 return *this;
394 bool operator==(const StackFrame& rhs) const {
395 return base()->ptr == rhs.base()->ptr;
397 bool operator!=(const StackFrame& rhs) const { return !(*this == rhs); }
399 explicit operator bool() const { return base()->ptr != nullptr; }
401 // Copy this StackFrame's source name into the given |destination|
402 // buffer. Copy no more than |length| characters. The result is *not* null
403 // terminated. Returns how many characters were written into the buffer.
404 size_t source(RangedPtr<char16_t> destination, size_t length) const;
406 // Copy this StackFrame's function display name into the given |destination|
407 // buffer. Copy no more than |length| characters. The result is *not* null
408 // terminated. Returns how many characters were written into the buffer.
409 size_t functionDisplayName(RangedPtr<char16_t> destination,
410 size_t length) const;
412 // Get the size of the respective strings. 0 is returned for null strings.
413 size_t sourceLength();
414 size_t functionDisplayNameLength();
416 // Methods that forward to virtual calls through BaseStackFrame.
418 void trace(JSTracer* trc) { base()->trace(trc); }
419 uint64_t identifier() const {
420 auto id = base()->identifier();
421 MOZ_ASSERT(JS::Value::isNumberRepresentable(id));
422 return id;
424 uint32_t line() const { return base()->line(); }
425 JS::TaggedColumnNumberOneOrigin column() const { return base()->column(); }
426 AtomOrTwoByteChars source() const { return base()->source(); }
427 uint32_t sourceId() const { return base()->sourceId(); }
428 AtomOrTwoByteChars functionDisplayName() const {
429 return base()->functionDisplayName();
431 StackFrame parent() const { return base()->parent(); }
432 bool isSystem() const { return base()->isSystem(); }
433 bool isSelfHosted(JSContext* cx) const { return base()->isSelfHosted(cx); }
434 [[nodiscard]] bool constructSavedFrameStack(
435 JSContext* cx, MutableHandleObject outSavedFrameStack) const {
436 return base()->constructSavedFrameStack(cx, outSavedFrameStack);
439 struct HashPolicy {
440 using Lookup = JS::ubi::StackFrame;
442 static js::HashNumber hash(const Lookup& lookup) {
443 return mozilla::HashGeneric(lookup.identifier());
446 static bool match(const StackFrame& key, const Lookup& lookup) {
447 return key == lookup;
450 static void rekey(StackFrame& k, const StackFrame& newKey) { k = newKey; }
454 // The ubi::StackFrame null pointer. Any attempt to operate on a null
455 // ubi::StackFrame crashes.
456 template <>
457 class ConcreteStackFrame<void> : public BaseStackFrame {
458 explicit ConcreteStackFrame(void* ptr) : BaseStackFrame(ptr) {}
460 public:
461 static void construct(void* storage, void*) {
462 new (storage) ConcreteStackFrame(nullptr);
465 uint64_t identifier() const override { return 0; }
466 void trace(JSTracer* trc) override {}
467 [[nodiscard]] bool constructSavedFrameStack(
468 JSContext* cx, MutableHandleObject out) const override {
469 out.set(nullptr);
470 return true;
473 uint32_t line() const override { MOZ_CRASH("null JS::ubi::StackFrame"); }
474 JS::TaggedColumnNumberOneOrigin column() const override {
475 MOZ_CRASH("null JS::ubi::StackFrame");
477 AtomOrTwoByteChars source() const override {
478 MOZ_CRASH("null JS::ubi::StackFrame");
480 uint32_t sourceId() const override { MOZ_CRASH("null JS::ubi::StackFrame"); }
481 AtomOrTwoByteChars functionDisplayName() const override {
482 MOZ_CRASH("null JS::ubi::StackFrame");
484 StackFrame parent() const override { MOZ_CRASH("null JS::ubi::StackFrame"); }
485 bool isSystem() const override { MOZ_CRASH("null JS::ubi::StackFrame"); }
486 bool isSelfHosted(JSContext* cx) const override {
487 MOZ_CRASH("null JS::ubi::StackFrame");
491 [[nodiscard]] JS_PUBLIC_API bool ConstructSavedFrameStackSlow(
492 JSContext* cx, JS::ubi::StackFrame& frame,
493 MutableHandleObject outSavedFrameStack);
495 /*** ubi::Node
496 * ************************************************************************************/
498 // A concrete node specialization can claim its referent is a member of a
499 // particular "coarse type" which is less specific than the actual
500 // implementation type but generally more palatable for web developers. For
501 // example, JitCode can be considered to have a coarse type of "Script". This is
502 // used by some analyses for putting nodes into different buckets. The default,
503 // if a concrete specialization does not provide its own mapping to a CoarseType
504 // variant, is "Other".
506 // NB: the values associated with a particular enum variant must not change or
507 // be reused for new variants. Doing so will cause inspecting ubi::Nodes backed
508 // by an offline heap snapshot from an older SpiderMonkey/Firefox version to
509 // break. Consider this enum append only.
510 enum class CoarseType : uint32_t {
511 Other = 0,
512 Object = 1,
513 Script = 2,
514 String = 3,
515 DOMNode = 4,
517 FIRST = Other,
518 LAST = DOMNode
522 * Convert a CoarseType enum into a string. The string is statically allocated.
524 JS_PUBLIC_API const char* CoarseTypeToString(CoarseType type);
526 inline uint32_t CoarseTypeToUint32(CoarseType type) {
527 return static_cast<uint32_t>(type);
530 inline bool Uint32IsValidCoarseType(uint32_t n) {
531 auto first = static_cast<uint32_t>(CoarseType::FIRST);
532 auto last = static_cast<uint32_t>(CoarseType::LAST);
533 MOZ_ASSERT(first < last);
534 return first <= n && n <= last;
537 inline CoarseType Uint32ToCoarseType(uint32_t n) {
538 MOZ_ASSERT(Uint32IsValidCoarseType(n));
539 return static_cast<CoarseType>(n);
542 // The base class implemented by each ubi::Node referent type. Subclasses must
543 // not add data members to this class.
544 class JS_PUBLIC_API Base {
545 friend class Node;
547 // For performance's sake, we'd prefer to avoid a virtual destructor; and
548 // an empty constructor seems consistent with the 'lightweight value type'
549 // visible behavior we're trying to achieve. But if the destructor isn't
550 // virtual, and a subclass overrides it, the subclass's destructor will be
551 // ignored. Is there a way to make the compiler catch that error?
553 protected:
554 // Space for the actual pointer. Concrete subclasses should define a
555 // properly typed 'get' member function to access this.
556 void* ptr;
558 explicit Base(void* ptr) : ptr(ptr) {}
560 public:
561 bool operator==(const Base& rhs) const {
562 // Some compilers will indeed place objects of different types at
563 // the same address, so technically, we should include the vtable
564 // in this comparison. But it seems unlikely to cause problems in
565 // practice.
566 return ptr == rhs.ptr;
568 bool operator!=(const Base& rhs) const { return !(*this == rhs); }
570 // An identifier for this node, guaranteed to be stable and unique for as
571 // long as this ubi::Node's referent is alive and at the same address.
573 // This is probably suitable for use in serializations, as it is an integral
574 // type. It may also help save memory when constructing HashSets of
575 // ubi::Nodes: since a uint64_t will always be smaller-or-equal-to the size
576 // of a ubi::Node, a HashSet<ubi::Node::Id> may use less space per element
577 // than a HashSet<ubi::Node>.
579 // (Note that 'unique' only means 'up to equality on ubi::Node'; see the
580 // caveats about multiple objects allocated at the same address for
581 // 'ubi::Node::operator=='.)
582 using Id = uint64_t;
583 virtual Id identifier() const { return Id(uintptr_t(ptr)); }
585 // Returns true if this node is pointing to something on the live heap, as
586 // opposed to something from a deserialized core dump. Returns false,
587 // otherwise.
588 virtual bool isLive() const { return true; };
590 // Return the coarse-grained type-of-thing that this node represents.
591 virtual CoarseType coarseType() const { return CoarseType::Other; }
593 // Return a human-readable name for the referent's type. The result should
594 // be statically allocated. (You can use u"strings" for this.)
596 // This must always return Concrete<T>::concreteTypeName; we use that
597 // pointer as a tag for this particular referent type.
598 virtual const char16_t* typeName() const = 0;
600 // Return the size of this node, in bytes. Include any structures that this
601 // node owns exclusively that are not exposed as their own ubi::Nodes.
602 // |mallocSizeOf| should be a malloc block sizing function; see
603 // |mfbt/MemoryReporting.h|.
605 // Because we can use |JS::ubi::Node|s backed by a snapshot that was taken
606 // on a 64-bit platform when we are currently on a 32-bit platform, we
607 // cannot rely on |size_t| for node sizes. Instead, |Size| is uint64_t on
608 // all platforms.
609 using Size = uint64_t;
610 virtual Size size(mozilla::MallocSizeOf mallocSizeof) const { return 1; }
612 // Return an EdgeRange that initially contains all the referent's outgoing
613 // edges. The caller takes ownership of the EdgeRange.
615 // If wantNames is true, compute names for edges. Doing so can be expensive
616 // in time and memory.
617 virtual js::UniquePtr<EdgeRange> edges(JSContext* cx,
618 bool wantNames) const = 0;
620 // Return the Zone to which this node's referent belongs, or nullptr if the
621 // referent is not of a type allocated in SpiderMonkey Zones.
622 virtual JS::Zone* zone() const { return nullptr; }
624 // Return the compartment for this node. Some ubi::Node referents are not
625 // associated with Compartments, such as JSStrings (which are associated
626 // with Zones). When the referent is not associated with a compartment,
627 // nullptr is returned.
628 virtual JS::Compartment* compartment() const { return nullptr; }
630 // Return the realm for this node. Some ubi::Node referents are not
631 // associated with Realms, such as JSStrings (which are associated
632 // with Zones) or cross-compartment wrappers (which are associated with
633 // compartments). When the referent is not associated with a realm,
634 // nullptr is returned.
635 virtual JS::Realm* realm() const { return nullptr; }
637 // Return whether this node's referent's allocation stack was captured.
638 virtual bool hasAllocationStack() const { return false; }
640 // Get the stack recorded at the time this node's referent was
641 // allocated. This must only be called when hasAllocationStack() is true.
642 virtual StackFrame allocationStack() const {
643 MOZ_CRASH(
644 "Concrete classes that have an allocation stack must override both "
645 "hasAllocationStack and allocationStack.");
648 // In some cases, Concrete<T> can return a more descriptive
649 // referent type name than simply `T`. This method returns an
650 // identifier as specific as is efficiently available.
651 // The string returned is borrowed from the ubi::Node's referent.
652 // If nothing more specific than typeName() is available, return nullptr.
653 virtual const char16_t* descriptiveTypeName() const { return nullptr; }
655 // Methods for JSObject Referents
657 // These methods are only semantically valid if the referent is either a
658 // JSObject in the live heap, or represents a previously existing JSObject
659 // from some deserialized heap snapshot.
661 // Return the object's [[Class]]'s name.
662 virtual const char* jsObjectClassName() const { return nullptr; }
664 // Methods for CoarseType::Script referents
666 // Return the script's source's filename if available. If unavailable,
667 // return nullptr.
668 virtual const char* scriptFilename() const { return nullptr; }
670 private:
671 Base(const Base& rhs) = delete;
672 Base& operator=(const Base& rhs) = delete;
675 // A traits template with a specialization for each referent type that
676 // ubi::Node supports. The specialization must be the concrete subclass of Base
677 // that represents a pointer to the referent type. It must include these
678 // members:
680 // // The specific char16_t array returned by Concrete<T>::typeName().
681 // static const char16_t concreteTypeName[];
683 // // Construct an instance of this concrete class in |storage| referring
684 // // to |referent|. Implementations typically use a placement 'new'.
685 // //
686 // // In some cases, |referent| will contain dynamic type information that
687 // // identifies it a some more specific subclass of |Referent|. For
688 // // example, when |Referent| is |JSObject|, then |referent->getClass()|
689 // // could tell us that it's actually a JSFunction. Similarly, if
690 // // |Referent| is |nsISupports|, we would like a ubi::Node that knows its
691 // // final implementation type.
692 // //
693 // // So we delegate the actual construction to this specialization, which
694 // // knows Referent's details.
695 // static void construct(void* storage, Referent* referent);
696 template <typename Referent>
697 class Concrete;
699 // A container for a Base instance; all members simply forward to the contained
700 // instance. This container allows us to pass ubi::Node instances by value.
701 class Node {
702 // Storage in which we allocate Base subclasses.
703 mozilla::AlignedStorage2<Base> storage;
704 Base* base() { return storage.addr(); }
705 const Base* base() const { return storage.addr(); }
707 template <typename T>
708 void construct(T* ptr) {
709 static_assert(
710 sizeof(Concrete<T>) == sizeof(*base()),
711 "ubi::Base specializations must be the same size as ubi::Base");
712 static_assert(std::is_base_of_v<Base, Concrete<T>>,
713 "ubi::Concrete<T> must inherit from ubi::Base");
714 Concrete<T>::construct(base(), ptr);
716 struct ConstructFunctor;
718 public:
719 Node() { construct<void>(nullptr); }
721 template <typename T>
722 MOZ_IMPLICIT Node(T* ptr) {
723 construct(ptr);
725 template <typename T>
726 Node& operator=(T* ptr) {
727 construct(ptr);
728 return *this;
731 // We can construct and assign from rooted forms of pointers.
732 template <typename T>
733 MOZ_IMPLICIT Node(const Rooted<T*>& root) {
734 construct(root.get());
736 template <typename T>
737 Node& operator=(const Rooted<T*>& root) {
738 construct(root.get());
739 return *this;
742 // Constructors accepting SpiderMonkey's other generic-pointer-ish types.
743 // Note that we *do* want an implicit constructor here: JS::Value and
744 // JS::ubi::Node are both essentially tagged references to other sorts of
745 // objects, so letting conversions happen automatically is appropriate.
746 MOZ_IMPLICIT Node(JS::HandleValue value);
747 explicit Node(JS::GCCellPtr thing);
749 // copy construction and copy assignment just use memcpy, since we know
750 // instances contain nothing but a vtable pointer and a data pointer.
752 // To be completely correct, concrete classes could provide a virtual
753 // 'construct' member function, which we could invoke on rhs to construct an
754 // instance in our storage. But this is good enough; there's no need to jump
755 // through vtables for copying and assignment that are just going to move
756 // two words around. The compiler knows how to optimize memcpy.
757 Node(const Node& rhs) {
758 memcpy(storage.u.mBytes, rhs.storage.u.mBytes, sizeof(storage.u));
761 Node& operator=(const Node& rhs) {
762 memcpy(storage.u.mBytes, rhs.storage.u.mBytes, sizeof(storage.u));
763 return *this;
766 bool operator==(const Node& rhs) const { return *base() == *rhs.base(); }
767 bool operator!=(const Node& rhs) const { return *base() != *rhs.base(); }
769 explicit operator bool() const { return base()->ptr != nullptr; }
771 bool isLive() const { return base()->isLive(); }
773 // Get the canonical type name for the given type T.
774 template <typename T>
775 static const char16_t* canonicalTypeName() {
776 return Concrete<T>::concreteTypeName;
779 template <typename T>
780 bool is() const {
781 return base()->typeName() == canonicalTypeName<T>();
784 template <typename T>
785 T* as() const {
786 MOZ_ASSERT(isLive());
787 MOZ_ASSERT(this->is<T>());
788 return static_cast<T*>(base()->ptr);
791 template <typename T>
792 T* asOrNull() const {
793 MOZ_ASSERT(isLive());
794 return this->is<T>() ? static_cast<T*>(base()->ptr) : nullptr;
797 // If this node refers to something that can be represented as a JavaScript
798 // value that is safe to expose to JavaScript code, return that value.
799 // Otherwise return UndefinedValue(). JSStrings, JS::Symbols, and some (but
800 // not all!) JSObjects can be exposed.
801 JS::Value exposeToJS() const;
803 CoarseType coarseType() const { return base()->coarseType(); }
804 const char16_t* typeName() const { return base()->typeName(); }
805 JS::Zone* zone() const { return base()->zone(); }
806 JS::Compartment* compartment() const { return base()->compartment(); }
807 JS::Realm* realm() const { return base()->realm(); }
808 const char* jsObjectClassName() const { return base()->jsObjectClassName(); }
809 const char16_t* descriptiveTypeName() const {
810 return base()->descriptiveTypeName();
813 const char* scriptFilename() const { return base()->scriptFilename(); }
815 using Size = Base::Size;
816 Size size(mozilla::MallocSizeOf mallocSizeof) const {
817 auto size = base()->size(mallocSizeof);
818 MOZ_ASSERT(
819 size > 0,
820 "C++ does not have zero-sized types! Choose 1 if you just need a "
821 "conservative default.");
822 return size;
825 js::UniquePtr<EdgeRange> edges(JSContext* cx, bool wantNames = true) const {
826 return base()->edges(cx, wantNames);
829 bool hasAllocationStack() const { return base()->hasAllocationStack(); }
830 StackFrame allocationStack() const { return base()->allocationStack(); }
832 using Id = Base::Id;
833 Id identifier() const {
834 auto id = base()->identifier();
835 MOZ_ASSERT(JS::Value::isNumberRepresentable(id));
836 return id;
839 // A hash policy for ubi::Nodes.
840 // This simply uses the stock PointerHasher on the ubi::Node's pointer.
841 // We specialize DefaultHasher below to make this the default.
842 class HashPolicy {
843 typedef js::PointerHasher<void*> PtrHash;
845 public:
846 typedef Node Lookup;
848 static js::HashNumber hash(const Lookup& l) {
849 return PtrHash::hash(l.base()->ptr);
851 static bool match(const Node& k, const Lookup& l) { return k == l; }
852 static void rekey(Node& k, const Node& newKey) { k = newKey; }
856 using NodeSet =
857 js::HashSet<Node, js::DefaultHasher<Node>, js::SystemAllocPolicy>;
858 using NodeSetPtr = mozilla::UniquePtr<NodeSet, JS::DeletePolicy<NodeSet>>;
860 /*** Edge and EdgeRange *******************************************************/
862 using EdgeName = UniqueTwoByteChars;
864 // An outgoing edge to a referent node.
865 class Edge {
866 public:
867 Edge() = default;
869 // Construct an initialized Edge, taking ownership of |name|.
870 Edge(char16_t* name, const Node& referent) : name(name), referent(referent) {}
872 // Move construction and assignment.
873 Edge(Edge&& rhs) : name(std::move(rhs.name)), referent(rhs.referent) {}
875 Edge& operator=(Edge&& rhs) {
876 MOZ_ASSERT(&rhs != this);
877 this->~Edge();
878 new (this) Edge(std::move(rhs));
879 return *this;
882 Edge(const Edge&) = delete;
883 Edge& operator=(const Edge&) = delete;
885 // This edge's name. This may be nullptr, if Node::edges was called with
886 // false as the wantNames parameter.
888 // The storage is owned by this Edge, and will be freed when this Edge is
889 // destructed. You may take ownership of the name by `std::move`ing it
890 // out of the edge; it is just a UniquePtr.
892 // (In real life we'll want a better representation for names, to avoid
893 // creating tons of strings when the names follow a pattern; and we'll need
894 // to think about lifetimes carefully to ensure traversal stays cheap.)
895 EdgeName name = nullptr;
897 // This edge's referent.
898 Node referent;
901 // EdgeRange is an abstract base class for iterating over a node's outgoing
902 // edges. (This is modeled after js::HashTable<K,V>::Range.)
904 // Concrete instances of this class need not be as lightweight as Node itself,
905 // since they're usually only instantiated while iterating over a particular
906 // object's edges. For example, a dumb implementation for JS Cells might use
907 // JS::TraceChildren to to get the outgoing edges, and then store them in an
908 // array internal to the EdgeRange.
909 class EdgeRange {
910 protected:
911 // The current front edge of this range, or nullptr if this range is empty.
912 Edge* front_;
914 EdgeRange() : front_(nullptr) {}
916 public:
917 virtual ~EdgeRange() = default;
919 // True if there are no more edges in this range.
920 bool empty() const { return !front_; }
922 // The front edge of this range. This is owned by the EdgeRange, and is
923 // only guaranteed to live until the next call to popFront, or until
924 // the EdgeRange is destructed.
925 const Edge& front() const { return *front_; }
926 Edge& front() { return *front_; }
928 // Remove the front edge from this range. This should only be called if
929 // !empty().
930 virtual void popFront() = 0;
932 private:
933 EdgeRange(const EdgeRange&) = delete;
934 EdgeRange& operator=(const EdgeRange&) = delete;
937 typedef mozilla::Vector<Edge, 8, js::SystemAllocPolicy> EdgeVector;
939 // An EdgeRange concrete class that holds a pre-existing vector of
940 // Edges. A PreComputedEdgeRange does not take ownership of its
941 // EdgeVector; it is up to the PreComputedEdgeRange's consumer to manage
942 // that lifetime.
943 class PreComputedEdgeRange : public EdgeRange {
944 EdgeVector& edges;
945 size_t i;
947 void settle() { front_ = i < edges.length() ? &edges[i] : nullptr; }
949 public:
950 explicit PreComputedEdgeRange(EdgeVector& edges) : edges(edges), i(0) {
951 settle();
954 void popFront() override {
955 MOZ_ASSERT(!empty());
956 i++;
957 settle();
961 /*** RootList *****************************************************************/
963 // RootList is a class that can be pointed to by a |ubi::Node|, creating a
964 // fictional root-of-roots which has edges to every GC root in the JS
965 // runtime. Having a single root |ubi::Node| is useful for algorithms written
966 // with the assumption that there aren't multiple roots (such as computing
967 // dominator trees) and you want a single point of entry. It also ensures that
968 // the roots themselves get visited by |ubi::BreadthFirst| (they would otherwise
969 // only be used as starting points).
971 // RootList::init itself causes a minor collection, but once the list of roots
972 // has been created, GC must not occur, as the referent ubi::Nodes are not
973 // stable across GC. It returns a [[nodiscard]] AutoCheckCannotGC token in order
974 // to enforce this. The token's lifetime must extend at least as long as the
975 // RootList itself. Note that the RootList does not itself contain a nogc field,
976 // which means that it is possible to store it somewhere that it can escape
977 // the init()'s nogc scope. Don't do that. (Or you could call some function
978 // and pass in the RootList and GC, but that would be caught.)
980 // Example usage:
982 // {
983 // JS::ubi::RootList rootList(cx);
984 // auto [ok, nogc] = rootList.init();
985 // if (!ok()) {
986 // return false;
987 // }
989 // JS::ubi::Node root(&rootList);
991 // ...
992 // }
993 class MOZ_STACK_CLASS JS_PUBLIC_API RootList {
994 public:
995 JSContext* cx;
996 EdgeVector edges;
997 bool wantNames;
998 bool inited;
1000 explicit RootList(JSContext* cx, bool wantNames = false);
1002 // Find all GC roots.
1003 [[nodiscard]] std::pair<bool, JS::AutoCheckCannotGC> init();
1004 // Find only GC roots in the provided set of |JS::Compartment|s. Note: it's
1005 // important to take a CompartmentSet and not a RealmSet: objects in
1006 // same-compartment realms can reference each other directly, without going
1007 // through CCWs, so if we used a RealmSet here we would miss edges.
1008 [[nodiscard]] std::pair<bool, JS::AutoCheckCannotGC> init(
1009 CompartmentSet& debuggees);
1010 // Find only GC roots in the given Debugger object's set of debuggee
1011 // compartments.
1012 [[nodiscard]] std::pair<bool, JS::AutoCheckCannotGC> init(
1013 HandleObject debuggees);
1015 // Returns true if the RootList has been initialized successfully, false
1016 // otherwise.
1017 bool initialized() { return inited; }
1019 // Explicitly add the given Node as a root in this RootList. If wantNames is
1020 // true, you must pass an edgeName. The RootList does not take ownership of
1021 // edgeName.
1022 [[nodiscard]] bool addRoot(Node node, const char16_t* edgeName = nullptr);
1025 /*** Concrete classes for ubi::Node referent types ****************************/
1027 template <>
1028 class JS_PUBLIC_API Concrete<RootList> : public Base {
1029 protected:
1030 explicit Concrete(RootList* ptr) : Base(ptr) {}
1031 RootList& get() const { return *static_cast<RootList*>(ptr); }
1033 public:
1034 static void construct(void* storage, RootList* ptr) {
1035 new (storage) Concrete(ptr);
1038 js::UniquePtr<EdgeRange> edges(JSContext* cx, bool wantNames) const override;
1040 const char16_t* typeName() const override { return concreteTypeName; }
1041 static const char16_t concreteTypeName[];
1044 // A reusable ubi::Concrete specialization base class for types supported by
1045 // JS::TraceChildren.
1046 template <typename Referent>
1047 class JS_PUBLIC_API TracerConcrete : public Base {
1048 JS::Zone* zone() const override;
1050 public:
1051 js::UniquePtr<EdgeRange> edges(JSContext* cx, bool wantNames) const override;
1053 protected:
1054 explicit TracerConcrete(Referent* ptr) : Base(ptr) {}
1055 Referent& get() const { return *static_cast<Referent*>(ptr); }
1058 // For JS::TraceChildren-based types that have 'realm' and 'compartment'
1059 // methods.
1060 template <typename Referent>
1061 class JS_PUBLIC_API TracerConcreteWithRealm : public TracerConcrete<Referent> {
1062 typedef TracerConcrete<Referent> TracerBase;
1063 JS::Compartment* compartment() const override;
1064 JS::Realm* realm() const override;
1066 protected:
1067 explicit TracerConcreteWithRealm(Referent* ptr) : TracerBase(ptr) {}
1070 // Define specializations for some commonly-used public JSAPI types.
1071 // These can use the generic templates above.
1072 template <>
1073 class JS_PUBLIC_API Concrete<JS::Symbol> : TracerConcrete<JS::Symbol> {
1074 protected:
1075 explicit Concrete(JS::Symbol* ptr) : TracerConcrete(ptr) {}
1077 public:
1078 static void construct(void* storage, JS::Symbol* ptr) {
1079 new (storage) Concrete(ptr);
1082 Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
1084 const char16_t* typeName() const override { return concreteTypeName; }
1085 static const char16_t concreteTypeName[];
1088 template <>
1089 class JS_PUBLIC_API Concrete<JS::BigInt> : TracerConcrete<JS::BigInt> {
1090 protected:
1091 explicit Concrete(JS::BigInt* ptr) : TracerConcrete(ptr) {}
1093 public:
1094 static void construct(void* storage, JS::BigInt* ptr) {
1095 new (storage) Concrete(ptr);
1098 Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
1100 const char16_t* typeName() const override { return concreteTypeName; }
1101 static const char16_t concreteTypeName[];
1104 template <>
1105 class JS_PUBLIC_API Concrete<js::BaseScript>
1106 : TracerConcreteWithRealm<js::BaseScript> {
1107 protected:
1108 explicit Concrete(js::BaseScript* ptr)
1109 : TracerConcreteWithRealm<js::BaseScript>(ptr) {}
1111 public:
1112 static void construct(void* storage, js::BaseScript* ptr) {
1113 new (storage) Concrete(ptr);
1116 CoarseType coarseType() const final { return CoarseType::Script; }
1117 Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
1118 const char* scriptFilename() const final;
1120 const char16_t* typeName() const override { return concreteTypeName; }
1121 static const char16_t concreteTypeName[];
1124 // The JSObject specialization.
1125 template <>
1126 class JS_PUBLIC_API Concrete<JSObject> : public TracerConcrete<JSObject> {
1127 protected:
1128 explicit Concrete(JSObject* ptr) : TracerConcrete<JSObject>(ptr) {}
1130 public:
1131 static void construct(void* storage, JSObject* ptr);
1133 JS::Compartment* compartment() const override;
1134 JS::Realm* realm() const override;
1136 const char* jsObjectClassName() const override;
1137 Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
1139 bool hasAllocationStack() const override;
1140 StackFrame allocationStack() const override;
1142 CoarseType coarseType() const final { return CoarseType::Object; }
1144 const char16_t* typeName() const override { return concreteTypeName; }
1145 static const char16_t concreteTypeName[];
1148 // For JSString, we extend the generic template with a 'size' implementation.
1149 template <>
1150 class JS_PUBLIC_API Concrete<JSString> : TracerConcrete<JSString> {
1151 protected:
1152 explicit Concrete(JSString* ptr) : TracerConcrete<JSString>(ptr) {}
1154 public:
1155 static void construct(void* storage, JSString* ptr) {
1156 new (storage) Concrete(ptr);
1159 Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
1161 CoarseType coarseType() const final { return CoarseType::String; }
1163 const char16_t* typeName() const override { return concreteTypeName; }
1164 static const char16_t concreteTypeName[];
1167 // The ubi::Node null pointer. Any attempt to operate on a null ubi::Node
1168 // asserts.
1169 template <>
1170 class JS_PUBLIC_API Concrete<void> : public Base {
1171 const char16_t* typeName() const override;
1172 Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
1173 js::UniquePtr<EdgeRange> edges(JSContext* cx, bool wantNames) const override;
1174 JS::Zone* zone() const override;
1175 JS::Compartment* compartment() const override;
1176 JS::Realm* realm() const override;
1177 CoarseType coarseType() const final;
1179 explicit Concrete(void* ptr) : Base(ptr) {}
1181 public:
1182 static void construct(void* storage, void* ptr) {
1183 new (storage) Concrete(ptr);
1187 // The |callback| callback is much like the |Concrete<T>::construct| method: a
1188 // call to |callback| should construct an instance of the most appropriate
1189 // JS::ubi::Base subclass for |obj| in |storage|. The callback may assume that
1190 // |obj->getClass()->isDOMClass()|, and that |storage| refers to the
1191 // sizeof(JS::ubi::Base) bytes of space that all ubi::Base implementations
1192 // should require.
1194 // Set |cx|'s runtime hook for constructing ubi::Nodes for DOM classes to
1195 // |callback|.
1196 void SetConstructUbiNodeForDOMObjectCallback(JSContext* cx,
1197 void (*callback)(void*,
1198 JSObject*));
1200 } // namespace ubi
1201 } // namespace JS
1203 namespace mozilla {
1205 // Make ubi::Node::HashPolicy the default hash policy for ubi::Node.
1206 template <>
1207 struct DefaultHasher<JS::ubi::Node> : JS::ubi::Node::HashPolicy {};
1208 template <>
1209 struct DefaultHasher<JS::ubi::StackFrame> : JS::ubi::StackFrame::HashPolicy {};
1211 } // namespace mozilla
1213 #endif // js_UbiNode_h