Section 12.1. WHAT IS REFLECTION?


12.1. WHAT IS REFLECTION?

Reflection is the ability of a system to observe and change its own execution [29, 36]. A programming language is said to be reflective if it provides an explicit representation (i.e., reification) of entities that either represent program building blocks (e.g., classes, methods) or are involved in program execution (e.g., stack, garbage collector). Thus, using a reflective language, developers can not only define system (i.e., software) functionalities, but can also define new program building blocks or execution mechanisms (i.e., define how functionalities will be performed). Put another way, developers can not only write the program, but can also extend the interpreter.

12.1.1. Base-Level Versus Meta-Level

Separation between functionalities and execution mechanisms leads to a system with different levels (i.e., layers[2]). On one hand, system functionalities are defined on the base-level (e.g., deposits and withdrawals in a banking system). On the other hand, reified entities such as system's building blocks (e.g., fields, methods) and execution mechanisms (e.g., message handling, process scheduling) are located on the meta-level. The meta-level can be viewed as an interpreter that evaluates the base-level. This layering allows some separation of concerns and hence provides a first support for AOP, as we will discuss in Section 12.2.

[2] In the context of reflective systems, people prefer the term "level" instead of "layer."

As shown in Figure 12-1, a reflective system can have more than two levels. After all, the meta-level itself is part of the system. Since a reflective system can reason and act on itself, then the system can reason and act on the meta-level. Thus, the meta-level can be viewed as a program that is interpreted by some meta-meta-level. The meta-meta-level is also part of the system, so we need an extra level, and so on.

Figure 12-1. Levels of a reflective system.


Although the tower of levels can be arbitrarily high, the number of levels should always be finitewe require that the system be able to perform its tasks within afinite amount of time and memory. Thus, reflective systems rely on the existence of some default interpreter that acts as the topmost level. The evaluation of this topmost level is hardwired so as to keep the tower of meta-levels finite.

Note that the relationship between the base-level and the meta-level can be translated along the reflective tower. For any pair of contiguous levels, the higher will be the "meta-level" of the lower. Thus, we can simplify the remainder of this section by focusing only on the two first levels (base-level and meta-level).

12.1.2. Meta-Objects and Their Protocols

In the context of object-oriented reflective systems, both the base-level and the meta-level consist of collections of objects. Objects that define program functionalities are called base-level objects (base objects for short). Objects defining program building blocks or execution mechanisms are called meta-level objects (meta-objects for short).

APIs (Application Programming Interfaces) that manipulate meta-objects are called Meta-Object Protocols (MOPs) [24]. MOPs allow you to access program structures such as class, inheritance relationships, methods, and fields defined within classes. They also provide access to execution mechanisms. Object creation, message sending and receptions, method lookup and evaluation, and field reading and writing are all examples of execution mechanisms. Section 12.2 shows that by providing access to execution mechanisms, MOPs give access to join points.

Since meta-objects are objects, they are instances of some classes that define fields and methods to properly handle these execution mechanisms. Using inheritance, you can build new meta-object classes that extend the execution mechanisms. You can even define meta-objects with completely different semantics (e.g., single vs. multiple inheritance).

12.1.3. Meta-Link and Meta-Object Cooperation

Abstraction levels of a reflective system introduce a new kind of relationship between objects: base objects and meta-objects are connected through a meta-link. Functionalities provided by each base object are then executed using the execution mechanisms defined in that base object's meta-object. Thus, a meta-object controls its linked base object. For example, consider a meta-object that extends the default message reception action with a logging mechanism. Linking any object to the "log meta-object" then causes its messages to be logged.

In the previous example, we linked a single base object to one meta-object. However, other alternatives exist [19, 29, 30, 34, 41, 43]. A single base object can be controlled by several meta-objects, and a single meta-object can be shared among several base objects. When many meta-objects control the same base object, they have to cooperate somehow in order to handle the execution. Often, the cooperation policy is not hardwired, so it can be extended and adapted like most of a system's other features.

Figure 12-2 shows four objects (o1 to o4) that are linked to different meta-objects (mo1 to mo5). The mo1 meta-object controls the o1 object. Three meta-objects (mo2, mo4, and mo5) cooperate in order to control the o2 object.

Figure 12-2. Base versus meta-objects and their relationships.


Meta-objects mo3 and mo5 cooperate in order to control objects o3 and o4. Thus, o3 and o4 share meta-objects mo3 and mo5. Meta-object mo5 is also shared with object o2.

12.1.4. Example of a Reflective Programming Language and its MOP

Our example is a running reflective programming language named MetaclassTalk [2, 3]. MetaclassTalk is a reflective extension of Smalltalk [18, 21, 23] for simplifying experiments with new programming paradigms. Smalltalk has been chosen to implement MetaclassTalk, because it provides many reflective facilities [35]. For example, classes, methods, and the execution stack are reified and can be handled like plain objects.

Although Smalltalk has many reflective facilities, they provide little help for changing the execution mechanisms. MetaclassTalk addresses this weakness by opening up the execution process. For example, MetaclassTalk allows changing the message dispatch schema, memory allocation, and the inheritance policy.

12.1.4.1 The MetaclassTalk MOP

MetaclassTalk provides control of most execution mechanisms of a reflective OO language. The methods defined by the root class for meta-objects include

send: methodName from: sender to: receiver arguments: argArray superSend: superFlag originClass: originClass

controls message sends (i.e., outgoing messages). Its arguments are:

  • methodName. The name of the method to invoke. Because we use Smalltalk, this name is also the signature of the method.[3]

    [3] Smalltalk is dynamically typed, and the name of a method reflects the number of its arguments. A Smalltalk method name is a compound of many words separated by colons. The number of colons is equal to the number of method arguments.

  • sender. The object that emits the message.

  • receiver. The object that should receive the message.

  • argArray. An array with message parameters.

  • superFlag. Is true if the message is sent to super.

  • originClass. The class where the message is emitted. This is useful for super sends since it makes it possible to find out the starting class for method lookup.

receive: methodName from: sender to: receiver arguments: argArray superSend: superFlag originClass: originClass

controls message receptions (i.e. incoming messages). It takes the same arguments as the previous method. The only difference is that it is performed by the meta-object of the receiver of the message.

atIV: fieldIndex of: anObject

controls field (instance variable in Smalltalk jargon) read access. This method takes two arguments:

  • fieldIndex. The index (within the array representing the object structure) of the field to access.

  • anObject. The object that holds the field.

atIV: fieldIndex of: anObject put: value

controls fields write accesses. The last parameter denotes the value to store.

This list is only part of the actual MetaclassTalk MOP. Other reflective facilities are also available, including mechanisms for memory allocation for created objects, method lookup, and evaluation.

12.1.4.2 Meta-Link and Meta-Object Cooperation in MetaclassTalk

MetaclassTalk makes it possible to implement a variety of relationships between base objects and meta-objects. At this time, three policies have been implemented:

  • A single meta-object shared between instances of a same class

  • A single specific meta-object private to each base object

  • Many meta-objects shared among many base objects

In the following section, when many meta-objects cooperate to control one or many base objects, we use a simple cooperation rule that relies on the "chain of responsibility" design pattern [17]. All meta-objects that control the same base object are arranged into a single chain [31]. The head of the chain is the meta-object that is actually linked to the base object. Whenever this meta-object takes control of some base object activity (e.g., handling messages received by the base object), it first performs its meta-processing and then forwards information about the activity to control to the next meta-object on the chain. Once the last meta-object of the chain performs its meta-processing, the flow of control goes back through the chain. This cooperation scheme requires the explicit cooperation of the meta-objects [31].

12.1.4.3 Example: Logging

Suppose we have a banking application, and we want to log the activity of a specific account (e.g., deposits, withdrawals). First, we need to build a class of meta-objects that perform logging, as shown in Figure 12-3.

Figure 12-3. Logging meta-object class.


The LogMetaObject class defines a field named logStream, which references the stream where logs are written (e.g., a stream on some log file). LogMetaObject also defines a method for setting the stream and redefines the method for handling received messages. This method simply prints the message signature into the log stream. Then the default behavior for reception handling is performed.

In order to log the behavior of a given account, we need to use a "log" meta-object. Listing 12-1 shows code that:

  • Declares two temporary variables myAccount and myMeta (line 1).

  • Creates a new account and references it using myAccount (line 2).

  • Creates a new log meta-object and stores it in myMeta (line 3).

  • Sets up the stream where logs should be stored (line 4).

  • Links myAccount to myMeta (line 5). As suggested by the protocol used for this linking (addMetaObject:), a base object can be linked to many meta-objects. Those meta-objects cooperate in order to control the behavior of the base object. Of course, some (or all) of these meta-objects can be shared with another base object.

Listing 12-1. Setting up a specific meta-behavior for a base object
 1. | myAccount myMeta | 2. myAccount := BankAccount new. 3. myMeta := LogMetaObject new. 4. myMeta logStream: (WriteStream...) 5. myAccount addMetaObject: myMeta. 

Once myAccount is linked to the MyMeta log meta-object, its activity gets logged. So, whenever the myAccount receives the deposit: 100 message, its meta-object (myMeta) takes control. Concretely, an implicit message is sent to myMeta as shown in Figure 12-4. As a result, first myMeta performs the logging, and then it evaluates the deposit: 100 message.

Figure 12-4. Handling the reception of the deposit: message.


In this example, among all instances of the BankAccount, only the one referenced by myAccount is logged. Now, imagine that we want to log every instance of BankAccount, including ones that are not created yet. This can be done by sending the addMetaObjectClass: message to the BankAccount class as BankAccount addMetaObjectClass: LogMetaObject.

This message makes the BankAccount class link every new instance with a new meta-object instance of LogMetaObject at initialization. Thus, logs of every new account go into a specific stream.



Aspect-Oriented Software Development
Aspect-Oriented Software Development with Use Cases
ISBN: 0321268881
EAN: 2147483647
Year: 2003
Pages: 307

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net