Abstract Object Interface (AOI) Representation

The Abstract Object Interface (aoi, pronounced "ow-ee") is a set of data structures designed to represent parsed idl files. Most of Flick's front ends -- all but the mig front end -- convert idl into aoi and then save the resultant aoi to disk. This provides a level of abstraction between Flick's front ends, which simply parse idl, and Flick's presentation generators, which map interface specifications onto specific programming language constructs.

4.1 AOI Overview

The aoi format is intended to satisfy the following goals:

Finally, because aoi acts as the interface between Flick's front ends and presentation generators, aoi was designed to be simple, and easily written to and read from disk.

4.2 AOI Data Structures

Because aoi is written to and read from files, the aoi format is defined using the onc rpc idl. The data structures are defined in mom/aoi.x; this file is processed by rpcgen when Flick is compiled1 to produce functions that write and read aoi data to and from files. Other functions for manipulating aoi structures are defined in the aoi library, which is contained the libaoi directory.

The root of an aoi tree is a structure that holds a meta data structure (as described in Chapter 3) and an array of aoi definitions corresponding to definitions given in the input idl file(s):
 struct aoi {
         aoi_def         defs<>;
         meta            meta_data;
 };

The meta_data field holds the meta information describing the input idl files: i.e., the file names and the inclusion graph. The defs field is an array of associations between names and bindings, described below.

4.2.1 Definitions

An aoi definition -- represented by an aoi_def structure -- consists of a name, a scope identifier, a type that is bound to the name, and a reference (an io_file_index) to the source file for the definition. The file reference is an index into the meta array of file descriptions as described in Chapter 3.
 struct aoi_def
 {
         string    name<>;
         int       scope;
         io_file_index idl_file;
         aoi_type  binding;
 };




 
corba idl
Corresponding aoi_def Values


module MyMod_1 {
name = "MyMod_1", scope = 0, ...
interface MyIntf_1 {
name = "MyIntf_1", scope = 1, ...
typedef ... T_1;
name = "T_1", scope = 2, ...
typedef ... T_2;
name = "T_2", scope = 2, ...
};
interface MyIntf_2 {
name = "MyIntf_2", scope = 1, ...
};
};
module MyMod_2 {
name = "MyMod_2", scope = 0, ...
};



Figure 4.1Representation of idl Scopes in aoi.

The name is a string, usually taken from the idl file without any mangling. The scope is a number representing the lexical scope of the definition. In aoi, all aoi_def structures are contained in a single array: in other words, scopes are not represented by nested aoi_def structures. Rather, scoping is represented by keeping track of the lexical scoping depth of each definition, as illustrated in Figure 4.1. The aoi_defs are arranged so that the order of the idl source definitions is preserved. A top level idl definition has a scoping depth of zero. When a definition introduces a new scope, the aoi "current scope" depth is increased by one: thus, the aoi_defs within a scope will have scope values that are greater than the the scope value of the containing aoi_def. Obviously, when an idl scope ends, the aoi "current scope" depth is decreased by one. This representation of scopes makes it easy to search for the "parent" of an aoi_def: simply search backward in the array of aoi_defs for a definition with a lower scope value, handling zero as a special case. Searching for peer and child definitions is also straightforward.2

The binding in an aoi_def refers to the definition's type, translated from the idl file and represented by an aoi_type, to be described shortly.

Before moving on, note that an aoi_ref is a reference to an aoi_def, implemented as an index into the aoi_def array contained in the top-level aoi structure shown in Section 4.2. A null reference is represented by the special constant aoi_ref_null.
 typedef int aoi_ref;
 const aoi_ref_null = -1;

4.2.2 Fields

An aoi_field is similar to an aoi_def except it is meant for use in compound types: structures, unions, and so on. An aoi_field associates a name and an type, but does not need a scope identifier or source file reference.
 struct aoi_field
 {
         string          name<>;
         aoi_type        type;
 };

4.2.3 Types




aoi Type Kind
Description


AOI_INTEGER
an integer with a specified minimum value and range
AOI_SCALAR
an integer with range determined by # of bits
AOI_FLOAT
a floating-point type with a specified # of bits
AOI_CHAR
a character with flags and a specified # of bits
AOI_VOID
void
AOI_CONST
an idl-defined constant value
AOI_ENUM
an enumeration (i.e., a set of symbolic constants)
AOI_ARRAY
an array, fixed- or variable-length
AOI_STRUCT
a structure containing named fields
AOI_EXCEPTION
a structure used to signal exceptions
AOI_UNION
a discriminated union
AOI_OPTIONAL
an optional value (i.e., possibly no value)
AOI_ANY
"any" value, without a type tag
AOI_TYPE_TAG
an opaque type identifier
AOI_TYPED
a pair: a type identifier and a value of that type
AOI_INTERFACE
an idl-defined interface or object type
AOI_FWD_INTRFC
a forward interface or object declaration
AOI_NAMESPACE
a namespace for other definitions
AOI_INDIRECT
a reference to a named type (an aoi_def)
AOI_ERROR
a parse error



Table 4.1Summary of the Available aoi Type Kinds.

An idl type is represented by an aoi_type_u: a discriminated union with branches that represent all idl types. An aoi_type is a pointer to an aoi_type_u. All kinds of name bindings are represented as "types," including bindings to constant values and references to other bindings (i.e., usage of named types). The set of representable types is determined by the aoi_kind enumeration, summarized in Table 4.1. The aoi_type_u union has a branch or "arm" for each possible aoi_kind. The details of a particular aoi type definition are specified by further aoi structures, as shown below.
 enum aoi_kind
 {
         AOI_INDIRECT    = 1,
         ...
         AOI_ERROR       = 20
 };
 
 typedef struct aoi_type_u *aoi_type;
 
 union aoi_type_u
 switch (aoi_kind kind)
 {
         case AOI_INDIRECT:      aoi_ref                 indirect_ref;
         case AOI_INTEGER:       aoi_integer             integer_def;
         case AOI_SCALAR:        aoi_scalar              scalar_def;
         case AOI_FLOAT:         aoi_float               float_def;
         case AOI_CHAR:          aoi_char                char_def;
         case AOI_ARRAY:         aoi_array               array_def;
         case AOI_STRUCT:        aoi_struct              struct_def;
         case AOI_UNION:         aoi_union               union_def;
         case AOI_INTERFACE:     aoi_interface           interface_def;
         case AOI_EXCEPTION:     aoi_exception           exception_def;
         case AOI_ENUM:          aoi_enum                enum_def;
         case AOI_VOID:          void;
         case AOI_CONST:         aoi_const_def           const_def;
         case AOI_NAMESPACE:     void;
         case AOI_OPTIONAL:      aoi_optional            optional_def;
         case AOI_FWD_INTRFC:    aoi_ref                 fwd_intrfc_def;
         case AOI_ANY:           void;
         case AOI_TYPE_TAG:      void;
         case AOI_TYPED:         aoi_typed               typed_def;
         case AOI_ERROR:         void;
 };

The current aoi type kinds are described below, in the order in which they appear in Table 4.1.

Integers

An AOI_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 AOI_INTEGER describes a type with only one possible value. This is useful for describing other types such as fixed-length arrays.)
 struct aoi_integer
 {
         int      min;
         unsigned range;
 };

Scalars

An AOI_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 an AOI_INTEGER cannot represent (e.g., 64-bit and 128-bit integers).
 typedef u_int aoi_scalar_flags;
 const AOI_SCALAR_FLAG_NONE      = 0;
 const AOI_SCALAR_FLAG_SIGNED    = 1;
 const AOI_SCALAR_FLAG_UNSIGNED  = 2;
 
 struct aoi_scalar
 {
         int              bits;
         aoi_scalar_flags flags;
 };

Floats

An AOI_FLOAT describes a floating point type in terms of the number of bits in the encoding of the type.
 struct aoi_float
 {
         int bits;
 };

Characters

An AOI_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."
 typedef u_int aoi_char_flags;
 const AOI_CHAR_FLAG_NONE     = 0;
 const AOI_CHAR_FLAG_SIGNED   = 1;
 const AOI_CHAR_FLAG_UNSIGNED = 2;
 
 struct aoi_char
 {
         int            bits;
         aoi_char_flags flags;
 };

Voids

An AOI_VOID represents a void type: a type with no values.

Constants

An AOI_CONST corresponds to a literal constant value. (An aoi_def can be used to describe a symbolic constant.) The type of the constant is described by an aoi_type and is also manifest in the aoi_const value itself. aoi currently supports integer, character, and floating-point literals, and arrays and structures built from these literals.
 typedef long aoi_const_int;
 typedef char aoi_const_char;
 typedef double aoi_const_float;
 typedef aoi_const aoi_const_struct<>;
 typedef aoi_const aoi_const_array<>;
 
 enum aoi_const_kind
 {
         AOI_CONST_INT           = 1,
         AOI_CONST_CHAR          = 2,
         AOI_CONST_FLOAT         = 3,
         AOI_CONST_STRUCT        = 4,
         AOI_CONST_ARRAY         = 5
 };
 
 union aoi_const_u
 switch (aoi_const_kind kind)
 {
         case AOI_CONST_INT:     aoi_const_int           const_int;
         case AOI_CONST_CHAR:    aoi_const_char          const_char;
         case AOI_CONST_FLOAT:   aoi_const_float         const_float;
         case AOI_CONST_STRUCT:  aoi_const_struct        const_struct;
         case AOI_CONST_ARRAY:   aoi_const_array         const_array;
 };
 
 struct aoi_const_def
 {
         aoi_type        type;
         aoi_const       value;
 };

Enumerations

An AOI_ENUM represents an enumerated data type, i.e., a set of symbolic constants. The set is represented as an array of aoi_fields, which are name-value pairs. The associated values, of course, are expected to be constants, not arbitrary aoi types. The enum_label contains any idl-specified label for the enumeration.
 struct aoi_enum
 {
         string           enum_label<>;
         struct aoi_field defs<>;
 };

Arrays

An AOI_ARRAY describes an array type: this includes fixed-length arrays, variable-length arrays (what some idls call "sequences"), and NUL-terminated strings. The length of an array is specified as an AOI_INTEGER, thus giving the minimum and maximum lengths of instances of the array type. The AOI_ARRAY description also refers to the type of the array elements, as shown in the code.
 typedef u_int aoi_array_flags;
 const AOI_ARRAY_FLAG_NONE                       = 0x00000000;
 const AOI_ARRAY_FLAG_NULL_TERMINATED_STRING     = 0x00000001;
 const AOI_ARRAY_FLAG_OPAQUE                     = 0x00000002;
 const AOI_ARRAY_FLAG_ALL                        = 0x00000003;
 
 struct aoi_array
 {
         aoi_type           element_type;
         aoi_type           length_type;
 
         aoi_array_flags    flgs;
 };

Additional properties of the type are specified as a set of flags in the flgs field of the aoi_array structure. The AOI_ARRAY_FLAG_NULL_TERMINATED_STRING flag indicates that the array type represents a NUL-terminated string type. At one time, AOI_ARRAY_FLAG_OPAQUE was used to indicate that the array elements should be packed together when encoded. This flag is no longer meaningful, since data encoding decisions are made elsewhere (i.e., by back ends, based on the element types of mint arrays).

Structures

An AOI_STRUCT represents a compound data type with a fixed number of named fields (also known as slots or data members). The structure type is represented as an array of (zero or more) slot descriptors, which give the name and type of each slot. The order of the slots is preserved from the idl source definition of the structure type.
 typedef aoi_field aoi_struct_slot;
 struct aoi_struct
 {
         /* Type of each structure slot, in order.  */
         aoi_struct_slot slots<>;
 };

Exceptions

An AOI_EXCEPTION type is similar to a regular structure type, but with an additional semantic: an AOI_EXCEPTION represents an idl-specified exceptional runtime condition and its associated data (if any). Because idls distinguish between normal structure types and exception types, aoi makes this distinction as well.
 typedef aoi_field aoi_exception_slot;
 struct aoi_exception
 {
         aoi_exception_slot slots<>;
 };

Unions

An AOI_UNION represents a discriminated union: a compound data type containing a discriminator that selects from a set of possible "arms" (also called cases, branches, or variants). The value of the discriminator determines which one of the possible union arms is valid.

Each arm is represented by an aoi_union_case containing a discriminator value -- the value that selects the arm -- as well as a name and type for the arm. (The aoi representation of constant values will be described shortly.)

The union as a whole is essentially represented as a kind of structure with a distinguished discriminator member, an array of aoi_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 aoi_union will be null. If there is a default case and that default case is void, then dfault must refer to a valid aoi_field of type AOI_VOID. Finally, the union_label slot holds the idl-specified name (if any) of the union arms as a whole. This may be used when mapping the discriminated union type into an appropriate C/C++ representation (e.g., a struct with a slot that is a union of the aoi-described union branches).
 struct aoi_union_case
 {
         aoi_const       val;
         aoi_field       var;
 };
 
 struct aoi_union
 {
         aoi_field      discriminator;
         string         union_label<>;
         aoi_union_case cases<>;
         aoi_field     *dfault;
 };

Optional Values

An AOI_OPTIONAL represents a "maybe value" type: a type that allows for empty values. Intuitively, an AOI_OPTIONAL corresponds to a pointer type that may be null.

In common idls, optional data can be described in several ways: as a variable-length array of zero or one elements, as a discriminated union with a void variant, or as a denoted optional element. (The onc rpc idl provides "*" for denoting optional data. corba idl does not provide an equivalent construct.) These techniques for "optionality" are logically equivalent, but each idl idiom generally results in a different presentation -- that is, a different set of generated programming language types. Thus, no technique for describing optional data is redundant, and aoi must describe them all. AOI_OPTIONAL corresponds to denoted optional data types; i.e., the onc rpc idl "*" construct.
 struct aoi_optional
 {
         aoi_type type;
 };

Anys

An AOI_ANY represents "any type," the type of all possible values. Because AOI_ANY is all-encompassing, there is no additional data associated with an AOI_ANY.

Unlike the any types specified by some idls, an AOI_ANY does not contain an implicit type tag. The notion of a type-tagged value is represented by a separate aoi notion called AOI_TYPED. aoi distinguishes between tagged and untagged "any" values in order to provide flexibility.

Type Tags

An AOI_TYPE_TAG represents an opaque indicator of a type. In other words, the value of a type tag is a description of 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, an AOI_TYPE_TAG has no attributes.

Typed Values

Combining the two notions above, an AOI_TYPED represents a pair containing a type description and a value of the described type. In other words, an AOI_TYPED represents type-tagged values. The tag and type fields of an aoi_typed structure may conceptually refer to any aoi type, but in practice, the former is always an AOI_TYPE_TAG and the latter is always an AOI_ANY.
 struct aoi_typed
 {
         aoi_type        tag;
         aoi_type        type;
 };

Interfaces

An AOI_INTERFACE represents an idl-defined interface or object type. aoi's notion of an interface is flexible enough to represent corba interface declarations and onc rpc version declarations, and should be flexible enough to represent the corresponding notions of other idls as well.

An AOI_INTERFACE supports separate notions for attributes ("data members") and operations ("function members"). Interface operations are stored in aoi_operation structures, which are contained within the aoi_interface structure shown below. Each aoi_operation contains several bits of information: the operation name, its request and reply message identifiers, the parameters, exceptions that may be raised, and various flags. The aoi data structures for representing these operation data are shown in the code below. Most of these are what one would expect, except perhaps for the request and reply message identifiers. These are intended to be idl-specified "suggestions" to the presentation generator and back end stages of Flick. These later stages are responsible for deciding the actual identifiers and codes used to implement an interface. A presentation generator and back end can use identifiers that come from the idl (as passed through aoi), but are not required to use them.3

Interface attributes are stored separately from general operations and are represented as special facilities for reading and writing and interface's "data members." An aoi_attribute specifies four separate codes: two for reading (request and reply) and two for writing. The type of the interface attribute is stored in the aoi_attribute structure.

Note that all of an interface's operation and attribute request and reply codes must be unique. Moreover, the request and reply codes for a single operation or attribute cannot be identical. These restrictions ease the translation of aoi into mint during presentation generation. (See Section 11.2.1 for details.) In addition, because operations and attributes are quite similar, some presentation generators may choose to translate aoi_attributes into various aoi_operations for accessing the attributes.
 enum aoi_direction
 {
         AOI_DIR_IN      = 1,
         AOI_DIR_OUT     = 2,
         AOI_DIR_INOUT   = 3,
         AOI_DIR_RET     = 4
 };
 
 struct aoi_parameter
 {
         string          name<>;
         aoi_direction   direction;
         aoi_type        type;
 };
 
 typedef u_int aoi_op_flags;
 const AOI_OP_FLAG_NONE          = 0x00000000;
 const AOI_OP_FLAG_ONEWAY        = 0x00000001;
 const AOI_OP_FLAG_IDEMPOTENT    = 0x00000002;
 const AOI_OP_FLAG_SETTER        = 0x00000004;
 const AOI_OP_FLAG_GETTER        = 0x00000008;
 
 struct aoi_operation
 {
         string          name<>;
 
         /*
          * Code uniquely identifying this operation within this interface;
          * works the same way as the code_type and code in aoi_interface.
          */
         aoi_const       request_code;
         aoi_const       reply_code;
 
         aoi_op_flags    flags;
 
         /* Parameters to/from this operation.  */
         aoi_parameter   params<>;
 
         /* Return type, if any. */
         aoi_type        return_type;
 
         /*
          * Exceptions that can be raised by this method.  These must be
          * AOI_INDIRECTS so that we can access the exceptions' names.
          */
         aoi_type        exceps<>;
 };
 
 struct aoi_attribute
 {
         string    name<>;
 
         /* Attribute code/identifier.  */
         aoi_const read_request_code;
         aoi_const read_reply_code;
         aoi_const write_request_code;
         aoi_const write_reply_code;
 
         /* Type of this attribute.  */
         aoi_type  type;
 
         /* Is the attribute readonly */
         bool      readonly;
 };
 
 struct aoi_interface
 {
         /* IDL this interface was defined in - "top-level" interface ID.  */
         aoi_idl_id      idl;
 
         /*
          * `code_type' is the type of the identifying interface, e.g. a
          * char-array (string) for CORBA, a number for MIG, a struct for DCE
          * uuid's, etc.  `code' is the actual code identifying this interface.
          */
         aoi_type        code_type;
         aoi_const       code;
 
         /* Other interfaces this inherits from. */
         aoi_type        parents<>;
 
         /* Type of the identifying code of each attribute/operation.  */
         aoi_type        op_code_type;
 
         /* Operations and attributes accessible through this interface. */
         aoi_operation   ops<>;
         aoi_attribute   attribs<>;
 
         /* Exceptions that can be raised by any method in this interface.  */
         aoi_type        excepts<>;
 };

Note that the idl and excepts slots of an aoi_interface are not currently used for anything useful. (The idl slot is occasionally used to work around certain translation problems in the presentation generator and back end stages of Flick, but all of these cases should be handled in more principled ways.)

Forward Interfaces

An AOI_FWD_INTRFC is essentially a placeholder for an interface or object type that has been named but not yet defined. After the input idl has been parsed, a front end must make a cleanup pass over the aoi in order to attach AOI_FWD_INTRFC definitions to the corresponding "real" AOI_INTERFACE definitions. In some idls (e.g., corba idl), it is an error to forward-declare and interface and then not define it.

Namespaces

An AOI_NAMESPACE describes a namespace in the input idl file(s). These are used, for instance, to represent corba module constructs. At present, AOI_NAMESPACEs are not entirely suitable for describing onc rpc programs because an AOI_NAMESPACE provides nowhere to store the program identifier. See Section 10.3 for details.

Indirect References

An AOI_INDIRECT represents a reference to another aoi_def, i.e., a reference to a name binding. An AOI_INDIRECT is used whenever an idl source construct refers to a named type -- e.g., a previously typedef'ed type -- or to a named constant, or to any other kind of name binding.

The indirect_ref value within an AOI_INDIRECT in the index of the aoi_def that is being referenced. (Remember that all aoi definitions are stored in a single array.)

Errors

Finally, AOI_ERROR is a placeholder used by Flick's front ends to handle idl parse errors in a graceful manner. Instances of AOI_ERROR should never be written to an aoi file.

4.3 The AOI Library

Common functions for creating and manipulating aoi data structures are contained in Flick's aoi library. The source code for the library is in the libaoi directory, and the library header file is mom/libaoi.h. The library functions are summarized below.

aoi_const aoi_new_const(aoi_const_kind kind)
Return an aoi_const that points to a newly allocated aoi_const_u structure initialized with the given aoi constant kind.

In the structure returned by aoi_new_const, any additional fields for describing the constant are uninitialized and must be set by the caller. For this reason, it is often more convenient to call one of the following, more specific constant constructors:

aoi_const aoi_new_const_char(char c)
Return an aoi_const that points to a new aoi_const_u describing the constant character c.
aoi_const aoi_new_const_int(int val)
Return an aoi_const that points to a new aoi_const_u describing the constant integer val.
aoi_const aoi_new_const_float(double val)
Return an aoi_const that points to a new aoi_const_u describing the constant floating-point val.
aoi_const aoi_new_const_string(const char *s)
Return an aoi_const that points to a new aoi_const_u describing the constant string s. Note that a string is represented as an array of characters, without a terminating NUL.
aoi_const aoi_new_const_string_cat(const char *s1, const char *s2)
Return an aoi_const that points to a new aoi_const_u describing the concatenation of s1 and s2.

int aoi_const_eq(aoi_const c1, aoi_const c2)
Returns 1 if the two constants are equal (i.e., structurally identical, representing the same constant value) and 0 if they are not.
void aoi_get_int_size(aoi_integer *ai, int *out_bits, int *out_is_signed)
Determine the number of bits (1, 8, 16, or 32) required to represent the given aoi_integer type. In essence, this function converts a description of an integer range into a bits/signedness representation. The required number of bits is stored in *out_bits and the signedness indicator (zero or one) is stored in *out_is_signed.
void aoi_get_array_len(aoi *a, aoi_array *at, unsigned *min, unsigned *max)
Determine the minimum and maximum lengths of the given aoi_array type. The minimum length is stored in *min and the maximum is stored in *max. (If the lengths are equal, then the array is fixed-length.) Note that the entire aoi structure must be passed to this function, because the length of the array may be an AOI_INDIRECT to another aoi type definition.
aoi_ref aoi_get_parent_scope(aoi *the_aoi, aoi_ref ref)
Return the index (aoi_ref) of the aoi definition that started the scope in which the definition at ref is contained. More simply put, find the definition of the parent scope of ref. If ref refers to a top-level definition (i.e., a definition in scope zero), then the return value is the constant aoi_ref_null.
char *aoi_get_scoped_name(aoi_ref ref, const char *separator)
Compute and return the fully scoped name of the aoi definition referenced by ref, according to the aoi representation of scopes (described in Section 4.2.1). The name components are separated by the separator string.
int aoi_def_find_member(aoi *in_aoi, aoi_def *d, const char *name, aoi_def **out_d, int  *out_memindex)
Search through the given aoi_def d for any members that have the given name. The kinds of definitions that contain named members are AOI_STRUCT, AOI_UNION, AOI_EXCEPTION, AOI_ENUM, and AOI_INTERFACE. In the case of interfaces, parents are also searched for the given name.

If a matching member is found, this function returns true (1) and sets **out_d and *out_memindex to refer to the location of the member. **out_d is set to point to the aoi_def in which the matching member was found; this will always be the same as the original definition except when a member is located in a parent interface. *out_memindex is set to the index of the member in the array of named elements in the definition (e.g., the array of slots in an AOI_STRUCT, or the array of values inside an AOI_ENUM). Because interfaces have two lists of names -- operations and attributes -- *out_memindex may be set specially. If the matching member is an attribute, the *out_memindex is offset by the number of operations in the interface. For example, if there were five operations (at array indices zero through four), a *out_memindex value of six would correspond to the second attribute.

Of course, if no matching member is found, aoi_def_find_member returns false (0).

int aoi_def_has_member(aoi *in_aoi, aoi_def *d, const char *name)
Returns true if the given aoi_def contains a member with the given name. This is a simplified interface to the aoi_def_find_member function described above.
aoi_type aoi_indir(aoi *a, aoi_type t)
Follow a chain of AOI_INDIRECT type definitions, starting at t, to determine the underlying "real" type. Return the located aoi_type (a pointer to the aoi_type_u structure contained in the terminal aoi_def). Note that if t is not an indirect definition, then t is its own underlying type.
aoi_type aoi_indir_1(aoi *a, aoi_type t)
Follow a single level of an AOI_INDIRECT chain; in other words, "dereference" t and return the referenced aoi_type. If t is not an indirect definition, then this function simply returns t.
aoi_ref aoi_deref_fwd(aoi *a, aoi_ref fwd)
Locate and return the index of the AOI_INTERFACE definition corresponding to the AOI_FWD_INTRFC definition at aoi index fwd. If the "real" interface definition cannot be found, this function returns aoi_ref_null.

(If fwd refers to an actual AOI_INTERFACE, then fwd is returned. If fwd refers to a non-interface type, then aoi_ref_null is returned.)

This function is primarily useful for front ends only. Other stages of compilation can simply refer to the aoi_ref contained within an AOI_FWD_INTRFC definition in order to find the actual definition of an interface.

void aoi_check(aoi *dest)
Check the validity of the given aoi structure. At present, this is largely unimplemented.
void aoi_readfh(aoi *dest, FILE *fh)
Read an aoi structure from fh into dest. (The disk data is stored in xdr format, and the actual I/O is performed by functions that are generated by rpcgen from the mom/aoi.x file.) If input fails for some reason, this function invokes panic, which causes the process to print an error message and exit.
void aoi_writefh(aoi *dest, FILE *fh)
Write the given aoi structure to fh. (As before, the data in the file is stored in xdr format, and the actual I/O is performed by rpcgen-generated functions.) This function invokes aoi_check to verify the validity of the aoi data before writing the data to disk. If output fails for some reason, this function calls panic.
void translate_aoi_to_mint()
Convert a set of aoi definitions into an equivalent mint (Message Interface) representation. mint is the intermediate language for describing, in an abstract way, the messages that will be exchanged between clients and servers. Chapter 5 describes mint in detail, and Chapter 11 describes Flick's presentation generators, which invoke the aoi-to-mint translator.

4.4  aoid, the AOI Printing Utility

The aoid program is a simple pretty-printer that takes aoi input from a file or standard input and outputs a human-readable, ASCII translation. If the aoi input comes from a file, the output is a corresponding `.aod' (aoi dump) file. Otherwise, if the aoi input comes from stdin, the human-readable output is sent to stdout. Note that as the aoi format changes, this program must also be changed to support any new constructs, or modifications to existing ones.

4.5 Summary and Comments

Because aoi has a relatively simple purpose, the aoi format has been quite stable. When features are added, it is generally to support new (or previously unsupported) idl constructs: only rarely has aoi been changed in order to support the needs of presentation generators. Still, there are several ideas for improving aoi and its library, principally to support Flick's later stages of compilation:

Uniform Handling of Names.
Not all name bindings are represented as aoi_def structures. Most notably, operation and attribute names are stored within an interface type, not within the top-level aoi_def array. The same problem applies to structure, union, enumeration, and exception members. The non-uniform handling of names makes it more difficult to locate and refer to certain entities; aoi would likely be improved if all names were handled in the same way.
Multiple Names.
Some idls provide multiple names for entities. For example, in onc rpc idl, a program has both a name and a code. onc rpc versions and operations also have both names and codes. These cases are handled specially in aoi, but in general, aoi should be designed to support multiple identifiers (in multiple identifier spaces) for all nameable objects. This would be useful not only for onc rpc idl, but also for corba idl (which has both idl-given names and "repository identifiers") and mig idl (in which a subsystem has both a name and a "message base identifier").

Note that if aoi were improved in this way, then mint would need to be extended in a similar way. This would in fact solve an outstanding problem with mint: namely, that the top levels of the mint tree are too specific. Inter- face and operation identifiers in mint are too specific, are singular, and are chosen by the presentation generator. This makes it difficult for back ends to construct on-the-wire message identifiers appropriate to their message formats.4 This problem could largely be solved by allowing aoi to keep multiple names, and extending mint to preserve the names as "hints" to the presentation generator and back end.

Annotations.
Experience has shown that aoi's "purity" is practically inconvenient. It is useful and necessary to distinguish interface specification from presentation-level "cruft" when designing and implementing a flexible idl compiler such as Flick. However, the decision to exclude all presentation-level details from aoi -- and to represent only "pure" interface specifications -- means that idls with annotations cannot be supported.

An extended aoi format with support for annotations would be useful for a number of interesting projects. Immediately, it would make it possible for one to rearchitect Flick's mig front end and presentation generator to use aoi as an intermediate format. Later, an annotatable aoi would make it easier for people to experiment with annotated idl specifications, e.g., specifications with embedded quality-of-service constraints).

AOI_INTEGER Versus AOI_SCALAR.
Obviously, these two types should be unified in some way.
Using rpcgen.
See Section 2.1 for general comments about using onc rpc idl to define Flick's intermediate languages. The aoi library in particular needs more functions that abstract the underlying aoi data structures. (Some of these functions already exist in the individual front ends, and should be relocated into libaoi.)