XPConnect interface plans
This is a quick and dirty overview of some of the interfaces and the internal mechanisms I am working on for XPConnect to support COM identity and maximize sharing of objects. Much handwaving remains...
Note that what is elsewhere called
JS2CPPProxy is here called
WrappedNative and what is elsewhere called
The InterfaceInfo subsystem
[Scott Furman is looking closer at representing interface data on his own. These are just my ideas]
This interface will encapsulate information files. It can return an nsIInterfaceInfo object for any given UIID. It should provide services (on its own or through some helper class?) to read and write InterfaceInfo files. A single instance of this factory, and the InterfaceInfo objects it dispenses, could be shared at runtime by various client modules.
This interface will encapsulate one XPCOM interface - i.e. one UIID. It can return information about that interface including the name, the superinterface, the number of methods, an information object about each method, and information about constants defined by the interface. It should be able to hide superinterface boundaries. i.e. when asked for method info objects it should return those of the superinterface chain as if they were part of this interface. We may have methods in this object to return these 'flattened' sets of methods and constants, and alternate methods to return only those of the directly represented interface.
This interface will encapsulate one method. It will have methods to get the name, the number of params, and each param.
This interface will encapsulate one paramerter. It will have methods to get the type and modifiers (e.g. in, out, in/out, is_iid reference, etc). Optionally we could expose the param name. We might decide to not expose nsIMethodInfo as an object and instead just have nsIMethodInfo return an encoded string or array of bytes representing the params?
The InterfaceInfo system will be used by XPConnect, but it ought to stand on its own. The XPIDL compiler ought to use it to write InterfaceInfo files. Other tools should be able to use it to read, modify, merge. and split those files. It should not depend either on XPIDL or XPConnect if we can avoid those dependencies.
The XPConnect Runtime
nsIXPConnectRuntime (implemented by nsXPConnectRuntime)
nsXPConnectRuntime objects will be instantiated one to one with nsIJSRuntime; i.e. there will be a factory which takes an nsIJSRuntime as a param to create instances of nsXPConnectRuntime. nsIXPConnectRuntime is used to bootstrap XPCOM into the JS environment and to supply wrappers used for JS calling XPCOM and XPCOM calling JS.
We should develop a standard namespace in JS for access to these well known objects: perhaps an object named 'xpcom' to which these well known objects can be attached as properties. We may add default code to make this easy. But, in fact, this can be done easily with normal jsapi calls.
The primary interfaces of nsIXPConnectRuntime are:
NS_IMETHOD WrapNative(nsIJSContext* aJSContext, nsISupports* aCOMObj, REFNSIID aIID, nsICCWrappedNative** aWrapper) = 0; NS_IMETHOD WrapJS(nsIJSContext* aJSContext, nsIJSObject* aJSObj, REFNSIID aIID, nsICCWrappedJS** aWrapper) = 0;
The wrappers are XPCOM objects and are reference counted. The wrappers constructed (or found) by WrappedJS expose a GetJSObject method to allow callers to construct relationships between the wrapper and other JSObjects using the jsapi.
Both kinds of wrappers follow similar rules...
I'm planning a 'fully-factored' class graph to implement this system. We need to construct distinct wrapper instances for each interface of a given object, however, many different objects may expose the same interface. I'll factor out those common parts into objects which can be shared by separate wrappers.
In order to correctly support COM identity rules the various wrappers for the various interfaces of a given wrapped object will be linked in a list. The root object of the linked list of wrappers will be the wrapper representing nsISupports. Often that wrapper is also the same wrapper that supports some other interface. In fact, most typically, a given wrapped object will only be wrapped for nsISupports and one 'interesting' interface. And most objects will return the same pointer when QueryInterface calls are made asking for nsISupports and an 'interesting' interface. In these most common cases we need only generate one wrapper.
Each wrapper will have a pointer to the 'root' wrapper and to a 'next' wrapper. The root wrapper's 'root' pointer will point to itself (and thus we can see that it is the root). A hash table will be maintained that allows us to get at the root wrapper for any wrapped object. From there we can walk the links looking for a wrapper that 'does' the interface desired. It will sometimes be necessary to do a QueryInterface of 'nsISupports' on the interface we want to wrap in order to get the 'right' key for the hash table lookup. We can find or add the appropriate wrapper as necessary (sometimes having to create a new root wrapper for an object if none existed before).
For each instance of the WrappedNative wrapper we need to build a JSObject with the appropriate method stubs, getters/setters, and constant properties. I propose that to simplify this we lazily build a JSClass for each kind of interface (one per InterfaceInfo) and share that JSClass between WrappedNative instances. These can not be directly shared across JSRuntimes, so they will be per nsIXPConnectRuntime. So, for each nsXPConnectRuntime we'll maintain a hashtable keyed by UIID that returns an object that contains both an InterfaceInfo reference (which can be shared across JSRuntimes) and a JSClass. We build these as needed. They can be found/constructed whenever a new WrappedNative wrapper is constructed.
10 Dec - jband - Scott Furman pointed out that just sharing a
JSClass would not have the intended effect because property set calls cause new
properties to be set on the object instances rather than calling the class's
setter as I intended. He suggested building these Wrapper objects using the
newer ObjectOps callback system in the jsapi to allow more direct per object
handling. LiveConnect uses this strategy. I'll look closer at that code.
Nevertheless, a 'per class' shared object can handle this aspect for the various
Wrapper instances used for a given interface.]
10 Dec 1998 - jband -changed "WrapJS" to "WrappedJS"
10 Dec 1998 - jband -changed "WrapNative" to "WrappedNative"
10 Dec 1998 - jband -Added note about use of ObjectOps rather than JSClass
11 Jan 1999 - jband -changed 'comconnect' references to be 'XPConnect'