Hi,
we have xpcom components here that inherit from nsIXPCScriptable and we've run into a situation where we can't seem to get rid of a memory leak. Basically, we're returning an xpcom component from GetProperty:
NS_IMETHODIMP acXPCDynamicProperties::GetProperty
(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, jsval id, jsval * vp, PRBool *_retval)
{ [...]
scope= ??? nsISupports * native = ...
{ nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
nsresult rv = getXPConnect()->WrapNative(cx, scope, native, aIID, getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, rv);
JSObject* obj = nsnull; rv = holder->GetJSObject(&obj); NS_ENSURE_SUCCESS(rv, rv);
*vp = OBJECT_TO_JSVAL(obj); } [...] }
On the native side, we seem to be reference counting correctly. In the WrapNative() call, the reference count goes up by two - I have no way of verifying that that is as intended.
I'm not certain if you are talking about the ref count of your native object or the wrapper (held in holder). So, let's talk about both...
Upon successful return from WrapNative the wrapper should have a refcount of 2. One is from your nsCOMPtr that will go away when it goes out of scope. The other is held by xpconnect internally and it will go away when of the JSObject of the wrapper is garbage collected by the JS engine.
When the wrapper is created (and as it is used from JS) the reference count it holds on your native object will vary depending of which interfaces on the object it is using. But, it will maintain at least one reference as long as the wrapper is alive. It is perfectly reasonable that it will hold the two references that you report. The actual number held is not important. When the reference count on the wrapper itself goes to zero then the wrapper will release all the references it holds on your native object.
The code you show above looks correct.
The thing holding the wrapper and thus your native object alive is the JS engine. The JSObject that you get from GetJSObject and return via vp is apparently reachable from some root object in JS (or JS garbage collection is not occurring?).
You don't say if this is running in the browser or in your own embedding. In the browser, JS garbage collection will happen on a regular basis as the application is used. But, in an embedding of the JS engine you would need to invoke the GC now and then.
We're also unsure about what to pass as scope.
Passing the obj you receive as a param to your GetProperty should be fine.
FWIW... The 'scope' here is used as a scheme to partition wrapper spaces. The idea is that one might want a given native object to be represented by one wrapper (with its own JSObject) in one place and by a different wrapper in a different place. For instance, one might expose an xpcom singleton service in multiple windows and allow JS code to set or override arbitrary JS properties on the wrapper. One might not want the changes made to the wrapper in one window to impact the wrapper visible in the other window. Wrappers around a given native object are reused within a given scope; i.e. one and only one wrapper for a given native object will exist at any one time within a given scope. So, by supporting an explicit 'scope' param, xpconnect allows one to control the scope used in wrapper creation. JSObjects that represent wrappers 'have' a scope that xpconnect can detect internally. XPConnect will discover any given JSObject's scope by looking at it and up its parent chain until one of those JSObjects is found to represent a wrapper. In a mozilla browser embedding the global object is always a wrapped native object. So, this search will always work. In your case the obj passed in is typically going to itself represent a wrapper anyway. So, there should be no problem there.
Since we're returning a value, the reference to that value should be gone when the stack frame is discarded. How is this accomplished?
If I understand what you are asking... The value you are returning is a JSObject and its lifetime is controlled by the JS garbage collector (not any stack frame). The SpiderMonkey JS engine (to which xpconnect is tightly bound) uses a mark and sweep garbage collector. Any JS object (and other sorts of JSGC objects) that is reachable by a root object in JS will continue to live. For instance if 'window.foo.bar' is valid then that 'bar' object will not get collected.
So, the lifetime of your object is entirely dependent on how your object is used in JS. After being retrieved from this getter is your object attached as a property of some other object? Perhaps it is passed as a parameter to some other xpcom object which holds on to it?
No way for me to know.
Nevertheless, once the wrapper is created, its lifetime (and thus the lifetime of the native that it wraps) is dependent on when the wrapper's JSObject gets garbage collected. This is not C++ where going out of scope causes destuctors to be called.
Hope this helps you figure out what is going on.
John.
Thanks for any help,
Uli
