Skip to content

Commit 0bebdc9

Browse files
committed
Add a comment
1 parent 1058d0d commit 0bebdc9

1 file changed

Lines changed: 59 additions & 101 deletions

File tree

src/bun.js/bindings/BunGCOutputConstraint.cpp

Lines changed: 59 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -23,114 +23,72 @@
2323
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2424
*/
2525

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+
2685
#include "config.h"
2786

2887
#include <JavaScriptCore/WeakInlines.h>
2988
#include <JavaScriptCore/AbstractSlotVisitorInlines.h>
3089

3190
#include <JavaScriptCore/VM.h>
3291
#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-
13492
#include "BunGCOutputConstraint.h"
13593

13694
#include "WebCoreJSClientData.h"

0 commit comments

Comments
 (0)