Last week, I finally finished a paper that not only describes the new API in some detail, but also describes the principles that helped steer our design. From the paper’s abstract:
Some history: early 2010, Mark and I proposed a first version of our Proxy API that got accepted as a Harmony proposal. While that API was sufficient to cover its stated goals, it suffered from a number of drawbacks:
- Proxies could not emulate non-extensible, sealed or frozen objects. Proxies were always extensible, and could not advertise non-configurable properties.
- The handler API distinguished between what we called “fundamental” and “derived” traps, where the former were mandatory and the latter were optional. The interaction between fundamental and derived traps was subtle, and always having to implement all fundamental traps was cumbersome.
- A large number of proxy use cases involve wrapping an existing “target” object, to which intercepted operations are forwarded. These use cases were cumbersome to write, requiring an accompanying ForwardingHandler to be able to forward operations.
- Due to various spec. details, we ended up making a distinction between “object proxies” and “function proxies”, where only function proxies could emulate functions. This introduced irregularity in our API.
Despite these drawbacks, the API was well-received and prototyped, very early on in Mozilla’s tracemonkey engine (since Firefox 4, docs are here) and later also in Google’s v8 engine.
Late 2011, when Mark visited our lab in Brussels, we substantially revised our earlier API to address the above issues (in all honesty, the first issue was what got us started, the other issues were fixed somewhat as a consequence of our refactoring). We referred to our revised API as direct proxies, since in the revised API, a proxy always directly refers to a wrapped target object, as opposed to the old API in which wrapping was indirect (the proxy referred to the handler only, and the handler would refer to the target object).
Direct proxies solve the above issues, and for many simple use cases (i.e. those involving wrapping other objects), require considerably less code. For these reasons, our updated API was accepted as a replacement for the earlier API at the TC39 November 2011 meeting.
I’m currently in the process of migrating my DirectProxies.js shim to github. I’ve renamed it to reflect.js since this more accurately captures the fact that this is a shim for the entire upcoming reflection module, not only proxies. Emulation of direct proxies currently works in Firefox 8 only, but the Reflect API in general should run on other browsers as well (currently tested on tracemonkey and v8). Look for more tests soon.
One last point: in the paper, we use membranes as an example use case for proxies. Membranes are transitive wrappers: you start out wrapping a single object, and every object that crosses the membrane (typically via function parameter-passing or return values) is transitively wrapped. Each membrane comes with a single kill-switch that can instantly nullify all references that cross the membrane (this is called “revocation”). I have been experimenting with membranes and the new direct proxies API and things seem to be working out so far, but membranes deserve a post all by themselves. Stay tuned.