## `nsIClassInfo` Overview

Mike Shaver <shaver@mozilla.org >
Last Modified: March 12, 2001

### Table of Contents

- What is `nsIClassInfo`?
- How do I use it?
- Doesn't that break the COM `QueryInterface` rules?
- What are the "language helpers" for?
- That sounds useful. How do I make that work with my code?

- I'm writing C++ code, and

- I use the generic factory and `nsModuleComponentInfo`
- I use a custom factory or a singleton service object

- I'm writing JS code.

- What's "interface flattening"?

#### What is `nsIClassInfo`?

`nsIClassInfo` is an interface that provides information about a specific implementation class, to wit:

- a contract ID and class ID through which it may be instantiated;
- the language of implementation (C++, JavaScript, etc.);
- a list of the interfaces implemented by the class;
- flags indicating whether or not the class is threadsafe or a singleton; and
- helper objects in support of various language bindings.

#### How do I use it?

To get the `nsIClassInfo` object for the implementation behind a given interface, simply `QueryInterface` for the nsIClassInfo interface:

> C++:
> `nsCOMPtr<nsIClassInfo> info = do_QueryInterface(ifacePtr);`
> JavaScript:
> `var info = ifacePtr.QueryInterface(Components.interfaces.nsIClassInfo);`

It's important to note that this will usually return a singleton object: there often exists only one nsIClassInfo implementation for all implementations of a given class. This is very important, because it means that you can't `QueryInterface` back to the original object, no matter how hard you try.

#### Doesn't that break the COM `QueryInterface` rules?

Quite. As discussed in bug 67699, though, it's a reasonable concession to make in order to avoid an additional vtbl entry on every class that wants to export `nsIClassInfo` data.

#### What are the "language helpers" for?

The language helpers are basically hooks for alternate language bindings to use for providing language-specific wrapping and manipulation behaviour, without adding code to every wrapped object. In bug 67669, jband tells us what XPConnect does with the language helpers:

> When wrapping an object xpconnect QIs the object for its classinfo (say 'foo'). If it has seen that foo classinfo pointer before then it knows that this object is a foo and it can reuse all the info is knows about foo objects. If it has never seen foo it will gather info (such as the JS helper) and remember that for future wrappings of foo objects.

> Assuming that the foo helper tells xpconnect to not bother QI'ing each foo instance for a per instance helper (or if the instances don't respond to QI for the helper) then the same foo helper is used on all calls from JS relating to the foo instances.

> What you may be missing is that methods on the helper (nsIXPCScriptable in the xpconnect case) *all* receive a pointer to the instance wrapper when called. I.e. the helper methods have an explicit 'self' param. This allows the helper to customize its response for each method call without requiring a helper per instance.

See bug 67699 for more interesting discussion about pluggable language helpers and decoupling them from specific wrapped classes.

#### That sounds useful. How do I make it work with my code?

Why, yes, it is useful. To provide `nsIClassInfo` data for your class, you need to ensure that it returns an `nsIClassInfo` -implementing object when it is `QueryInterface`d for `nsIClassInfo` . Simple enough, but it can be even simpler through the use of a handful of helper macros/functions. Choose your own adventure:

#### I'm writing C++ code, and I use the generic factory and `nsModuleComponentInfo`.

First, make sure that your class has the `nsIClassInfo` helpers, by changing the `NS_IMPL_ISUPPORTS` line:

> `NS_IMPL_ISUPPORTS2(nsSampleImpl, nsISample, nsIOther)`

becomes

> `NS_IMPL_ISUPPORTS2_CI(nsSampleImpl, nsISample, nsIOther)`

This will provide an implementation of a helper function, named `nsSampleImpl_GetInterfacesHelper`, which handles the processing of `nsIClassInfo::getInterfaces`.

Next, in your module code, use `NS_DECL_CLASSINFO` to provide the rest of the per-class infrastructure (a global pointer into which to stash the `nsIClassInfo` object, and an extern declaration of the interfaces-helper, in case it's defined in another file):

> `NS_DECL_CLASSINFO(nsSampleImpl)`

You'll need one of these lines for each `nsIClassInfo` -supporting class represented in your `nsModuleComponentInfo` array.

Lastly, fill in the appropriate members of `nsModuleComponentInfo` to wire everything up:

> `static nsModuleComponentInfo components[] =`
> `{`
> `{
> "Sample Component", NS_SAMPLE_CID, NS_SAMPLE_CONTRACTID,
> nsSampleImplConstructor,`
> `nsSampleRegistrationProc,`
> `nsSampleUnregistrationProc,`
> `nsnull /* no factory destructor */,`
> `NS_CI_INTERFACE_GETTER_NAME(nsSampleImpl), /* interface getter */`
> `nsnull /* no language helper */,`
> `&NS_CLASSINFO_NAME(nsSampleImpl), /* global class-info pointer */`
> `0 /* no class flags */
> }`
> `};`

If you want to add a callback which provides language-helper objects, replace the last `nsnull` with your callback. See the `nsIClassInfo` IDL file for details on language helpers and other esoterica.

Note: the details of these macros may change slightly over the next week or so, as we refine a system for using table-driven QI and sharing data between QI and the class-info calls.

#### I'm writing C++ code, and I use a custom factory or a singleton service object.

You need some utility macros, don't ya? We're working on it. (You should really use the generic factory and module, though. See nsUConvModule.cpp for an example of how to use `nsModuleComponentInfo` and the generic modules even when you have highly-custom registration requirements.)

#### I'm writing JS code.

You poor thing. You suffer without even a GenericModule helper. We're working on that, too.

#### What's "interface flattening"?

Interface flattening is a way to present multiple interfaces of the same object to a language binding, without requiring explicit `QueryInterface` calls. Consider the following interfaces and an object `obj` that implements both of them:

> `interface nsIFoo : nsISupports {
> void fooMeth(in string message);
> };`

> `interface nsIBar : nsISupports {
> void barMeth(in PRUint32 meaning);
> };`

You could use the following code to call both fooMeth and barMeth without any QueryInterface:

> `obj.fooMeth("welcome to foo");
> obj.barMeth(42);`

Pretty, no? Pretty, yes.

There are also intricacies related to conflicting method names and the difference between interface sets that are part of a contract's promise and those which are simply artifacts of the implementation, but they're beyond the scope of this overview.