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

Tom Van Cutsem tomvc.be at gmail.com
Thu Jul 14 07:23:55 PDT 2011


Hi David,

2011/7/13 David Bruant <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. 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.


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


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

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
> https://mail.mozilla.org/listinfo/es-discuss
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110714/8f09eafa/attachment.html>


More information about the es-discuss mailing list