On Mon Feb 9 18:43:44 2026 +0000, Jacek Caban wrote:
I did not mean to suggest removing reference counting from JS objects. My point was that Gecko faces the same problem: objects without an associated `nsCycleCollectingAutoRefCnt` can still be part of a cycle. I was wondering what the intended solution is in that case, that is, how the cycle collector authors expected this to be handled. From your description, it sounds like GC is supposed to be used for that instead of the CC? I do not think Gecko's GC is directly useful for us, but in theory, whatever strategy they use there could be replicated in our jscript GC. In your implementation, what you are doing in `describe_node` is not really how `nsCycleCollectingAutoRefCnt` is meant to be used. This might happen to work for full CC runs, but using a temporary stack-based ref is still a hack, right? And to make that hack work, you end up needing more hacks in places like QI, which now depends on CC state? Yeah, but the stack reference is not actually held, since it's only used during the purple buffer, and we don't use it. That's why I set it to NULL so it would otherwise crash (if it was ever used, which it shouldn't be), sort of like an assertion. (I can actually add a function that asserts instead, but it adds a bit of complexity)
And yes that's the reason for the QI. Note that this QI is "special" already (it doesn't follow COM rules) like some other gecko's stuff, so making it conditional isn't *that* much of a hack, because it's not meant to be cached nor treated as a normal interface to the object. You can see it doesn't even add a ref to the output, so it's already a hack by gecko to begin with. I investigated a bit more, I *think* I understood now how the CC and GC cooperate, it's iterative/several step approach. I mean for a mixed cycle like: A->B->J->A (J is the only JS object, rest are XPCOM objects) CC can't break this normally, it treats JS objects as special and has special wrappers for them. Since JS objects have no refcount it doesn't even use refcounts to see if a cycle can be broken/unlinked, it just keeps the cycle alive. GC has to run first and mark the JS object as unreachable (assuming it's not reachable from a JS root obviously, then it can't be collected). Unreachable doesn't instantly mean removed though, cause they're still linked by the XPCOM objects. Unreachable or "gray" JS objects are treated specially by the CC, they don't participate in keeping the cycle alive when traversing their edges. So it finds out it can unlink to break the cycle using CC rules. This only cleans up A and B though. The next GC run will finally release J since it's not reachable *and* it has no refs from an XPCOM object. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10045#note_129255