2012/10/9 Tom Van Cutsem <span dir="ltr"><<a href="mailto:tomvc.be@gmail.com" target="_blank">tomvc.be@gmail.com</a>></span><br><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="gmail_quote">[+markm]</div><div class="gmail_quote"><br></div><div class="gmail_quote"><div class="im">2012/10/9 David Bruant <span dir="ltr"><<a href="mailto:bruant.d@gmail.com" target="_blank">bruant.d@gmail.com</a>></span></div>
<div class="im"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="gmail_quote">
<div>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)</div>

</div></blockquote><div><br></div></div><div>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: <<a href="https://gist.github.com/3857930" target="_blank">https://gist.github.com/3857930</a>> (the code prints "true" twice)</div>
</div></blockquote><div>True, my bad.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="gmail_quote"><div class="im">
<div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="gmail_quote">
<div>My point is that by enforcing some identity invariants, we're breaking some others.</div><div>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.</div>

</div></blockquote><div><br></div></div><div>It's throw, leak or return a wrapper backed by a dummy target.</div><div class="im"><div> </div></div></div></blockquote><div>Indeed.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="gmail_quote"><div class="im"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="gmail_quote"><div>
<div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div>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.</div>


<div><br></div><div>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.</div>


</blockquote></div><div>...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).</div>

</div></blockquote><div><br></div></div><div>True, the extra object allocation sucks. But let's not naively state that you always need "twice as much memory":</div><div>- 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.</div>

<div>- 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.</div>
<div class="im">
<div> </div></div></div></blockquote><div>True. I admit the situation isn't as bad as I described it.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="gmail_quote"><div class="im"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="gmail_quote"><div>
<div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">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.</blockquote>


</div><div>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] ;-)</div>

</div></blockquote><div><br></div></div><div>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.</div><div class="im"><div>
 </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="gmail_quote">
<div>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.</div>

</div></blockquote><div><br></div></div><div>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.</div>
</div></blockquote><div>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).</div>
<div>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?</div>
<div>As the dummy target example shows, actual identities don't make much of a difference.</div><div><br></div><div>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.<br>
</div><div><br></div><div>David</div></div>