Membranes, unmediated access to objects through Object.getPrototypeOf

Tom Van Cutsem tomvc.be at gmail.com
Tue Oct 9 03:57:09 PDT 2012


[+markm]

2012/10/9 David Bruant <bruant.d at gmail.com>

>
>> 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.
>

No, there's a third choice: use a dummy target and return its wrapped
prototype.


> 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)


> 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.


>
> 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.


>
> 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.

Cheers,
Tom
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20121009/d13d67aa/attachment-0001.html>


More information about the es-discuss mailing list