[Harmony Proxies] LazyReadCopy experiment and invariant checking for [[Extensible]]=false

David Bruant david.bruant at labri.fr
Thu Jul 14 07:54:09 PDT 2011

Le 14/07/2011 16:23, Tom Van Cutsem a écrit :
> Hi David,
> 2011/7/13 David Bruant <david.bruant at labri.fr
> <mailto:david.bruant at labri.fr>>
>     Hi,
>     Recently, I've been thinking about the structured clone algorithm used
>     in postMessage
>     The browser has to do a copy of an object and pass it to the message
>     receiving context. In some cases, if the object has a lot of
>     properties,
>     it could be costly to do this copy. So I thought "hey, what about
>     wrapping it with a proxy and just hand the proxy?".
> This is a good use-case, and definitely one that Mark and I would like
> to explore with Proxies, as indicated by the "Eventual reference
> proxy" example on the harmony:proxies wiki page. Also, it's not just
> that copying objects is sometimes too costly, sometimes it's just not
> what the programmer wants. If the passed object is mutable, you don't
> want to create copies that can then diverge when e.g. distributed to
> separate web workers.
You need to create copies, don't you? Otherwise I think you introduce
non-determinism since web workers can have parallel execution. That's
the point of having a structured clone algorithm I thought.
Having mutable shared data across different contexts (potentially
running in parallel) is prevented, isn't it?

> Also, if the object represents some local resource that is unavailable
> to the receiver endpoint, you want to hand it a reference to the
> service, not a copy of some data.
>     For the rest of this use case, I will consider myself in the role
>     of the
>     browser (implementing postMessage and tryiing to do so in JavaScript)
>     and I may use the "become" primitive if needed since I'm in
>     "privileged
>     code".
>     Basically, the proxy has to forward "reads" to the target object, keep
>     the "writes" internally and make sure future "reads" are consistent if
>     some "writes" occured.
>     You can see my implementation of "LazyReadCopy" at:
>     https://github.com/DavidBruant/HarmonyProxyLab/tree/master/LazyReadCopy
>     (I discuss limitations below)
> I'm having trouble relating the "LazyReadCopy" proxy to your above use
> case. If I were to create a proxy that represents a reference to an
> object in another frame/web worker, I wouldn't cache writes in the
> proxy. I'm not saying LazyReadCopy never makes sense, it just seems to
> make less sense in the context you describe.
My intention is to give to the two contexts the impression they're
manipulating two different objects (because in postMessage, the message
in the receiving and sending contexts are two different objects)

>     So now we can send an object in the message Receiving Context
>     (RC). This
>     object is a copy of the one in the message Sending Context (SC).
>     The RC
>     can manipulate the object and this will have no noticeable effect
>     in the SC.
>     However, the opposite is not true. If a property is added in the
>     SC, the
>     RC can read this new property. So, for my implementation to work, I
>     would need 2 lazy read copies. One for the SC, one for the RC. The one
>     in the SC can replace the object thanks to the "become" privileged
>     primitive.
> Why the need for "become"? Why does the SC need to refer to a proxy,
> rather than to the real target object directly?
// In sending context
var o = {};
o.a = 1; // after having sent the message
at no time the RC should be able to observe an 'a' property. The message
was an empty object, so that's what the RC should observe (at least in
the postMessage use case).
Yet, I have no reason to prevent the SC to change o after a postMessage,
so in order to keep the reads consistent and still allow writes, I can
turn my object into a LazyReadCopy object and do the become.

>     So far, so good, we have an object, two lazy read copies of it (one in
>     each context), basic expectations are respected.
>     Now, consider the following snippet:
>     -----
>     var o = {};
>     Object.preventExtension(o);
>     Object.isExtensible(o); // false
>     receivingContext.postMessage(o);
>     // The browser replaces o with a lazy read copy of o. That's now a
>     proxy.
>     Object.isExtensible(o); // true, because it's a proxy :-s
>     -----
>     Of course, that's my fault, I shouldn't play too much with the
>     "become"
>     primitive.
>     However I feel that I should be allowed to create non-extensible
>     proxies
>     to cover this use case.
> Again, why the need for "become" here? We're in the sending context,
> which already has direct access to "o". No need to wrap it here.
>     And well, you probably see my proposal coming: what about doing
>     invariant-enforcing with [[Extensible]]=false instead of the
>     fix+become?
>     There would only be a need for a preventExtension fundamental trap
>     (freeze and seal would be derived or even not trap at all, calling the
>     preventExtension trap anyway)
>     I haven't taken the time to try to implement it, but starting from
>     Tom's
>     FixedHandler implementation [1], it looks like it would be just
>     adding a
>     this.extensible boolean (and the set of properties can be
>     retrieved from
>     this.fixedProps).
> I'll have to think about it. It may be that with the FixedHandler
> infrastructure in place, supporting [[Extensible]]:true proxies falls
> out naturally. But it might as well impose a burden on every single
> handler to explicitly deal with the extensible vs non-extensible
> state, which would be a high price to pay.
> We'll still need a fix() trap so that the FixedHandler can ask the
> real handler for all of its configurable properties as well, although
> those could also be derived by calling getOwnPropertyNames() +
> getOwnPropertyDescriptor().
>     One-property traps would need a lookup to a property set
>     (hopefully, JS
>     engines have efficient implementations for that for the "in" operator)
>     to avoid lies on whether the property is part of the object already or
>     not. The rest of the one-property traps invariants are already taken
>     care of by FixedHandler.
>     Several-property traps (get{Own}PropertyNames, enumerate, keys) could
>     call the trap, throw away the result and return the set of properties.
>     The inconvenient of this would be that the user-returned order
>     wouldn't
>     be respected. The costly alternative is to check that the returned
>     array
>     isn't adding properties that shouldn't be there.
> If the number of properties doesn't match up, that would be a quick
> test to detect when the trap lies. Unfortunately that only tests for
> the supposedly uncommon case.
>     Another idea is to provide the choice between the invariant-checking
>     flavor and the "turn me into a normal object" flavor with the return
>     value of the fix/preventExtension trap.
> I would prefer one mechanism over two. If the FixedHandler can be
> extended with reasonable checks to uphold all invariants related to
> Object.{preventExtensions|seal|freeze}, I don't see a need for the
> fix+become mechanism anymore.
Good to know. I'm working on an implementation. It will be independent
from the FixedHandler one. It should be possible to combine both easily
I'll give a follow-up here when I'm done.


> Cheers,
> Tom
>     Any thoughts?
>     Limitations of my implementation regarding the structured clone
>     algorithm.
>     * Non-enumerable and inherited properties are not filtered out
>     * Doing a lazy copy read is not performed recursively
>     * The object in the receiving context should only have data
>     property, so
>     if there are accessors, their getters should be called once at
>     copy time
>     and the result stored in the copy.
>     (this last point cannot be implemented efficiently in JavaScript,
>     I think)
>     David
>     [1]
>     http://code.google.com/p/es-lab/source/browse/trunk/src/proxies/FixedHandler.js
>     _______________________________________________
>     es-discuss mailing list
>     es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>
>     https://mail.mozilla.org/listinfo/es-discuss

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110714/dc873fc1/attachment-0001.html>

More information about the es-discuss mailing list