Membranes, unmediated access to objects through Object.getPrototypeOf

David Bruant bruant.d at gmail.com
Tue Oct 9 02:48:11 PDT 2012


 [I re-ordered a bit your message before answering]

2012/10/8 Tom Van Cutsem <tomvc.be at gmail.com>

> Hi David,
>
> I really don't think this is an "elephant in the room".
>
It's reassuring, I was somewhat shocked by the thought something like this
could have been overlooked :-p


>
> Finally, note that a "naively implemented" membrane that just always wraps
> the prototype, without using a dummy target, would still not have leaked
> any references: this code would instead trip on the assertions that protect
> the "getPrototypeOf" trap as it tries to return a wrapped prototype.
>
> True, I forgot this second half about throwing, but the overall point
remains: Object.getPrototypeOf cannot be used within a membrane. The two
choices are leak or throw, neither being practical.
Within a membrane, the following object-identity equailty is broken:
Object.getPrototypeOf(new wrappedC) !== wrappedC.prototype.constructor
(either because the operation throws or the returned object is a wrapped
version)
My point is that by enforcing some identity invariants, we're breaking some
others.
Likewise, for frozen properties, membraned code either can't make use of
them (because they throw when wrapped values are presented) or
the unwrapped values leak. Once again, "throw or leak" is not a practical
choice.

We've encountered this before. The issue you point out is exactly the same
> issue with regard to the "get" trap having to return an identical value (as
> per [[SameValue]]) for non-configurable own properties of the target.
>
> The solution for wrapping non-configurable own properties was to introduce
> a "dummy" target on which to store wrapped versions of the properties. The
> solution to wrapping [[Prototype]] is to similarly use a dummy target whose
> prototype is the wrapped prototype of the real target.
>
...and well, creating an entire new subgraph of proxies over dummy targets
doesn't look very practical either. Basically, for a fully frozen graph,
you need twice as much memory to membrane it (once for the original
targets, once for the dummy targets).

Your proposed alternative involving the target-chain is the solution
> employed by Racket chaperones [1]. Mark and I have been hesitant to use
> this solution as it violates invariants pertaining to object-identity in
> Javascript. For example, using the current proxy spec and assuming no
> mutable __proto__ (e.g. Object.prototype.__proto__ was deleted), you're
> guaranteed that Object.getPrototypeOf(obj) always returns the same (as per
> "===") object. The chaperone solution voids that guarantee.

Why do we need this guarantee anyway? From inside a membrane, objects you
know about are wrapped, you're not able to differentiate the
target.[[Prototype]] from the one that's been returned because you never
have access to the original target.[[Prototype]]. Even if you did acquire
access to this object by another mean, having a different identity is
actually a feature, especially when it comes to solving some problems where
identity is the only element that can enable differenciation [1] ;-)

Just to clarify, when I ask "Why do we need this guarantee anyway?", I
don't ask to be able to return any arbitrary object, just a proxy which has
the expected object in its target chain, which sounds like a reasonable way
to bend the identity equality invariant.

David

[1] http://www.erights.org/elib/equality/grant-matcher/
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20121009/28ffd912/attachment.html>


More information about the es-discuss mailing list