Go to the previous, next section.
This document is for the Modula-3 programmer who wishes to use ILU. ILU currently supports only DEC SRC Modula-3 version 2.08.
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.
ISL types appear in Modula-3 as follows:
SHORT INTEGER
becomes [-32768 .. 32767]
.
INTEGER
becomes INTEGER
.
LONG INTEGER
becomes
TYPE LongInt = RECORD high: [-16_80000000 .. 2147483647]; low : Word.T (*[0 .. 4294967295]*) END;This represents the number
high*2^32 + low
. We always have the invariants -2^31 <= high < 2^31
and 0 <= low < 2^32
, even on systems whose natural word size is greater than 32 bits.
BYTE
becomes [0 .. 255]
.
SHORT CARDINAL
becomes [0 .. 65535]
.
CARDINAL
becomes Word.T
.
LONG CARDINAL
becomes RECORD high, low: Word.T END
. This representation works analogously to that for LONG CARDINAL
.
SHORT REAL
becomes REAL
.
REAL
becomes LONGREAL
.
LONG REAL
becomes an opaque type. Values of this type can only be handed around; no other operations are provided, not even equality testing. LONG REAL
is not really supported yet.
SHORT CHARACTER
becomes ['\000' .. '\377']
.
CHARACTER
becomes [0 .. 65535]
.
ARRAY
s of SHORT CHARACTER
become TEXT
.
REF ARRAY OF
.
SHORT CHARACTER
become arrays of BITS 8 FOR ['\000' .. '\377']
.
ARRAY
s of BYTE
become arrays of BITS 8 FOR [0 .. 255]
.
ARRAY OF L1, ... Ln
, becomes ARRAY [0 .. L1-1] OF ... ARRAY [0 .. Ln-1] OF
.
TYPE Foo = DiscT UNION case1: T1 = val1-1, ... val1-j END, ... casen: Tn = valn-1, ... valn-k END END OTHERS;maps to the Modula-3
TYPE Foo = BRANDED OBJECT d: DiscT END; TYPE Foo_case1 = Foo BRANDED OBJECT v: T1 END; CONST Foo_case1__Code : DiscT = val1-1; ... TYPE Foo_casen = Foo BRANDED OBJECT v: Tn END; CONST Foo_casen__Code : DiscT = valn-1; TYPE Foo_OTHERS = Foo BRANDED OBJECT END; (* Where every Foo is of one of the subtypes enumerated here, and the tag field (d) is consistent with the subtype. *)The
Foo_OTHERS
subtype appears only for union constructions including the OTHERS
keyword.
If the ISL union has a DEFAULT
arm
cased: Td = DEFAULTit maps to another subtype in Modula-3:
TYPE Foo_cased = Foo BRANDED OBJECT v: Td END;The
Foo_casen__Code
constants are conveniences for filling in and decoding the d
field.
Note that code that creates a Foo is responsible for filling in the d
field.
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.
SINGLETON
, DOCUMENTATION
, COLLECTIBLE
, OPTIONAL
, AUTHENTICATION
, and BRAND
have no effect on the mapping into the Modula-3 type system.
OUT
and INOUT
method parameters in ISL become VAR
parameters in Modula-3; IN
parameters become VALUE
(by default) parameters. The SIBLING
constraint in ISL has no manifestation in the Modula-3 type system.
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.
ISL exceptions are exactly like Modula-3 exceptions, and are mapped directly.
Here's a sample ISL spec, and the resulting Modula-3 mappings:
INTERFACE Foo; TYPE String = ilu.CString; TYPE UInt = 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 = OBJECT METHODS M1(r1: R1, INOUT v: VASC, OUT s1: S1): UInt RAISES Except1 END, FUNCTIONAL Hash(v: VASC): FASC, ASYNCHRONOUS Note(x: LONG REAL) END;
The Modula-3 mapping:
INTERFACE Foo; IMPORT Ilu, IluBasics, Thread; IMPORT ilu; <*NOWARN*> TYPE UInt = CARDINAL; TYPE E1 = { val1, val2, val3}; TYPE R1 = RECORD field1 : CARDINAL; field2 : E1; END; TYPE VASC = TEXT; (* NIL not allowed *) TYPE S1 = REF ARRAY OF E1; (* NIL not allowed *) TYPE FASC = ARRAY [0..9] OF Ilu.PackedShortChar; (* declaration of M3 type "Foo.O1" from ILU class "Foo:O1" *) TYPE O1 = Ilu.Object OBJECT METHODS M1 (r1: R1; VAR v: VASC; VAR s1: S1): UInt RAISES {IluBasics.Failed, Thread.Alerted, Except1}; Hash (v: VASC): FASC RAISES {IluBasics.Failed, Thread.Alerted}; Note (x: Ilu.LongReal) RAISES {IluBasics.Failed, Thread.Alerted}; 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; PROCEDURE ILU_Qua_O1 (x: Ilu.Object): O1; TYPE A2 = ARRAY [0..40] OF ARRAY [0..2] OF R1; TYPE U1 = BRANDED OBJECT d: Ilu.ShortInt END; (* NIL not allowed *) TYPE U1_R1 = U1 BRANDED OBJECT v: R1 END; CONST U1_R1__Code : [-32768..32767] = 0; TYPE U1_A2 = U1 BRANDED OBJECT v: A2 END; CONST U1_A2__Code : [-32768..32767] = 1; TYPE VAC = REF ARRAY OF Ilu.Character; (* NIL not allowed *) TYPE FAC = ARRAY [0..4] OF Ilu.Character; TYPE VAB = REF ARRAY OF BITS 8 FOR Ilu.Byte; (* NIL not allowed *) TYPE FAB = ARRAY [0..199] OF Ilu.PackedByte; TYPE String = TEXT; (* NIL not allowed *) CONST Zero : CARDINAL = 0; (* Exceptions *) EXCEPTION Except1 (String); END Foo.
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 Simple Binding facility is available in an integrated way, as exhibited later.
The client can then proceed to make calls on the object.
A server uses the following interface to expose itself to the ILU/M3 runtime.
INTERFACE Ilu; 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 = RECORD high: [-16_80000000 .. 2147483647]; low : Word.T (*[0 .. 4294967295]*) END; TYPE ShortCard = [0 .. 65535]; TYPE Cardinal = Word.T; TYPE LongCard = RECORD high, low: Word.T (*[0 .. 4294967295]*) END; TYPE ShortReal = REAL; TYPE Real = LONGREAL; TYPE LongReal <: REFANY; 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}; 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}; 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; 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; 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 through a name service or by passing the object to another module among the arguments, results, or exception parameter contents of a call on a different language-specific object. The Simple Binding facility described later is integrated with ILU. To use a non-integrated name service, the object's string binding handle and most specific type ID are needed; they can be determined by calling Ilu.SbhFromObject(obj)
and Ilu.IdOfObjectType(obj.ILU_Get_Type())
.
The full API is presented in the previous section.
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.
The Simple Binding functionality is available through the following interface.
INTERFACE IluSimpleBind; FROM IluBasics IMPORT Failed; IMPORT Ilu; <*PRAGMA lL, Ll, Main*> <*Main invariant holds*> TYPE Cookie <: REFANY; PROCEDURE Publish (obj: Ilu.Object): Cookie RAISES {Failed}; PROCEDURE Withdraw (obj: Ilu.Object; c: Cookie) RAISES {Failed}; PROCEDURE Lookup (iid: Ilu.InstanceId; ot: Ilu.ObjectType): Ilu.Object RAISES {Failed}; END IluSimpleBind.
The instance ID used in the Lookup
call is what's called an object ID in chapter 1. It is the concatenation of: (1) the object handle, as determined by the server's Ilu.ObjectTable
; (2) an at-sign (@); and (3) the server ID, determined in the call on Ilu.InitTrueServer
.
To generate Modula-3 stubs from an ISL file, you use the program m3-stubber. Five files are generated from the `.isl' file:
% 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... %
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.
Go to the previous, next section.