[Harmony Proxies] Proposal: Property fixing

Tom Van Cutsem tomvc.be at gmail.com
Mon Jun 20 01:52:03 PDT 2011


2011/6/19 David Bruant <david.bruant at labri.fr>

> **
> Le 19/06/2011 17:43, Tom Van Cutsem a écrit :
>
> 2011/6/17 David Bruant <david.bruant at labri.fr>
>
>> Symetrically to the forwarding of defineProperty, maybe that
>> getOwnPropertyDescriptor could be forwarded too (requires to call the
>> getOwnPropertyDescriptor trap for non-configurable properties) and the
>> return value could be used to redefine writable on the proxy fixed property
>> if necessary.
>>
>
>  Yep, trapping getOwnPropertyDescriptor on fixed properties seems to close
> the loop.
>
>  However, I find it strange that the return value of
> getOwnPropertyDescriptor would actually implicitly side-effect the fixed
> property of the proxy. I naturally think of defineProperty as a
> side-effecting trap, not so for getOwnPropertyDescriptor. Perhaps it should
> only check the return value for consistency with the fixed descriptor,
> without side-effects.
>
> It won't be enough to ensure invariants.
> ----
> var ep = EvilProxy();
> Object.defineProperty(ep, 'a', {value:1, configurable:false,
> writable:true}); // works as expected
> Object.getOwnPropertyDescriptor(ep, 'a'); // {value:1, configurable:false,
> writable:true}
> // This is the stored fixed property descriptor
> Object.getOwnPropertyDescriptor(ep, 'a'); // {value:1, configurable:false,
> writable:false} is legitimate
> // because it doesn't break any invariant. No side effect-here, stored
> descriptor remains the same
> Object.getOwnPropertyDescriptor(ep, 'a'); // {value:1, configurable:false,
> writable:true}
> // This is coherent with what is stored in the fixed property record
> ----
> My point is that the second Object.getOwnPropertyDescriptor call is
> legitimate, but if we do not store that writable changed from "true" to
> "false", then it can be later observed as "true" which violates the ES5.1
> invariant:
> "If the [[Writable]] attribute may change from false to true, then the
> [[Configurable]] attribute must be true."
> Consequently, for this very case (changing writable from false to true for
> non-configurable properties), getOwnPropertyDescriptor needs to have a
> side-effect. That's probably the only one.
>
> To sum up, we need to trap getOwnPropertyDescriptor for writable
> consistency in the forwarding use case. However, if we trap and do not have
> a side effect, an EvilProxy could break an invariant.
>
>
> Also, as it turns out, there is actually no need to store the entire
> property descriptor for fixed properties to enforce invariants. There is a
> need to store:
> 1) Whether the property has been observed as non-configurable and if it's
> the case:
> 1.1) Whether writable has been observed to false and if it's the case
> 1.1.1) latest observed/provided value (no need to keep the value until
> configurable and writable or both false since it can be changed anytime.
> First universal property invariant)
> 1.2) latest observed/provided get/set
> 1.3) latest observed/provided enumerable (there are no invariants on this
> one, so I'm not sure)
>
> I think that the notion of "latest observed" is the one requiring
> getOwnPropertyDescriptor to have side-effects (even though it may sound
> counter intuitive).
>

Yes, you're right. Thanks for the clarifying example. I gave a first shot at
trying to define the semantics of [[GetOwnProperty]] for Proxies with
support for fixed properties:

[[GetOwnProperty]] (P)
1. Let handler be the value of the [[Handler]] internal property of O.
2. Let getOwnProperty be the result of calling the [[Get]] internal method
of handler with argument “getOwnPropertyDescriptor”.
3. If getOwnProperty is undefined, throw a TypeError exception.
4. If IsCallable(getOwnProperty) is false, throw a TypeError exception.
5. Let trapResult be the result of calling the [[Call]] internal method of
getOwnProperty providing handler as the this value and P as the first
argument.
6. Let fixedProperty be the result of calling Object.[[GetOwnProperty]]( P )
7. If trapResult is undefined
  a. If fixedProperty is undefined, return undefined
  b. Otherwise, fixedProperty is defined, so throw a TypeError
8. Let desc be ToCompletePropertyDescriptor(trapResult)
9. If fixedProperty is not undefined, or desc.[[Configurable]] is false
  a. call Object.[[DefineOwnProperty]](P, desc, true)
10. Return desc

Here, Object.[[GetOwnProperty]] and Object.[[DefineOwnProperty]] refer to
the algorithms for Object values
from ES5 sections 8.12.1 and 8.12.9 respectively.
Line 7.b. is necessary to guard against a handler reporting a previously
fixed property as undefined.

It's true that the proxy does not necessarily need to store the entire fixed
property descriptor. However, in terms of specifying the semantics, reusing
the existing built-in semantics for Objects seems easier to grasp.

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


More information about the es-discuss mailing list