<div dir="ltr">Claude's additional example is indeed evidence that Object.freeze is not to blame, but rather that the invariant checks of [[GetOwnPropertyDescriptor]] and [[DefineOwnProperty]] are too weak. The culprit is, as far as I can tell, that we re-used the state transitions allowed by DefineOwnProperty, which allows the transition from non-configurable,writable (-c+w) to non-configurable,non-writable (-c-w) and which is unwanted here, as Claude rightfully concludes <i>[to better understand the state transitions involved, see MarkM's lucid state transition chart of property attributes <<a href="http://wiki.ecmascript.org/doku.php?id=es3.1:attribute_states">http://wiki.ecmascript.org/doku.php?id=es3.1:attribute_states</a>>. I wish this diagram were in the spec btw, once you know how to read it, it is significantly easier to understand than the DefineOwnProperty algorithm itself]</i><div><div><br></div><div>I like the simplicity of Claude's suggestion, but I think that check is too tight. Technically if the descriptors are both -c+w data descriptors, their value attributes need not be identical to honor the spec invariants. Instead I would propose to tackle the problem head-on and explicitly disallow a proxy from reporting a -c-w property if the corresponding target property is -c+w. We already have a similar check in place in precisely those two algorithms to test if the configurable attribute matches. It is just a matter of tightening that check to also verify the writable attribute.</div><div><br></div><div>This would entail the following changes to the ES2015 spec (new or modified text in bold):</div><div><br></div><div><div><br></div><div>9.5.5 [[GetOwnProperty]] (P)</div><div>  ...</div><div>  22. If resultDesc.[[Configurable]] is false, then</div><div>    a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then</div><div>      i. Throw a TypeError exception.</div><div><b>    b. If resultDesc.[[Writable]] is false and targetDesc.[[Writable]] is true, then</b></div><div><b>      i. Throw a TypeError exception.</b></div><div>      </div><div>NOTE [[GetOwnProperty]] for proxy objects enforces the following invariants:</div><div><br></div><div>  ...</div><div><b>  * A property cannot be reported as non-configurable, non-writable if it exists as a non-configurable, writable own property on the target object.</b></div><div><br></div><div><br></div><div>9.5.6 [[DefineOwnProperty]] (P, Desc)</div><div>  ...</div><div>  20. Else targetDesc is not undefined,</div><div>    a.  If IsCompatiblePropertyDescriptor(extensibleTarget, Desc , targetDesc) is false, throw a TypeError exception.</div><div><b>    b. If settingConfigFalse is true</b></div><div><b>      i. If targetDesc.[[Configurable]] is true, throw a TypeError exception.</b></div><div><b>      ii. If Desc.[[Writable]] is false and targetDesc.[[Writable]] is true, throw a TypeError exception.</b></div><div>      </div><div>NOTE [[DefineOwnProperty]] for proxy objects enforces the following invariants:</div><div><br></div><div>  ...</div><div><b>  * A property cannot be successfully set to non-configurable, non-writable if the corresponding own property of the target object is non-configurable, writable.</b></div></div><div><br></div><div>WDYT?</div><div><br></div><div>Regards,</div></div><div>Tom</div></div><div class="gmail_extra"><br><div class="gmail_quote">2016-08-08 15:37 GMT+02:00 Claude Pache <span dir="ltr"><<a href="mailto:claude.pache@gmail.com" target="_blank">claude.pache@gmail.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="HOEnZb"><div class="h5"><br>
> Le 8 août 2016 à 11:02, Claude Pache <<a href="mailto:claude.pache@gmail.com">claude.pache@gmail.com</a>> a écrit :<br>
><br>
> Here is another test case, with [[GetOwnPropertyDescriptor]]:<br>
><br>
> ```js<br>
> var target = Object.seal({x: 2});<br>
> var proxy = new Proxy(target, {<br>
>    getOwnPropertyDescriptor(o, p) {<br>
>        var d = Reflect.<wbr>getOwnPropertyDescriptor(o, p)<br>
>        if (d && 'writable' in d)<br>
>            d.writable = false<br>
>        return d<br>
>    }<br>
> });<br>
><br>
> Object.<wbr>getOwnPropertyDescriptor(<wbr>proxy, 'x'); // { value: 2, writable: false, configurable: false }<br>
><br>
> Object.defineProperty(proxy, 'x', { value: 3 })<br>
><br>
> Object.<wbr>getOwnPropertyDescriptor(<wbr>proxy, 'x'); // { value: 3, writable: false, configurable: false }<br>
> ```<br>
><br>
> IMHO, the most robust fix is the following: Both the [[DefineOwnProperty]] and the [[GetOwnPropertyDescriptor]] contain the following check:<br>
><br>
> * If IsCompatiblePropertyDescriptor<wbr>(extensibleTarget, resultDesc, targetDesc) is false, throw a TypeError exception.<br>
><br>
> I think there should be also the following check (except when targetDesc is undefined, in which case another appropriate check is used):<br>
><br>
> * If IsCompatiblePropertyDescriptor<wbr>(extensibleTarget, targetDesc, resultDesc) is false, throw a TypeError exception.<br>
><br>
> That would replace the weaker adhoc check about the [[Configurable]] attribute (search for settingConfigFalse) in those algorithms.<br>
><br>
> (Alternatively, in order to avoid double checks, define an AreBothWaysCompatiblePropertyD<wbr>escriptors(extensibleTarget, desc1, desc2) abstract operation.)<br>
><br>
> —Claude<br>
><br>
<br>
</div></div>Looking closer, it seems that using IsCompatiblePropertyDescriptor is in fact an overkill, because we probably don’t want the special case of conditionally mutable [[Writable]] attribute for nonconfigurable properties.<br>
<br>
That is, I think that the following condition must hold: If either the target has a nonconfigurable property, or the proxy claims to have a nonconfigurable property, then every attribute of the property descriptor claimed by the proxy must be identical to the corresponding attribute of the property descriptor of the target.<br>
<span class="HOEnZb"><font color="#888888"><br>
—Claude<br>
<br>
<br>
</font></span></blockquote></div><br></div>