I think the easiest way to fix this in all cases (complicated cyclic refs) is to treat WeakMaps entries as *extensions* to the key object, rather than as part of the WeakMap itself.
So when traversing in the GC, traverse the entry values as part of the key object's traversal (like we traverse its props, prototype, etc), as if they were linked from the key object. Do **not** traverse them during the WeakMap traversal itself, because the WeakMap shouldn't hold a ref to the entry values, it's the key object who should, basically (we still add a ref when setting the entry, but the ref is held by the key object!).
After all, WeakMap entries cannot be accessed without the key, so it makes sense to "move" the ref to the key itself.
When unlinking the key, as an optimization, nothing special needs to be done, because we can remove the entry when fixing the weak refs later if the keys are gc_marked (the mark means the key was unlinked already, and so should its "extensions" from the WeakMaps, since we haven't).
What do you think?