Membranes, unmediated access to objects through Object.getPrototypeOf

David Bruant bruant.d at gmail.com
Tue Oct 9 06:25:24 PDT 2012


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

> [+markm]
>
> 2012/10/9 David Bruant <bruant.d at gmail.com>
>
>>  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)
>>
>
> Sorry, I don't get it. I coded up this example using a membrane that uses
> a dummy target and the invariant is preserved across membranes: <
> https://gist.github.com/3857930> (the code prints "true" twice)
>
True, my bad.


>
>
>> 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.
>>
>
> It's throw, leak or return a wrapper backed by a dummy target.
>
>
Indeed.


>
>> 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).
>>
>
> True, the extra object allocation sucks. But let's not naively state that
> you always need "twice as much memory":
> - first, the graph is membraned lazily: only objects that cross the
> membrane actually require a proxy. Wrapping a large graph in a membrane
> does not imply immediately copying the whole graph.
> - second, the dummy target stores wrapped versions of the original frozen
> values. It doesn't need to copy the frozen values themselves. If the target
> has a frozen property pointing to a large string value or huge array, the
> dummy points to a wrapper to the same large string value or huge array.
>
>
True. I admit the situation isn't as bad as I described it.


>
>> 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] ;-)
>>
>
> I don't see how this strengthens your case of voiding the identity
> guarantee. Everything you say above is true for the way membranes currently
> work.
>
>
>>  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.
>>
>
> I agree this is a (probably the only) reasonable way to relax the current
> invariant. Chaperones in Racket show that this is workable. I'd still like
> Mark to weigh in though. IIRC he had good reasons for not wanting to break
> the identity-invariants related to frozen properties. I think the grant
> matcher puzzle actually strengthens the case for not weakening the identity
> guarantees provided by Javascript today.
>
For a target, if a proxy wrapping target.[[Prototype]] can be returned by
the getPrototypeOf trap, then, someone who knows target.[[Prototype]] can
differentiate the wrapped version from the actual one thanks to identity.
Being able to differentiate is what protects from being confused by 2
objects which are presented to be the same (like in the grant-matcher
problem).
My point is that if the trap returns something else than the original
object, a defender who knows which object is expected can know that either
the object is a proxy or the genuine one, so it's not that big of a deal to
allow a different object. If you don't (and won't) know the original
object, what difference does it make to be lied to if you can't ever know
the truth?
As the dummy target example shows, actual identities don't make much of a
difference.

In a way, what I'm asking is to make dummy target "official" and more
restricted, by not allowing arbitrary objects, but only proxies with the
actual target in their target chain.

David
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20121009/675f9bcf/attachment.html>


More information about the es-discuss mailing list