7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 1/26
A Generic Reification Technique for
Object-Oriented Reflective Languages
Rémi Douence , Mario Südholt
École des Mines de Nantes
Département Informatique, 44307 Nantes cedex 3, Francehttp://www.emn.fr/{douence, sudholt}
Abstract
Computational reflection is gaining interest in practical applications as witnessed by the useof reflection in the JAVA programming environment and recent work on reflective middleware.Reflective systems offer many different reflection programming interfaces, the so-called Meta-Object Protocols (MOPs). Their design is subject to a number of constraints relating to, amongothers, expressive power, efficiency and security properties. Since these constraints are differentfrom one application to another, we should be able to easily provide specially-tailored MOPs.
In this paper, we present a generic reification technique based on program transformation.It enables the selective reification of arbitrary parts of object-oriented metacircular interpreters.The program transformation can be applied to different interpreter definitions. Each resulting
reflective implementation provides a different MOP directly derived from the original interpreterdefinition.
Keywords: reflection, OO languages, program transformation, language implementation
1 Introduction
Computational reflection, that is, the possibility of a software system to inspect and modify itself
at runtime, is gaining interest in practical applications: modern software frequently requires strong
adaptability conditions to be met in order to fit a heterogenous and evolving computing environment.
Reflection allows, for instance, host services to be determined dynamically and enables the modifica-
tion of interaction protocols at runtime. Concretely, the JAVA programming environment [java] relies
heavily on the use of reflection for the implementation of the JAVABEANS component model and its
remote method invocation mechanism. Furthermore, adaptability is a prime requirement of middle-
ware systems and several groups are therefore doing research on reflective middleware [coi99][bc00].
Reflective systems offer many different reflection programming interfaces, the so-called Meta-
Object Protocols (MOPs)1. The design of such a MOP is subject to a number of constraints relating
to, among others, expressive power, efficiency and security properties. For instance, using reflection¡
Extended version. c¢
2001 Kluwer Academic Publishers. “Higher-Order and Symbolic Computation”, 14(1),
2001, to appear.1We use the term “MOP” in the sense of Kiczales et al. [kic91] (page 1): “Metaobject protocols are interfaces to the
language that give users the ability to incrementally modify the language’s behavior and implementation, as well as the
ability to write programs within the language.”
1
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 2/26
for debugging purposes may require the MOP to provide access to the execution stack. However,
because of security concerns stack access must frequently be restricted: in JAVA, for example, it is not
allowed to modify the (untyped) stack because security properties essentially rely on type information.Since these constraints are different from one application to another, we should be able to provide
a specially-tailored MOP for a particular set of constraints. Moreover, the constraints may change dur-
ing the overall software life cycle. Hence, the development of such specially-tailored MOPs should
be a lightweight process. Traditional approaches to the development of MOPs do not meet this goal
instead each of them only provides a specific MOP which can hardly be modified (see the discussion
of related work in Section 9). Consider, for instance, a single-processor application which is to be
distributed. In this case, distinct tasks have to be performed on the message sending side and the
receiving side: for example, on the sender side local calls are replaced by remote ones (instead of
relying on proxies) and on the receiver side incoming messages can be synchronized. Many existing
MOPs do not allow the behavior of message senders to be modified. Hence, such a distribution strat-
egy cannot be implemented using reflection in these systems. Some systems (see, for instance COD
A[aff95]) provide access to senders right from the start. Therefore, they can introduce an overhead for
local applications.
In this paper, we present a reification mechanism for object-oriented interpreters based on program
transformation techniques. We use a generic transformation which can be applied at compile time to
any class of a non-reflective interpreter definition. This mechanism can be used to transform different
subsets of a metacircular interpreter in order to generate increasingly reflective interpreters. It can
also be applied to different interpreter definitions in order to automatically get different reflective
interpreters. Each resulting reflective implementation provides a different MOP directly derived from
the original interpreter definition.
The paper is structured as follows: in Section 2, we briefly introduce Smith’s seminal reflective
towers upon which our work is based and we sketch the architecture of our transformational system.Section 3 provides an overview of a metacircular interpreter for JAVA. Our generic reification tech-
nique is formally defined and its application to the non-reflective interpreter is exemplified in Section
4. Section 5 is devoted to reflective programming: it details our reification technique at work by
presenting several applications. Section 6 complements Section 4 by presenting a few technicalities
postponed for the sake of readability. Section 7 discusses the correctness of the transformation and
sketches a formal correctness proof. Section 8 illustrates how a refined definition of the non-reflective
interpreter produces a more expressive reflective interpreter. Section 9 discusses related work. Fi-
nally, Section 10 concludes and discusses future work. Code occuring in the paper refers to a freely
available prototype implementation, called METAJ [metaj], which enables execution of the reflective
programming examples we present and provides a platform for experimentation with our technique.
2 Overview of the reification process
In our opinion, Smith’ definition of reflection [smi84] remains a key reference because of its clean
semantic foundation and generality. This paper proposes one method to transpose his technique into
the domain of object-oriented languages. In this section, we first introduce Smith-like reflection before
presenting the architecture of our reification method.
2
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 3/26
Level 0
Level 1
Level 2
Level 3
Interpreter
Program Program
Interpreter
Interpreter Interpreter
Interpreter’
Program
Interpreter
Interpreter’’
Interpreter’
Program
Figure 1: Smith-like reflective towers
2.1 Smith-like Reflection
Smith’s seminal work on reflective 3-Lisp defines reflection with the notion of reflective towers. In
Figure 1, the left hand side tower shows a user-written (i.e. level 0) Program in a double-square boxand its Interpreter, which defines its operational semantics. A simple classic example of reflective pro-
gramming deals with the introduction of debugging traces. Trace generation requires the interpreter
to be modified, that is, two steps have to be performed at runtime: provide an accessible represen-
tation of the current interpreter and change this representation. Such a computation creates an extra
interpretation layer by means of a reification operator “reify,” so that the level 1 Interpreter becomes
now part of the program: in the illustration, it is included in the double square box. We get a second
tower with three levels. The Program can now modify the standard semantics of the language defined
by the level 1 Interpreter to get Interpreter ’ which generates traces during execution (see the third
tower). Finally, when a non-standard semantics Interpreter” of Interpreter ’ is required, a further extra
interpretation level can be introduced as illustrated by the fourth tower. The fourth tower would be
required, for example, to trace Interpreter’.On a more abstract level, Smith’s reflection model — as well as our reification technique — has
two essential properties: there is a potentially infinite tower of reflective interpreters and the interpreter
at level interprets the actual code of the interpreter at level ¢ ¡ £ .
2.2 Making object-oriented interpreters reflective
In order to get a first intuition of our reification technique, consider the following simple example of
how we intend reflection to be used: color information (represented by the class Color) should be
added to pairs at runtime. Using reflection, we could dynamically modify the inheritance graph such
that Pair inherits from Color. This can be achieved by
// Pair extends Object( ¥ Pair).extendsLink = Color;
// Pair extends Color
where¥
is a reification operator. The application of the reification operator to an expression yields
an accessible representation of the value denoted by the expression. In this example, the expression
Pair denotes the corresponding Class object, say c, in the interpreter’s memory (see Figure 7).
¥ Pair returns an instance (say i), i.e. an object of type Instance, in the interpreter’s memory
which represents c and which can be inspected and modified. The default superclass of Pair is
replaced by Color by assigning the field extendsLink. From now on, newly instantiated pairs
contain color information.
It is crucial to our approach that the reified representation i is based on the definition of c. This
3
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 4/26
...
...
Parser
Java.jjt
Runtime System
ExpAssign.java
Reflective Interpreter
Reflective_Prog.java
Java2ExpVisitor.java
...
Parser
Java.jjt
Runtime System
ExpAssign.java
Non-Reflective Interpreter
Prog.java
...
Java2ExpVisitor.java
Instance.java
BaseClass.java
Class.java
Class.java
Instance.java
generation of reflective interpreter
pr o gr a m t r a n s f or m a t i on
Class.java
Figure 2: System architecture
is achieved by the system architecture shown in Figure 2. Our non-reflective JAVA interpreter (repre-
sented by the box at the top) takes a non-reflective program Prog.java as input. This program is
parsed into a syntax tree and evaluated. According to the required reflective capabilities, the language
designer2 transforms a subset of the classes of the non-reflective interpreter. Basically, this transfor-
mation generates two classes for each original class. In our example, the file Class.java, which
represents classes in the non-reflective interpreter, becomes BaseClass.java and a different ver-
sion of Class.java in the reflective one.
The reflective interpreter relies on the non-reflective interpreter in order to build levels of the
reflective tower. This is the core issue of our approach: the tower levels shown in Figure 1 are
effectively built at runtime on the basis of the (verbatim) definition of the non-reflective interpreter as
in Smith’s model. This is why the original definition of Class.java is an input of the reflective
interpreter in Figure 2. So, the behavior of the reflective interpreter is derived from the non-reflective
one. Furthermore, our approach is selective and complete because the transformation is applicable to
any class of the non-reflective interpreter definition.
METAJ implements one version of this system architecture . Its parser has been implemented
by means of JAVACC and JJTREE (versions 0.8pre2 and 0.3pre6, respectively). METAJ itself is
operational with the JDK versions 1.1.6 and 1.2.
3 A simple non-reflective interpreter
We have implemented a non-reflective metacircular interpreter for a subset of JAVA, which provides
support for all essential object-oriented and imperative features, such as classes, objects, fields, meth-
ods, local variables and assignment statements. (We did not implement features such as some primitive
2Note that building a reflective interpreter by transformation and writing reflective programs are two different tasks: the
former is performed by language designers and the latter by application programmers.
4
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 5/26
class ExpId extends Exp {
private String id;
ExpId(String id) { this.id = id; }
Data eval(Environment localE) {return localE.lookup(this.id);
}
}
Figure 3: Class ExpId
class ExpAssign extends Exp {
private Exp lhs;
private Exp rhs;
ExpAssign(Exp lhs, Exp rhs) {
this.lhs = lhs; this.rhs = rhs;
}
Data eval(Environment localE) {
Data d1 = this.rhs.eval(localE);
Data d2 = this.lhs.eval(localE);
d2.write(d1.read());
return d2;
}
}
Figure 4: Class ExpAssign
types or loop constructs; all of these could be integrated and reified similarly.)
JAVA programs are represented as abstract syntax trees the nodes of which denote JAVA’s syn-
tactic constructs and are implemented by corresponding classes. For example, variables, assignment
statement, method call, and class instantiation expressions are respectively encoded by the classes
ExpId, ExpAssign, ExpMethod and ExpNew. All of these classes define an evaluation method
Data eval(Environment localE) that takes the values of local variables in localE and
returns the value of the expression (wrapped in a Data object).
In particular, ExpId (see Figure 3) holds the name of a variable and its evaluation method yields
the value currently associated to the variable in the local environment. An ExpAssign node (see
Figure 4) stores the two subexpressions of an assignment. Its evaluation method evaluates the location
of the right-hand side expression, followed by the value represented by the left-hand side expression
and finally performs the assignment. ExpMethod (see Figure 5) represents a method call with a re-
ceiver expression (exp), a method name (methodId) and its argument expressions (args). Method
call evaluation proceeds by evaluating the receiver, constructing an environment from the argument
values, looking up the method definition and applying it. ExpNew (see Figure 6) encodes the class
name (classId) and constructor argument expressions. Its evaluation fetches the class definition
from the global environment, instantiates it and possibly calls the constructor.
As suggested before, the interpreter defines a few other classes to provide a runtime system and
implement an operational semantics. For example, the class Class (see Figure 7) represents classes
by a reference to a superclass (extendsLink), a list of fields (dataList) and a list of methods
(methodList). It provides methods for instantiating the class (instantiate()), accessing the
list of methods including those in super classes (methodList()), etc. Methods are represented by
5
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 6/26
class ExpMethod extends Exp {
private Exp exp; // receiverprivate String methodId; // method name
private ExpList args; // arguments
ExpMethod(Exp exp, String methodId, ExpList args) {
this.exp = exp; this.methodId = methodId; this.args = args;
}
Data eval(Environment localE) {
// evaluate the lhs (receiver)
Instance i = (Instance) this.exp.eval(localE).read();
// evaluate the arguments to get a new local environment
Environment argsE = Environment.Empty;
this.args.eval(localE, argsE);
// lookup and apply method
Method m = i.lookupMethod(this.methodId);
return m.apply(argsE, i);
}
}
Figure 5: Class ExpMethod
class ExpNew extends Exp {
private String classId; // class name
private ExpList args; // constructor arguments
ExpNew(String classId, ExpList args) {
this.classId = classId; this.args = args;
}
Data eval(Environment localE) {
// get the Class and create an Instance
Class aClass = (Class) (Main.globalE.lookup(this.classId).read());
Instance i = aClass.instantiate();
// call non default constructor if it exists
if (i.getInstanceLink().methodList().member(this.classId).booleanValue()) {
Environment argsE = Environment.Empty;
this.args.eval(localE,argsE);// lookup and apply method
Method m = i.lookupMethod(this.classId);
m.apply(argsE, i);
}
return new Data(i);
}
}
Figure 6: Class ExpNew
6
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 7/26
class Class {
Class extendsLink; // superclass
DataList dataList; // field list
MethodList methodList;
Class(Class eL, DataList dL, MethodList mL) {
this.extendsLink = eL;
this.dataList = dL;
this.methodList = mL;
}
Class getExtendsLink() { return this.extendsLink; }
// implementation of Java’s ’new’ operator
Instance instantiate() { ... }// compute complete method list (incl. superclasses)
MethodList methodList() { ... }
...
}
Figure 7: Class Class
class Method {
private StringList args; // parameter names
private Exp body; // method body
Method(StringList args, Exp body) {
this.args = args; this.body = body;
}
Data apply(Environment argsE, Instance i) {
// name each argument
argsE.zipWith(this.args);argsE.add("this", new Data(i));
// eval the body definition of the method
return this.body.eval(argsE);
}
}
Figure 8: Class Method
7
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 8/26
the class Method (see Figure 8) by means of a list of argument names ( args) and a body expression
(body). Its method apply() binds argument names to values including this and evaluates the
body. Other classes include Instance (contains a reference instanceLink to its class and alist of field values; provides field lookup and method lookup), MethodList, Data (implements
mutable memory cells such as fields), DataList, Environment (maps identifiers to values), etc.
The architecture of the interpreter follows the standard design for object-oriented interpreters as
presented in Gamma et al. [ghjv95] by the Interpreter design pattern. Instantiating this design pattern,
the following correspondences hold: their ‘Client’ is our interpreter’s main() method, their meth-
ods ‘Interpret(Context)’ is our eval(Environment). The reification technique described in this
paper is applicable to other interpreters having such an architecture. Note that these interpreters may
implement many different runtime systems.
4 Generic reification by code transformation
In this section, we give an overview of our generic reification scheme for the class Class and for-
mally define the underlying program transformation. (For the sake of readability, we postpone the
discussion of a few technicalities to Section 6.) Then, we apply it in detail to the class Instance.
4.1 Overview of the generic reification scheme
Reification of an object should not change the semantics of that object but change its representation
and provide access to the changed representation. For example, it is not possible to modify the su-
perclass of a class at runtime in our non-reflective interpreter (although a reference representing the
inheritance relation exists in the memory of the underlying implementation). The reified representa-
tion of a class provides access to this reference. Once the internal representation has been exposed,access to this structure allows the semantics of the program to be changed (e.g. by means of dynamic
class changes). Note that this form of structural reification of the interpreter memory subsumes the
traditional notions of structural and behavioral reflection.
For illustration purposes, consider a class Pair with two fields fst and snd which is imple-
mented in the interpreter memory by a Class (from here on, C denotes an instance of the class C).
In order to reify Pair, we choose Class to be reifiable. Basically, a reifiable entity can have two
different representations as exemplified in Figure 9: either a base representation or a reified represen-
tation. Since reification of any object does not change its behavior, the object should provide the same
method interface in both representations. This common interface is implemented using a dispatch
object3: the Class denoted by Pair.
The dispatch object points to the currently active representation: either the base representation(BaseClass in Figure 9a) or the reified representation (Instance denoted by ¥ Pair in Figure
9b). The dispatch object provides a method reify() (triggered by ¥ ) to switch from the base
representation to the reified one: a call to reify() creates a new tower level. The dispatch object
executes incoming method calls according to the active representation: when the base representation
is active, the dispatch simply delegates incoming method calls to it. When the reified representation
is active, the dispatch object interprets the method call.
Whether an object is accessed through its dispatch object or through its reified representation is
irrelevant, that is, modification of the object through the access path Pair is visible through the other
access path ¥ Pair. (This property is commonly referred to as the causal connection between levels.)
3The dispatch technique is close to the bridge and state patterns introduced in Gamma et al. [ghjv95].
8
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 9/26
denotes
methodList = Pair()
extendLink = Object
dataList = fst, snd
BaseClass
dispatch object
Class
isReified = false
representation =
dispatch object
Class
isReified = true
representation =
Instance
dataList = dataList,
methodList, extendLink
instanceLink = Class
X X
active representationinstance of
a) before ∆ b) after ∆Pair Pair
Pair Pair Pair∆
diff erent
representations
Figure 9: Before and after reification of the class Pair
Obviously, the two paths provide different interfaces. Consider, for example, the problem of keeping
track of the number of Pair instances using a static field countInstances: this field could be ac-
cessed either by Pair.countInstances or by ( ¥ ( ¥ Pair).staticDataList).lookup
("countInstances")4. In the last expression, the outermost reification operation is necessary in
order to call lookup() on a data list object (cf. the fourth item below).
In order to conclude this overview, we briefly mention other important properties of our reification
scheme:
Since reflection provides objects representing internal structure for use in user-level programs,
every reification operation returns an Instance (e.g. the one in Figure 9b). This implies that
reification of reified entities requires that Instances are reifiable.
¥ Exp yields an accessible representation of the value denoted by Exp (i.e. an object in the
interpreter’s memory, such as Class, Instance, Method)5
. References from dispatch objects to their active representations cannot be accessed by user
programs. Only a call to the reification operator may modify these references. This ensures that
the tower structure cannot be messed up by user programs.
The scope of the reification process is limited to individual objects in the interpreter’s memory.
For example, the reification of a class does not reify its list of methods methodList nor its
superclass. So, three categories of objects coexist at runtime: reified objects, non-reified (but
4METAJ does not allow static fields but could be extended easily to deal with such examples.5 ¡ is a strict operator. A syntax extension would be necessary to reify the expression (e.g. the AST representing 1+4)
rather than the value denoted by the expression (e.g. the integer 5).
9
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 10/26
class Name {
Type
¡
field £ ¡
;
...
Type
¤
field
¤
;
Name(Type ¡
arg ¡
, ..., Type
¤
arg
¤
) { body }
Type §
¡
method §
¡
(Type §
¡¡
arg§
¡¡
, ..., Type §
¡
arg §
¡
) { body §
¡
}
...
Type § method §
(Type §
¡
arg §
¡
, ..., Type§ arg§
) { body § }
}
Figure 10: Original class definition
reifiable) ones and non-reifiable ones. If a program accesses an object o through a reified one,
the use of o is restricted exactly as in the non-reflective case. ¥ Pair.extendsLink, for
example, references a Class representing the superclass of Pair. Therefore, the only valid
operations on this reference are new ( ¥ Pair.extendsLink)()6 as well as accesses to
static fields and members of this class. If the structure or behavior of the superclass is to be
changed, it must be reified first. This implies that accesses to non-reifiable objects through
reified ones are safe.
4.2 Formal definition of the generic reification scheme
Based on the implementation technique outlined above, our generic reification scheme is an automatic
program transformation which can be applied to an arbitrary class, called Name in the followingdefinition, of the original interpreter. As shown in Figure 10, such classes consist of a number of fields
and methods and must have a constructor with arguments for all of their fields. The transformation of
a set of classes has time and space complexity linear in the number of classes.
The transformation consists of two main steps:
1. Introduce the class BaseName (see Figure 11) which defines the base representation of the
original class Name. This class is very similar to the original class Name.
2. Redefine the class Name (see Figure 12) such that it implements the corresponding dispatch
object. This class provides the same method interface as the original class Name and imple-
ments a methodreify()
which creates the reified representation and switches from the baserepresentation to the reified one.
Figure 11 shows the generated base class. (In the figures of this section, we use different style
conventions for verbatim text, schema variables and [text substitutions].) Ba-
sically, the original class is renamed and a field referent is added. Remember that a reifiable entity
is implemented by a dispatch object that points to the current representation. The referent field,
which is initialized in the constructor and points back from the representation to the dispatch object,
is mandatory to distinguish the dispatch object and the representation: if this is not used to access
6The current parser of METAJ does not allow such an expression: new requires a class identifier. However, the parser
could be easily extended to deal with such expressions and we allow this notation in this paper.
10
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 11/26
class BaseName {
Type
¡
field
¡
;
...
Type
¤
field
¤
;
Name referent;
BaseName(Type
¡
arg
¡
, ..., Type
¤
arg
¤
, Name referent) {
body
this.referent = referent;
}
Type §
¡
method §
¡
(Type §
¡¡
arg§
¡¡
, ..., Type §
¡
arg §
¡
) {
body§
¡
[ this.referent / this(~.) ]
}
...
Type § method §
(Type §
¡
arg §
¡
, ..., Type§ arg§ ) {
body§ [ this.referent / this(~.) ]
}
}
Figure 11: Generated base class
fields or methods in the base class it should denote the dispatch object 7. In the transformation, this
is implemented by substituting this(~.) (matching the keyword this followed by anything but adot) by this.referent.
The generated dispatch class, shown in Figure 12, has two fields: representation that points
to either the base representation or the reified representation, and a boolean field isReified that
discriminates the active representation. Its constructor creates a base representation for the object.
The methods method ¡
have the same signature as their original version. When the base repre-
sentation is active (i.e. isReified is false), the method call is delegated to the base representation.
When the reified representation is active (i.e. isReified is true), the method call is interpreted:
the corresponding call expression is parsed (Parser.java2Exp()), a local environment is built
(argsE.add()) from the method arguments and the field representation of the dispatch ob-
ject and the method call is evaluated (eval()). Note that for the sake of clarity, this code is inten-
tionally naive. The actual implemented version could be optimized: for example, the call to the parsercould be replaced by the corresponding syntax tree.
The method reify() builds a reified representation of the base representation by evaluating a
new-expression. The corresponding class is cloned in order to build a new tower level. So, every
reified object has its own copy of a Class. This way, the behavior of each reified object can be spe-
cialized independently. If sharing is required the application programmer can achieve it by explicitly
manipulating references. Finally, the reified representation is installed as the current representation
and a reference to it is returned. A series of experiments led us to this sharing strategy. A previous
version of the transformation did not clone the class. This sharing led to cycling dependency relation-
ships and reflective overlap after reification: in particular, reification of the class Class introduced
7This is a typical problem of wrapper-based techniques that introduce two different identities for an object.
11
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 12/26
class Name {
Object representation;
boolean isReified;
Name(Type
¡
arg
¡
, ..., Type
¤
arg
¤
) {
this.isReified = false;
this.representation = new BaseName(arg
¡
, ..., arg
¤
, this);
}
Type §
¡
method §
¡
(Type §
¡¡
arg§
¡¡
, ..., Type §
¡
arg §
¡
) {
if (this.isReified) {
Exp exp = Parser.java2Exp(
"reifiedRep. method §¡
(arg§
¡¡
,...,arg §
¡
)");
Environment argsE = Environment.Empty;
argsE.add("reifiedRep", this.representation);
argsE.add("arg §
¡
", arg §
¡
);
...
argsE.add("arg §
¡¡
", arg §
¡¡
);
Data result = exp.eval(argsE);
return result.read();
} else
return (BaseName)this.representation. method §
¡
(arg §
¡¡
, ... , arg §
¡
);
}
...
Instance reify() {
if (!this.isReified) {
Class aClass = Main.globalE.lookup(" Name").clone();
Exp exp = Parser.java2Exp("new aClass(baseRep_field
¡
,...,
baseRep _field ¤
,
Environment argsE = Environment.Empty;
argsE.add("baseRep_ field
¡
", this.representation.field
¡
);
...
argsE.add("baseRep_ field
¤
", this.representation.field
¤
);
argsE.add("aClass", aClass);
this.isReified = true;
this.representation = exp.eval(argsE).read();
}
return (Instance)this.representation;
}
}
Figure 12: Generated dispatch class
12
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 13/26
non-termination. Alternatively, we experimented with one copy of each class per level but in this case
the reification (without modification) of an object could already change its behavior.
This generic reification technique is based on only two assumptions:
1. Each syntactic construct is represented by an appropriate expression during interpreter execu-
tion. We assume that all of these expressions can be evaluated using the method eval(argsE)
where argsE contains the current environment, i.e. the values of the free variables in the cur-
rent expression.
2. We assume that the textual definitions of all reifiable classes have been parsed at interpreter cre-
ation time and that they are stored as Class objects in the global environment Main.globalE.
These objects have to be cloneable.
This way, reify() creates an extra interpreter layer based on the actual interpreter definition.
Note that these simple assumptions and the formal definition enable the transformation to be per-formed automatically.
In Java, the operator new returns an object (i.e. an Instance). Therefore, in order to let the user
build other runtime entities than Instances, such as Classes and Methods, we provide a family
of deification8 operators, one for each of these entities. These operators are the inverse of the generic
reification operator. For example, in the reflective program (where
Class denotes the deification
operator for classes):
( ¥ Pair).extendsLink =
Class(new Class(...));
the right-hand side expression returns a Class dispatch object in front of the Instance created by
new. Note that the deification operators — while functionally inverting the reification operation —
do not change the representation of an object “back” to its unreified structure (e.g. to a BaseClass
in the case of classes).The dispatch objects engender the structure of the reflective tower; their implementation is not
accessible to the user. In particular, the reification operator and the deification operators encapsulate
the fields representation and isReified of dispatch objects as well as the field referent
from the base class. So, user programs cannot arbitrarily change the tower structure. However, the
user or a type system to be developed should avoid the creation of meaningless structures, such as
Class(new Method(...)).
4.3 Example: making the class Instance reifiable
To illustrate the definition of the transformation, we apply it to the class Instance (see Figure 14),
which is used in the examples of reflective programming in the next section. This class implementsobjects in the interpreter. For example, a pair object with two fields fst and snd is implemented
by an Instance the field dataList of which contains two memory cells labelled fst and snd.
Its field instanceLink points to a Class containing the methods of the class Pair. The method
lookupData() is called whenever a field of pair is accessed. (For the sake of conciseness, we
did not show the other methods of Instance, such as lookupMethod().)
The application of the transformation defined above to Instance yields the two classes Ba-
seInstance (see Figure 15) and the dispatch class Instance (see Figure 16). Now, pair is
implemented by a dispatching Instance as shown in Figure 13. Its default unreified representation
is a BaseInstance (say¡ ¢
) whose dataList field contains the fields labelled fst and snd (see
8We prefer the term ‘deification’ [iyl95] to the equivalent terms ‘reflection’ [wf88] and ‘absorption’ [meu98].
13
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 14/26
dispatch object
Instance
representation =
isReified = false
dispatch object
Instance
representation =
isReified = false
denotesX X active representationinstance of
a) before b) after
BaseInstance
dataList = fst, snd
instanceLink = Pair
∆ ∆
BaseInstance
dataList = dataList,
instanceLink
instanceLink = Instance
pair pair
d i f f e r e n t
r e p r e s e n t a
t i o n s
pairpair pair∆
dispatch object
Instance
representation =
isReified = true
Figure 13: Before and after reification of the object pair
class Instance {
public Class instanceLink; // ref. to Class
public DataList dataList; // field list
Instance(Class instanceLink, DataList dataList) {
this.instanceLink = instanceLink;this.dataList = dataList;
}
// field access
Data lookupData(String name) {
return this.dataList.lookup(name);
}
...
}
Figure 14: Original class Instance
14
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 15/26
class BaseInstance {
Class instanceLink;
DataList dataList;
Instance referent;BaseInstance (Class instanceLink, DataList dataList,
Instance referent) {
this.instanceLink = instanceLink;
this.dataList = dataList;
this.referent = referent;
}
Data lookupData(String name) {
return this.dataList.lookup(name);
}
...
}
Figure 15: Class BaseInstance
Figure 13a). Once pair has been reified (see Figure 13b), it is represented by an Instance which
points to a BaseInstance (say¡
). Note that in contrast to the reification of classes shown in Fig-
ure 9, the reified representation of an instance is reifiable (because it is an instance itself; hence, the
second dispatching Instance in Figure 13b). Since the reification is based on the actual definition of
the original Instance, the dataList of ¡
contains the three fields instanceLink, dataList
(itself containing fst and snd) and referent. The definition of the method lookupData() in
the dispatch object calls the method lookupData()of ¡ ¢
as long as pair is not reified. Once it is
reified, the definition of lookupData() of Instance is interpreted .In order to prove the feasibility of our approach, we applied this reification technique to different
classes defining object-oriented features of our JAVA interpreter resulting in the prototype METAJ. The
imperative features of the non-reflective interpreter can be tackled analogously. This way we could,
for example, redefine the sequentialization operator ‘;’ in order to count the number of execution steps
in a given method (say m). One way to achieve this is by reification of occurrences of ExpS in reified
m and dynamically changing their classes by a class performing profiling within the eval() method.
Another solution would be to replace ExpS nodes in reified m by nodes including profiling.
5 Reflective Programming
In this section, we express several classic examples of reflective programming in our framework.
These detailed examples of our reflective interpreter at work should help the reader’s understanding
of the system’s working.
The examples highlight an important feature of our design: since our reification scheme relies on
the original interpreter definition, the meta-object protocol of the corresponding reflective interpreter
(i.e. the interface of a reflective system) is quite easy to apprehend. It consists of a few classes which
are reifiable in METAJ, the reification operator ¥ and the deification operators
.
In Figure 17 the class Pair is defined, and in main() a new instance pair is created. In the
interpreter, the object pair is represented by an Instance (see Figure 13a). Our generic reification
method provides access to a representation of this Instance which we name metaPair (denoted
by ¥ pair in Figure 13b). The most basic use of reflection in object-oriented languages consists in
15
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 16/26
class Instance {
Object representation;
boolean isReified ;
Instance(Class instanceLink, DataList dataList) {
this.isReified = false;
this.representation =
new BaseInstance(instanceLink, dataList, this);
}
Data lookupData(String name) {
if (this.isReified) {
// interpret lookup method callExp exp = Parser.java2Exp("reifiedRep.lookupData(name)");
// pass already evaluated values
Environment argsE = Environment.Empty;
argsE.add("name", name);
argsE.add("reifiedRep", this.representation);
Data result = exp.eval(argsE);
// unpack result
return (Data)result.read();
} else
return ((BaseInstance)this.representation).lookupData(name);
}
...
Data reify() {
if (!this.isReified) {
// copy the base class BaseInstance
Class aClass = Main.globalE.lookup("Instance").clone();
// create and initialize new representation
Exp exp = Parser.java2Exp("new aClass(baseRep_instanceLink,
baseRep_dataList)");
Environment argsE = Environment.Empty;
argsE.add("baseRep_instanceLink", this.representation.instanceLink);
argsE.add("baseRep_dataList", this.representation.dataList);
argsE.add("aClass", aClass);
this.isReified = true;
this.representation = exp.eval(argsE).read();}
return new Data(this.representation);
}
}
Figure 16: Dispatch class Instance
16
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 17/26
class Pair {String fst;
String snd;
Pair(String fst, String snd) {
this.fst = fst;
this.snd = snd;
}
}
class PrintablePair extends Pair {
String toString() {
return "(" + this.fst + "," + this.snd + ")";
}}
class InstanceWithTrace extends Instance {
Method lookupMethod(String name) {
// trace method-called
System.out.println("method called: " + name);
return this.instanceLink.methodList().lookup(name);
}
}
class Main {
void main() {
Pair pair = new Pair("1", "2");
// 1 - invariance under reification
Instance metaPair =
pair;
System.out.println("pair.fst: " + pair.fst);
// 2 - introspection: test existence of a super class
Class metaClass =
Pair;
if (metaClass.getExtendsLink() == null)
System.out.println("Class Pair has no superclass");
// 3 - intercession: dynamic class change
metaPair.instanceLink = PrintablePair;
System.out.println("pair.toString(): " + pair.toString());
// 4 - intercession: change method-call semantics
Instance metaMetaPair =
metaPair;
metaMetaPair.setInstanceLink(InstanceWithTrace);
System.out.println("pair.toString(): " + pair.toString());// 5 - instance and class deification
System.out.println((¡ InstancemetaPair).fst);
reify(PrintablePair).extendsLink =¡ ClassmetaClass;
System.out.println("pair.toString(): " + pair.toString());
}
}
Figure 17: Examples of Reflective Programming
17
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 18/26
reifying an object: changing the internal representation without modifying its behavior (see Example
1). Another simple use is introspection. Let us consider the problem of testing the existence of a super
class of a given class. In Example 2, the class Pair (represented by a Class in the interpreter) isreified which enables its method getExtendsLink() to be called.
In METAJ, reflective programming is not limited to introspection, but the internal state of the
interpreter can also be modified (aka intercession). The third example in main() shows how the
behavior of an instance can be modified by changing its class dynamically. Imagine that we would
like to print pairs using a method called toString(). We define a class PrintablePair which
extends the original class Pair and implements a method toString(). A pair can then be made
printable by dynamically changing its class from Pair to PrintablePair (remember that the
field instanceLink of Instance holds the class of the represented instance, see Figure 15).
Afterwards the object pair understands the method toString().
The fourth example deals with method call tracing for debugging purposes. The class Instance
of the interpreter defines the method Method lookupMethod(String name) that returns theeffective method to be called within the inheritance hierarchy. In our interpreter each lookup-
Method() is followed by an apply(). Thus, method call tracing can be introduced by defining
a class InstanceWithTrace which specializes the class Instance of the interpreter such that
its method lookupMethod() prints the name of its parameter. In order to install the tracing of
method calls of the instance pair, its standard behavior defined in the interpreter by the class In-
stance (note that this class can be accessed because the interpreter definition is an integral part of
the reflective system built on top of the reflective interpreter) is replaced by InstanceWithTrace.
Reification of pair provides access to an Instance whose field instanceLink denotes the class
Pair. A sequence of two reification operations on pair provides access to an Instance whose
instanceLink denotes the class Instance. This link can then be set to the class Instance-
WithTrace. A method call of the object pair then prints the name of the method. Therefore,"toString" is printed by our third example. Finally, note that our tower-based reflection scheme
makes it easy to trace the tracing code if required because any number of levels may be created by a
sequence of calls to ¥ .
The fifth (rather artificial) example illustrates deification by deifying metaPair and meta-
Class in order to create an instance and a class at the base level. After deification of the reified
representation metaPair we show that base-level operations can be performed on the resulting ob-
ject. In the case of class deification, we restore the original class of pair.
More advanced examples that illustrate our approach rely on the capacity to reify arbitrary parts
of the underlying interpreter. As discussed in Section 4.3, the reification of ExpS allows the behavior
of the sequence operator ‘;’ to be changed. This way, we could, for instance, stop program execution
at every statement for debugging purposes or handle numeric overflow exceptions by re-executingthe current statement block with higher-precision data representations. Furthermore, reification of the
control stack would allow Java’s try/catch-mecanism for exception handling to be extended by a retry
variant.
6 The nuts and bolts of generic reification
Section 4 presents the essential parts of the generic reification mechanism. However, the actual imple-
mentation of a full-fledged reflective system requires several intricacies to be handled. In the current
section, we motivate the problems which must be handled and sketch the solution we developed. For
an in-depth understanding of these technicalities we refer the reader to the METAJ source code.
18
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 19/26
class ExpId extends Exp {
// same fields and constructor as in
Data eval_original(Environment localE) {// same definition as eval in
}
Data eval(Environment localE) {
if (!localE.member("#meta_level").booleanValue())
return this.eval_original(localE);
else {
if (this.id.equals("this"))
return new Data(((Instance) localE.lookup("this").read()).referent);
else return eval_original(localE);
}
}
Figure 18: Class ExpId
class ExpMethod extends Exp {
...
Data eval(Environment localE) {
Instance i = null;
if (!localE.member("#meta_level").booleanValue())
return this.eval_original(localE);
else {
// evaluate the lhs (object part)
Object o = this.exp.eval(localE).read();if (o instanceof Reifiable && ((Reifiable) o).getIsReified()) {
// evaluate the receiver
i = ((Reifiable) o).reify();
// evaluate the arguments to get a new local environment
Environment argsE = new Environment(null, null, null);
argsE.add("#meta_level", new Data(null));
this.args.eval(localE, argsE);
// lookup the method and apply it
Method m = i.lookupMethod(this.methodId);
return m.apply(argsE, i);
} else {
if ((o instanceof DataList) && this.methodId.equals(lookup)) {
Environment argsE = Environment.Empty;
this.args.eval(localE, argsE);
return new Data(((DataList) o)
.lookup((String) argsE.getData().read()));
} else ... // other delegation cases
}
}
}
}
Figure 19: Class ExpMethod
19
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 20/26
First, in the reflective interpreter a reified object is represented by a dispatch object and a reified
representation. So, basically a reified object has two different identities. With our technique, this is
bound to the representation rather than the dispatch object by parsing the expression "reifiedRep.method
(arg
,\dots,arg
¢
)" in the dispatch object (see Figure 12). However, if a state-
ment return this is to be interpreted, this should denote the dispatch object. Otherwise, user-
level programs could expose the reified representations. The interpreter class ExpId is in charge
of identifier evaluation (including this) and has therefore to be modified to account for this be-
havior. In Figure 18 the method eval() distinguishes two cases by means of the environment-tag
#meta_level.9 First, interpretation has been initiated by the interpreter’s entry point and non-
reflective evaluation is necessary. Second, interpretation has been initiated by a dispatch object and
reflective interpretation is required. In the first case eval_original() is called: this method has
the same definition as eval() in the non-reflective interpreter. In the second case if the identifier is
this, the dispatch object of the current representation is returned. Remember that the field refer-
ent points back from the base representation to the dispatch object, the same mechanism is used tolink the reified representation to the dispatch object. This field must be set by the methods reify(),
so the class Instance has to provide such a field10.
Second, remember that the scope of reification is limited to a single object in the interpreter
memory. This means interpretation involves reified and non-reified objects. For example, the reifi-
cation of an Instance does not reify neither its field list dataList nor its class denoted by in-
stanceLink. In particular, once an Instance has been reified, the interpretation of its method
lookupData (repeated from Figure 14):
Data lookupData(String name){return this.dataList.lookup(name);}
requires this.dataList to be interpreted and the call lookup(name) to be delegated because
this.dataList denotes a non-reifiable object. In abstract terms, a dispatch object introduces an
interpretation layer (a call to eval()) and this layer has to be eliminated when the scope of thecurrent (reified) object is left. This scheme is implemented in ExpMethod.eval() (see Figure
19).
Because of these two problems, the methods ExpData.eval() and ExpNew.eval() have
to be modified similarly. This means that our reification scheme cannot be applied to the four classes
ExpId, ExpMethod, ExpData, ExpNew11. However, our method provides much expressive
power: these restrictions fix the relationship between certain syntactic constructs and the runtime
system, but the runtime mechanisms themselves can still be modified as exemplified in Section 5. In
order to weaken this restriction, we designed and implemented a variant 12 of our reification scheme
that does not require ExpId and ExpData to be modified. Unfortunately, this advantage comes at a
price: the field referent can be exposed and modified by reification in this case.
7 Discussion of the correctness of the transformation
A complete treatment of the correctness of our technique is beyond the scope of this paper. However,
in this section we discuss very briefly work related to semantics of reflective systems and sketch a few
essential properties constituting a skeleton for a formal correctness proof of our technique.
9The dispatch objects insert this tag into the local environment.10For the sake of simplicity, the code shown in Figures 12 and 16 does not mention the field referent.11The restriction that all parts of a reflective system cannot be reified seem to be inherent to reflection [wf88].12This variant is also bundled in the METAJ distribution.
20
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 21/26
Semantics of reflective programming systems is a complex research domain. Almost all of the
existing body of research work in this domain is about reflection in functional programming languages
[wf88][dm88][mul92][mf93]. Even in this context, foundational problems still exist. For example, itseems impossible to give a clean semantics which avoids introducing non-reifiable components [wf88]
and logics of programming languages must be considerably weakened in order to obtain a consistent
theory of reification [mul92]. One of the very few formal studies of reflection in a non-functional
setting has been done by Malenfant et al. [mdc96]. This work deals with reflection in prototype-
based languages and focuses on the lookup() ; apply() MOP formalized by means of rewriting
systems. This approach is thus too restricted to serve as a basis for our correctness concerns. In
general, semantic accounts of imperative languages are more difficult to define than in the functional
case. In particular, the transposition of the results obtained in the functional case to our approach
requires further work. We anticipate that this should be simpler in a transformational setting such as
ours than for arbitrary reflective imperative systems.
In order to prove the correctness of our scheme, the basic property to satisfy would be equivalencebetween a non-reflective interpreter¢ ¡ £
and a reflective interpreter¥ ¦ ¨ ¨
¡ £
generated by applying our
transformation to ¡ £
, i.e.
! # $ % & '
¡ £ ) 1 3
¥ ¦ ¨ ¨
¡ £
) 1
Since the transformation¥ ¦
is operating on individual classes, this property can be tackled by
establishing an equivalence between an arbitrary class (say c) of the non-reflective interpreter and its
transformed counterpart. Essentially, the transformation introduces an extra interpretation layer into
the evaluation of the methods of c. Programs and their interpretations introduced by transformation
satisfy the property
java2Exp("
").eval(Environment.Empty.add("6
$
",6
$ 8
)).read()3
¨ 6
$ @
6
$ 8
This property can be proven by induction on the structure of the AST representation of
. (Note
that the formulation of this property is intentionally simplistic and should be parameterized with
contextual information, such as a global environment and a store.) It can be applied to the dispatch
classes (see Figure 12) to fold interpreting code into delegating code. When the then-branches of
dispatching methods are rewritten using the property from left to right, the then-branches equal the
corresponding else-branches. Henceforth, the conditionals become useless and the dispatch objects
become simple indirections that can be suppressed. In the case of the method reify(), the rewriting
leads to the expression new Name(...) that creates a copy of the non-reified representation.
Finally, we strongly believe our transformation is type-safe (although we did not formally prove
this): every well-typed interpreter is transformed into a well-typed reflective interpreter. Obviously,
wrongly-typed user programs may crash the non-reflective interpreter. In the same way, some reflec-
tive programs may crash the reflective interpreter, for instance by confusing reflective levels or trying
to access a field which has been previously suppressed using intercession. Specialized type systems
and static analysis methods for safe reflective programming should be developed.
8 Generating alternative metaobject protocols
We have already mentioned that each set of reified classes along with their definitions determines a
MOP of its own. We think that this is a key property of our approach because it provides a basis for the
systematic development of specially-tailored MOPs. In this section, we modify the message-sending
part of the non-reflective interpreter in order to provide a finer-grained MOP which distinguishes the
sender and the receiver of a message.
21
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 22/26
class Instance {
...
// add two new methods
Data send(Msg msg) {
return msg.to.receive(msg);
}
Data receive(Msg msg) {
return msg.to.lookupMethod(msg.methodId)
.apply(msg.argsE, msg.to);
}
}
class ExpMethod extends Exp {
...
Data eval(Environment localE) {
// as before evaluate receiver and arguments: o, argsE
...
// new code: determine sender, build and send message
Instance self = (Instance)(localE.lookup("this").read());
Msg msg = new Msg(self, o, this.methodId, argsE);
return self.send(msg);
}
}
Figure 20: Alternative original interpreter
class InstanceWithSenderTrace extends Instance {
Data send(Msg msg) {System.out.println("method called " + msg.methodId
+ " by " + msg.from);
return super.send(msg);
}
}
Figure 21: (User-defined) extension of Instance
22
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 23/26
In the original interpreter, ExpMethod.eval() evaluates a method call by implementing the
composition lookupMethod();apply(). So, the behavior of the receiver of a method call can
be modified easily by changing the definition of lookupMethod() (as illustrated by trace insertionin the Section 5). However, a modification concerning the sender of the method call (see CODA
[aff95] for a motivation of making the sender explicit in the context of distributed programming) is
much more difficult to implement. Such a change would require the modification of all instances of
ExpMethod in the abstract syntax tree, i.e. all occurrences of the operator ’.’. Indeed, we have to
check whether the object this in such contexts has a non-standard behavior.
A solution to this problem is to modify the non-reflective interpreter, such that its reflective version
provides a MOP enabling explicit access to the sender in a method call. Intuitively, we split message
sending in two parts: the sender side and the receiver side. First, we introduce a new class Msg which
is a four-tuple. For each method call, it contains the sender from, the receiver to, the method name
methodId and the corresponding argument values argsE. Then, two methods dealing with mes-
sages are added to the definition of Instance in the original interpreter: send() and receive()(see Figure 20). Finally, ExpMethod.eval() is redefined such that it creates and sends a message
to the receiver.
This new version of the non-reflective interpreter is made reflective by applying our program
transformation. Then, the user can, for example, introduce tracing for message senders (see Figure
21), the same way traces have been introduced in the previous section.
This example highlights three advantages of our approach: MOPs are precisely defined, applica-
tion programmers are provided with the minimal MOPs tailored to their needs and language designers
can extend MOPs at compile time without anticipation of these changes.
9 Related work
A comparison between reflective systems is inherently difficult because of the wide variety and the
conceptual complexity of reflective models and implementations. For example, the detailed defini-
tion of the CLOS MOP requires a book [kic91] and a thorough comparison between CLOS and
SMALLTALK already fills a book chapter [coi93].
Consequently, we restrict our comparison to the three basic properties our reflection model obeys
(the first and second characterizing Smith-like approaches, the third being fundamental to our goal of
the construction of specially-tailored MOPs):
1. (tower) There is a potentially infinite tower of reflective interpreters.
2. (interpreter) The interpreter at level interprets the code of the interpreter at level ¡ £ .
3. (selectivity & completeness) Any part of the runtime system and almost all of the syntax tree
(see Section 6) of an interpreter at level can be reified and has an accessible representation at
level
£ .
First, most reflective systems are based on some notion of reflective towers and provide a potentially
infinite number of levels. A notable exception to this are OPEN-C++ [chi95] and IGUANA [gc96]
whose MOPs only provide one metalevel.
Second, our approach is semantics-based following Smith’s seminal work on reflective 3-LISP
[smi84] for functional languages. This is also the case for the prototype-based languages 3-KRS
[mae87] and AGORA [meu98]. The other object-oriented approaches to reflection (including OBJ-
VL ISP [coi87], SMALLTALK [bri89] [riv96], CLASSTALK [bri89], CLOS [kic91], MetaXa [gol97])
23
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 24/26
are not semantics-based (in the sense of the second property cited above) because they do not feed
higher-level interpreters with the code of lower-level interpreters. Instead, different levels are repre-
sented by appropriate pointer structures. This proceeding allows more efficient implementations buthas no semantic foundation. Moreover, these reflective languages are monolithic entities while our
modular approach consists of three simple parts: a non-reflective interpreter, the operator ¥ and the
operators
.
Third, our approach enables language designers to precisely select which mechanisms of the lan-
guage are reflective. With the exception of IGUANA and OPEN-C++, all the reflective systems cited
above do not have this characteristic. Finally, note that our approach shares a general notion of com-
pleteness with 3-LISP, 3-KRS and AGORA: the programming model is defined by the interpreter
and almost all of its features can be made reifiable (“up” and “down” are primitives in 3-L ISP and
cannot be reified, for instance). Asai et al. [amy96] also starts from such a complete model but this
interesting approach to reflection in functional languages restricts reifiable entities in order to allow
optimization by partial evaluation. In contrast, the remaining reflective systems described above donot base reflection on features of an underlying interpreter but implement an ad hoc MOP. The notion
of completeness therefore does not make sense for them.
10 Conclusion and future work
In this paper we have presented a program transformation technique to generate reflective object
oriented interpreters from non-reflective ones. This technique allows specially-tailored MOPs to be
produced quickly. New MOPs can be developed from scratch or by refinement from existing ones
as exemplified in Section 8. Compared to general MOPs, specially-tailored ones could be tuned, for
instance, towards better efficiency and security properties.
To the best of our knowledge, the resulting framework for reflective object-oriented languages isthe first one satisfying the three basic properties mentioned in Section 9. Consequently, our approach
cleanly distinguishes between reifiable and non-reifiable entities, thus helping the understanding of
reflective programs.
A prototype implementation, called METAJ [metaj], is available.
Future work. We presented a generic reification technique for object-oriented reflective languages,
which provides a basis for the exploration of the metaprogramming design space, optimization tech-
niques and the formalization of reflective systems.
First, at the system level the design space of MOPs should be explored by defining and refining
different non-reflective interpreters as exemplified in Section 8, yielding a taxonomy of reflective
mechanisms. At the user level, the proliferation of reflective dialects requires appropriate design and
programming tools, including libraries of user-friendly reflective operators, program analyses and
type systems.
Second, reflection is deeply related to interpretation. Each dispatch object introduces a new inter-
pretation layer by calling the method eval(). So, specialization techniques like partial evaluation
[bn00] are prime candidates for efficiency improvements. Furthermore, user-written reflective pro-
grams may not use all reflective capabilities provided by a reflective interpreter (e.g. only make use
of a bound number of reflective levels). In this case, optimization techniques such as that presented
by Asai et al. [amy96] could be used to merge interpretation levels.
Third, since reflective programming is a rather complex task, it should be based on a formal
semantics, e.g. to define and ensure security properties. We believe that our transformation could be
24
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 25/26
used to generate specially-tailored reflective semantics from a non-reflective one.
Finally, we firmly believe that our reification technique can also be applied to (parts of) applica-
tions instead of an interpreter in order to make them reflective (preliminary results can be found in arelated paper by the authors [ds00]).
Acknowledgements. We thank the anonymous referees for their numerous constructive comments
and the editor Olivier Danvy. The work reported here has also benefited from remarks by Kris de
Volder, Shigeru Chiba and Jan Vitek. It has been improved through many discussions with our col-
leagues Noury Bouraqadi, Mathias Braux and Thomas Ledoux.
References
[aff95] J. McAffer. Meta-Level Programming with CODA. Proceedings of ECOOP, LNCS 952,
Springer Verlag, pp. 190-214, 1995.
[amy96] K. Asai, S. Matsuoka, A. Yonezawa. Duplication and Partial Evaluation — For a Better
Understanding of Reflective Languages. Lisp and Symbolic Computation, 9(2/3), pp. 203-
241, 1996.
[bc00] G. Blair, R. Campbell (chairs). Workshop on Reflective Middleware, 2000.
http://www.comp.lancs.ac.uk/computing/RM2000/
[bn00] M. Braux, J. Noyé. Towards Partial Evaluating Reflection in JAVA. Proceedings of Workshop
on Partial Evaluation and Semantics-Based Program Manipulation, ACM Press, pp. 2-11,
2000.
[bri89] J.P. Briot, P. Cointe. Programming with Explicit Metaclasses in SMALLTALK. Proceedings
of OOPSLA, ACM SIGPLAN Notices, 24(10), pp. 419-431, 1989.
[chi95] S. Chiba. A Metaobject Protocol for C++. Proceedings of OOPSLA, ACM SIGPLAN No-
tices, 30(10), pp. 285-299, 1995.
[coi87] P. Cointe. Metaclasses are First Class Objects: the OBJVL ISP Model. Proceedings of OOP-
SLA, ACM SIGPLAN Notices, 22(12), pp. 156-162, 1987.
[coi93] P. Cointe. CLOS and SMALLTALK: a comparison. In “Object-Oriented Programming: The
CLOS perspectives?”, A. Päpcke (ed.), MIT Press, ch. 9, pp. 215-274, 1993.
[coi99] P. Cointe (ed.). Proceedings of Reflection’99, LNCS 1616, Springer Verlag, 1999.
[dm88] O. Danvy, K. Malmkjær. Intensions and Extensions in a Reflective Tower. Proceedings of
the ACM Conference on Lisp and Functional Programming, pp. 327–341, 1988.
[ds00] R. Douence, M. Südholt. On the lightweight and selective introduction of reflective capabil-
ities in applications. International Workshop on Reflection and Metalevel Architectures at
ECOOP, 2000.
ftp://ftp.disi.unige.it/person/CazzolaW/EWRMA/sudholt.ps.gz
[ghjv95] E. Gamma, R. Helms, R. Johnson, J. Vlissides. Design Patterns. Addison-Wesley, 1995.
25
7/27/2019 A Generic Reification Technique
http://slidepdf.com/reader/full/a-generic-reification-technique 26/26
[gol83] A. Goldberg, D. Robson. SMALLTALK 80, the Language and its Implementation. Addison-
Wesley, 1983.
[gol97] M. Golm. Design and Implementation of a Meta Architecture for Java. Master’s Thesis.
Universität Erlangen, 1997.
[gc96] B. Gowing, V. Cahill. Meta-Object Protocols for C+ +: The IGUANA Approach. Informal
Proceedings of Reflection’96, pp. 137-152, 1996.
[iyl95] J.-I. Itoh, Y. Yokote, R. Lea. Using Meta-Objects to Support Optimisation in the Apertos Op-
erating System. Proceedings of the USENIX Conference on Object-Oriented Technologies
(COOTS), USENIX Association, pp. 147-158, 1995.
[java] JAVA home page. Sun Microsystems, Inc. http://java.sun.com
[kic91] G. Kiczales, J. des Rivières, D. Bobrow. The Art of the Metaobject Protocol. MIT Press,1991.
[mae87] P. Maes. Concepts and Experiments in Computational Reflection. Proceedings of OOPSLA,
ACM SIGPLAN Notices, 22(12), pp 147-155, 1987.
[mdc96] J. Malenfant, C. Dony, P. Cointe. A Semantics of Introspection in a Reflective Prototype-
Based Language. Lisp and Symbolic Computation, 9(2/3), pp. 153-180, 1996.
[mf93] A. Mendhekar, D. P. Friedman. Towards a Theory of Reflective Programming Languages.
Informal Proceedings of the Third Workshop on Reflection and Metalevel Architectures in
Object-Oriented Programming at OOPSLA, 1993.
[metaj] METAJ home page: http://www.emn.fr/sudholt/research/metaj.
[meu98] W. De Meuter. AGORA: The Story of the Simplest MOP in the World. In “Prototype-based
Programming”, J. Noble et al. (ed.), Springer Verlag, 1998.
[mul92] R. Muller. M-LISP: A Representation-Independant Dialect of LISP with Reduction Seman-
tics. ACM TOPLAS, 14(4), pp. 589-616, 1992.
[riv96] F. Rivard. SMALLTALK: a Reflective Language. Informal Proceedings of Reflection’96, pp.
21-38, 1996.
[smi84] B.C. Smith. Reflection and Semantics in LISP. Proceedings of POPL, ACM Press, pp. 23-35,
1984.
[wf88] M. Wand, D. P. Friedman. The Mystery of the Tower Revealed: A Non-Reflective Descrip-
tion of the Reflective Tower. Lisp and Symbolic Computation, 1(1), pp. 11-38, 1988.
26