The Message Interface (mint) intermediate language is designed to represent a collection of rpc/rmi interfaces in terms of the messages -- requests and replies -- that will be exchanged between clients and servers. Like aoi, mint descriptions are abstract: not concerned with particular message formats, data encoding schemes, or stub styles. These concrete notions are determined by Flick's presentation generators and back ends, after the mint representation of the interfaces has been built.
mint does not generally stand on its own, because by itself, mint does not carry enough information to completely represent an interface. Rather, mint is a part of the more expressive and complete pres_c language. mint simply describes collections of abstract messages; other parts of pres_c attach meanings to those messages, thus creating a complete representation language for interface description.
|
mint represents a collection of interfaces as a tree, as illustrated in Figure 5.1. (In truth, mint represents graphs, not trees, but the "top level" of the graph is always a tree.) At the root of the tree is a discriminated union, representing the collection of different idls. The separate branches of the union correspond to different idls: corba, onc rpc, mig, or other idls to be supported. Although mint was designed to support interfaces from multiple idls at the same time, but in current practice, only one idl is represented by any one mint tree.
At the next level of the tree, a node represents a collection of defined interfaces or object type. Here, each branch corresponds to a different interface type. At the third level, a mint node represents the collection of messages, both requests and replies, that are part of the interface. For each request, there is a fourth-level node (a MINT_STRUCT) that describes the request parameter types. For each reply, there is a fourth-level node (a MINT_UNION) that describes the types of the reply data. A reply is a union because it represents both normal and exceptional reply messages.
Beyond the fourth level of the tree, mint describes the types of individual message elements, much as aoi describes the types of idl operation parameters. Mapping from the aoi representation of an interface and its operations to the corresponding mint representation is straightforward. Since both aoi and mint are abstract description languages, the translation can in fact be implemented by a library function shared by all of Flick's presentation generators.1 The translation from aoi to mint is "lossy," however: some of the information in the aoi description of an interface or idl-defined type is lost. This is because mint only represents the structure or syntax of messages that will be exchanged between clients and servers. Semantic notions -- names (type names, structure slot names, . . . ), inheritance, exceptions, and so on -- that give meanings to messages are discarded when aoi is translated into mint. These semantics are "recovered" when the presentation generator makes a second pass over the aoi while building the pres_c representation of the types, stubs, and skeletons that will ultimately implement the interface.
Like aoi, mint must be written to and read from files, and it therefore defined using the onc rpc idl. The data structures are defined in mom/mint.x.
The top-level mint data structure is called mint_1 and contains two slots. The first slot, defs, contains a mint type graph, represented as a single array of type definitions, with each definition represented by a mint_def structure. mint definitions will be described shortly, in Section 5.2.1. Unlike aoi, there is no semantic significance to the order of the definitions in the array.
The second slot in a mint_1 is called standard_refs and contains a references to certain "well known" and common mint type definitions. It is convenient to keep these references around because certain basic types are used again and again. A reference to a mint type definition is a mint_ref, which is implemented as an index into the mint_1 array of mint_defs. A null reference is represented by the special mint_ref_null value.
typedef int mint_ref; const mint_ref_null = -1; struct mint_1 { mint_def defs<>; /* `standard_refs' are initialized by `mint_add_standard_defs'. */ mint_standard_refs standard_refs; }; /* * A `mint_standard_refs' contains `mint_ref's for commonly-used MINT types. * It is useful to have immediate access to these basic building blocks. */ struct mint_standard_refs { mint_ref void_ref; mint_ref bool_ref; mint_ref signed8_ref; mint_ref signed16_ref; mint_ref signed32_ref; mint_ref signed64_ref; mint_ref unsigned8_ref; mint_ref unsigned16_ref; mint_ref unsigned32_ref; mint_ref unsigned64_ref; mint_ref char8_ref; mint_ref float32_ref; mint_ref float64_ref; mint_ref interface_name_ref; mint_ref interface_invoke_ref; mint_ref interface_invoke_once_ref; mint_ref interface_service_ref; mint_ref system_exception_ref; }; |
|
A mint type definition is implemented by a discriminated union of the possible type kinds, which are summarized in Table 5.1. The details of individual mint types are specified by further mint structures as shown in the code below.
It is important to note the differences between an aoi_def (described in Section 4.2.1) and a mint_def. Unlike an aoi_def, a mint_def does not include a name, a scope, or any other "semantic" information beyond the essential structure of the type.
union mint_def switch (mint_def_kind kind) { case MINT_VOID: void; case MINT_BOOLEAN: void; case MINT_INTEGER: mint_integer_def integer_def; case MINT_SCALAR: mint_scalar_def scalar_def; case MINT_FLOAT: mint_float_def float_def; case MINT_CHAR: mint_char_def char_def; case MINT_ARRAY: mint_array_def array_def; case MINT_STRUCT: mint_struct_def struct_def; case MINT_UNION: mint_union_def union_def; case MINT_INTERFACE: mint_interface_def interface_def; case MINT_SYSTEM_EXCEPTION: void; case MINT_ANY: void; case MINT_TYPE_TAG: void; case MINT_TYPED: mint_typed_def typed_def; }; |
The current mint type kinds are described below, in the order in which they appear in Table 5.1.
A MINT_INTEGER represents an integer type with a specified (constant) minimum value and range. This type is used to represent all integer types whose values can be represented with not more that 32 bits. This includes unsigned and signed 32-, 16-, and 8-bit types, as well as booleans and certain integer "constants." (If the specified range is zero, then the MINT_INTEGER describes a type with only one possible value. This is useful for describing other types such as fixed-length arrays.)
Obviously, MINT_INTEGER has a direct correspondence with AOI_INTEGER.
struct mint_integer_def { /* Lowest possible value this integer can take. If >= 0, it's an unsigned integer. */ int min; /* Number of possible values this integer can take in addition to `min'. The maximum legal value is `min + range'. If range is 0, the integer can take only one value - i.e. it carries no information; useful for representing fixed-length arrays. If range is 1, it's a boolean. */ unsigned range; }; |
A MINT_SCALAR describes an integer type by specifying the number of bits in its range. The minimum value is determined by flags that indicate whether the type is signed or unsigned. Scalars are only used to describe integer types that a MINT_INTEGER cannot represent (e.g., 64-bit and 128-bit integers).
Obviously, MINT_SCALAR is the mint equivalent of aoi's AOI_SCALAR type.
typedef u_int mint_scalar_flags; const MINT_SCALAR_FLAG_NONE = 0; const MINT_SCALAR_FLAG_SIGNED = 1; const MINT_SCALAR_FLAG_UNSIGNED = 2; struct mint_scalar_def { int bits; mint_scalar_flags flags; }; |
A MINT_FLOAT describes a floating point type in terms of the number of bits in the encoding of the type. Again, there is an obvious and direct correspondence with the equivalent aoi type.
struct mint_float_def { /* Currently may be 32 or 64. */ int bits; }; |
A MINT_CHAR describes a character type. Again, the range of the type is described by the number of bits required to encode values of the type (generally 8). In addition, a set of flags describe additional properties, such as whether the characters are "signed" or "unsigned." As with most primitive types, the representations of mint and aoi characters are identical.
/* These are flags to modify mint_char's: signed, unsigned, or default. */ typedef u_int mint_char_flags; const MINT_CHAR_FLAG_NONE = 0; const MINT_CHAR_FLAG_SIGNED = 1; const MINT_CHAR_FLAG_UNSIGNED = 2; struct mint_char_def { /* Currently, `bits' may be 8 (char) or 16 (wchar). */ int bits; mint_char_flags flags; }; |
A MINT_BOOLEAN represents the standard boolean type: true or false. Currently, this type is unused: the boolean type is instead represented as a kind of MINT_INTEGER.
A MINT_VOID represents a void type: a type with no values.
A MINT_ARRAY describes an array type: a numbered and ordered collection of values of a single (element) type. The set of possible array lengths is described by a MINT_INTEGER, referenced by the length_type slot of the mint_array_def. The array may be variable-length (if the MINT_INTEGER has a non-zero range) or fixed-length (if the MINT_INTEGER has a zero range). Obviously, the MINT_ARRAY description also contains a reference to the type of the array elements, as shown below.
Note that unlike an AOI_ARRAY, a MINT_ARRAY definition does not have a set of flags describing semantic properties of the array (e.g., NUL-termination).
struct mint_array_def { /* Type of each of the elements in this array. */ mint_ref element_type; /* Type representing the possible lengths of this array. Must be an integer type of some kind. If the integer's `range' is zero, it's a fixed-length array. */ mint_ref length_type; }; |
A MINT_STRUCT describes a collection of unordered, unnamed values of heterogeneous types. This is different that the usual notion of a "structure" type in most programming languages. Here, a structure is simply a set of (zero or more) independent data elements. Each element has a specific type, but there is no significance to the order of the elements, nor are there any semantic connections between elements. For example, no element describes the "length" or the "type" of another.
In truth, the elements of a MINT_STRUCT are ordered in the sense that order is preserved when translating from an AOI_STRUCT, and the order of elements in an AOI_STRUCT is the order in which the slots were named in the original idl input. Order is also important when Flick must create constant values that are of specific structure types. But this ordering of slots is only "coincidental" to the meaning of a MINT_STRUCT. The ordering of the slots in a MINT_STRUCT does not, for example, specify or constrain the order in which the structure elements might eventually be written into memory or into a request or reply message. Flick presentation generators and back ends can choose to process the MINT_STRUCT elements in any order they see fit.
struct mint_struct_def { mint_ref slots<>; }; |
A MINT_UNION represents a discriminated union: a compound data type containing a discriminator that selects from a set of possible "arms" or cases. The value of the discriminator determines which one of the possible union cases is valid (i.e., part of the complete value of the union).
Note that the discriminator does not have to be a simple integer type: it can be a complex type such as an array of characters or a structure. This allows mint to describe unions in which the discriminator values are strings (e.g., interface or operation names) or other complex values (e.g., onc/tcp program/version pairs, or dce uuids).
Each arm is represented by a mint_union_case containing a discriminator value -- the value that selects the arm -- and type for the arm. The mint representation of constant values is described in Section 5.2.2. The union cases come in no particular order (although order is preserved when translating from an AOI_UNION).
The union as a whole is essentially represented as a kind of structure with a distinguished discriminator member, an array of mint_union_cases representing the arms of the union, and an optional "default" arm that is selected when none of the ordinary union cases are selected. If there is no default branch, then the dfault slot of the mint_union_def will be set to mint_ref_null. In this case, a discriminator value that does not match any of the cases is considered illegal (i.e., decoding will fail on an incoming message containing a union with a bad discriminator). If there is a default case and that default case is void, then dfault must refer to a valid MINT_VOID type node.
struct mint_union_case { mint_const val; mint_ref var; }; struct mint_union_def { /* Union discriminator variable. Type must be based on an int or enum prim_type. */ mint_ref discrim; /* Variable for each non-default case. Any case can be void, meaning no additional data. */ mint_union_case cases<>; /* Variable for the default case, `mint_ref_null' if no default. (If there's a default case but it's void, then `dfault' should point to a MINT_VOID.) */ mint_ref dfault; }; |
A MINT_ANY represents "any type," the type of all possible values. Because MINT_ANY is all-encompassing, there is no additional data associated with a MINT_ANY.
A MINT_TYPE_TAG represents an opaque indicator of a type. In other words, the value of a type tag is a description of type. Like AOI_TYPE_TAGs, MINT_TYPE_TAGs are opaque in the sense that the compiler portions of Flick do not provide, understand, or enforce any semantics for type tags. Other than marshaling and unmarshaling tag instances, Flick does nothing with tags. Semantics must be provided by runtime libraries.
Since type tags are opaque, a MINT_TYPE_TAG has no attributes.
A MINT_TYPED describes pairs containing a type description and a value of the described type. In other words, a MINT_TYPED represents type-tagged values. The tag and ref fields of a mint_typed_def structure may conceptually refer to any mint type, but in practice, the former is always a MINT_TYPE_TAG and the latter is always a MINT_ANY.
There is an obvious and direct correspondence between the mint and aoi representations of type-tagged "any" values.
/* used for type-tagged data, e.g. MIG's polymorphic type */ struct mint_typed_def { mint_ref tag; mint_ref ref; }; |
A MINT_INTERFACE represents a reference to an instance of an idl-defined object type. Note that this is quite different than the notion described by an AOI_INTERFACE. An AOI_INTERFACE describes the type of object instances; a MINT_INTERFACE describes the type of object references. In common idls, one passes object references in messages, not the objects themselves.2
A MINT_INTERFACE has a single datum describing the high-level rpc characteristics of the reference: e.g., whether the reference is for sending or receiving requests. (The current set of characteristics are derived from those implemented in the Mach operating system.) More specific properties, such as the on-the-wire encoding of object references, are not represented by mint: by being abstract, mint allows different back ends to choose different encodings.
enum mint_interface_right { MINT_INTERFACE_NAME = 0, /* just names a interface */ MINT_INTERFACE_INVOKE = 1, /* can invoke an interface */ MINT_INTERFACE_INVOKE_ONCE = 2, /* can invoke only once */ MINT_INTERFACE_SERVICE = 3 /* can service an interface */ }; struct mint_interface_def { mint_interface_right right; }; |
Finally, a MINT_SYSTEM_EXCEPTION describes the type of exceptions generated by the rpc/rmi runtime layers. These are implicit exceptions, not generally defined by in idl. Explicit exceptions, on the other hand, are defined in idl to be a part of an interface. Explicit exceptions are represented as ordinary mint types (e.g., as MINT_STRUCT); only implicit, "system-generated" exceptions are represented by MINT_SYSTEM_EXCEPTION.
System exceptions are handled specially because different rpc standards define different properties for system exceptions. For example, corba defines a standard set of exceptions that may be raised by the underlying orb, and also defines the data to be communicated within these system exceptions. onc rpc, on the other hand, defines a completely different method for communicating rpc errors to client programs.
Thus, in order to be general, mint must support system exceptions in an abstract way: mint defines when they may occur, but does not define the actual content or structure of system-generated exceptions. (These things are determined by Flick's individual back ends, long after the translation from aoi to mint takes place.) In a sense, a MINT_SYSTEM_EXCEPTION is a kind of placeholder that a back end must fill in as part of implementing a complete message protocol.
Because system exceptions are opaque to mint, a MINT_SYSTEM_EXCEPTION has no attributes.
In general, mint is only concerned with the structure of data, not with particular data values. Some data types, however, incorporate constants as part of their descriptions. Discriminated unions are a prime example. (In fact, discriminated unions are the only mint type that currently makes use of constants.) To support union discriminators and possibly other future mint type kinds, mint provides a way to express constant values.
A constant is represented as a mint_const, which is a pointer to a mint_const_u. A mint_const_u is a discriminated union of the possible constant kinds, as shown in the code below. There are three primitive constant types (integers, characters, and floats) and two constructed types (structures and arrays).
A primitive constant can be described as a literal value (a MINT_CONST_LITERAL) or as a symbolic value (a MINT_CONST_SYMBOLIC). Symbolic values are used when the specific value of a constant is unimportant in terms of abstract message format; in general, idl-specified constants are represented as literals and idl-unspecified constants (e.g., the discriminator values that select between normal and exceptional reply messages) are symbolic. In mint, it is not necessary to define the value of a symbolic constant: mint really only concerns itself with types, not specific values. Of course, a symbolic constant must eventually be assigned a value if the constant is to be used in generated stub code. This value is generally assigned by a Flick back end or by one of Flick's runtime library header files. In any case, it happens outside the scope of mint.
A compound (array or structure) constant is simply represented as an array of other mint constants. In a MINT_CONST_ARRAY, the number of sub-nodes is the array length, and the nodes themselves are the array elements, in order. In a MINT_CONST_STRUCT, the number of sub-nodes is the number of slots in the structure type. When matching a constant structure against a particular MINT_STRUCT type description (e.g., when interpreting a union discriminator value in terms of the defined union discriminator type), the slots of the constant and type descriptions are paired in order. The number of elements in each must be the same.
Other kinds of complex constants (e.g., unions) are not currently supported, but may be supported in the future if need arises.
typedef struct mint_const_u *mint_const; enum mint_const_kind { MINT_CONST_INT = 1, MINT_CONST_CHAR = 2, MINT_CONST_FLOAT = 3, MINT_CONST_STRUCT = 4, MINT_CONST_ARRAY = 5 }; enum mint_const_category { MINT_CONST_LITERAL = 1, MINT_CONST_SYMBOLIC = 2 }; union mint_const_int_u switch (mint_const_category kind) { case MINT_CONST_LITERAL: long value; case MINT_CONST_SYMBOLIC: string name<>; }; union mint_const_char_u switch (mint_const_category kind) { case MINT_CONST_LITERAL: char value; case MINT_CONST_SYMBOLIC: string name<>; }; union mint_const_float_u switch (mint_const_category kind) { case MINT_CONST_LITERAL: double value; case MINT_CONST_SYMBOLIC: string name<>; }; typedef mint_const mint_const_struct<>; typedef mint_const mint_const_array<>; union mint_const_u switch (mint_const_kind kind) { case MINT_CONST_INT: mint_const_int_u const_int; case MINT_CONST_CHAR: mint_const_char_u const_char; case MINT_CONST_FLOAT: mint_const_float_u const_float; case MINT_CONST_STRUCT: mint_const_struct const_struct; case MINT_CONST_ARRAY: mint_const_array const_array; }; |
Comparing aoi and mint, one notices that certain aoi types are missing from mint:
In addition, AOI_INTERFACE and MINT_INTERFACE represent different notions as described previously in Section 5.2.1. The different parts of an aoi interface type declaration become different mint constructs:
To summarize, the primary distinction between aoi and mint is that aoi describes interfaces in idl-like terms, and mint describes interfaces in terms of the messages that will implement an interface.
The libmint library is provided to help deal with constructing and manipulating the various mint data structures. Its header file is mom/libmint.h and has the following functions:
In addition, libmint.h provides a set of simple MINT_*_REF macros to be used in combination with the above tags to aid in the construction of mint type definitions. See the header file for details. Flick implementors must use these macros with caution, however, so as not to introduce portability problems. With these macros, it is easy to create an argument list that contains multiple calls to mint_ add_ def_ tags . Each call is a separate parameter, and each call has side effects, so the order in which the calls take place is significant. However, C doesn't guarantee an order of evaluation for function parameters. This means that the calls might be evaluated in different orders, depending on the compiler, thus producing different (although logically equivalent) mint_ def arrays. This in turn would cause Flick to produce different pres_c files on different platforms, thus making testing much more difficult!
Note that a literal constant and a symbolic constant are never equal.
mint allows Flick to represent messages in an abstract manner, without describing the exact message formats or data encodings that will eventually be used. There are ways, however, in which the current implementation of mint is problematic: either too abstract, or not abstract enough.
The union discriminators used in the upper levels of the mint tree are chosen by the presentation generator from the names and codes described in aoi. In practice, these mint union discriminators then become the identifiers used by back ends when implementing their message formats. This is a problem: the presentation generators should not be choosing the identifiers that should be used in "unpresented" parts of a message. To fix this problem, mint must be extended to represent abstract interface and operation identifiers. These abstract identifiers should allow for multiple names, carried forward from aoi. (See the discussion in Section 4.5.) It would be the job of a back end to translate a mint abstract identifier into a concrete value, much in the way that back ends already handle mint symbolic constants.