2011/4/5 David Bruant <[email protected]>

>  (reposting source code from previous message as plain text)
>
>    fix: function() {
>     // As long as target is not frozen, the proxy won't allow itself to be
> fixed
>     if (!Object.isFrozen(this.target))
>       return undefined;
>     var props = {};
>     var handler = this;
>     Object.getOwnPropertyNames(this.target).forEach(function (name) {
>       var desc = Object.getOwnPropertyDescriptor(this.target, name);
>       // turn descriptor into a trapping accessor property
>       props[name] = {
>                 get: function( ) { return handler.get(this, name); },
>                 set: function(v) { return handler.set(this, name, v); },
>          enumerable: desc.enumerable,
>        configurable: desc.configurable
>       };
>     });
>     return props;
>   },
>
> I really love this.
> The Object.isFrozen call is unfortunate but necessary since we only have
> one trap for the three extension-preventing methods. Throwing a TypeError on
> Object.preventExtension(proxy) because the target isn't frozen sounds a bit
> wrong to me though. Maybe that something could be passed as an argument to
> fix in order to discriminate which call happened.
> From what I understand, all interaction that set [[Extensible]] to false
> will end up calling the fix trap (+ TypeError||become) for proxies. As we
> can't know now what will later be added to the language to do that, it may
> be a good thing to be able to discriminate the origin of the fix trap call.
>
> Also, an idea for so-called "forwarding" proxies could be that the proxy
> forwards preventExtension/seal/freeze calls to the target too. It could be a
> nice thing if target is itself a proxy.
>

Indeed. I guess both approaches are equally feasible. In a secure wrapper
scenario, where the 'target' is a trusted object and untrusted code can only
access it through its forwarding proxy, I can imagine that it could be more
desirable not to forward freeze|seal|preventExtensions calls to the target.
In other scenarios, the opposite may hold true.


>
> Combining the ideas, we could have something like:
>
> fix: function(type){
>   if (type === Proxy.PREVENT_EXTENSION) // syntax example
>     Object.preventExtension(this.target);
> // a switch case could be a good idea too
>  // same code than you starting at "var props"
> }
>
> Even if not forwarding the call, it could allow better granularity than
> Object.isFrozen.
>

The design point of whether fix() should be able to distinguish between
preventExtensions, seal or freeze has come up before. IIRC, we didn't have a
use case that would justify it. Perhaps your comment about allowing the
forwarding handler to better query the state of its target object (via
isFrozen,isSealed,isExtensible) is the first proper use case.


> Now that I think about it, if forwarding Object.seal/freeze calls, the
> target and previously-proxy would act the same with very few exception. The
> non-configurability of all properties ensure that both keep the exact same
> property set will remain (non-deletable) with same [[enumerable]] for each
> (non-configurable). Your get/set method ensure that property access react
> the same in both objects. Besides Object.getOwnPropertyDescriptor and their
> identity, I can't think of any way to distinguish them.
>

Yep, that's the idea: fixed proxy and target are nearly indistinguishable.
The big difference being, of course, that all property access on the proxy
still traps the handler's get/set methods, which can continue to do
arbitrarily different things than just forwarding the property access, e.g.
logging, redirecting, ignoring, ...

Cheers,
Tom


>
> David
>
>
>
>
> 2011/4/5 Tom Van Cutsem <[email protected]>
>
>>
>>
>>  2011/3/24 Tom Van Cutsem <[email protected]>
>>
>>>   I just wanted to discuss forwarding patterns when it comes to
>>>> inheritance because the current forwarding proxy doesn't discuss the point
>>>> and when it comes to fixing the proxy, surprising results could arise 
>>>> (since
>>>> inheritance changes because target and proxy prototypes aren't the same
>>>> object). Actually, if forwarding handlers were part of the spec, would it
>>>> make sense to fix them? shouldn't "return undefined;" be the default fix
>>>> trap? (
>>>> http://wiki.ecmascript.org/doku.php?id=harmony:proxy_defaulthandler)
>>>> (just the default, of course, the user can change it anytime)
>>>>
>>>
>>>  "return undefined;" is definitely a sensible default. The current
>>> default goes a bit further: if the target object is frozen, it will fix the
>>> forwarding proxy in such a way that the proxy and the target have the exact
>>> same own properties. I don't recall us giving much thought to this default
>>> implementation though. In fact, I think it's broken since it doesn't take
>>> into account any custom proxying behavior. As an example: say I implement a
>>> "logging" proxy that just logs all operations performed on "target". It's
>>> sensible to extend the default forwarding handler to do this. Now, if both
>>> the target and the proxy are frozen, the logging behavior will be lost! The
>>> solution is for my logging proxy to override the fix() trap and perhaps
>>> return a property descriptor map that replaces each own property in target
>>> with a wrapped version that still performs the logging. If fix() returns
>>> "undefined" by default, the above scenario would lead to an exception
>>> instead of silently doing the wrong thing.
>>>
>>
>>  I've been thinking about the default implementation of the |fix()| trap.
>> Another sensible default, other than just returning undefined
>> unconditionally, is to freeze the proxy's structure, but to replace all
>> original properties with accessor properties that still trigger the proxy's
>> handler. A fixed forwarding proxy can then still intercept property access,
>> but other than its get and set traps, none of its other traps would be
>> called anymore:
>>
>>      fix: function() {
>>       // As long as target is not frozen, the proxy won't allow itself to
>> be fixed
>>       if (!Object.isFrozen(this.target))
>>         return undefined;
>>       var props = {};
>>       var handler = this;
>>       Object.getOwnPropertyNames(this.target).forEach(function (name) {
>>         var desc = Object.getOwnPropertyDescriptor(this.target, name);
>>         // turn descriptor into a trapping accessor property
>>         props[name] = {
>>                   get: function( ) { return handler.get(this, name); },
>>                   set: function(v) { return handler.set(this, name, v);
>> },
>>            enumerable: desc.enumerable,
>>          configurable: desc.configurable
>>         };
>>       });
>>       return props;
>>     },
>>
>>  (full forwarding handler available at <
>> http://code.google.com/p/es-lab/source/browse/trunk/src/proxies/forwardingHandler.js
>> >)
>>
>>  (untested because fix() isn't yet supported in fx4, see <
>> https://bugzilla.mozilla.org/show_bug.cgi?id=600677>)
>>
>>  Cheers,
>> Tom
>>
>
>
>
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to