world that would provide it with the new functionality. The objects
added to the system to represent programs, (annotations, modules,
etc.) are indeed meta-level objects that truck in code instead of
computation. Despite our own best intentions, when it came time
to share programs, we found that this principle applied after all.
4.2 Structural Correspondence
Structural correspondence implies that every language construct is
mirrored. This principle has long been recognized in the reflection
community [23]. Nevertheless, in practice it is often violated. We
discuss some of the issues that arise below.
4.2.1 Reifying Both Code and Computation
Meta-object protocols ideally introduce a meta-object for every
object in the computation. However, in many languages, important
notions like modules, import and export statements, metadata,
types and comments exist only at compile time. Such constructs
are liable to be excluded by a MOP that only reifies elements of
the actual computation. Similarly, compile-time MOPs deal only
with compile-time constructs; the MOP is not present at run-time,
and cannot reify entities that exist only at run-time (see section 6.4
for further discussion of compile-time MOPs).
4.2.2 Mirroring Method Bodies
In most languages, constructs below the method level, such as
statements and expressions, do not have corresponding meta-
objects. This is also the case in the mirror systems we have dis-
cussed. At the VM level, byte codes are often available, and these
can often be mapped back into source code. This strategy is typi-
cally used by tools such as debuggers.
True structural correspondence would imply that a higher level
representation of method bodies should be available. This would
be useful, so that tools that manipulate source code, such as com-
pilers, could use a standardized representation.
Because source code (or even byte code) may not always be avail-
able, many implementors have shied away from providing such
facilities. However, it is often possible to provide such functional-
ity conditionally (i.e., if it is available) and/or on-demand. For
example, in JDI, clients can query a
VirtualMachine about what
kinds of operations it supports. This enables JDI to define an API
to access a method’s byte code, but allows for implementations
that do not retain byte code as well.
4.2.3 Which Language to Reflect?
Programming languages that support reflection are often imple-
mented on top of a virtual machine (e.g., Java and the JVM, C#
and the CLR, etc.). One must not confuse the metaprogramming
API for the language of the underlying virtual machine (the VML)
with that of the high level language (HLL) running on top of it.
When designing a metaprogramming API, it is important to be
clear what the base-level language is. This is true regardless of
whether the API in question is mirror-based.
Reflection has to be supported by the VM at some basic level, so a
reflective API to the VML is a given. High level languages imple-
mented on top of a virtual machine should ideally include their
own reflection API. Maintaining a distinct reflective API for the
HLL is valuable for a number of reasons.
The VML reflective API may not maintain the invariants of the
HLL, thereby introducing potential security and correctness prob-
lems.
There is a risk of discrepancies between the VML and the HLL.
Such discrepancies often arise when implementing high level con-
structs that are not directly supported by the VM.
A prominent example are nested classes in the Java programming
language. Implementing nested classes requires the generation of
synthetic classes, interfaces, methods and fields that are not
present in the original source code. In some cases, constructors
may require additional parameters.
Such features should be hidden from HLL programs because they
expose the details of a specific translation scheme between the
HLL and the VML. Such a translation scheme is an implementa-
tion detail that HLL programs must never rely on. In particular,
these details should not leak through the reflective API.
As a counter-example, consider java.lang.Class.getMethods, which
returns the methods declared by a class. All the methods declared
at the VM level are returned, regardless of whether they are syn-
thetic. This exposes the translation strategy of the Java compiler to
clients of reflection.
If multiple source languages are implemented on a given virtual
machine, the risk of discrepancies among the virtual machine lan-
guage and the various source languages increases. A common
example is method overloading, typically supported by the HLL
compiler but not by the underlying VM. If two languages have dif-
ferent overload resolution schemes, a single reflective API will
support only one of them correctly.
Even if the HLL and VML are in complete agreement, it is likely
that discrepancies will arise over time as new HLL features are
added and implemented by the HLL front end without VM sup-
port. Again, both nested classes and generics in the Java program-
ming language are examples of this.
To avoid such difficulties, the Strongtalk mirror API is subdivided
into high level mirrors and low-level mirrors. High-level mirrors
reflect Smalltalk, and low level mirrors reflect the underlying
structures in the virtual machine. This distinction is not present in
any other reflective API that we are aware of.
High level mirrors are defined by the Mirror class hierarchy. High
level mirrors support Smalltalk level semantics. Low level mirrors
are defined by the class
VMMirror and its subclasses. VM mirrors
manifest representational differences between different kinds of
objects (e.g., integers, arrays, classes, mixins, regular objects) that
are hidden at the language level. One can ask a
ClassVMMirror for
the physical size of its instances, or the size of their header, for
example. The low level mirror API is inherently sensitive to the
design of the underlying virtual machine language and implemen-
tation.
We conclude that: there should be distinct reflective APIs for
each language in a system, in particular for the underlying virtual
machine language and for each high level language running on top