|
|
# Design Regularities in the Interpreter Implementation
|
|
|
|
|
|
This page lists the regularities and conventions of the interpreter implementation. A more detailed (but also incomplete) overview of the design of the AmbientTalk interpreter can also be found in the [design document](http://ambienttalk.googlecode.com/files/at2design.pdf) in the [Downloads](http://code.google.com/p/ambienttalk/downloads/list) section.
|
|
|
|
|
|
# Naming conventions
|
|
|
|
|
|
- all subclasses of NativeAmbientTalkObject should start with NAT (except classes prefixed with Java- in the symbiosis package)
|
|
|
- all subclasses of NATAbstractGrammar should start with AG
|
|
|
- all subclasses of InterpreterException should start with X
|
|
|
- all subinterfaces of ATObject should start with AT
|
|
|
- the names of Singleton Java classes representing special AmbientTalk objects should be prefixed with 'OBJ'
|
|
|
- all subclasses of EventLoop should start with EL
|
|
|
- all subclasses of VMCommand should start with CMD
|
|
|
|
|
|
# Abstract Syntax Tree nodes
|
|
|
|
|
|
All subclasses of NATAbstractGrammar are considered abstract syntax tree nodes. These classes should override and meaningfully implement meta_eval, meta_print and meta_quote.
|
|
|
|
|
|
# AmbientTalk objects
|
|
|
|
|
|
## Native AmbientTalk Objects
|
|
|
|
|
|
All subclasses of NativeATObject are considered native AmbientTalk objects. By convention, all methods prefixed with `base_` are considered to be 'primitive methods' of the native object, which may be invoked from within AmbientTalk. All methods prefixed with `meta_` are considered to be primitive methods defined on the *mirror* of the native AmbientTalk object.
|
|
|
|
|
|
To be a primitive AmbientTalk method, a Java method must:
|
|
|
- be prefixed with `base_` or `meta_`
|
|
|
- be declared in a Java interface extending the ATObject interface
|
|
|
- declare its formal parameters to be subtypes of ATObject
|
|
|
- declare its return type to be a subtype of ATObject
|
|
|
- list InterpreterException in its throws clause
|
|
|
|
|
|
Cookbook for adding a new native AmbientTalk object:
|
|
|
1. define a new interface prefixed with AT that defines the public interface to your object in terms of `base_/meta_` methods.
|
|
|
1. define a new class prefixed with NAT that inherits from NATByCopy or NATByRef (depending on whether the native object is serializable or not) and implement the AT... interface.
|
|
|
1. override meta_print to provide a printed representation for your native object.
|
|
|
|
|
|
## Non-native AmbientTalk Objects
|
|
|
|
|
|
The class NATObject defines the core structure of AmbientTalk's "user-defined" or "programmer-defined" objects. Subclasses of this class are not considered "native" AmbientTalk objects, but simply first-class AmbientTalk objects, even though these first-class AmbientTalk objects may introduce new primitive methods.
|
|
|
|
|
|
The root of the lexical parent chain of an AmbientTalk object is the singleton instance of the class OBJLexicalRoot. The root of the dynamic parent chain of an object is the thread-local instance of class NATNil.
|
|
|
|
|
|
# The Meta-object Protocol
|
|
|
|
|
|
The reflective architecture of AmbientTalk is implemented by the classes NATMirage, NATIntrospectiveMirror and NATMirrorRoot. NATMirage instances represent mirage objects (i.e. AmbientTalk objects with a custom implicit mirror). NATIntrospectiveMirror instances represent "native" mirrors on non-mirage objects. Finally, NATMirrorRoot is the implementation of the 'defaultMirror' object.
|
|
|
|
|
|
NATMirage and NATMirrorRoot depend upon one another as follows:
|
|
|
- for all `meta_` methods in ATObject, NATMirage should implement an equivalent method prefixed with `magic_`. The purpose of NATMirage's `magic_` methods is to implement the 'default' MOP implementation. They do so simply by invoking the implementation of NATMirage's superclass, NATObject.
|
|
|
- for all `meta_` methods in ATObject, NATMirrorRoot should implement an equivalent method named `base_`. This reflects the fact that the entire meta-level API encoded by means of ATObject's `meta_` methods in the implementation becomes accessible at the base-level by means of the 'defaultMirror' object.
|
|
|
- for all `meta_` methods in ATObject, NATMirage must override the `meta_` methods with "forwarding methods" that reify the implicit Java call of the `meta_` method into an explicit AmbientTalk call on the mirage's implicit mirror.
|
|
|
|
|
|
This design gives us a cookbook for adding a meta-level operation to the interpreter. To add a MOP method:
|
|
|
1. add the method to the ATObject interface.
|
|
|
1. add a default implementation for all native AmbientTalk objects to NativeATObject (and optionally a specialized implementation in certain subclasses, e.g. in NATObject).
|
|
|
1. add a `base_` equivalent of the method to ATMirrorRoot. Make sure to document this method: the documentation is included in the language reference.
|
|
|
1. implement the `base_` method in NATMirrorRoot by invoking the mirage's `magic_` variant.
|
|
|
1. add the `magic_` variant encoding the default behavior to NATMirage
|
|
|
1. override the `meta_` method to reify the call and forward it to the mirage's mirror.
|
|
|
|
|
|
# Typecasting protocol
|
|
|
|
|
|
The AmbientTalk interpreter implementation avoids the use of type casts and instanceof tests to convert and test the types of AmbientTalk objects. Rather:
|
|
|
- Every NATType class should override the method named isType returning true and the method asType returning this (our hand-made alternative to directly using Java type casts). I.e. there should not be 'obj instanceof NAT...' tests in our code or '(NAT...) obj')
|
|
|
- No code in the implementation should directly use a Java type cast to cast an ATObject into a NAT... class. Instead, conversion from ATObject to a NAT... class must be performed via the isType/asType methods
|
|
|
- NATObject should override most isType/asType methods to return a 'coercer' object matching the type.
|
|
|
|
|
|
# Cloning protocol
|
|
|
|
|
|
Every subclass of NATObject that introduces additional non-static Java state and which does not override meta_clone from NATObject should override the protected `createClone` method by returning a new instance of the specific subclass. This requires the subclass to have a special 'copy constructor' which does nothing but re-initializing all state based on the cloned object's state. For an example, see the class NATNamespace.
|
|
|
|
|
|
# Concurrency
|
|
|
|
|
|
In AmbientTalk, *all* concurrent activities should be encapsulated within event loops, even at the implementation level. The virtual machine itself is also an event loop (cf. class ELVirtualMachine).
|
|
|
|
|
|
An event loop (and thus also an actor) is represented at the implementation level as a subclass of EventLoop, and thus prefixed with EL. Normally, an event loop cannot simply be made to execute arbitrary code. Instead, each event loop can accept a specific set of events. These events are represented as methods on subclasses of EventLoop. We distinguish two kinds of events:
|
|
|
|
|
|
Asynchronous events are methods that:
|
|
|
- are prefixed with `event_`.
|
|
|
- invoke `EventLoop.receive`, to schedule a piece of code for execution in the event loop.
|
|
|
|
|
|
Synchronous events are methods that:
|
|
|
- are prefixed with `sync_event_`.
|
|
|
- invoke `EventLoop.receiveAndWait`, to schedule a piece of code for execution in the event loop and block the calling thread until that piece of code has been executed.
|
|
|
|
|
|
An asynchronous event notification is performed by invoking an asynchronous event method. These methods normally return void and schedule code for execution in the receiver event loop. Event loops normally communicate strictly by means of asynchronous events. Synchronous events should usually only be signalled by non-event loop threads.
|
|
|
|
|
|
To further ensure thread-safety, event loops should not share mutable state. Therefore, all static class field values in classes shared by multiple event loops must either be immutable or be made thread-safe.
|
|
|
|
|
|
# Selector not found exceptions
|
|
|
|
|
|
When catching an XSelectorNotFound exception, often, the exception should *only* be caught if the selector not being found matches a certain known selector. Hence, a `try { ... } catch(XSelectorNotFound e) { ... `} handler should begin with a call to `e.catchOnlyIfSelectorEquals(symbol)` as follows:
|
|
|
|
|
|
```
|
|
|
try {
|
|
|
obj.meta_select(name);
|
|
|
} catch(XSelectorNotFound e) {
|
|
|
e.catchOnlyIfSelectorEquals(name);
|
|
|
// handle exception
|
|
|
}
|
|
|
```
|
|
|
|
|
|
# Type tag protocol
|
|
|
|
|
|
- all NAT... classes should override meta_typeTags to return a table including `NativeTypeTags.*CLASSNAME*`
|
|
|
- NativeTypeTags should define a `*CLASSNAME*` field for every distinct ADT (every AT... interface) in the interpreter.
|
|
|
|
|
|
# Coding conventions
|
|
|
|
|
|
- fields have a trailing `*` to distinguish them from local variables
|
|
|
- every field in a NAT... class that can be made final should be made final
|
|
|
- every NAT... class without children should be made final
|
|
|
- all `base*` and `meta_` methods should have a Javadoc comment (used to generate the language reference) |