Hi Tom, this looks very promising. Some comments below; quoting the wiki page 
inline.

> * target is the object which the direct proxy wraps

Just checking: presumably this proposal doesn't allow for target to be a 
primitive, right? (Other than the special case of null you mention later.) 
I.e., this is still just a spec for virtualized objects, not for virtualized 
primitives.

> Unlike in the original proxy proposal, any non-configurable properties 
> reported by the handler of a direct proxy are also stored on the target 
> object. In doing so, the proxy ensures that the target object always 
> “matches” the proxy as far as non-configurability of properties is concerned.

I'm confused about this. Do you mean that, if the proxy provides a property 
descriptor that said .foo is non-configurable, the semantics automatically adds 
the property to the target and from that point on, all operations involving 
.foo go through the target instead of through the handler? Does this let you 
faithfully virtualize the .length property of arrays? How? You still want to 
intercept gets and sets, but you want getOwnPropertyDescriptor to say that it's 
a data property, not an accessor property.

> When a direct proxy is made non-extensible, so is its target. Once a direct 
> proxy is non-extensible, all properties reported by the handler are stored on 
> the target, regardless of their configurability. This ensures that the 
> handler cannot report any “new” properties, since the target object will now 
> reject any attempt to add new properties.

I'm still confused about when operations go through the handler and when they 
go through the target. If they can still go through the handler after making 
the proxy non-extensible, then what's to stop the handler from making it look 
like new properties are appearing?

> A direct proxy may acquire some of its internal properties from its target 
> object. This includes the [[Class]] and [[Prototype]] internal properties:

This is awesome.

> * typeof aProxy is equal to typeof target.


To make sure I'm following along correctly: the typeof result can only be 
"object" or "function", right?

> We could even allow for direct proxies to acquire non-standard internal 
> properties from their target object. This could be a useful principle when 
> wrapping host objects.

This seems important in order to make host methods work, e.g., the ones that 
access the [[Value]] property. I guess you could code around it by proxying 
those methods as well?

> For Direct Proxies, rather than adopting the handler_access_to_proxy strawman 
> that adds the proxy itself as an additional argument to all traps, we propose 
> to instead add the target as an additional, last, argument to all traps. That 
> allows the handler to interact with the target that it implicitly wraps.

You still might want access to the identity of the proxy itself, e.g., to store 
the object in a WeakMap. But as you guys have pointed out, you can store this 
in the handler object, which can still inherit its traps from prototype 
methods. So I guess this isn't critical.

> The protect trap no longer needs to return a property descriptor map...

This seems like a big deal to me. The property descriptor map could potentially 
be quite large.

> Proxy.stopTrapping()

This one makes me a little queasy. I'm sure you guys already thought of and 
dismissed the possibility of having Proxy.for(...) return a pair of a proxy and 
a stopTrapping() thunk that's tied to the one proxy. That's obviously got 
wretched ergonomics. But I'm not crazy about the idea of drive-by 
deproxification. Just my initial reaction, anyway.

> Both the call and new trap are optional and default to forwarding the 
> operation to the wrapped target function.

Nicely done! Much cleaner than Proxy.create and Proxy.createFunction.

> Proxy.startTrapping() (a.k.a. Proxy.attach)

I don't fully understand how this one works. Is it essentially a Smalltalk 
become, in the sense that the existing object is turned into a proxy, and its 
guts (aka brain) now become a different object that the proxy uses as its 
target?

So, this has obvious appeal; for example, it addresses the data binding use 
cases.

But I have some serious reservations about it. For one, tying the notion of 
"becomeability" to extensibility seems sub-optimal. I'm not sure you always 
want an object to be non-extensible when you want it to be non-becomeable. And 
a serious practical issue is whether host objects could be becomeable. I'm 
pretty sure that's going to be a serious problem for implementations.

> It’s still as easy to create such “virtual” proxies: just pass a fresh empty 
> object (or perhaps even null?)

Please, make it null. So much more pleasant (and avoids needless allocation).

(The only downside of allowing null to mean "no target" would be if you wanted 
to future-proof for virtualizable primitives, including a virtualizable null.)

>   Proxy.create = function(handler, proto) {
>     return Proxy.for(Object.create(proto), handler);
>   };
>   Proxy.createFunction = function(handler, call, opt_construct) {
>     var extHandler = Object.create(handler);
>     extHandler.call = call;
>     extHandler.new = opt_construct;
>     return Proxy.for(call, extHandler);
>   };


Doesn't this leak the property table of the call function? ISTM you want:

    Proxy.createFunction = function(handler, call, opt_construct) {
        var extHandler = Object.create(handler);
        extHandler.call = call;
        extHandler.new = opt_construct;
        var target = function(...args) { return call(...args); }; // new object
        return Proxy.for(target, extHandler);
    }

> We propose to bind the singleton forwarding handler to Proxy.forward.

Singleton within a single window (aka loader), or singleton shared across all 
windows (aka loaders)? I would expect the former.

> Advantages w.r.t. existing proxies

These are pretty awesome. :)

> * Proxy.for could be renamed to Proxy.create.

If we were not in a module setting, I would say just make Proxy a function 
(which behaves the same whether called via |new| or not), now that there's only 
one function needed. But we still need to figure out our naming conventions for 
the module-ized standard library, so maybe this is best decided after we have 
made headway on hat.

> * We may still want to make all traps mandatory rather than defaulting to 
> forwarding to the target:


Please, let's keep them optional. The future-proofing is going to be crucial, 
I'd wager. And it will make an enormous difference in ergonomics and 
palatability. The difference between saying here's a simple proxy:

    Proxy.for(obj, { get: function(key) { alert("getting " + key + "!") } })

and the current state of affairs is pretty huge.

Dave

On Oct 17, 2011, at 12:15 PM, Tom Van Cutsem wrote:

> Hi,
> 
> A couple of weeks ago, Mark and I sat together to work on a number of open 
> issues with proxies, in particular, how to make proxies work better with 
> non-configurable properties and non-extensible objects. The result is what we 
> call "direct proxies": in our new proposal, a proxy is always a wrapper for 
> another "target" object. By slightly shifting our perspective on proxies in 
> this way, many of the earlier open issues go away, and the overhead of 
> proxies may be substantially reduced in some cases.
> 
> I just finished a first draft of the strawman: 
> <http://wiki.ecmascript.org/doku.php?id=strawman:direct_proxies>. Feedback 
> welcome.
> 
> A prototype implementation that builds upon FF7 proxies is available also: 
> <http://code.google.com/p/es-lab/source/browse/trunk/src/proxies/DirectProxies.js>
> (note: I still need to thoroughly test this code, but it's otherwise an 
> accurate description of the semantics of direct proxies in terms of current 
> proxies.)
> 
> Mark and I will present this proposal in detail at the TC39 November meeting. 
> In the meantime, it may use some es-discuss eyeballs.
> 
> To whet your appetite, some advantages of direct proxies w.r.t. the current 
> proposal:
> - Enable emulation of non-configurable properties.
> - Enable emulation of non-extensible, sealed and frozen objects.
> - Proxies inherit [[Class]] from target. Enables faithful wrapping of Host 
> objects, Arrays, etc.
> - “Fixing” a proxy (i.e. stopping the handler from trapping), is decoupled 
> from “protecting” a proxy (making it non-extensible, sealed or frozen).
> - No need to distinguish between object and function proxies anymore.
> - All traps become optional.
> - Wrapping an existing object using a direct proxy creates less overhead, no 
> need to pass through an explicit ForwardingHandler anymore.
> - Proxies can't violate non-configurability / non-extensibility invariants. 
> This in turn enables safe transformation of regular objects into proxies 
> (Proxy.attach)
> - The default forwarding handler becomes a stateless singleton object.
> 
> Also, thanks to David Bruant for being an early sounding board and providing 
> some useful API tweaks.
> 
> Cheers,
> Tom
> _______________________________________________
> es-discuss mailing list
> es-discuss@mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to