On Jan 22, 2013, at 9:06 AM, Brandon Benvie wrote:

> Kevin Smith pointed out something I hadn't though about before but is obvious 
> in retrospect. The hazard that proxies bring to the language in general: 
> proxies make it possible to break the target object by inconsistently 
> handling forwarding during the operation of methods.
> 
>     class Counter {
>       constructor(){
>         this.value = 0;
>       }
>       increment(){
>         this.state = "working";
>         this.value++;
>         this.state = "idle";
>         return this.value;
>       }
>     }
>     
>     
>     const counter = new Counter;
>     
>     const counterfeit = new Proxy(counter, {
>       get(target, key, receiver){
>         if (key === 'value') {
>           throw 'break target';
>         }
>         return Reflect.get(target, key, receiver);
>       }
>     });
> 
> 
> While this may not be common, and the example is contrived, it seems like it 
> may be less uncommon when there's a trap that exists who's sole purpose is to 
> throw or not.
> 
> It is my opinion that the primary use case for private symbols is for 
> properties that proxies expressly shouldn't be given a chance, in any manner, 
> to corrupt or modify. They are likely used for sensitive internal state that 
> will only be accessed by methods or friend classes created in service of the 
> target.
> 
> A membrane becomes less valuable if breaking the target is an easily 
> accomplished accidental side effect. This is already visible in practice 
> today when you attempt to use WeakMaps to create private state for objects 
> and they are proxied, since the private state will be keyed on `this` in the 
> constructor which won't match `this` in methods invoked on the proxy.
> 

Right, more concretely:

let fooRegistry = new WeakMapl
class Foo {
   constructor() {
         fooRegistry.set(this,true);  //should really be in a @@create method 
to prevent counterfeiting
   }
   op() {
       if (!fooRegisty.has (this)) throw Error("Foo called on non-Foo object");
    }
}

let f = new Foo;
let pf = new Proxy(f, {});

f.op();   //no problem
pf.op();   //throws

This problem has nothing to do with private Symbols and is the same problem Tom 
and I talked about in 
https://mail.mozilla.org/pipermail/es-discuss/2012-December/027277.html 

As that thread discussed, the root cause is that the lookup that retrieved a 
method is a separate MOP level operation from the call of the method and there 
currently is no mechanism to coordinate the this value between those MOP 
operations.

On way to fix this would be to introduce a new MOP operation [[CallProperty]] 
that combines [[Get]]/[[Call]].  I know Brendan has opposed this when it has 
come up in the past (bad E4X experiences) but I'm not talking about any new 
user level semantics (other than making Proxy work reasonably).  The ordinary 
object definition of [[CallProperty]] would have exactly the same semantics as 
occurs for the [[Get]]/[[Call]] sequence that evaluates for expr.key()

Another way to address this issue avoids introducing a new MOP level operation. 
Instead we would introduce a new kind of Reference value that is used for the 
result of Proxy [[Get]] operations. The [[Call]] op would know about these 
special Proxy References and adjust the this value accordingly before invoking 
the function.  Something similar to that is currently in the spec. to support 
super based property accesses. 

I think, [[CallProperty]]  actually has less hair, but either one could be used 
to make default Proxy behavior be consistent forwarding to the target with the 
target value always substituted for this position proxy values.  We would get 
rid of the confusing and error prone combination of forwarding and delegation 
that is now the default for Proxy.

Allen


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

Reply via email to