Proxies: Design Principles for Robust Object-oriented
Intercession APIs∗

Tom Van Cutsem

Mark S. Miller

Vrije Universiteit Brussel
Pleinlaan 21050 Brussels, Belgium

Google Research
1600 Amphitheatre Parkway
Mountain View, CA, USAerights@google.com

tvcutsem@vub.ac.be
ABSTRACT
Proxies are a powerful approach to implement meta-objects
in object-oriented languages without having to resort tometacircular interpretation. We introduce such a meta-level
API based on proxies for Javascript. We simultaneously introduce a set of design principles that characterize such APIsin general, and compare similar APIs of other languages
in terms of these principles. We highlight how principled
proxy-based APIs improve code robustness by avoiding interference between base and meta-level code that occur inmore common reflective intercession mechanisms.

Categories and Subject Descriptors
D.3.2 [Language Classifications]: Object-oriented languages

General Terms

Virtual objects are proxies that emulate other objects,
without the emulated objects ever having to be present
in the same address space. Examples include remoteobject proxies (emulate objects in other address spaces),persistent objects (emulate objects stored in databases),transparent futures (emulate results not yet computed),
etc.
The contributions of this paper are first, the introductionof a proxy-based metaprogramming API for Javascript (Section 4), second, the enumeration of a set of design principles that characterize general message-based object-oriented
metaprogramming APIs (Sections 4.2 through 4.9), third, acharacterization of related metaprogramming APIs in termsof these design principles (Section 7.1) and fourth, the characterization of Proxy APIs as stratified APIs that avoid
interference between message interception and applicationlevel code (Sections 3.3 and 4.3).

Design, Languages

2.

Keywords

A metaobject protocol (MOP) [11] is an object-oriented
framework that describes the behavior of an object-oriented
system. It is a term most commonly associated with reflective object-oriented programming languages. It is customary for a MOP to represent language operations definedon objects as method invocations on their meta-objects.
Throughout the rest of this paper, we use the general term
operation to denote mechanisms such as message sending,field access and assignment, defining a method, performing
an instanceof operation, and so on.
According to Kiczales et. al [11] a MOP supports introspection if it enables reflective read-only access to thestructure of an object. It supports self-modification if it ispossible to modify this structure. Finally, it supports intercession if it enables programmers to redefine the semantics
of operations on specific objects. Introspection is typicallysupported by all meta-level APIs. Self-modification is more
exceptional, and meta-level APIs with extensive support forintercession are rare in mainstream languages, the CLOS
MOP being a notable exception (see Section 7.4).
We will use the term intercession API to refer to any
API that enables the creation of new base-level objects withcustom meta-level behavior.

Proxies, Javascript, Reflection, Intercession

1.

INTRODUCTION

We introduce a new meta-level API for Javascript basedon dynamic proxies. Proxies have a wide array of use cases [8].
In this paper, we distinguish two general cases dependingon whether the proxy is proxying another object within thesame address space:
Generic wrappers are proxies that wrap other objects inthe same address space. Example uses include access
control, profiling, adaptors that intermediate between
different versions of an interface, etc.
∗Tom Van Cutsem is a Postdoctoral Fellow of the Research
Foundation, Flanders (FWO). This work was carried out
while on a Visiting Faculty appointment at Google, sponsored by Google and a travel grant from the FWO.

3.
Submitted to DLS 2010, October 2010, Reno, Nevada.

REFLECTION TERMINOLOGYJAVASCRIPT

Javascript is a scripting language whose language runtime is often embedded within a larger execution environment. By far the most common execution environment forJavascript is the web browser. While the full Javascript language as we know it today has a lot of accidental complexity
as a side-effect of a complex evolutionary process, at its core
it is a fairly simple dynamic language with first-class lexical
closures and a concise object literal notation that makes it
easy to create one-off anonymous objects. This simple coreis what Crockford refers to as “the good parts” [5].
The standardized version of the Javascript language isnamed ECMAScript. The Proxy API described in this paper
was designed based on the latest standard of the language,
ECMAScript 5 [7]. Because ECMAScript 5 adds a number of important features to the language that have heavily
influenced our Proxy API, we briefly summarize the newfeatures relevant to our discussion in the following section.

3.1

ECMAScript 5

ECMAScript 5 defines a new object-manipulation API
that provides more fine-grained control over the nature ofobject properties [7]. In Javascript, objects are records ofproperties mapping names (strings) to values. A simple twodimensional diagonal point can be defined as:var point = {x: 5,
get y() { return this.x; },toString: function() { return ’(’+x+’,’+y+’)’]; }};ECMAScript 5 distinguishes between two kinds of properties. Here, x is a data property, mapping a name to a value
directly. y is an accessor property, mapping a name to a“getter” and/or a “setter” function. The expression point.y
implicitly calls the getter function.
ECMAScript 5 further associates with each property a
set of attributes. Attributes are meta-data that describe
whether the property is writable (can be assigned to), enumerable (whether it appears in for-in loops) or configurable(whether the property can be deleted and whether its attributes can be modified). The following code snippet shows
how these attributes can be inspected and defined:
var pd = Object.getOwnPropertyDescriptor(o, ’x ’);// pd = {// value: 5,// writable: true,// enumerable: true,// configurable : true// }Object.defineProperty(o, ’z ’, {get: function() { return this.x; },enumerable: false,
configurable: true});

The pd object and the third argument to defineProperty are called property descriptors. These are objects that
describe properties of objects. Data property descriptors
declare a value and a writable property, while accessor
property descriptors declare a get and/or a set property.
The Object.create function can be used to generate new
objects based on a set of property descriptors directly. Its
first argument specifies the prototype of the object to becreated (Javascript is a prototype-based language with delegation). Its second argument is an object mapping property
names to property descriptors. We could have also defined
the point object explicitly as:var point = Object.create(Object.prototype, {x: { value: 5,enumerable: true,writable: true,configurable: true },y: { get: function() { return this.x; }, enumerable: true, ... },toString: { value: function() {...}, enumerable: true, ...}});ECMAScript 5 supports the creation of tamper-proof objects that can protect themselves from modifications by client
objects. Objects can be made non-extensible, sealed or frozen.
A non-extensible object cannot be extended with new properties. A sealed object is a non-extensible object whose own(non-inherited) properties are all non-configurable. Finally,a frozen object is a sealed object whose own properties are
all non-writable. The call Object.freeze(obj) freezes theobject obj. Section 4.4 details how tamper-proof objects
have influenced the design of our intercession API.

3.2

Reflection in Javascript

Javascript has built-in support for introspection and selfmodification. These features are provided as part of the language, rather than through a distinct metaobject protocol.
This is largely because Javascript objects are represented as
flexible records mapping strings to values. Property names
can be computed at runtime and their value can be retrieved
using array indexing notation. The following code snippet
demonstrates introspection and self-modification:
var o = { x: 5, m: function(a) {...} };// introspection:o [”x ”] // computed property access”x” in o // property lookup
for (prop in o) {...} // property enumerationo [”m ”]. apply(o ,[42]) // reflective method call// self −modification:o [”x ”] = 6 // computed property assignmento.z = 7 // add a property
delete o.z // remove a property

The new property descriptor API discussed in the previous
section provides for more fine-grained introspection and selfmodification of Javascript objects, as it additionally reveals
the property attributes.

3.3Intercession in Javascript

Javascript has no standard support for intercession. It is
not possible to intercept an object’s property access, how
it responds to the in-operator, for-in loops and so on.
Mozilla’s Spidermonkey engine has long included a nonstandard way of intercepting method calls based on Smalltalk’s
doesNotUnderstand: mechanism (see Section 7.1). In Spidermonkey, the equivalent method is named __noSuchMethod__.
For example, a proxy that can generically forward all received messages to a target object o is created as follows:
function makeProxy(o) {return {__noSuchMethod__: function(prop, args) {return o[prop].apply(o,args);}};}

In what follows, we will refer to methods that intercept
language-level operations as traps, a term borrowed from theOperating Systems community. Methods such as __noSuchMethod__ and doesNotUnderstand: are traps because theyintercept method calls.
The problem with doesNotUnderstand: and derivatives
is that the trap is not stratified : it is defined in the same
name space as the rest of the application code. The only
way in which a trap is distinguished from a regular method

is by its name. This violation of stratification can lead toconfusion:• Say an object purposely defines the trap to intercept
invocations. Since the trap is part of the object’s interface, its clients can accidentally invoke the trap as
if it were an application-level method. This confuses
meta-level code, since the call “originated” from thebase-level.
• Say an object accidentally defines an application-level
method whose name matches that of the trap. The VM
may then incidentally invoke the application method
as if it were a trap. This confuses the base-level code,
since the call “originated” from the meta-level.
Without stratification, the intercession API pollutes theapplication’s namespace. We conjecture that this lack ofstratification has not posed a significant problem in practicebecause systems such as Smalltalk and Spidermonkey define
only one such special method. But the approach does notscale. What if we were to introduce additional such traps tointercept not only method invocation, but also property access, assignment, lookup, enumeration, etc.? The number of“reserved method names” would quickly grow out of control.
We have discussed how the lack of stratification leads toconfusion when a trap is invoked by the wrong caller. There
are other ways in which the presence of a trap as part ofan object’s interface may cause confusion. For example, acommon Javascript idiom is to enumerate all properties of an
object. Say we want to populate the content of a drop-down
list in an HTML form with id id with all of the property
names of an object obj:
function populateList(id, obj) {var select = document.getElementById(id);for (var name in obj) {// skip inherited properties
if (! obj.hasOwnProperty(name)) continue;var opt = document.createElement(’option’);
opt.text = name;select.add(opt, null); // append opt}}

If obj is a proxy that defines __noSuchMethod__ for thesake of intercepting property access, this method will show
up in the enumeration of obj’s properties, which is probably
not what the code expects.

4.

JAVASCRIPT PROXIES

We now describe an intercession API for Javascript. This
API is officially proposed for inclusion in the next version of
the ECMAScript standard1 .
Our intercession API for Javascript supports intercession
by means of distinct proxy objects. The behavior of a proxyobject is controlled by a separate handler object. The methods of the handler object are traps that are called whenevera corresponding operation is applied to the proxy object.
Handlers are effectively “meta-objects” and their interface
effectively defines a “metaobject protocol”. A proxy object
is created as follows:
var proxy = Proxy.create(handler, proto);1
Its draft specification is available at www.tinyurl.com/
harmony-proxies.

Here, handler is an object that must implement a particular meta-level API and proto is an optional argument
object representing the proxy’s prototype.
Table 1 lists those base-level operations applicable to objects that can be trapped by handlers. The name proxyrefers to a proxy instance, handler to that proxy’s handler,
proto to the prototype specified in Proxy.create and receiver to either a proxy or an object that inherits (directly
or indirectly) from a proxy. The distinction between fundamental and derived traps is explained in Section 4.7.
The relationship between traps and the operations they
trap should be clear in most cases. The fix trap is describedin Section 4.4. The enumerate trap must return an array ofstrings representing the enumerable property names of theproxy. The corresponding for-in loop is then driven by
iterating over this array. Because Javascript methods are
just functions, method invocation, as in proxy.m(a,b) is reified
as a property access handler.get(proxy ,’ m ’) that is expected toreturn a function. That function is immediately applied tothe arguments [a,b] and with this bound to proxy.
The distinction between proxy objects and regular objects
ensures that non-proxy objects (which we expect make up
the vast majority of objects in a system) do not pay theruntime costs associated with intercession (cf. Section 6).
Finally, note that the link between a proxy and its handler
is immutable and inaccessible to clients of the proxy.

4.1

Function Proxies

In Javascript, functions are objects. However, they differ
from non-function objects in a number of ways. In particular functions support two operations not applicable to objects: they can be called and constructed. For reasons thatwill be made clear in Section 4.6, our API explicitly distinguishes between object proxies and function proxies. The
call Proxy.create returns object proxies. To create a function proxy, one invokes the method Proxy.createFunction:
var call = function() { ... };var cons = function() { ... };var f = Proxy.createFunction(handler, call, cons);
f (1,2,3); // calls call (1,2,3)
new f(1,2,3); // calls cons(1,2,3)
f.x // calls handler.get(f ,’ x’)

The first argument to Proxy.createFunction is exactly
the same kind of handler object passed to Proxy.create. It
intercepts all operations applicable to functions used as objects. The additional arguments call and construct arefunctions that respectively trap Javascript’s function calland new operator.
Unlike Proxy.create, Proxy.createFunction does not
accept a prototype argument. Functions always inherit from
Function.prototype, and so do function proxies.

4.2

Generic Wrappers

To implement generic wrappers around existing objects,
a useful handler is one that simply forwards all operations
applied to the proxy to the wrapped object:
function makeForwardingHandler(target) {return {get: function(rcvr,name) { return target[name];},set: function(rcvr,name,val) { target[name] = val; return true; },has: function(name) { return name in target; },delete: function(name) { return delete target[name]; },
...
};
};

Operation
Fundamental traps
Own descriptor access
Descriptor access
Descriptor definition
Own keys
Property deletion
Key enumeration
Object fixing
Derived traps
Property lookup
Own Property lookup
Property access
Property assignment
Own enumerable keys

Triggered by

Reified as

Object.getOwnPropertyDescriptor(proxy,name)Object.getPropertyDescriptor(proxy,name)Object.defineProperty(proxy,name,pd)
Object.getOwnPropertyNames(proxy)delete proxy.namefor (name in proxy) {...}
Object.{freeze,seal,preventExtensions}(proxy)

handler.getOwnPropertyDescriptor(name)handler.getPropertyDescriptor(name)handler.defineProperty(name,pd)
handler.getOwnPropertyNames()
handler.delete(name)handler.enumerate()
handler.fix()name in proxy({}).hasOwnProperty.call(proxy,name)receiver.name
receiver.name = val
Object.keys(proxy)

handler.has(name)handler.hasOwn(name)handler.get(receiver, name)handler.set(receiver, name, val)handler.enumerateOwn()

Table 1: Operations reified on handlers by the Proxy API

var proxy = Proxy.create(makeForwardingHandler(target),Object.getPrototypeOf(target));A particular generic wrapper can inherit from this forwarder and override only the methods for the operations
that it needs to intercept. We make use of this handler in
Sections 4.5 and 5.

4.3

Stratification

Bracha and Ungar [3] introduce the principle of stratification for mirror-based architectures. The principle statesthat meta-level facilities must be separated from base-level
functionality. Bracha and Ungar focus mostly on stratification in the context of introspection and self-modification. In
this paper we focus on the application of this principle tointercession. Mirrors are further discussed in Section 7.2.
The distinction between a proxy and its handler object
enforces stratification of the traps. Traps are not defined aspart of the interface of the proxy object, but as part of theinterface of the handler.
The handler is a regular object. It may inherit from otherobjects, and its inheritance chain is completely independent
from that of the proxy it handles. A single handler may
handle multiple proxies. The handler can be a proxy itself(we will illustrate a use case of this in Section 4.5).
Accessing aProxy.has explicitly on a proxy will not trigger
the proxy’s corresponding has trap. Instead, the access will
be reified like any other as handler.get(aProxy ,‘ has’) . Likewise, aProxy.get is reified as handler.get(aProxy ,‘ get’) . Traps
can only be invoked explicitly on a proxy’s handler, not on
the proxy itself. This enforces stratification (the meta-leveltraps should not interfere with base-level method names).
Thus, proxies continue to work correctly if an application(by accident or by design) uses the names get, set, etc.
The principle of stratification when applied to proxies canbe summarized as follows.
Stratification: a proxy’s traps should be stratified bydefining them on a separate handler object.

4.4

Temporary Intercession

Recall from section 3.2 that ECMAScript 5 enables thecreation of tamper-proof objects. A tamper-proof object
provide useful guarantees that programmers can rely upon.
When designing a proxy API, care should be taken thatproxies cannot break these guarantees. For example, if o
is an object, then Object.freeze(o) freezes that object. If

the programmer knows o is frozen, he can rely on the factthat the number of properties contained in o will no longer
change. If o is a proxy, we do not want a proxy to violate
such assumptions.
To reconcile non-extensible, sealed and frozen objects withproxies, we introduce an additional trap named fix. A callto Object.freeze(proxy) will be interpreted as:var props = handler.fix();
if (props === undefined) {throw new TypeError();} else {become(proxy, Object.freeze(Object.create(proto, props )));}

Again, handler denotes proxy’s handler and proto denotes the prototype argument passed to the Proxy.create
call that created the proxy.
The fix trap is introduced to enable a proxy to interact with the Object.preventExtensions, Object.seal andObject.freeze primitives available in ECMAScript 5. A
non-extensible, sealed or frozen object should restrict thehandler’s freedom in terms of what it can return from subsequent calls to has, get, etc. For example, once proxyp is frozen, all invocations of ’foo’ in p must return thesame value. If this operation traps to the proxy’s handler’s
has trap (i.e., as handler.has(’foo’)), then this restriction
would be difficult to enforce.
Proxies enforce these restrictions as follows: when an attempt is made to make a proxy non-extensible, sealed orfrozen, the fix trap is invoked on the proxy’s handler. The
handler has two options. It can refuse the request (by making its fix trap return undefined). This causes the operation to throw an exception. Or, the handler can allow therequest by returning a “fixed” representation of the proxy’s
properties. The implementation instantiates this description into an actual object. From that point on the proxyeffectively becomes that object and the handler is bypassed
entirely. The proxy is now said to be “fixed”.
The become operation used in the above code snippet ispseudo-code: it can only be implemented in the VM. The
props object returned by the fix trap is the same kind of
object that can be passed as the second argument to Object.create: a record that maps property names to property descriptors.
Because of the fixing-protocol, proxies can essentially bein two states. A proxy is created in a trapping state, duringwhich its handler traps all operations applied to the proxy.
Once the fix trap returns an object description, the proxyis fixed. Once a proxy is fixed, it remains forever fixed.
A fixed proxy is essentially a “static” proxy, and as far as
the Javascript programmer is concerned, it is at that point
indistinguishable from a normal Javascript object.
When an object proxy is fixed, the prototype of the generated object is the proto object passed as an argument to
Proxy.create. The prototype of a function proxy is always
Function.prototype, and this remains unchanged when fixing the function proxy.
The essence of the fixing mechanism can be summed up
in the following principle:
Temporary Intercession: if an operation imposes strong
consistency requirements on a proxy object (such as becoming immutable), intercession should be disabled for some or
all of the operations intercepted by proxies.
Fixing does not necessarily need to be an “all or nothing”
process. However, in the particular case of ECMAScript, wefound this to be the simplest solution.

4.5

Proxies as Handlers

A common use case for proxy handlers is, for example, toperform an access control check before performing the operation on some wrapped object. Only if the check succeeds isthe operation forwarded. Since there are 12 different operations that proxy handlers can intercept, a straightforward
implementation would have to duplicate the pattern of access checking and forwarding the operation in each trap. It’sdifficult to abstract this pattern, because each operation has
to be “forwarded” in a different way, as exemplified by theforwarding handler in Section 4.2. Ideally, if all operations
could be uniformly funneled through a single trap, the handler would only have to perform the access check once, inthe single trap.
Such funneling of all operations through a single trap canbe achieved by implementing the proxy handler itself asa proxy. In meta-programming terms, this corresponds toshifting to the meta-meta-level. Figure 1 depicts how metalevel shifting works. The base-level proxy is the proxy object
with which other regular application objects directly interact. That proxy’s handler is the base handler. The crucialpart is that the base handler is itself a proxy, call it a metalevel proxy. The handler of a meta-level proxy is a meta
handler.
Note how all operations performed on p are intercepted by
mh’s get trap. We carefully designed the intercession API
such that proxies only interact with their associated handler
by invoking their traps. Proxies never assign, enumerate
or test for the presence of traps on their handler. Becauseof this uniformity, if the base handler is only used in its
role as the handler for a base-level proxy, the meta handler
only needs to implement the get trap. Because method
invocation is the only operation performed on meta-levelproxies, only this single trap is triggered on meta handlers.
Another way to explain why this funneling works is asfollows. At the base-level, running programs perform an assortment of operations on Javascript objects (e.g. property
lookup, access, assignment, enumeration, . . . ). At the metalevel (that is: in the intercession API), all of these operations
are reified uniformly as method invocations on handlers.
Therefore, at the meta-level, the only operation performed
on meta-level objects (base-level handlers) is property ac-

Figure 1: Meta-level shift using a proxy as a handler

cess, and meta-meta-level objects (meta-level handlers) only
need to handle this single operation in their protocol.
If one shifts an extra level upwards to funnel all operations through a single trap of a meta-level handler, onemust also shift an extra level downwards if these operations
must eventually be applied to a base-level object. This canbe accomplished by having the meta handler forward theoperations to a generic forwarding handler. The forwarding handler essentially translates meta-level operations backinto base-level operations on a target object.
Naturally, one can apply this trick of shifting meta-levels
at the meta-meta-level recursively. The API allows an arbitrary number of such meta-level shifts. This brings up thequestion of infinite meta-regress. Such an infinite regress isavoided as long as a handler is eventually implemented as aconcrete Javascript object, rather than as yet another proxy.
To summarize, this section reveals two important designprinciples of proxy-based intercession APIs:
Meta-level shifting: a proxy-based intercession API
supports shifting to a new meta-level by implementing ahandler as a proxy.
Meta-level funneling: the interaction between a proxy
and its handler determines the API of the meta-meta-level.
If the proxy only invokes the handler’s traps, then the metameta-level API collapses to a single trap through which all
meta-level operations are funneled.
Meta-level funneling is demonstrated in Section 5.1.

4.6

Selective Interception

A proxy-based intercession API introduces a tradeoff between what operations can be intercepted by proxy handlers on the one hand, versus what operations have a reliable
outcome from the language runtime and the programmer’s
point of view. We already briefly touched upon this issue
in Section 4.4 when discussing how proxies interact with operations that make objects non-extensible, sealed or frozen.
As we have discussed, the fix trap enables a proxy handler
to dynamically decide whether or not to commit to a fixed
representation.
Some operations may be so critical that we don’t wantproxies to ever influence their outcome. One such operation
is testing for equality. In Javascript, the expression a === b
determines whether a and b refer to identical objects. The=== operator comes with a number of guarantees that are
implicitly taken for granted by programmers. For example,=== on objects is commutative, transitive, symmetric anddeterministic (it always reports the same answer given thesame arguments). Furthermore, testing a and b for equality
should not grant a access to b or vice versa. For all thesereasons, we decided not to enable proxy handlers to trap ===.
Proxies cannot influence the outcome of this operation.
As in the case of ===, the fact that an operation should bedeterministic is a requirement for many operations. To uphold determinism, the operation should not trap to a proxy
handler. However, just because an operation should have adeterministic result does not mean that it cannot be reliablyintercepted. When an operation requires a deterministic,
stable result, this result can be provided once to the proxywhen it is created. There are two concrete examples of thisin our intercession API.
The first example is that proxies must commit to their prototype upfront. Recall from section 4 that the second argument to Proxy.create denotes the proxy’s prototype. Thishas two important effects: the first is that ECMAScript’s
built-in operation Object.getPrototypeOf remains deterministic, even when applied to a proxy. The second is thatproxies cannot influence instanceof when they are usedas the left-hand operand: to determine whether proxy instanceof Foo is true, the implementation need only lookat the prototype of proxy, which using our API it can dowithout consulting the proxy handler.
The second example is that proxies have to decide upfront whether they will proxy a plain object or a function
object. By requiring proxies to commit to representing either an object or a function at proxy creation time, the language runtime is able to uphold important invariants that
it would have to forfeit if it would have to consult the handler. For example the expression typeof o should evaluate
to "function" if o is a function, and to "object" otherwise.
Also, functions always inherit from Function.prototype.
The distinction between object and function proxies allows
our API to enforce these invariants.
Another aspect to take into account when deciding whether
or not a certain operation can be intercepted by a proxy is
the fact that it may lead to arbitrary code being run inplaces where the language runtime or the programmer did
not expect this to. For example, without our intercession
API, the expression name in obj does not trigger Javascript
code. Since in is a primitive operation, the evaluation ofthis expression occurs entirely within the language runtime,
without it ever having to run non-native code. Proxies maychange these assumptions.
In our particular case, proxies enable non-native code to
run while evaluating the in operator, the delete operator
and for-in loops, yet they preserve the integrity of === andtypeof. It’s hard to make general claims about what operations should generally be interceptable and which shouldnot. It is a design decision that depends on the particularsof the system at hand. The point that we want to make isthat it’s important to recognize that such a tradeoff exists.
This brings us to the following design principle:
Selective interception: determine what properties ofan object should be to committed to upfront. If these properties are required to be stable but may be configurable, turnthem into parameters of the proxy constructor rather thanretrieving them via a handler trap.

4.7

Fundamental versus derived traps

As shown in Table 1, our handler API defines 12 traps
in total, each intercepting a different Javascript operation

applied to a proxy. However, some traps can be expressedin terms of the semantics of other traps (i.e. the semanticsof the operation which they intercept is subsumed by thesemantics of some other operation). We refer to such traps
as derived traps, since their implementation may be derived
from other, more fundamental traps.

4.7.1

Efficiency: the case for derived traps

Our handler API is comprised of 7 fundamental traps and
5 derived traps. If 7 traps are sufficient to faithfully emulate
an object, why define 12 of them? The reason is efficiency:
implementing a derived trap directly may be more efficientthan using its default implementation based on the corresponding fundamental trap. In our particular design, asa rule of thumb, we introduced derived traps only if theirimplementation could intercept the operation with fewer allocations than the corresponding implementation based on
the fundamental trap.
As a concrete example, consider the expression name inobj. The in operator returns a boolean indicating whether
obj defines or inherits a property named name. If obj is a
proxy, this operation triggers the handler’s has(name) trap,
which should return a boolean. has is a derived trap: its semantics can be directly derived from the fundamental trap
getPropertyDescriptor which, given a property name, returns either a property descriptor for a property corresponding to that name if it exists, or returns undefined otherwise.
Clearly, has could have just been defined implicitly as:
has: function(name) {return this.getPropertyDescriptor(name) !== undefined;}

The downside of this implementation is that getPropertyDescriptor must needlessly allocate a property descriptor
object if the named property exists. A custom implementation of has can often avoid this allocation.
While defining the intercession API, we found that there
is another force at play that can prevent designers from introducing a derived trap, even if it were more efficient.

4.7.2

Consistency: the case against derived traps

One reason not to introduce a derived trap, even in theface of improved efficiency, is that introducing the distinction may break language invariants that may surprise thelanguage runtime and programmers.
Once the API provides a proxy handler with both a fundamental and a derived trap, there is no guarantee that a
proxy handler will effectively make sure that the derived trap
is indeed semantically equivalent to the canonical derived
implementation. For example, if a proxy handler is given
the option to override both the has and the getPropertyDescriptor traps, it is not enforced that these traps return
mutually consistent results. Without proxies, the programmer can count on the invariant that name in obj is equivalent to Object.getPropertyDescriptor(obj, name) !==undefined. If obj is a proxy, this invariant is no longer guaranteed: has could return true and getPropertyDescriptor
could nevertheless return undefined.
For proxies, both the language runtime and programmers
can no longer count on such invariants being upheld, although a “correct” handler should attempt to uphold them.
Even if the handler implementor does not intend to violate
the invariants, the distinction between fundamental and derived traps nevertheless introduces a maintenance cost: ifeither of the two traps is changed or gets overridden in adelegating object, the other trap should be changed or overridden in a compatible way.
The requirement of mutual consistency between operations is an opposing force in the decision of whether or not
to introduce a derived trap for a certain operation. Whether
the benefits of efficiency outweigh the mutual consistency ofoperations is, again, based on the particulars of the systemand there is no general answer.
Fundamental versus Derived Traps: an intercession
API faces a tradeoff between defining a minimal API consisting only of fundamental traps versus defining a more complex API that introduces derived traps. Derived traps may
be more efficient, but may break invariants that hold between the operations corresponding to the fundamental andthe derived trap.

4.8

Handler Encapsulation

A Proxy-based intercession API encapsulates its handler
if, given a reference to a proxy, one cannot gain direct access to the proxy’s handler. For our proxy API, this is thecase. If a handler can be encapsulated behind its proxy, thehandler can ensure that its traps can only ever be invokedby manipulating its corresponding proxy.
If the handler explicitly wants to be accessible from its
proxy, it can cater to such access itself without violating
stratification. The handler could register the association between its proxy and itself in a registry. Objects with accessto this registry and to the proxy can then look up the handler, using the proxy as a key. Such a registry plays a similarrole in our architecture as mirror factories in a mirror-basedarchitecture (see Section 7.2).

4.9

Uniform Intercession

Eugster [8] introduces the term uniform proxies to denote
an object model in which objects of all types can be proxified. More generally, we state that an intercession API is
uniform if it enables the creation of proxies for all possible types of values in the language. Javascript proxies do
not support uniform intercession, since they are only able
to proxy for objects (object proxies) or functions (function
proxies). Primitive values cannot be proxified.
The advantage of having a uniform intercession API isthat it does not restrict programmers to use only language
values that can be proxied. Conversely, it enables metaprogrammers to potentially use the intercession API on morebase-level objects.

5.

ACCESS CONTROL WRAPPERS

We now show how to apply our Proxy API to implement
generic wrappers for access control purposes.

5.1

Revocable Object References

Say an object Alice wants to hand out to Bob a reference
to Carol. Carol could represent a precious resource, andfor that reason Alice may want to limit the lifetime of thereference it hands out to Bob. In other words, Alice wants
to have the ability to revoke Bob’s access to Carol.
One can implement this pattern of access control by wrapping Carol in a forwarder that can be made to stop forwarding. Such a forwarder is also known as a caretaker [17].
Without proxies, the programmer is forced to write a distinct caretaker for each kind of object that should be wrapped.

Proxies enable the programmer to abstract from the detailsof the wrapped object’s protocol and instead write a genericcaretaker2 :function makeRevocableRef(target) {var enabled = true;
return Object.freeze({caretaker: Proxy.create({
get: function(rcvr, name) {if (! enabled) { throw new Error(”revoked”); }return target[name];},
has: function(name) {if (! enabled) { throw new Error(”revoked”); }return name in target;},// ... and so on for all other traps}, Object.getPrototypeOf(target)),revoker: Object.freeze({
revoke: function() { enabled = false; }
})
});
}

Given this abstraction, Alice can now wrap Carol in a
revokable reference, pass the caretaker to Bob, and hold on
to the revoker such that access can later be revoked.
The caretaker is a proxy whose handler is very similar to
the generic forwarder defined in Section 4 except that it explicitly checks the enabled flag before forwarding. As notedin Section 4.5, this is a pattern that can itself be abstractedby shifting meta-levels once more. If the caretaker’s handler is itself a proxy, the meta-level handler can funnel all
meta-level operations through a single get trap:
function makeRevocableRef(target) {var enabled = true;
var fwdHandler = makeForwardingHandler(target);var baseHandler = Proxy.create({
get: function(rcvr, name) {if (! enabled) { throw new Error(”revoked”); }return fwdHandler[name];}});
return Object.freeze({caretaker: Proxy.create(baseHandler,Object.getPrototypeOf(target)),revoker: Object.freeze({
revoke: function() { enabled = false; }
})
});
}

A limitation of the above caretaker abstraction is that
objects exchanged via the caretaker are themselves not recursively wrapped in a revocable reference. For example, if
Carol defines a method that returns this, an unwrapped reference may be exposed to Bob, circumventing the caretaker.
This is an instance of the two-body problem [8], the fact thatwrappers may be “leaky” because the wrapping proxy andthe wrapped object are distinct entities. The abstraction
discussed in the following section addresses this issue.

5.2

Membranes

A membrane is an extension of a caretaker that transitively imposes revocability on all references exchanged via
the membrane [13]. In the following implementation, we
make use of meta-level funneling once more to centralize
the access control in a single trap:

2
For brevity, we left out the case in which target is a function, requiring the caretaker to be a function proxy.

function makeMembrane(initTarget) {var enabled = true;
function wrapFunction(f) {return function() { // variable−argument function
if (! enabled) {throw new Error(”revoked”);}
try {return wrap(f.apply(wrap(this), toArray(arguments).map(wrap )));} catch (e) { throw wrap(e); }}}function wrap(target) {//primitives provide irrevocable knowledge, no need to wrap themif (isPrimitive(target)) { return target; }var fwdHandler = makeForwardingHandler(target);var baseHandler = Proxy.create({
get: function(rcvr, name) {return wrapFunction(fwdHandler[name]);}});if (typeof target === ”function”) {var wrappedF = wrapFunction(target);return Proxy.createFunction(baseHandler, wrappedF);} else {return Proxy.create(baseHandler,wrap(Object.getPrototypeOf(target)));}}return Object.freeze({wrapper: wrap(initTarget),revoker: Object.freeze({
revoke: function() { enabled = false; }
})
});
}

A membrane consists of one or more wrappers. Everysuch wrapper is created by a call to the wrap function. Notethat all wrappers belonging to the same membrane sharea single enabled variable. Assigning the variable to false
instantaneously revokes all of the membrane’s wrappers.
Recall that this example makes use of meta-level funneling. The baseHandler is itself a proxy with a handler. This
handler need only implement a get trap that returns a metalevel trap (a function) that will immediately be invoked. To
be able to wrap the arguments of these invocations, the get
trap returns a wrapped version of the default forwarding
trap contained in the fwdHandler.
If the object to be wrapped is a function (or a method,
because Javascript methods are just functions), a function
proxy is returned whose call trap is a wrapped version of
the function, as defined by wrapFunction. The variableargument function3 returned by wrapFunction performs theactual wrapping of values that cross the membrane: arguments to methods cross the membrane in one direction, returned values or thrown exceptions in the other direction.
The membrane abstraction in Javascript enables innovative new ways of composing code from untrusted third parties on a single web page (so-called “mash-ups”). By loading
the untrusted code using a membrane-wrapped eval function, it becomes possible to isolate scripts from one anotherand from their container page.

Identity-preserving Membranes.
One problem with the above membrane abstraction isthat it fails to uphold object identity across both sides ofthe membrane. If an object o is passed through the same
3
In Javascript, arguments is an array-like object that contains all arguments passed to a function. It is used to define
variable-argument functions, and it is customary to convert
this object into a proper array first.

Proxies
Disabled
Operation
typeofinstanceof
calldeletehas
set
getenumeratea.38 µs.49 µs.47 µs.42 µs.41 µs.40 µs.38 µs
1.58µs

Proxies
Enabled(non-proxy)(proxy)bc = b/ade = d/b.39
1.02 .39
1.00.51
1.04 .52
1.01.47
1.00 .61
1.29.42
1.00 .71
1.69.41
1.00 .73
1.78.41
1.02 .74
1.80.39
1.03 .73
1.87
1.58
1.00 4.0
2.53

Table 2: Micro-benchmarks measuring overhead of
the Proxy API (c) and of a proxy wrapper (e).

membrane twice, an object on the other side of the membrane will receive two distinct wrappers w1 and w2, suchthat w1 !== w2, even though both wrap an identical object.
Likewise, passing w1 or w2 back through the membrane does
not unwrap them but rather wraps them again. These limitations can be addressed by having the membrane maintaintwo maps. The first maps objects to their wrappers, suchthat only one canonical wrapper is created per object. The
second maps wrappers back to the objects they wrap, allowing these objects to be unwrapped when they cross themembrane in the other direction.
Unfortunately, implementing these maps in Javascript is
impossible without introducing memory leaks. Ephemerontables are a proposed language feature for the upcoming ECMAScript standard that address this issue [14]. An ephemerontable is almost identical to a non-enumerable weak-key identity hashtable, but it avoids a crucial memory leak whencyclic dependencies exist between the keys and the values
stored in the table.

6.

IMPLEMENTATION

Andreas Gal has implemented a prototype of our proposed
Proxy API as an extension of Tracemonkey. Tracemonkey isan adaptation of Spidermonkey, Mozilla’s Javascript engine,that employs trace trees for aggressive optimization [10]4 .
We used the prototype implementation to get an initialidea of the overhead of our Proxy API. To this end, we performed the following micro-benchmark: first, we measured
the runtime of operations applied to regular objects in regular Tracemonkey, without the Proxy API extensions. Next,
we measured the runtime of the same operations on regular,
non-proxy objects in the Tracemonkey extension with support for proxies. Finally, we measured the runtime of theoperations on a proxy object whose handler is the default
forwarding handler from Section 4.
Table 2 shows results for a subset of the operations, obtained on a MacPro Dual-core Intel Xeon (2.66Ghz) with
4GB Memory, running Mac OS X 10.5.8, using the following version of the Tracemonkey prototype: http://hg.
mozilla.org/tracemonkey/file/eba4f78cdca4. Runtimes
shown are an average over multiple runs. We should notethat these results are obtained from a non-optimized proto4

At the time of writing, proxies are in the tracemonkey buildof the Firefox 3.7a5 pre-release at http://ftp.mozilla.
org/pub/mozilla.org/firefox/tinderbox-builds

type implementation. The final tracemonkey implementation may generate significantly different results.
The results in column (c) show that the addition of proxies in the virtual machine introduces no significant overhead
for non-proxy objects. Hence, proxies only introduce a performance penalty when they are needed.
The results in column (e) show the overhead of using proxies to implement generic wrappers. Traps are ordered fromleast to most overhead. Our results confirm that operations
like typeof and instanceof that do not consult the handler
introduce no overhead. The overhead of intercepting theother operations, save enumeration, is less than a factor of
2. The overhead comes from the additional invocation of thehandler method. Presumably, the get and set traps introduce the most overhead because they are the most optimized
for non-proxy objects. Enumeration is the only operationthat is more than twice as slow when applied to proxies.
This is because enumeration must now proceed by iteratingover a user-created array returned by the enumerate trap,
rather than using the built-in iteration algorithm.

7.

RELATED WORK

7.1

OO Intercession APIs: a comparison

In this section we summarize a variety of message-based,
object-oriented intercession APIs. We discuss whether andhow each of them upholds the design principles put forth in
Section 4. We do not claim that our survey is exhaustive,
but we believe the most representative intercession APIs arecovered. We briefly discuss the surveyed mechanisms andthen summarize which of our design principles they upholdin Table 3.
Java The Java 1.3 java.lang.reflect.Proxy API is amajor precedent to our Javascript proxy API. Java’s dynamic proxies can be used to intercept invocations on instances of interface types:
InvocationHandler h = new InvocationHandler() {Object invoke(Object pxy, Method m, Object [] args) {...}};
Foo proxy = (Foo) Proxy.newProxyInstance(aClassloader, new Class[] { Foo.class }, h );Here, proxy implements the Foo interface. h is an object
that implements a single invoke method. All method invocations on proxy are reified by calling the h.invoke method.
The main difference between Java proxies and Javascript
proxies is that Java proxies can only intercept method invocation. There are no other meta-level operations to trap.
For instance, since interfaces cannot declare fields, proxies
do not need to intercept field access.
The Java Proxy API is non-uniform: proxies can only beconstructed for interface types, not class types. As a result,proxies cannot be used in any situation where code is typed
using class types rather than interface types, limiting theirgeneral applicability. Eugster [8] describes how to extend
the Java Proxy API to work uniformly with instances of
non-interface classes. Next to the usual InvocationHandler,
proxies for class types have an additional AccessHandler totrap field access.
AmbientTalk The design of Javascript proxies was influenced by AmbientTalk mirages [16]. AmbientTalk is a
distributed dynamic language, with a mirror-based metalevel API. AmbientTalk enables intercession through mi-rages, which are proxy-like objects controlled explicitly by aseparate mirror object:def mirage := object: {...} mirroredBy: (mirror: {def invoke(receiver, message) { ... };def addSlot(slot) { ... };def removeSlot(slot) { ... };...
});The mirror is to the mirage what the proxy handler is
to a Javascript proxy. Like Javascript proxy handlers, mirrors define an extensive set of traps, enabling near-complete
control over the mirage.
E is a secure, distributed dynamic language [12]. In E,
everything is an object, but there are two kinds of objectreferences: near and eventual references. Similarly, there
are two message passing operators: immediate call (o.m() , a
synchronous method invocation) and eventual send (o<−m(),an asynchronous message send). Both operations are allowed on near references, but eventual references carry only
asynchronous messages. Because of this distinction, E has
two separate intercession APIs: one for objects and one forreferences.
E has a proxy-based API to represent user-defined eventual references [15]:def handler {to handleSend(verb :String, args :List) {...}
to handleSendOnly(verb :String, args :List) {...}
to handleOptSealedDispatch(brand) {...}}
def proxy := makeProxy(handler, slot, state);

This API is very similar to the one for Javascript proxies. The proxy represents an eventual reference, and any
asynchronous send proxy<-m() either triggers the handler’s
handleSend or handleSendOnly trap, depending on whether
the sender expects a return value.
The handleOptSealedDispatch trap is part of E’s trademarking system and is beyond the scope of this paper. The
slot argument to makeProxy can be used to turn the proxyreference into an actual, so-called resolved reference. Once a
reference is resolved, the proxy is bypassed and the handler
no longer consulted. It fulfills a role similar to the fix trap
described in Section 4.4. The state argument to makeProxydetermines the state of the reference. The details are outside the scope of this paper, but note that this allows theeventual reference proxy to determine its state without consulting the handler, similar to how Javascript proxies candetermine their prototype without consulting the handler.
E has a distinct intercession API for objects. A nonmethodical object is an empty object with no methods. Instead, its implementation consists of a single match clausethat encodes an explicit message dispatch:def obj match [verb, args] {# handle the message generically}

The variable obj is bound to a new object whose dispatch
logic, if any, is explicitly encoded in the match clause. An immediate call obj.m(x) will trigger this clause, binding verb
to "m" and args to a list [x].
Finally, it is worth noting that AmbientTalk inherits fromE the distinction between near and eventual references andthe distinction between immediate call and eventual send.
Unlike E, AmbientTalk has only one intercession API (mi-rages), but a mirage can represent both objects and eventual references, depending on how the handler implements
its traps (immediate calls and eventual sends each trigger aseparate trap).
Smalltalk Smalltalk-80 popularized generic message dispatch via its doesNotUnderstand: mechanism. Briefly, if
standard method lookup does not find a method corresponding to a message, the Smalltalk VM instead sends the message doesNotUnderstand: msg to the original receiver object. Here, msg is an object containing the message’s selector
and arguments. The default behavior of this method, inherited from Object is to throw an exception.
The doesNotUnderstand: trap is not stratified. It occupies the same namespace as application-level methods. This
lack of stratification did lead Smalltalk programmers to look
for alternative interception mechanisms. Foote and Johnson describe a particular extension to ParcPlace Smalltalk
called a dispatching class: “Whenever an object belonging
to a class designated as a dispatching class (using a bit in
the class object’s header) is sent a message, that object is instead sent dispatchMessage: aMessage.” [9]. Instances ofdispatching classes are effectively proxies, the dispatchMessage: method acting as the sole trap of an implicit handler.
Ducasse [6] gives an overview of the various message passing control techniques in Smalltalk. He concludes that doesNotUnderstand: is not always the most appropriate mechanism. Rather, he stresses the usefulness of method wrappers. This approach was elaborated by Brant et. al [4].
In this approach, rather than changing the method lookup,
the method objects returned by the lookup algorithm aremodified. This is possible because Smalltalk methods andmethod dictionaries are accessible from within the language.
The method wrapper approach is in many ways similar to
CLOS method combinations, enabling before/after/aroundaugmentation of existing methods. As their name suggests,
they are great for wrapping existing methods, but they seemless suitable to implement virtual objects and thus only support part of the use cases covered by doesNotUnderstand:.
Summary Table 3 shows that each of the aforementioned
intercession APIs has its own set of characteristics. It isdifficult to declare one API to be better than another one.
By their very nature, meta-level APIs are strongly tied totheir particular language. The tradeoffs made by these APIscannot be understood without reference to the idiosyncratic
constraints and motivations of their respective languages.
However, for an OO intercession API to be called robust,
we believe it should adhere to the principle of stratification.

7.2

Mirrors

This work is heavily influenced by the work on mirrors.
Bracha and Ungar [3] discuss the design principles of mirrorbased meta-level architectures. The principles of stratification and handler encapsulation as stated in this paper are
related to the corresponding principles for mirror-based architectures, but with a focus on how they apply to intercession rather than to introspection and self-modification.
Mirror-based architectures strive to decouple base-levelfrom meta-level code. Traditional reflection APIs usually
define access to the reflective interface of an object as partof that object’s own interface. A prominent example is the
getClass() method defined on java.lang.Object. The result is a tight coupling between the base-level object andits meta-level representation (in the case of Java the result-ing Class object). As a point in case, consider the difficulty of defining a local mirror for a remote object. In Java,the getClass() method, when applied to a remote object
proxy would invariably break the abstraction, as getClass()would reveal the proxy’s class.
Mirrors are meta-level objects that are manufactured by
objects known as mirror factories. To acquire a mirror on
an object o, one does not ask o for its mirror but rather asksa mirror factory for a mirror on o. The logic of what kindof mirror to associate with an object is not tied to that object’s representation. As discussed in Section 4.8, our Proxy
API does not enable clients of proxies to directly access thehandler through the proxy. If such access is required, thecleanest solution is to introduce a separate registry abstraction which can be regarded as a “handler factory”.
Systems with a mirror-based architecture include Self [19],Strongtalk [2], Newspeak [1] and the Java Debugger API.
Each of these architectures supports introspection and selfmodification, but they have very limited support for intercession. To the best of our knowledge, AmbientTalk’s
meta-level architecture based on mirages [16] was the firstto reconcile mirrors with support for intercession. Mirages
directly inspired our work on Javascript proxies. A mirage
is very much like our proxy. Its behavior is controlled bya separate handler, which in AmbientTalk is also a mirror.
The main difference between AmbientTalk and Javascriptin this regard, is that Javascript has no built-in notion of
mirrors or mirror factories.

7.3

Partial Behavioral Reflection

Partial Behavioral Reflection (PBH) [18] is a frameworkthat describes the spatiotemporal extent of reification. Reflex is an intercession API for Java, based on bytecode rewriting, that supports PBH. Reflex enables the definition ofmeta-objects for Java objects. A single meta-object can control multiple base-level objects. The spatial scope of metaobjects is delimited using three concepts: entity selection
enables a meta-object to specify what objects it will control(e.g. all instances of a class, or only a particular instanceof a class). Operation selection determines what particular
operations of the affected objects are reified (e.g. only field
access). Intra-operation selection enables reification to occur
only if the operation satisfies further conditions (e.g. only
reify calls to the foo method). Finally, temporal selection
controls the time during which a meta-object is active.
Reflex differs from our Proxy API in that it enables thecreation of meta-objects that can act upon objects not explicitly declared as proxies. Nevertheless, some aspects ofour proxy API can be understood in terms of PBH. Proxies induce a static form of entity selection: operations on
proxy objects are reified, operations on non-proxy objects
are not. Proxies may support temporal selection. For example, Javascript proxies only reify while they are in a trappingstate. Once fixed, the proxy no longer reifies operations. Finally, proxies enable a static form of operation selection:some operations on proxies (e.g. typeof) are never reified,
whereas others such as property access are always reified.
One could characterize Reflex as a meta-intercession API:
using Reflex, one can define many different specific intercession APIs, each with its own settings for entity, operationand temporal selection.

7.4

CLOS

What type of value can be virtualized?
JS proxies
Objects and functions
Java proxies
Objects whose static type is an interface or Object
AT mirages
Objects and eventual references
E proxies
Eventual references
E non-methodicals
Objects
ST doesNotUnderstand: Objects
Is the API stratified?
JS proxies
Yes, proxy versus handler
Java proxies
Yes, Proxy versus InvocationHandler
AT mirages
Yes, mirage versus mirror
E proxies
Yes, proxy versus ProxyHandler
E non-methodicals
Yes, non-methodicals have no behavior other than their match clause
ST doesNotUnderstand: No, proxy and handler are the same object
Is the handler encapsulated?
JS proxies
Yes
Java proxies
No, because of Proxy.getInvocationHandler(proxy)
AT mirages
Yes, but can get access indirectly via a mirror factory
E proxies
Yes
E non-methodicals
Yes
ST doesNotUnderstand: No, proxy and handler are the same object
Can intercession be limited in time (temporary intercession)?
JS proxies
Yes, by fixing the proxy (see Section 4.4)
Java proxies
No, proxies remain proxies forever
AT mirages
No, mirages remain mirages foreverE proxies
Yes, by resolving the proxied reference into a real reference
E non-methodicals
No, non-methodicals remain non-methodicals foreverST doesNotUnderstand: Yes, by replacing the object using Smalltalk’s become: primitive
Can one shift meta-levels?
JS proxies
Yes, if handler is itself a proxyJava proxies
Yes, if handler is itself a proxy
AT mirages
Yes, if mirror is itself a mirage
E proxies
No, handler cannot itself be an eventual reference proxy
E non-methodicals
No, non-methodicals have no handler
ST doesNotUnderstand: No, cannot trap invocations of doesNotUnderstand: itself
Is there a trap through which all operations can be funneled?
JS proxies
Yes, the get trap, for a meta-handler
Java proxies
Yes, the invoke trap, for any handler
AT mirages
Yes, the invoke trap, for a meta-handler
E proxies
Yes, if handler is a non-methodical object, its match clause funnels all operations
E non-methodicals
Yes, the match clause
ST doesNotUnderstand: Yes, doesNotUnderstand: itself, if class is otherwise empty (no implemented or inherited methods).
In this case, the class may not inherit from Object
Can all types of values in the language be virtualized (uniform intercession)?
JS proxies
No, primitives cannot
Java proxies
No, instances of non-interface classes and primitives cannot
AT mirages
Yes, all values are objects or references (mirages cover both)E proxies
Yes, all values are objects or references (proxies cover references)
E non-methodicals
Yes, all values are objects or references (non-methodicals cover objects)
ST doesNotUnderstand: Yes, all values are objects
Are there some operations applicable on proxies that cannot be intercepted (selective interception)?
JS proxies
Yes, identity, prototype and typeof are fixedJava proxies
Yes, identity, class, implemented interfaces and final methods inherited from Object
AT mirages
Yes, an object’s mirror is determined by a separate mirror factory object
E proxies
Yes, identity and the “state” of a reference are fixed
E non-methodicals
Yes, identity and “trademarks” (unforgeable types) are fixed
ST doesNotUnderstand: Yes, calls to methods implemented in or inherited by the defining class will not be intercepted
Is there a distinction between fundamental and derived traps?
JS proxies
Yes, see Table 1
Java proxies
No, invoke is the only trap
AT mirages
No, all traps are fundamental
E proxies
Yes, sendOnly can be derived from send
E non-methodicals
No, the match clause is the only trap
ST doesNotUnderstand: No, the doesNotUnderstand: method is the only trap
Table 3: Comparison of object-oriented intercession APIs

The Common Lisp Object System (CLOS) has an extensive MOP [11]. Because CLOS is function-oriented as opposed to a message-oriented, it is difficult to transpose thedesign principles described in this paper to CLOS. In CLOS,
computation proceeds mainly through generic function invocation, as opposed to sending messages to objects. The
dispatch mechanism of generic functions can be modified
via the MOP. However, if one simply wants to wrap existing methods, CLOS offers a method combination protocolthat can be used to insert behavior before, after or around
existing methods without modifying the protocol.

8.

CONCLUSION

This paper introduces an intercession API for Javascript
based on proxies, enabling the creation of generic wrappers
and virtual objects. We characterize our API as robust, primarily due to its stratified design and because it upholds
important invariants (pertaining to e.g. identity, runtime
types and tamper-proofness).
The second contribution of this paper is the identification of general design principles for proxy-based intercession
APIs. These design principles can be used to characterizesimilar APIs of other programming languages, as summarized in Table 3. The design principles put forward are:Stratification: traps are defined on a separate handler.
Selective interception: some operations are not trappedor their semantics is determined at proxy-creation time.
Derived traps can be more efficient than fundamental
traps but introduce the potential for inconsistencies.
Meta-level shifting: handlers as proxies shift meta-levels.
Temporary intercession: proxies can become fixed.
Handler encapsulation: a proxy encapsulates its handler.
Uniform intercession: every value can be proxified.

Acknowledgements
We thank the members of the ECMA TC-39 committee andthe es-discuss@mozilla.org community for their feedbackon our Proxy API. Thanks to Brendan Eich, Andreas Gal
and Dave Herman for detailed comments and to Andreas
Gal for his work on the Tracemonkey implementation.

9.

REFERENCES[1] G. Bracha, P. Ahé, V. Bykov, Y. Kashai, W. Maddox,
and E. Miranda. Modules as objects in newspeak. In
ECOOP ’10: Proceedings of the 24th European
Conference on Object Oriented Programming,
Maribor, Slovenia, LNCS. Springer Verlag, June 2010.
[2] G. Bracha and D. Griswold. Strongtalk: typechecking
smalltalk in a production environment. In OOPSLA’93: Proceedings of the eighth annual conference on
Object-oriented programming systems, languages, andapplications, pages 215–230, NY, USA, 1993. ACM.
[3] G. Bracha and D. Ungar. Mirrors: Design principles
for meta-level facilities of object-oriented
programming languages. In OOPSLA ’04: Proceedingsof the 19th annual Conference on Object-Oriented
Programming, Systems, Languages and Applications,pages 331–343, 2004.
[4] J. Brant, B. Foote, R. E. Johnson, and D. Roberts.
Wrappers to the rescue. In ECOOP ’98: Proceedingsof the 12th European Conference on Object-Oriented

Programming, pages 396–417, London, UK, 1998.
Springer-Verlag.
[5] D. Crockford. Javascript: The Good Parts. O’Reilly,
2008.
[6] S. Ducasse. Evaluating message passing control
techniques in smalltalk. Journal of Object-Oriented
Programming (JOOP), 12:39–44, 1999.
[7] ECMA International. ECMA-262: ECMAScript
Language Specification. ECMA, Geneva, Switzerland,
fifth edition, December 2009.
[8] P. Eugster. Uniform proxies for java. In OOPSLA ’06:
Proceedings of the 21st annual conference on
Object-oriented programming systems, languages, andapplications, pages 139–152, NY, USA, 2006. ACM.
[9] B. Foote and R. E. Johnson. Reflective facilities insmalltalk-80. In OOPSLA ’89: Conference proceedingson Object-oriented programming systems, languages
and applications, pages 327–335, NY, USA, 1989.
ACM.
[10] A. Gal. Efficient bytecode verification and compilation
in a virtual machine. PhD thesis, University ofCalifornia, Irvine, Long Beach, CA, USA, 2006.
[11] G. Kiczales, J. D. Rivieres, and D. G. Bobrow. The
Art of the Metaobject Protocol. MIT Press,
Cambridge, MA, USA, 1991.
[12] M. Miller, E. D. Tribble, and J. Shapiro. Concurrency
among strangers: Programming in E as plan
coordination. In Symposium on Trustworthy Global
Computing, pages 195–229. Springer, 2005.
[13] M. S. Miller. Robust Composition: Towards a Unified
Approach to Access Control and Concurrency Control.
PhD thesis, John Hopkins University, Baltimore,
Maryland, USA, May 2006.
[14] M. S. Miller. Ephemeron tables straw-man proposal
for ES-Harmony, 2010. http://wiki.ecmascript.
org/doku.php?id=strawman:ephemeron_tables.
[15] M. S. Miller and K. Reid. Proxies for eventualreferences in E, 2009.
http://wiki.erights.org/wiki/Proxy.
[16] S. Mostinckx, T. Van Cutsem, S. Timbermont, and
E. Tanter. Mirages: Behavioral intercession in a
mirror-based architecture. In Proceedings of the
Dynamic Languages Symposium - OOPSLA’07
Companion, pages 222–248. ACM Press, 2007.
[17] D. D. Redell. Naming and Protection in Extensible
Operating Systems. PhD thesis, Department ofComputer Science, University of California at
Berkeley, Nov 1974.
[18] É. Tanter, J. Noyé, D. Caromel, and P. Cointe. Partial
behavioral reflection: spatial and temporal selection ofreification. In OOPSLA ’03: Proceedings of the 2003
Conference on Object-Oriented Programming Systems,
Languages and Applications, pages 27–46. ACM, 2003.
[19] D. Ungar and R. B. Smith. Self: The power ofsimplicity. In OOPSLA ’87: Conference proceedings on
Object-oriented Programming Systems, Languages andApplications, pages 227–242. ACM Press, 1987.