Thoughts and Rants
The GNU Project
The Mozart framework is object oriented. For example, there are classes representing all the various items in a program, the Coda classes. Each declaration in the program being manipulated is an instance of CodaDeclaration. Such objects are being exchanged between the various thin tools which cooperate to manipulate the program.
Why a new object model?
The Mozart object model, that is the semantics and behavior of the Mozart objects, has specific requirements:
- Multiple tools need to share objects. For instance, when you ask Moka to invoke a plug-in on your source, the complete tree for your program changes hands. Each tool must be able to "scribble" annotations on the various objects.
- A given tool may need to store program-related information between sessions, just like a compiler stores object files.
- Tools may need to add operations (virtual functions in C++ speak) or classes. For instance, Moka adds support for Java synchronized methods, which is not part of the Coda classes. The derivation plug-in adds differentiation methods.
- The ownership of objects is difficult to establish. Many types of node can point to a CodaDeclaration, so who is responsible for destroying it?
These requirements translate into the following implementation and design choices:
- Individual objects must be extensible (that's the portion where you "scribble"), yet be recognizable individually by the various tools (so they can't arbitrarily change class).
- There must be a persistent form for the objects which can be stored to disk or other persistent storage. Nice to have: platform-independant and relatively compact.
- The set of operations on a class (the "methods") cannot be defined in a single place (like they are in a "class" in C++ or Java). Any tool must be able to add its own methods. Similarly, a tool needs to be able to add classes to the system and have the other components deal with these new classes gracefully.
- There must be a garbage collector. Only the Mozart system knows when there is no tool left referencing a particular object, the individual tools can't know that.
Unfortunately, the existing object models, notably the C++ or Java implementations of objects, do not qualify. For having tried before, I learned that an appropriate object model was important for the success of the project.
All Mozart objects derive from the Note C++ class. The Note class contains two fields that are known to the Mozart system: type information in the form of a Tone pointer (equivalent of the vptr in C++) and an extension pointer to an arbitrary number of extension objects, named a "chord".
The meta-class pointed to by the class pointer contains a description of the class and the associated "virtual" functions, called Performers. This is "dynamic" information, not static information. There are calls to insert functions or classes that will recompute the corresponding tables. This is what makes it possible to add either classes or functions at run-time. In short, Mozart implements dynamic dynamic dispatch, that is dynamic dispatch that can dynamically be updated.
The chord pointer is initially NULL, but various named entities can be stored in it. Accessing any object in the chord is slower, and chord objects also require more memory, so this is used only to "scribble" on the object.
Mozart contains a mark-and-sweep garbage collector. It is possible to put the garbage collector on hold while you are putting the tree in inconsistent state. The roots of the garbage collector must be explicitly marked, using the GCRoot class.
Mozart also defines the Melody persistent file format for Notes. A Melody file contains a representation of a complete Note tree, including all the Notes it references. In particular, it contains type-related information (the Tones) which makes it possible for other tools to process the data in a generic way even if they don't know about the particular class.
The Melody representation is compressed on the fly, resulting in relatively compact files. It is also platform independant, both with respect to endianness and with respect to word size. Thus, a Melody file written on an x86 32-bit little-endian system can safely be read on an Itanium 64-bit big-endian system.
Below is an example of tone, performer, and performer function to
implement a "synchronized if".
tone SyncIf : CodaIf
SyncIf (CodaExpression *condition,
StructTone *t = &MyOwnIf_T)
: CodaIf(condition, then_clause, else_clause, t),
ref CodaExpression * synchronized_on;
// Create a new performer
Note * performer(ConvertIf)(Note *n);
Note * perform(ConvertIf, Note) (Note *n)
CodaIf *perform(ConvertIf, SyncIf) (SyncIf *n)
return new MokaSynchronized(
new CodaIf(n->condition, n->then_do, n->else_do));
// Modify an existing performer
Note *perform(Expand, SyncIf)(SyncIf *n)
// First convert to normal if statement
// Dynamic dispatch on n, followed by arguments to the performer
Note *n = ConvertIf(n)(n);
// Then perform expansion on the note
Copyright Christophe de Dinechin
First published Feb 17, 2000
Version 1.3 (updated 2004/01/20 06:16:14)