Memory Management for nsIScriptContext
Script Object Lifetime Management
This discusses the Script Object lifetimes for all script objects returned by the nsIScriptContext interface. All such objects are returned as a "void *", and the lifetimes of these objects are implicitly tied up in the JS garbage collector and XPCWrappedNative lifetime management. This document attempts to explicitly define lifetime rules such that they can be implemented by any language (well, by JS and Python in the first instance)
Script Globals
A void * is obtained from the language for use as a "global" with an nsIScriptContext. Currently it is assumed the lifetime of the object is the same as the lifetime of the context itself. This model is reasonable and could be retained - however, if an alternate explicit mechanism is used in other places (see below), it may make sense to use that mechanism here.
CompileScript
CompileScript returns a "void *" for the script object. The interface comments for this function say:
The caller is responsible for GC rooting this object
The only caller of this function (nsXULElement) does indeed ensure the void * returned is GC rooted.
The reason for requiring this explicit management is that the result can not be assumed to have any other references (as opposed to Event Handlers, discussed later)
EvaluateStringWithValue/CallEventHandler
These functions are similar to CompileScript, but instead of a function object being returned, it is the result of calling the function is the issue.
This is similar in concept to CompileScript above - it can not be assumed any other references to the value exist, so the result must be explicitly managed.
Another alternative is that these functions will end up returning a language agnostic value, such as nsIVariant. For example, the result of CallEventHandler is explicitly tested for a string, or for a null value - presumably these checks must still be made regardless of the language making the call.
Event Handlers
Event handlers are a little trickier. In the process of "binding" a handler to an event target, an "implied reference" is created between the event target and the event handler. Thus, in the current implementation, a bound event handler does not need to be rooted by callers. Therefore, a case could be made that a bound event handler is a "borrowed reference" and requires no explicit cleanup.
Either way, event handling is a subject in its own right (see Language Agnostic Event Handling), and more analysis is required. However, the memory management issues for CompileScript etc are not related to event handlers, so can be resolved independently. That resolution then may, or may not, be applied to event handlers.
Proposal
Unbound function objects:
Unbound function objects (eg, returned by CompileScript, CompileEventHandler), and "void *" return values should be managed by a simple unlock/lock memory management API:
- All such objects returned by nsIScriptContext are considered "locked". They must remain locked while they are held externally. An 'unlock' function will be provided by the language to unlock the object.
- It is likely that users of this interface may need to copy the returned object and manage the lifetimes seperately (for example, storing the returned "void *" in 2 unrelated datastructures). For this scenario, an explicit "lock()" call will be provided (meaning 2 unlock calls are now needed to finalize the object). However, as mentioned above, objects will always be returned locked so calling this will be rare. XULElement::GetCompiledEventHandler() is the only identified place that does this.
Bound Event Handlers
TBD
Global Script Objects
As mentioned at the start, it may (or may not) make sense to use the same explicit model we use for compiled scripts and event handlers.