Experimenting with Mirrors for JavaScript

April 25, 2011

in JavaScript,Reflection

A common capability of many dynamic languages, such as JavaScript, is the ability of a program to inspect and modify its own structure.  This capability is generally called reflection. Examples of reflective capabilities of JavaScript include things like the hasOwnProperty and isPrototypeOf methods. ECMAScript 5 extended the reflection capability to JavaScript via functions such as Object.defineProperty and Object.getOwnPropertyDescriptor. There are many reasons you might use reflection but two very common uses are for creating development/debugging tool and for meta-programming.

There are many different ways you might define a reflection API for a programming language. For example, in JavaScript hasOwnProperty is a method defined by Object.prototype so it is, in theory, available to be called as a method on all objects. But there is a problem with this approach. What happens if an application object defines its own method named hasOwnProperty? The application object definition will override the definition of hasOwnProperty that is normally inherited from Object.prototype. Unexpected results are likely to occur if such an object is passed to code that expects to do reflection using the built-in hasOwnProperty method. This is one of the reasons that the new reflection capabilities in ES5 are defined as functions on Object rather than as methods of Object.prototype.

Another issue that arises with many reflection APIs is that they typically only work with local objects. Consider a tool that gives application developers the ability to graphically browse and inspect the objects in an application. If such a tool is effective, developers might want to use it in other situations. For example, they might want to inspect the objects on a remote server-based JavaScript application or to inspect a diagnostic JSON dump of objects produced when an application crashed. If JavaScript’s existing reflection APIs were used to create the tool there is no direct way it can be used to inspect such objects because the JavaScript reflection APIs only operate upon local objects within the current program.

There is also a tension between the power of reflection and security concerns within applications. Many of the reflection capabilities that are most useful to tool builders and meta-programmers can also be exploited for malicious purposes. Reflection API designers sometimes exclude potentially useful features in order to eliminate the potential of such exploits.

Mirrors is the name of an approach to reflection API design that attempts to address many of the issue that have been encountered with various programming languages that support reflection. The basic idea of mirrors is that you never perform reflective operations directly upon application objects. Instead all such operations are performed upon distinct “mirror” objects that “reflect” the structure of corresponding application objects. For example, instead of coding something like:

if (someObj.hasOwnProperty('customer')) {...

you might accomplish the same thing via mirrors via something like:

if (Mirror.on(someObj).hasOwnProperty('customer')) {...

Mirrors don’t have the sort of issues I discussed above because when using them you never directly reflect on application objects. There is never any problem  if the application just happens to define a method that  has the same name as a reflection API method.  Because reflection-based tools only indirectly interact with the underlying objects via mirror objects, it is possible to create different mirrors that use a common interface to access either local object, remote objects, or static objects stored in a file. Similar, it is possible to have have mirrors that present a common interface but differ in terms how much reflection they allow.  A trusted tool might be given access to a mirror that supports the must power reflective operations while an untrusted plug-in might be restricted to using mirrors that support only a limited set of reflective operations.

Gilad Bracha and David Ungar are the authors of a paper that explain the principals behind mirror-based reflection: Mirrors: Design Principles for Meta-level Facilities of Object-Oriented Programming Languages. I highly recommend it if you are interested in the general topic of reflection.

Mirrors were originally developed for the self programming language, one of the languages that influenced the original design of JavaScript. Recently, I’ve been experimenting with defining a mirror based reflection interface for JavaScript.  An early prototype of this interface named jsmirrors is now up on github.  It uses a common interface to support reflection on both local JavaScript objects and on a JSON-based object encoding that could be used for remote or externally stored objects. It also supports three levels of reflection privilege.

In my next post I’ll explain more of the usage and design details of jsmirrors.  In the meantime, please feel free to take a look at the prototype.

(Photo by “dichohecho”, Creative Commons Attribution License)

Previous post:

Next post: