On Mon Feb 9 20:53:12 2026 +0000, Gabriel Ivăncescu wrote:
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. I do not see how we can know that J may be treated that way. Consider another case: A -> J -> B -> J, where A has an external reference, so it can't be collected. According to your description, J is not a root (or is it?). The GC would mark it as gray, and if J does not participate as alive in the CC, the CC might release B, which is obviously incorrect.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10045#note_129777