Using RTCode to access module descriptors


Motivation

We need to allow modules to talk about themselves and the interfaces they import. We have two examples where this is necessary. First, we want to make the module which exports a procedure responsible for approving other handlers installed on that procedure. This requires that the module can provide some unforgeable proof of its identity when it installs its default handler. Second, a module which wants to create a Domain containing code exported by an interface X will be required to have imported X.

Implementation

The SRC Modula-3 system already defines symbols called MI_* for interfaces and MM_* for modules which have the type UNTRACED REF RT0.ModuleInfo, but these symbols are not accessible from Modula-3 code. Our solution is to create two new builtin functions called THIS_MODULE(), which returns the MM_ symbol for the module in which the function appears, and INTERFACE_UNIT(X) which returns MI_X for the named interface. Since THIS_MODULE() takes no argument, it is impossible for one module to get the descriptor for a second unless the second one explicitly provides it. Also the only names which may be passed to INTERFACE_UNIT() are those which are explicitly imported in the calling module. Additionally, we want to ensure that arbitrary safe programs cannot alter the contents of the interface or module descriptors, nor can they forge descriptors. So we define two opaque types which are the return types of THIS_MODULE() and INTERFACE_UNIT(X).
INTERFACE RTCodeBuiltin;
TYPE
    Module <: ADDRESS;      (* return type of THIS_MODULE()    *)
    Interface <: ADDRESS;   (* return type of INTERFACE_UNIT() *)
END RTCodeBuiltin.
The revelations of these types occur in RTCode.m3.
REVEAL RTCodeBuiltin.Module = UNTRACED BRANDED REF RT0.ModuleInfo;
REVEAL RTCodeBuiltin.Interface = UNTRACED BRANDED REF RT0.ModuleInfo;
We provide a second interface, called RTCode, that is imported by callers of THIS_MODULE() and INTERFACE_UNIT(). This interface declares two procedures that answer questions about modules. ModuleExportsInterface determines if the module m exports any of the procedures declared in the interface i. ModuleImplementsProcedure determines if the procedure referred to by p_ref is defined in the module m.
INTERFACE RTCode;
IMPORT RTCodeBuiltin;

TYPE
    Module = RTCodeBuiltin.Module;
    Interface = RTCodeBuiltin.Interface;

(* returns true iff m exports a function declared in i *)
PROCEDURE ModuleExportsInterface(m: Module; i: Interface): BOOLEAN;

(* return true iff m defines procedure p *)
PROCEDURE ModuleImplementsProcedure(m: Module; p_ref: PROCANY): BOOLEAN; 

Caveats

Our implementation has some quirks of which users should be aware. First, you must explicitly import an interface before you can call INTERFACE_UNIT() with that interface name. Just exporting an interface from a module is not enough to bring that interface's name into the top level scope of a module. A second related problem is that it is impossible to refer to the interface exported by a generic module, because you cannot name that interface at all. But you may use INTERFACE_UNIT() for other interfaces inside a generic module. The third problem is that the RTCode.ModuleExportsInterface function deduces the export relationship by looking for exported procedures. It is possible for a module to contain an EXPORTS declaration but not export any procedures, in which case this function will return FALSE.

Examples

These examples show how we expect to use the runtime descriptors in the Domain and Dispatcher interfaces.
	Domain.CreateFromInterface(i: RTCode.Interface): T
	(* safely create a domain *)

	Dispatcher.InstallAuthHandler(THIS_MODULE(), event, authHandler)
	(* assert authority over event.  Dispatcher checks
		RTCode.ModuleImplementsProcedure(firstArg, secondArg)
	*)


Other SPIN Modula-3 changes

May 20, 1996

garrett@cs.washington.edu