Proxy-for-array transparency issues

David Bruant bruant.d at gmail.com
Tue Jul 30 15:53:11 PDT 2013


Le 30/07/2013 22:19, Allen Wirfs-Brock a écrit :
>
> On Jul 30, 2013, at 12:40 PM, David Bruant wrote:
>
>> Le 30/07/2013 18:57, Allen Wirfs-Brock a écrit :
>>> ...
>>
>>> But it still doesn't work the way you would like for direct "call" 
>>> invocation or for things like Array.isArray.  The base issue for 
>>> either of these is that they don't indirect through the proxy 
>>> handler and hence the handler doesn't have an opportunity to replace 
>>> the proxy reference with the target reference.
>> Wouldn't replacing the proxy reference for the target reference be 
>> violating stratification?
>
> It depends upon the use for the proxy.  There is no stratification if 
> the Proxy is being used to implement the a virtual object.
>
>> Per exotic type traps would solve the issue generically, I think.
>
> It might for ES6 and for this use case.  But it doesn't generalize to 
> user defined classes with private state.
Spent 2 hours on this email refining a proposal. It started with the 
idea of per exotic type traps. Then moved to user-defined traps. Then to 
something more generic:

The problem we have is that some function have particular expectations 
on some of their arguments (including 'this') and proxies may violate 
these expectations. What if functions could "defend against" proxies by 
encouraging them to unwrap? new trap: "defensiveFunctionCall"

     var f = makeDefensiveFunction(a => a.a);
     var o = { a : 37 };
     f(o); // 37

     var p = new Proxy(o, {
         defensiveFunctionCall: (target, defensiveFunction, position) => {
             return; // means agreement for the target to be passed
             // to defensiveFunction as position-th argument
             // throw to disagree
         }
     });

     f(p) // calls the defensiveFunctionCall trap.
     // the proxy chooses to reveal its target to the function by returning
     // the function is called with the target as argument
     // 37 is returned

Class syntax could make methods defensive against proxies by default.

     class Secretive{
         private secret = String(Math.random()).substr(2)

         public getPartial(){
             return this.secret.substr(0, 3);
         }

         public changeSecret(){
             this.secret = String(Math.random()).substr(2);
         }

     }

     var s = new Secretive();
     console.log(s.getPartial()) // 3 digits

     var p = new Proxy(s, {
         defensiveFunctionCall: (target, defensiveFunction, position) => {
             return;
         }
     });
     console.log(p.getPartial()) // get trap, getPartial function is 
returned.
     // getPartial is about to be called with the p as 'this' (0th arg)
     // because it's defensive, the proxy's defensiveFunctionCall trap 
is called
     // return from the trap means agreement
     // the getPartial method is called on the actual instance, not the 
proxy
     // thus, the secret is never revealed to the proxy

What this takes is for the proxy to reveal its target. This shouldn't be 
a problem for built-ins as they won't leak the target, so they can all 
be "defensive against proxies" by default, I think (beware of chaining?).
A proxy for a Secretive (user-defined class with private state) has no 
reason to hide its target to a Secretive method if the target is a 
Secretive instance (because the class constructor created the target).


Please note that I have just described a new primitive that allows a 
defense against Sam's attack [1].


I imagine I've been overlooking details, but here is a generic solution 
that enable functions to defend against proxies (unwrapping the proxy), 
while providing a mechanism for proxies to keep their integrity 
(throwing in the trap).
In the case of a function against a proxy, one has to give up something. 
Either the function isn't defensive and lets the proxy "visit its guts" 
or the proxy lets the function see its guts (the target). But it's not 
possible for both to keep their integrity while the function runs 
against the proxy. And maybe that's a good trade-off?


> It also does solve the problem for identity based testing.  EG, using 
> a WeakMap for branding or holding private state.  The Proxy object and 
> its target object are not the same map key.
Is there any expectation to be able to solve identity-based branding 
with proxies?

>
>>
>>> Do you have new use cases, that motivate the design of an enhanced 
>>> Array.isArray?
>> I cited membrane transparency a couple of times. Tom seems to agree. 
>> Others have opinions?
>
> I was really asking about purpose of application level Array.isArray 
> tests.  What is the application really asking when it uses that test? 
> Is it really asking "array-like"?  What counts as "array-like"?
oh ok, sorry about that.
Then no, I don't think I have a new use case that motivates the design 
of an enhanced Array.isArray.

>
>>
>>>
>>> It would be quite easy to extend Array.isArray(obj) so that it 
>>> delegated back to obj with a @@areYouAnArray method such as we are 
>>> doing with @@isRegExp in certain places.  However, if we do that you 
>>> loose support for the original Array.isArray use case because any 
>>> object could decide to return true from Array.isArray.
>>> ...
>> If, for instance, you have an "areYouAnArray" trap that only works if 
>> the target is an Array (recursive definition with actual arrays as 
>> base case), then Array.isArray isn't spoofable by any object.
>> Mark and Tom once used a expression about direct proxies. Something 
>> like "invariant forwarding" or something like that, where the target 
>> imposes restrictions on the handler. This could be extended to exotic 
>> objects by adding per exotic type only traps.
>
> Let me reinterpret this as as request for a [[GetNomimalType]] trap.
That's not what I meant (I hesitated to use this example and I shouldn't 
have, sorry). I was bouncing on your @@areYouAnArray. This is a request 
for a [[thisTimeValue]] trap on Dates and a [[MapData]] trap (actually, 
maybe it's 4 traps) for Maps, etc.

David

[1] https://mail.mozilla.org/pipermail/es-discuss/2011-December/018901.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130731/e208e587/attachment.html>


More information about the es-discuss mailing list