Notification proxies (Was: possible excessive proxy invariants for Object.keys/etc??)

Allen Wirfs-Brock allen at
Sun Dec 2 11:58:57 PST 2012

On Dec 2, 2012, at 11:40 AM, Tom Van Cutsem wrote:

> 2012/12/2 Mark S. Miller <erights at>
> I think we can rescue around wrapping. I'll call this approach "opaque
> around wrapping". The idea is that the proxy passes into the handler
> trap a proxy-generated no-argument function (a thunk) called "action".
> Interesting. I had thought of a similar approach, but hadn't pursued it because I thought allocating a thunk per trap invocation would be deemed too expensive. I'll go ahead and sketch the design, which I think corresponds closely with your approach B)

This is my first reaction too.  I want Proxies to be lightweight enough so they really can be used be used for things like self hosting built-ins and web API objects.  The need to create an pass in a action thunk on each trap calls initially feels too expensive.

But that is only a first reaction.  Perhaps the expense really isn't that great. Or perhaps a lot of the cases where I have assumed the Proxies would be needed could be handled by lightweight mechanisms such such as the @elementGet/@elementSet hooks described in  (see Implementing Built-inArray without Proxy and Rationalizing DOM HTMLCollections examples)


> First, from the point-of-view of the trap implementor, things would work as follows (I show the "defineProperty" trap only as an example)
> All traps take an extra thunk as last argument, which I will name "forward", since calling that thunk has the effect of forwarding the operation to the target, returning the original result.
> defineProperty: function(target, name, desc, forward) {
>   // before
>   var result = forward();
>   // after
>   return result;
> }
> Now, from the point-of-view of the proxy implementation, how is the above trap called?
> // in the proxy, intercepting Object.defineProperty(proxy, name, desc)
> // where proxy = Proxy(target, handler)
> var result = Uninitialized;
> var thunk = function() {
>   if (result === Disabled) throw new Error("can only call forward() during trap invocation");
>   if (result !== Uninitialized) throw new Error("forward() can be called only once");
>   result = Reflect.defineProperty(target, name, desc); // forward to target
>   return result;
> };
> var trapResult = handler["defineProperty"].call(handler, target, name, desc, thunk);
> if (result !== Uninitialized && result === trapResult) return result; // no invariant checks when original result is returned
> if (result === Uninitialized || result !== trapResult) { /* do invariant checks */ ; return result; }
> result = Disabled; // ensures one cannot call forward() outside of dynamic extent of the invocation
> I'll answer your questions for the above design:
> Open questions that take us to different design possibilities:
> 1) what happens if the trap does not call action?
> Invariant checks are performed on the returned result.
> 2) what happens if the trap calls action more than once?
> An exception is thrown.
> 3) do actions ever return anything, in case the trap wants to pay
> attention to it?
> Yes, it returns the value of the operation, applied to the target.
> 4) are there any return values from the trap that the proxy would pay
> attention to?
> Yes. If the trap returns the original result, it doesn't do any extra checks. Otherwise it does.
> 5) if the original operation performed by action throws, should the
> action still just return to the trap normally?
> A thrown exception would escape and abort the entire operation, unless the trap wraps the call to forward() in a try-catch block.
> 6) what happens if the trap throws rather that returning?
> Thrown exceptions are always propagated to clients.
> I prefer #A to #B as it gains the full benefits of simplifying the
> overall spec and implementation. However, #B still seems better than
> current direct proxies, as the normal case gets target-maintenance and
> invariant checking for free.
> I agree that #B doesn't really simplify anything in the spec (the invariant checks are still there sometimes).
> I'm not sure if it is inherently better than the current design though: we gain performance in terms of avoiding invariant checks, but we lose performance by having to allocate a per-call thunk, + the API becomes more complex (an additional parameter to every trap)
> I do think that passing an action or "forward()" thunk to a trap as extra argument beats David's proposed "throw ForwardToTarget" trick in terms of elegance and usability (maybe not in terms of performance).
> Cheers,
> Tom
> _______________________________________________
> es-discuss mailing list
> es-discuss at

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the es-discuss mailing list