Xptcall Porting Guide


xptcall is a library that supports both invoking methods on arbitrary xpcom objects and implementing classes whose objects can impersonate any xpcom interface. It does this using platform specific assembly language code. This code needs to be ported to all platforms that want to support xptcall (and thus mozilla).

The tree

  +--public  // exported headers
  +--src  // core source
  |  \--md  // platform specific parts
  |     +--mac  // mac ppc
  |     +--unix  // all unix
  |     \--win32  // win32
  |     +--test  // simple tests to get started
  \--tests  // full tests via api

Porters are free to create subdirectories under the base md directory for their given platforms and to integrate into the build system as appropriate for their platform.

Theory of operation

There are really two pieces of functionality: invoke and stubs...

The invoke functionality requires the implementation of the following on each platform (from xptcall/public/xptcall.h):

NS_InvokeByIndex(nsISupports* that, PRUint32 methodIndex,
                   PRUint32 paramCount, nsXPTCVariant* params);
Calling code is expected to supply an array of nsXPTCVariant structs. These are discriminated unions describing the type and value of each parameter of the target function. The platform specific code then builds a call frame and invokes the method indicated by the index methodIndex on the xpcom interface that.

Here are examples of this implementation for Win32 and Linux x86, NetBSD x86, and FreeBSD. These implementations use the basic strategy of: figure out how much stack space is needed for the params, make the space in a new frame, copy the params to that space, invoke the method, cleanup and return. C++ is used where appropriate, Assembly language is used where necessary. Inline assembly language is used here, but it is equally valid to use separate assembly language source files. Porters can decide how best to do this for their platforms.

The stubs functionality is more complex. The goal here is a class whose vtbl can look like the vtbl of any arbitrary xpcom interface. Objects of this class can then be built to impersonate any xpcom object. The base interface for this is (from xptcall/public/xptcall.h):

class nsXPTCStubBase : public nsISupports
    // Include generated vtbl stub declarations.
    // These are virtual and *also* implemented by this class..
#include "xptcstubsdecl.inc"

    // The following methods must be provided by inheritor of this class.

    // return a refcounted pointer to the InterfaceInfo for this object
    // NOTE: on some platforms this MUST not fail or we crash!
    NS_IMETHOD GetInterfaceInfo(nsIInterfaceInfo** info) = 0;

    // call this method and return result
    NS_IMETHOD CallMethod(PRUint16 methodIndex,
                          const nsXPTMethodInfo* info,
                          nsXPTCMiniVariant* params) = 0;
Code that wishes to make use of this stubs functionality (such as XPConnect) implement a class which inherits from nsXPTCStubBase and implements the GetInterfaceInfo and CallMethod to let the platform specific code know how to get interface information and how to dispatch methods once their parameters have been pulled out of the platform specific calling frame.

Porters of this functionality implement the platform specific code for the stub methods that fill the vtbl for this class. The idea here is that the class has a vtbl full of a large number of generic stubs. All instances of this class share that vtbl and the same stubs. The stubs forward calls to a platform specific method that uses the interface information supplied by the overridden GetInterfaceInfo to extract the parameters and build an array of platform independent nsXPTCMiniVariant structs which are in turn passed on to the overridden CallMethod. The platform dependent code is responsible for doing any cleanup and returning.

The stub methods are declared in xptcall/public/xptcstubsdecl.inc. These are '#included' into the declaration of nsXPTCStubBase. A similar include file (xptcall/public/xptcstubsdef.inc) is expanded using platform specific macros to define the stub functions. These '.inc' files are checked into cvs. However, they can be regenerated as necessary (i.e. to change the number of stubs or to change their specific declaration) using the Perl script xptcall/public/genstubs.pl.

Here are examples of this implementation for Win32 and Linux x86, NetBSD x86, and FreeBSD. Both of these examples use inline assembly language. That is just how I decided to do it. You can do it as you choose.

The Win32 version is somewhat tighter because the __declspec(naked) feature allows for very small stubs. However, the __stdcall requires the callee to clean up the stack, so it is imperative that the interface information scheme allow the code to determine the correct stack pointer fixup for return without fail, else the process will crash.

I opted to use inline assembler for the gcc Linux x86 port. I ended up with larger stubs than I would have preferred rather than battle the compiler over what would happen to the stack before my asm code began running.

I believe that the non-assembly parts of these files can be copied and reused with minimal (but not zero) platform specific tweaks. Feel free to copy and paste as necessary. Please remember that safety and reliability are more important than speed optimizations. This code is primarily used to connect XPCOM components with JavaScript; function call overhead is a tiny part of the time involved.

I put together xptcall/src/md/test as a place to evolve the basic functionality as a port is coming together. Not all of the functionality is exercised, but it is a place to get started. xptcall/tests has an api level test for NS_InvokeByIndex, but no tests for the stubs functionality. Such a test ought to be written, but this has not yet been done.

A full 'test' at this point requires building the client and running the XPConnect test called TestXPC in mozilla/js/src/xpconnect/tests.

Getting these ports done is very important. Please let me know if you are interested in doing one. I'll answer any questions as I get them.

Porting Status

Original Author: John Bandhauer, 31 May 1999.