July 26, 2012 TC39 Meeting Notes

David Bruant bruant.d at gmail.com
Wed Aug 1 07:16:50 PDT 2012


Le 01/08/2012 09:07, Tom Van Cutsem a écrit :
> 2012/7/31 David Bruant <bruant.d at gmail.com <mailto:bruant.d at gmail.com>>
>
>     2012/7/31 Tom Van Cutsem <tomvc.be at gmail.com
>     <mailto:tomvc.be at gmail.com>>
>
>         [...]
>
>     I think I missed the *Name trap design in the notes.
>     Returning [name, value] looks very heavy to me. If you know a
>     secret once and can prove it once, you can know it and prove it
>     forever (and very likely will), so the API should take that
>     property into account.
>     One idea would be to have a particular property in handlers, like
>     "knownPrivateNames" (which could smartly be expected to be an
>     ES.next Set, or more accurately a WeakSet if this one ever gets
>     mentionned in the spec) and whenever an *Trap returns for a
>     particular private name, the after-trap checks whether you have
>     the private name in your knownPrivateNames set. That should be
>     enough to prove you know the secret. When you get to a new private
>     name, put it in the knownPrivateNames set.
>     Even in the "return [name, value]" design, one needs to store
>     known private names somewhere anyway and it'll likely be on the
>     handler anyway too :-) So it may be a good idea to make this
>     storage "official" and make it a tool to communicate with the JS
>     engine.
>     Maybe the details I propose are not perfect, but I think there is
>     a game-changer in the idea of a handler being able to share with
>     the JS implementation which secrets it knows.
>
>
> I don't like it. It introduces mutable state into the proxy-handler 
> protocol, which is currently fully functional.
I partially disagree. One of the reason I chose Set/WeakSet in my 
demonstration is that the after-trap code would only call 
Set.prototype.has (the built-in one, not the dynamic one for security 
reasons), leaving the API somewhat fully functional.
knownPrivateNames could be made a function with signature Name -> 
Boolean (I would prefer too), but if the after-trap code calls it with 
the private name as argument, it leaks the private name, so that cannot 
work... or maybe there is a way.

An idea to have knownPrivateNames or rather isPrivateNameKnown a 
function and make sure this function doesn't leak private names to the 
handler would be to enforce isPrivateNameKnown to be a bound function of 
the built-in Set.prototype.has. The after-trap can make sure of that by 
comparing [[TargetFunction]] (which cannot be faked by user code) and 
the built-in capability.
As far as I can tell, it would work also with function proxies if the 
target is such a bound function, so this is membrane-proof.

> The proxy makes a minimum of dependencies on the handler's behavior, 
> and only interacts with it via property access of trap names (crucial 
> for double lifting).
The "isPrivateNameKnown" property could also be only interacted with 
through property access of trap names.

> Also, since a handler's properties may be mutable, you have to account 
> for the fact that a trap can be updated, thus there is the potential 
> issue of the handler's internal state growing out of date.
As you're saying below, handlers will often need weakmaps to tack 
additional state, so guarding internal state consistency is already a 
problem in the current setting.

> It may very well be that handlers will often need to resort to 
> WeakMaps to track additional state, but I'd prefer that to be explicit 
> in the code rather than buried implicitly in the Proxy API.
I'm not sure I understand your point. If it's on the handler, it's not 
buried implicitely. I even argue that if there is an "official" place to 
put the state, it makes the code more consistent and more easy to read.

(Also, isPrivateNameKnown can be extended to WeakMap.prototype.has if 
necessary)

>
>         AFAICT, the only two useful things a handler can do when it
>         intercepts a private name it knows nothing about is:
>
>         1) ask the proxy to forward
>
>         2) throw
>
>     Interestingly, you do not mention the public counterpart here :-)
>     Digging a bit deeper, from a trap point of view, if you get to
>     know 2 unique names for which you don't know the private part,
>
>
> Hold on, terminology check: unique names wouldn't have a private part. 
> For any unique name n, n.public === n.
Sorry, i was a bit confusing here. I did mean unique name, but from a 
proxy point of view. When user code tries to [[Set]] a value to a proxy 
with a private name, the proxy only gets to know a unique name in the 
current design. That's what I meant by "get to know 2 unique names". 
"get to know 2 unique names passed by the before-trap code as a 
translation of the private name... for which you don't know the private 
part".


>     then I don't think you can make any use of this information. Can
>     you do a more relevant choice (forward or throw) based on the
>     different unique name identities? I can't think of any now. From a
>     trap point of view, you just have 2 unique, unforgeable and
>     useless tokens, you can differnciate them thanks to identity, but
>     that's as far as it gets, so I agree with your analysis here.
>     Certainly trapping for private names, if it's to offer these two
>     choices, is valuable, so I take back the idea of not trapping for
>     private names. But I think i would take a different direction for
>     the trap design. Combined with the above idea of sharing a
>     knownPrivateNames set with the JS engine, what could happen is the
>     following:
>     1) regular get/set/delete/... traps even for unique names and
>     private names you have proven to know (since you have proven to
>     know the private name, they are passed directly, no need for a
>     public counterpart)
>     2) *Name traps when you don't know the private name. This trap
>     doesn't have the public part as argument (since there is no use
>     for it) but still leaves you the 2 choices of asking to forward or
>     throwing.
>
>     What do you think?
>
>
> Part of the reason why we decided to fork the regular traps into 
> additional *Name traps is that we wanted to keep the "type signature" 
> of the existing traps unmodified. Your proposal 1) would change the 
> type of the "name" argument from String to (String | Name). So a 
> programmer might still need to do a case-analysis in the body of each 
> trap.
Why would a programmer do that? Will the built-ins 
([[DefineOwnProperty]], [[Get]], etc.) do case-analysis to distinguish 
string and names? If they don't, I don't really see why the programmer 
would. In most cases, one will just forward to 
Reflect.trap(stringOrName, ...).
It's actually very likely that in specifying the default *Name traps, 
they will have the exact same code than their string counterpart, the 
only difference will be that the passed values have different types.
The argument I'm trying to make is that essential internal methods (as 
per terminology in [1]) will be polymorphic and there is no reason why 
traps shouldn't be.

> With the split traps, the old traps continue to work fine with 
> Strings. The *Name traps work with both private and unique names. If a 
> *Name trap wants to distinguish between private/public names, it can. 
> But the beauty of the |name.public === name| trick for unique names is 
> that all unique names can be effectively treated as private names 
> (Liskov substitutability), so a case-analysis is not always needed.
I agree it's a nice property.

David

[1] http://wiki.ecmascript.org/doku.php?id=strawman:subclassable-builtins
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120801/4efd8143/attachment-0001.html>


More information about the es-discuss mailing list