Hi
(If you're not interested in the details of the wrapper tracing, stop reading now :-)
I noticed that there may be some cases where a V8 minor GC mis-collect wrappers (if I'm not mis-understanding something).
Imagine the following setup:
// X.idl
interface X { attribute Y yyy; }
// X.h
class X {
DEFINE_TRACE_WRAPPERS() { visitor->TraceWrappers(yyy_) };
TraceWrapperMember<Y> yyy_;
};
// Y.idl
[DependentLifetime] // This annotates that Y is traced by TraceWrappers.
interface Y { }
// Window.idl
interface Window { attribute X xxx; }
In this example, the expected behavior is that Y's wrapper is kept alive as long as X's wrapper is alive.
However, there can be a scenario where Y's wrapper is mis-collected while X's wrapper is alive:
0) Assume that X's wrapper is unmodified (i.e., doesn't have any expando) but Y's wrapper is modified. For example, call window.xxx.yyy.foo = 11111.
1) A minor GC is triggered. The minor GC collects X's wrapper because it is unmodified. The minor GC doesn't collect Y's wrapper because it is annotated as [DependentLifetime]. (i.e., [DependentLifetime] is an IDL extended attribute that indicates that V8 cannot collect the wrapper without calling the wrapper tracing. Since the minor GC does not call the wrapper tracing, it simply gives up collecting wrappers annotated with [DependentLifetime].)
2) A major GC is triggered. The major GC calls the wrapper tracing. However, since X's wrapper is already gone, Y's wrapper is not traced. Hence Y's wrapper gets collected.
3) When you call window.xxx, X's wrapper is recreated. This is fine because the old X's wrapper was unmodified and thus there is no way for the script to notice that X's wrapper is recreated.
4) When you call window.xxx.yyy.foo, this causes a problem. Since Y's wrapper is already gone, window.xxx.yyy recreates Y's wrapper. Then window.xxx.yyy.foo returns undefined instead of 11111...
I think there are two approaches to avoid the problem:
a) Add [DependentLifetime] to objects that are calling TraceWrappers. In the above example, add [DependentLifetime] to X.
b) Make the minor GC call the wrapper tracing. (Then we can drop [DependentLifetime] entirely.)
I don't think a) is an option because we will end up with adding [DependentLifetime] to almost all DOM objects (we're planning to trace all DOM attributes in the future). On the other hand, I'm not sure if b) is acceptable from the performance perspective.
Do you have any idea?
(Note: I don't think this is a new problem introduced by the wrapper tracing. We had the same problem in the [SetWrapperReferenceFrom/To] world -- a very long-standing bug.)
--
Kentaro Hara, Tokyo, Japan