Go to the previous, next section.

Using ILU with Modula-3

This document is for the Modula-3 programmer who wishes to use ILU.

The ISL Mapping to Modula-3

Names

An item named Bar in ISL interface Foo becomes an item named Bar in the Modula-3 interface Foo. A hyphen in an ISL name becomes an underscore in the corresponding Modula-3 name.

Types

ISL types appear in Modula-3 as follows:

  1. SHORT INTEGER becomes [-32768 .. 32767].
  2. INTEGER becomes INTEGER.
  3. LONG INTEGER becomes ARRAY [0..1] OF INTEGER.
  4. BYTE becomes [0 .. 255].
  5. SHORT CARDINAL becomes [0 .. 65535].
  6. CARDINAL becomes Word.T.
  7. LONG CARDINAL becomes ARRAY [0..1] OF Word.T.
  8. SHORT REAL becomes REAL.
  9. REAL becomes LONGREAL.
  10. LONG REAL becomes EXTENDED.
  11. SHORT CHARACTER becomes ['\000' .. '\377'].
  12. CHARACTER becomes [0 .. 65535].
  13. Variable-length ARRAYs of SHORT CHARACTER become TEXT.
  14. Other variable-length arrays become REF ARRAY OF.
  15. Fixed-length arrays of SHORT CHARACTER become arrays of BITS 8 FOR ['\000' .. '\377'].
  16. Fixed or variable-length ARRAYs of BYTE become arrays of BITS 8 FOR [0 .. 255].
  17. No other arrays specify packing in the Modula-3.
  18. A fixed length array, ARRAY OF L1, ... Ln, becomes ARRAY [0 .. L1-1] OF ... ARRAY [0 .. Ln-1] OF.
  19. An ISL record becomes a M3 record.
  20. An ISL union becomes a M3 object type and some subtypes. TYPE Foo = UNION T1, ... Tn END becomes
    TYPE Foo = BRANDED OBJECT END;
    TYPE Foo_T1 = Foo BRANDED OBJECT v: T1 END;
    ...
    TYPE Foo_Tn = Foo BRANDED OBJECT v: Tn END;
    
    Here's an exercise for the reader: what, exactly, becomes of UNION Bar.Foo, Baz.Foo END?
  21. An ISL enumeration becomes a M3 enumeration. Due to the fact that Modula-3 offers no way to specify the codes used to represent enumerated values, the codes specified in ISL, if any, have no effect on the translation.
  22. When a Foo becomes a Bar, an OPTIONAL Foo becomes a REF Bar, unless Bar is a subtype of REFANY, in which case OPTIONAL Foo becomes Bar; NIL encodes the NULL case.
  23. An ISL object type becomes a Modula-3 object type. The methods are declared to raise the exceptions IluBasics.Failed and Thread.Alerted in addition to the exceptions declared in the ISL. Exception IluBasics.Failed is used to convey all the errors that can arise from the RPC mechanism, except Thread.Alerted. Is the surrogate (and the other surrogates from the same server?) broken after either of these exceptions is raised?

    Because ILU has multiple inheritance (i.e., an object type can have more than one direct supertype), the Modula-3 subtype relation is a sub-relation of the ILU subtype relation. In general, an ILU object type is mapped to a suite of Modula-3 object types, and a cohort of Modula-3 objects (one of each of the suite of Modula-3 types) correspond to one ILU object. There will be only one Modula-3 object (type) when only single-inheritance is used in constructing the ILU object type: when every ancestor type has at most one direct ancestor. Except where the programmer knows this is the case, and plans for it to remain so, she must abandon the native Modula-3 TYPECASE/NARROW/automatic-widen facilities for explicit calls that invoke the ILU subtype relation.

    To generalize the Modula-3 TYPECASE/NARROW/automatic-widen facilities, the Modula-3 object type Ilu.Object includes the following method:

    PROCEDURE ILU_Qua_Type(ot: ObjectType): Object;
    
    If the object has, in ILU, the given object type, the Modula-3 object of the appropriate Modula-3 type is returned; otherwise, NIL is returned. As an added convenience, the Modula-3 mapping of interface Foo will contain, for each of its object types Bar:
    PROCEDURE ILU_Qua_Bar(x: Ilu.Object): Bar;
    
    This procedure takes a non-NIL argument. If the argument is, in ILU, an instance of Bar or one of its subtypes, the corresponding language-specific object is returned; otherwise, NIL is returned.

Exceptions

ISL exceptions are exactly like Modula-3 exceptions, and are mapped directly.

Example

Here's a sample ISL spec, and the resulting Modula-3 mappings:

INTERFACE Foo;

TYPE String = ilu.CString;
TYPE UnsignedInt = CARDINAL;

TYPE E1 = ENUMERATION val1, val2, val3=40 END;
TYPE R1 = RECORD field1 : CARDINAL, field2 : E1 END;
TYPE FAB = ARRAY OF 200 BYTE;
TYPE VAB = SEQUENCE OF BYTE;
TYPE FASC = ARRAY OF 10 SHORT CHARACTER;
TYPE VASC = SEQUENCE OF SHORT CHARACTER;
TYPE FAC = ARRAY OF 5 CHARACTER;
TYPE VAC = SEQUENCE OF CHARACTER;
TYPE A2 = ARRAY OF 41, 3 R1;
TYPE S1 = SEQUENCE OF E1;
TYPE U1 = UNION R1, A2 END;

EXCEPTION Except1 : String;

CONSTANT Zero : CARDINAL = 0;

TYPE O1 = CLASS METHODS M1 (arg1 : R1) : UnsignedInt RAISES Except1 END;

The Modula-3 mapping:

INTERFACE Foo;
IMPORT Ilu, IluBasics, Thread;

TYPE
  String = TEXT;           (* NIL not allowed *)
  UnsignedInt = CARDINAL;
  E1 = {val1, val2, val3}; (* ORD # wire coding *)
  R1 = RECORD
         field1: CARDINAL;
         field2: E1;
       END;
  FAB = ARRAY [0 .. 199] OF Ilu.PackedByte;
  VAB = REF ARRAY OF Ilu.PackedByte;        (* NIL not allowed *)
  FASC = ARRAY [0 .. 9] OF Ilu.PackedShortChar;
  VASC = TEXT;                           (* NIL not allowed *)
  FAC = ARRAY [0 .. 9] OF Ilu.Character;
  VAC = REF ARRAY OF Ilu.Character;      (* NIL not allowed *)
  A2 = ARRAY [0 .. 40] OF ARRAY [0 .. 2] OF R1;
  S1 = REF ARRAY OF E1;                (* NIL not allowed *)
  U1 = BRANDED OBJECT END;             (* NIL not allowed *)
  U1_R1 = U1 BRANDED OBJECT v: R1 END;
  U1_A2 = U1 BRANDED OBJECT v: A2 END;

EXCEPTION Except1(String);

CONST Zero: CARDINAL = 0;

TYPE
  O1 = Ilu.Object OBJECT
       METHODS
         M1 (arg1: R1): UnsignedInt
             RAISES {IluBasics.Failed, Thread.Alerted, Except1};
       OVERRIDES
         Ilu_Get_Type := Ilu_Get_Type_O1
       END;

PROCEDURE ILU_SBH_To_O1 (sbh: TEXT; mostSpecificTypeID: TEXT := NIL):
  O1 RAISES {IluBasics.Failed, Thread.Alerted};

PROCEDURE Ilu_Get_Type_O1 (self : Ilu.Object): Ilu.ObjectType RAISES {};

PROCEDURE ILU_Qua_O1 (x: Ilu.Object): O1 RAISES {};

END Foo.

Stub Generation

To generate Modula-3 stubs from an ISL file, you use the program m3-stubber. Five files are generated from the `.isl' file:

Typically, client and server programmers directly reference only the first of these five files.

% m3-stubber foo.isl
translating interface foo to ./foo.i3...
private interface for foo to ./foo_x.i3...
common code for interface foo to ./foo_y.m3...
client stubs for interface foo to ./foo_c.m3...
server stubs of interface foo to ./foo_s.m3...
%

Server Programming

A server uses the following interface to expose itself to the ILU/M3 runtime.

INTERFACE Ilu;

(* What clients and server applications need to know about ILU.  See
   IluRuntime for the interface between stubs and the ILU runtime. *)

IMPORT IluKernel, Word;
FROM IluBasics IMPORT Failed, Failure;

<*PRAGMA lL, Ll, Main*>


(* Concurrency and locking:

   As in iluExports.h.  The ILU/Modula-3 runtime adds the folloing
   mutexes:
| ssMu		global mutex for server registry;
| srmu		global mutex for StrongRef implementation;
| ocMu		global mutex for ObjectCreator registry;
| Ilu.Server	each one is a mutex;

   and the following ordering constraints:
|  IluKernel.Server < ssMu < Ilu.Server
|  IluKernel.Server < srmu
|  IluKernel.Server < ocMu

   *)

(* RPC protocol failures *)

TYPE
  ProtocolFailure =
    Failure BRANDED OBJECT case: ProtocolFailureCase; END;


  ProtocolResultCode =
    {Success, NoSuchTypeAtServer, TypeVersionMismatch,
     NoSuchMethodOnType, GarbageArguments, Unknown, LostConnection,
     RequestRejected, RequestTimeout};

  ProtocolFailureCase = [ProtocolResultCode.NoSuchTypeAtServer ..
                          ProtocolResultCode.RequestTimeout];


(* Datatypes defined in ISL. *)

TYPE Byte = [0 .. 255];
TYPE PackedByte = BITS 8 FOR Byte;
TYPE ShortInt = [-32768 .. 32767];
TYPE Integer = INTEGER;
TYPE LongInt = ARRAY [0 .. 1] OF INTEGER;
TYPE ShortCard = [0 .. 65535];
TYPE Cardinal = Word.T;
TYPE LongCard = ARRAY [0 .. 1] OF Word.T;
TYPE ShortReal = REAL;
TYPE Real = LONGREAL;
TYPE LongReal = EXTENDED;
TYPE ShortChar = ['\000' .. '\377'];
TYPE PackedShortChar = BITS 8 FOR ShortChar;
TYPE Character = ShortCard; (* In Unicode. *)
TYPE String = TEXT; (* With no embedded '\000'. *)
TYPE WString = REF ARRAY OF Character; (* With no embedded 0. *)
TYPE Bytes = REF ARRAY OF PackedByte;


(* The String Binding Handle. *)

TYPE
  SBH = TEXT;
  (* A string that includes an instance ID and a contact-info *)

TYPE
  InstanceId = TEXT;
  (* A unique identifier for an object; it is factored into a ServerId
     and an ObjectHandle. *)

TYPE
  ServerId = TEXT;
  (* A unique identifier for a server *)

TYPE
  ObjectHandle = TEXT;
  (* A server-relative identifier for an object *)

TYPE
  ContactInfo = TEXT;
  (* An encoding of how to reach a server *)


(* ================ Server stuff ================ *)

TYPE
  ServerPrivate <: ROOT;
  Server = ServerPrivate OBJECT
             <*lL, Ll, Main unconstrained*>
             id: ServerId; (*READONLY*)
           METHODS
           END;
  (* A data structure that represents a server, either local to this
     program or remote.  Each server is actually one of the following
     two types. *)

TYPE SurrogateServer <: Server;

TYPE
  TrueServer <:
    Server OBJECT
    METHODS
      <*Main Invariant holds; Ll otherwise unconstrained*>

      HandleListenerFailure (f: Failure): FailureAction;
      (* When there's a failure in a listener for this server, this
         procedure is notified, and the result indicates whether the
         listener is abandoned or continues listening. *)
      HandleWorkerFailure (f: Failure): FailureAction;
      (* When there's a failure in a worker for this server, this
         procedure is notified, and the result indicates whether the
         connection is abandoned or continues listening. *)
    END;
  (* A server local to this program. *)

TYPE FailureAction = {Quit, Continue};

<*lL, Ll = {}*>
PROCEDURE DefaultHandleListenerFailure (self: TrueServer; f: Failure):
  FailureAction;

<*lL, Ll = {}*>
PROCEDURE DefaultHandleWorkerFailure (self: TrueServer; f: Failure):
  FailureAction;

<*Main Invariant holds; Ll otherwise unconstrained*>

PROCEDURE InitTrueServer (self  : TrueServer;
                          id    : ServerId := NIL;
                          objtab: ObjectTable := NIL ): TrueServer
  RAISES {Failed};
  (* A true server is created by calling InitTrueServer(NEW(TrueServer),
     ...).  The caller may specify the ILU "server-id", or let ILU
     generate one.  The caller may specify an ObjectTable; if NIL is
     given, a new standard one is used.  Failed is raised if the
     server-id is discovered to have been used before.  TrueServer may
     be sub-classed, overriding any of the public methods. *)

TYPE
  ObjectTable =
    OBJECT
    METHODS
      <*lL >= {the kernel server}*>
      <*lL >= {gcmu} if the object is collectible*>
      <*Ll, Main unconstrained*>

      ObjectToHandle (o: Object): ObjectHandle;
      (* Returns the handle associated with the given object, inventing
         and recording a new handle if necessary. *)
      HandleToObject (h: ObjectHandle): Object;
      (* Returns the Object associated with the given handle, or NIL if
         no such Object. *)
    END;
  (* An one-to-one association between Objects and ObjectHandles, such
     as a server might maintain. *)

PROCEDURE Export_Server (server: TrueServer;
                         p     : ProtocolInfo;
                         t     : TransportInfo ) RAISES {Failed};
  (* Make this server reachable via the given protocol and transport.
     The transport and protocol may use field values that mean
     "unspecified", in which case this procedure will choose suitable
     values.  This procedure must be called before SbhFromObject is
     invoked on any object of the server. *)

TYPE ProtocolInfo = BRANDED OBJECT END;
TYPE SunRpc2 = ProtocolInfo BRANDED OBJECT prognum, version := 0 END;
TYPE Courier = ProtocolInfo BRANDED OBJECT prognum, version := 0 END;

TYPE TransportInfo = BRANDED OBJECT END;
TYPE
  TCP = TransportInfo BRANDED OBJECT host, port := 0 END;
  UDP = TransportInfo BRANDED OBJECT host, port := 0 END;
  (* host and port are in host, not network, byte order. *)
TYPE SPP = TransportInfo BRANDED OBJECT addr := AnyXnsAddr END;

TYPE
  XnsAddr = RECORD
              net   : XnsNet;
              host  : XnsHost;
              socket: XnsSocket
            END;
  XnsNet = Cardinal;
  XnsHost = ARRAY [0 .. 5] OF PackedByte;
  XnsSocket = ShortCard;
CONST AnyXnsAddr = XnsAddr{0, XnsHost{0, ..}, 0};

TYPE
  Root <: ROOT; (* A hook for applications to jack up the base of the
                   world; look for some extra-lingual mechanism to
                   supply the concrete type. *)

  Object <: ObjectPublic;
  ObjectPublic =
    Root OBJECT
      <*lL, Ll, Main unconstrained*>
      ilu_is_surrogate: BOOLEAN := FALSE;
    METHODS
      <*lL, Ll, Main unconstrained*>

      ILU_Get_Server (): Server;
      ILU_Get_Type (): ObjectType;
      (* Returns the most specific ILU type known to this program for
         the ILU object represented by this Modula-3 object. *)
      ILU_Qua_Type (ot: ObjectType): Object;

      <*Main Invariant holds; Ll otherwise unconstrained*>

      ILU_Close           () RAISES {};
      ILU_Close_Surrogate () RAISES {};
    END;
  (* An ILU object, either true or a surrogate.  The implementor of a
     true object is responsible for ensuring that ilu_is_surrogate is
     FALSE and that ILU_Get_Server and ILU_Get_Type return appropriate,
     non-NIL values.  Surrogate objects will be implemented by
     ILU-generated stubs.

     ILU_Close is called on an object by the application program when it
     is done using the object; this enables the kernel to reclaim
     resources used by the object.  It is always safe (but maybe
     expensive to recover from if the object needs to be resurrected) to
     call ILU_Close on a surrogate object or a true object that will be
     found by the HandleToObject method of its server's ObjectTable.
     When Modula-3 gets WeakRefs, the runtime will automatically call
     ILU_Close when an object becomes globally inaccessible.
     ILU_Close_Surrogate is an obsolete version of ILU_Close that is
     applicable only to surrogate objects.

     ILU_Qua_Type facilitates embedding a multiple-inheritance structure
     in Modula-3.  If this object has, in the MI structure, the given
     type, ILU_Qua_Type returns an M3 object having an appropriate M3
     type; otherwise, it returns NIL.  For surrogate objects, the stubs
     take care of implementing this.  The implementor of a true object
     must override ILU_Qua_Type. *)

TYPE ObjectType = IluKernel.ObjectType;

PROCEDURE SbhFromObject (o: Object): SBH RAISES {Failed};
  (* May be applied to any Object; returns a reference that can be
     passed to other programs.  Export_Server must have been called on
     the object's server. *)

<*lL, Ll, Main unconstrained*>
PROCEDURE IdOfObjectType (ot: ObjectType): TEXT;
  (* Returns a shortish string that identifies this object type. *)


END Ilu.

A server module begins by creating an Ilu.TrueServer and calling Ilu.InitTrueServer on it. The server module may either specify the server's ID in this call, or let the ILU runtime choose one. The server module may specify how to handle errors arising in the server stubs, or let the ILU runtime handle them in the default way: print an error message to stdout and quit the listener or connection worker. The server module may assert control over the association between object-handles and objects in the server by supplying an ObjectTable, or let the ILU runtime manage the association in its default way.

The server module continues by calling Ilu.Export_Server, specifying the protocol and transport combinations through which the server should be contactable. Due to internal restrictions in the current runtime, this procedure should be called exactly once.

Each true object should be a subtype of Ilu.Object; the implementor of the true object is responsible for ensuring that the ilu_is_surrogate is filled in with FALSE and that the Ilu_Get_Server, Ilu_Get_Type, and ILU_Qua_Type methods have reasonable behavior. The ilu_is_surrogate field defaults to FALSE, and the object type declared in a Modula-3 interface generated by the m3-stubber from an ISL interface takes care of implementing Ilu_Get_Type, so a programmer using the stubs needs to worry only about Ilu_Get_Server and ILU_Qua_Type.

Once a true object has been created, and Ilu.Export_Server has been called, the server can export individual objects. This can be done by: (1) calling Ilu.Export_Server, which registers the object in the name server, (2) calling the lower-level procedure Ilu.SbhFromObject, which, when given a true object, ensures the object is exported and returns the string binding handle for that object, or (3) passing the object to another module among the arguments, results, or exception parameter contents of a call on a different language-specific object.

Client Programming

A client can acquire a Modula-3 language-specific object by calling the ILU_SBH_To_... stub procedure, passing the string binding handle and most specific type ID; these are typically obtained through some name service. The will soon be library modules named IluNms and IluPortMapper that facilitate using the NMS and Sun's Port Mapper.

The client can then proceed to make calls on the object.

Client or Server Programming

ILU currently supports DEC SRC Modula-3 version 2.08 -- which lacks finalization. When an application program -- any combination of client and server modules -- knows it is done with a particular object, it can explicitly free the resources associated with that object. This is done by invoking the ILU_Close method on that object.

It is always safe -- but may be expensive -- to invoke ILU_Close on a surrogate object or on a true object that will be found by the HandleToObject method of its server's ObjectTable; the HandleToObject method of the default ObjectTable implementation will not find a true object after ILU_Close has been called on that object.

Libraries and Linking

Clients of an ILU interface need to link with all but the server stubs; servers need to link with all five modules. It's convenient to make a library containing all five modules and let the linker worry about the details of which are needed; the imake macro IluM3Files (see later) conveniently generates the names of all five modules.

Both clients and servers also need to link with the libraries `ILUHOME/lib/libilu-m3.a' and `ILUHOME/lib/libilu.a' (in this order, as the former uses functions in the latter). Because the former library contains only Modula-3 code, and the latter only C code, invocations of the m3 command need to mention the latter library only when a complete program is being built.

Makefiles

Both the DEC SRC Modula-3 system and ILU use X11R?'s imake system to produce `Makefile's. See section Using Imake with ILU, for details.

Go to the previous, next section.