|
23 | 23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | 24 | */ |
25 | 25 |
|
| 26 | +/* |
| 27 | + * =========================== DOM GC OUTPUT CONSTRAINTS EXPLANATION =========================== |
| 28 | + * |
| 29 | + * What is DOMGCOutputConstraint? |
| 30 | + * ------------------------------ |
| 31 | + * DOMGCOutputConstraint is a garbage collection marking constraint that ensures certain DOM/WebCore |
| 32 | + * objects are revisited during garbage collection AFTER JavaScript execution (the "mutator") has |
| 33 | + * resumed. This is critical for maintaining GC correctness when objects can create new references |
| 34 | + * or change their reachability graph based on runtime JavaScript state. |
| 35 | + * |
| 36 | + * Why do we need this in Bun? |
| 37 | + * --------------------------- |
| 38 | + * Even though Bun doesn't have a full DOM implementation like a browser, we still use many WebCore |
| 39 | + * types that have "volatile" marking behavior - meaning their references to other objects can change |
| 40 | + * dynamically during JavaScript execution. Without this constraint, we risk: |
| 41 | + * |
| 42 | + * 1. Memory leaks - Objects staying alive that should be collected |
| 43 | + * 2. Premature collection - Objects being freed while still reachable through dynamic references |
| 44 | + * 3. Use-after-free crashes - Accessing collected objects through untracked references |
| 45 | + * |
| 46 | + * How does it work? |
| 47 | + * ----------------- |
| 48 | + * 1. During GC, objects are marked through their visitChildren/visitAdditionalChildren methods |
| 49 | + * 2. JavaScript execution resumes (mutator runs) |
| 50 | + * 3. New references may be created or changed during JS execution |
| 51 | + * 4. DOMGCOutputConstraint runs and calls visitOutputConstraints on relevant objects |
| 52 | + * 5. This re-visits the objects to catch any new references created in step 3 |
| 53 | + * |
| 54 | + * Which Bun objects need this? |
| 55 | + * ---------------------------- |
| 56 | + * Objects that implement visitOutputConstraints() need this constraint. In Bun, these include: |
| 57 | + * |
| 58 | + * - EventTarget & EventEmitter: Dynamic event listener references |
| 59 | + * - MessagePort & MessageChannel: Cross-context messaging with transferable objects |
| 60 | + * - PerformanceObserver: Dynamic observer callbacks |
| 61 | + * - CustomEvent, MessageEvent, ErrorEvent: Event objects with mutable properties |
| 62 | + * - SQLStatement: Prepared statements with dynamic bindings |
| 63 | + * - JSMockFunction: Test mocking with dynamic behavior |
| 64 | + * - Various WebCore types we inherit |
| 65 | + * |
| 66 | + * Relevant WebKit files for reference: |
| 67 | + * ------------------------------------ |
| 68 | + * - Source/WebCore/bindings/js/DOMGCOutputConstraint.cpp (original implementation) |
| 69 | + * - Source/WebCore/bindings/js/JSEventTargetCustom.cpp (visitAdditionalChildren example) |
| 70 | + * - Source/WebCore/bindings/js/JSDocumentCustom.cpp (complex marking example) |
| 71 | + * - Source/WebCore/bindings/js/JSMessagePortCustom.cpp (cross-context references) |
| 72 | + * - Source/WebCore/dom/EventTarget.idl (JSCustomMarkFunction attribute) |
| 73 | + * - Source/JavaScriptCore/heap/MarkingConstraint.h (base constraint class) |
| 74 | + * |
| 75 | + * The key insight: Any object whose reachability graph can change based on JavaScript execution |
| 76 | + * state needs output constraints. This is common for objects that: |
| 77 | + * - Maintain event listeners or callbacks |
| 78 | + * - Have cross-context or cross-heap references |
| 79 | + * - Use opaque roots or weak references |
| 80 | + * - Have mutable properties that affect GC reachability |
| 81 | + * |
| 82 | + * ========================================================================================= |
| 83 | + */ |
| 84 | + |
26 | 85 | #include "config.h" |
27 | 86 |
|
28 | 87 | #include <JavaScriptCore/WeakInlines.h> |
29 | 88 | #include <JavaScriptCore/AbstractSlotVisitorInlines.h> |
30 | 89 |
|
31 | 90 | #include <JavaScriptCore/VM.h> |
32 | 91 | #include <JavaScriptCore/MarkingConstraint.h> |
33 | | - |
34 | | -// namespace JSC { |
35 | | - |
36 | | -// class VisitCounter { |
37 | | -// public: |
38 | | -// VisitCounter() {} |
39 | | - |
40 | | -// VisitCounter(AbstractSlotVisitor& visitor) |
41 | | -// : m_visitor(&visitor) |
42 | | -// , m_initialVisitCount(visitor.visitCount()) |
43 | | -// { |
44 | | -// } |
45 | | - |
46 | | -// AbstractSlotVisitor& visitor() const { return *m_visitor; } |
47 | | - |
48 | | -// size_t visitCount() const |
49 | | -// { |
50 | | -// return m_visitor->visitCount() - m_initialVisitCount; |
51 | | -// } |
52 | | - |
53 | | -// private: |
54 | | -// AbstractSlotVisitor* m_visitor { nullptr }; |
55 | | -// size_t m_initialVisitCount { 0 }; |
56 | | -// }; |
57 | | - |
58 | | -// static constexpr bool verboseMarkingConstraint = false; |
59 | | - |
60 | | -// MarkingConstraint::MarkingConstraint(CString abbreviatedName, CString name, ConstraintVolatility volatility, ConstraintConcurrency concurrency, ConstraintParallelism parallelism) |
61 | | -// : m_abbreviatedName(abbreviatedName) |
62 | | -// , m_name(WTFMove(name)) |
63 | | -// , m_volatility(volatility) |
64 | | -// , m_concurrency(concurrency) |
65 | | -// , m_parallelism(parallelism) |
66 | | -// { |
67 | | -// } |
68 | | - |
69 | | -// MarkingConstraint::~MarkingConstraint() |
70 | | -// { |
71 | | -// } |
72 | | - |
73 | | -// void MarkingConstraint::resetStats() |
74 | | -// { |
75 | | -// m_lastVisitCount = 0; |
76 | | -// } |
77 | | - |
78 | | -// void MarkingConstraint::execute(SlotVisitor& visitor) |
79 | | -// { |
80 | | -// ASSERT(!visitor.heap()->isMarkingForGCVerifier()); |
81 | | -// VisitCounter visitCounter(visitor); |
82 | | -// executeImpl(visitor); |
83 | | -// m_lastVisitCount += visitCounter.visitCount(); |
84 | | -// if (verboseMarkingConstraint && visitCounter.visitCount()) |
85 | | -// dataLog("(", abbreviatedName(), " visited ", visitCounter.visitCount(), " in execute)"); |
86 | | -// } |
87 | | - |
88 | | -// void MarkingConstraint::executeSynchronously(AbstractSlotVisitor& visitor) |
89 | | -// { |
90 | | -// prepareToExecuteImpl(NoLockingNecessary, visitor); |
91 | | -// executeImpl(visitor); |
92 | | -// } |
93 | | - |
94 | | -// double MarkingConstraint::quickWorkEstimate(SlotVisitor&) |
95 | | -// { |
96 | | -// return 0; |
97 | | -// } |
98 | | - |
99 | | -// double MarkingConstraint::workEstimate(SlotVisitor& visitor) |
100 | | -// { |
101 | | -// return lastVisitCount() + quickWorkEstimate(visitor); |
102 | | -// } |
103 | | - |
104 | | -// void MarkingConstraint::prepareToExecute(const AbstractLocker& constraintSolvingLocker, SlotVisitor& visitor) |
105 | | -// { |
106 | | -// ASSERT(!visitor.heap()->isMarkingForGCVerifier()); |
107 | | -// dataLogIf(Options::logGC(), abbreviatedName()); |
108 | | -// VisitCounter visitCounter(visitor); |
109 | | -// prepareToExecuteImpl(constraintSolvingLocker, visitor); |
110 | | -// m_lastVisitCount = visitCounter.visitCount(); |
111 | | -// if (verboseMarkingConstraint && visitCounter.visitCount()) |
112 | | -// dataLog("(", abbreviatedName(), " visited ", visitCounter.visitCount(), " in prepareToExecute)"); |
113 | | -// } |
114 | | - |
115 | | -// void MarkingConstraint::doParallelWork(SlotVisitor& visitor, SharedTask<void(SlotVisitor&)>& task) |
116 | | -// { |
117 | | -// ASSERT(!visitor.heap()->isMarkingForGCVerifier()); |
118 | | -// VisitCounter visitCounter(visitor); |
119 | | -// task.run(visitor); |
120 | | -// if (verboseMarkingConstraint && visitCounter.visitCount()) |
121 | | -// dataLog("(", abbreviatedName(), " visited ", visitCounter.visitCount(), " in doParallelWork)"); |
122 | | -// { |
123 | | -// Locker locker { m_lock }; |
124 | | -// m_lastVisitCount += visitCounter.visitCount(); |
125 | | -// } |
126 | | -// } |
127 | | - |
128 | | -// void MarkingConstraint::prepareToExecuteImpl(const AbstractLocker&, AbstractSlotVisitor&) |
129 | | -// { |
130 | | -// } |
131 | | - |
132 | | -// } // namespace JSC |
133 | | - |
134 | 92 | #include "BunGCOutputConstraint.h" |
135 | 93 |
|
136 | 94 | #include "WebCoreJSClientData.h" |
|
0 commit comments