Darien Valentine (2018-09-04T07:32:37.000Z)
valentinium at gmail.com (2018-09-05T07:39:43.477Z)
The following instrinsic functions invoke the ToPropertyDescriptor internal operation to convert JS-object property descriptors into "Property Descriptors" (spec-internal object type): - `Reflect.defineProperty` - `Object.create` - `Object.defineProperty` - `Object.defineProperties` - a `Proxy` instance if its handler includes `getOwnPropertyDescriptor` The ToPropertyDescriptor operation uses HasProperty to check for the presence of the following properties of a JS object property descriptor in the order enumerable, configurable, value, writable, get, set. If any two incompatible properties are found to be “had properties,” an error is thrown. It seems unfortunate that this works: ```js function breakEveryPropertyDescriptor() { Object.prototype.value = undefined; Object.prototype.get = undefined; } ``` But this has been mentioned before. I found this prior discussion: https://esdiscuss.org/topic/topropertydescriptor-hasproperty-hasownproperty As explained there, one can address this, if sufficiently paranoid, by only ever passing in null-prototype objects (or objects with a prototype you control) as JS-object property descriptors. It gets a bit more complex. FromPropertyDescriptor produces JS-object PDs which also inherit from %ObjectPrototype%. Naturally you can perform the own-property check yourself — but it has a consequence I find particularly interesting: ```js const obj1 = {}; const obj2 = new Proxy({}, Reflect); breakEveryPropertyDescriptor(); const pd = Object.assign(Object.create(null), { value: 1 }); try { Reflect.defineProperty(obj1, 'foo', pd); console.log('successful on obj1'); } catch { console.log('unsuccessful on obj1'); } try { Reflect.defineProperty(obj2, 'foo', pd); console.log('successful on obj2'); } catch { console.log('unsuccessful on obj2'); } ``` In the Proxy case, the input JS PD object is converted to an internal Property Descriptor (`{ value: 1 }`) and then back to a fresh JS object (now with %ObjectPrototype%) — and then back to an internal Property Descriptor again. But it doesn’t successfully round trip! At this point the result of HasProperty(Obj, "get") changes from what it would have been if the PD had not passed through `Reflect.defineProperty` a second time, and the descriptor has become `{ value: 1, get: undefined }`. My understanding was that using `Reflect` as your handler object should mean the behaviors of all the trapped operations will be the same as they would have been for an ordinary object. This may be an incorrect assumption on my part (are there other examples like this?), but it does seem desirable for that to be true. I’m curious about whether changes to these algorithms to alter this behavior in some way would be web-safe. The obvious solution would be switching HasProperty to HasOwnProperty in ToPropertyDescriptor and/or creating objects that inherit from null in FromPropertyDescriptor. But I’m guessing those changes would not be safe. In any case, I wanted to share the non-parity edge case with Reflect.defineProperty since maybe this is not already a known quirk.
valentinium at gmail.com (2018-09-04T07:48:17.954Z)
The following instrinsic functions invoke the ToPropertyDescriptor internal operation to convert JS-object property descriptors into "Property Descriptors" (spec-internal object type): - `Reflect.defineProperty` - `Object.create` - `Object.defineProperty` - `Object.defineProperties` - a `Proxy` instance if its handler includes `getOwnPropertyDescriptor` The ToPropertyDescriptor operation uses HasProperty to check for the presence of the following properties of a JS object property descriptor in the order enumerable, configurable, value, writable, get, set. If any two incompatible properties are found to be “had properties,” an error is thrown. It seems unfortunate that this works: ```js function breakEveryPropertyDescriptor() { Object.prototype.value = undefined; Object.prototype.get = undefined; } ``` But this has been mentioned before. I found this prior discussion: https://esdiscuss.org/topic/topropertydescriptor-hasproperty-hasownproperty As explained there, one can address this, if sufficiently paranoid, by only ever passing in null-prototype objects (or objects with a prototype you control) as JS-object property descriptors. It gets a bit more complex. FromPropertyDescriptor produces JS-object PDs which also inherit from %ObjectPrototype%. Naturally you can perform the own-property check yourself — but it has a consequence I find particularly interesting: ```js const obj1 = {}; const obj2 = new Proxy({}, Reflect); breakEveryPropertyDescriptor(); const pd = Object.assign(Object.create(null), { value: 1 }); try { Reflect.defineProperty(obj1, 'foo', pd); console.log('successful on obj1'); } catch { console.log('unsuccessful on obj1'); } try { Reflect.defineProperty(obj2, 'foo', pd); console.log('successful on obj2'); } catch { console.log('unsuccessful on obj2'); } ``` In the Proxy case, the input JS PD object is converted to an internal Property Descriptor (`{ value: 1 }`) and then back to a fresh JS object (now with %ObjectPrototype%) — and then back to an internal Property Descriptor again. But it doesn’t successfully round trip! At this point the result of HasProperty(Obj, "get") changes from what it would have been if the PD had not passed through `Reflect.defineProperty`, and the descriptor has become `{ value: 1, get: undefined }`. My understanding was that using `Reflect` as your handler object should mean the behaviors of all the trapped operations will be the same as they would have been for an ordinary object. This may be an incorrect assumption on my part (are there other examples like this?), but it does seem desirable for that to be true. I’m curious about whether changes to these algorithms to alter this behavior in some way would be web-safe. The obvious solution would be switching HasProperty to HasOwnProperty in ToPropertyDescriptor and/or creating objects that inherit from null in FromPropertyDescriptor. But I’m guessing those changes would not be safe. In any case, I wanted to share the non-parity edge case with Reflect.defineProperty since maybe this is not already a known quirk.
valentinium at gmail.com (2018-09-04T07:46:07.907Z)
The following instrinsic functions invoke the ToPropertyDescriptor internal operation to convert JS-object property descriptors into "Property Descriptors" (spec-internal object type): - `Reflect.defineProperty` - `Object.create` - `Object.defineProperty` - `Object.defineProperties` - a `Proxy` instance if its handler includes `getOwnPropertyDescriptor` The ToPropertyDescriptor operation uses HasProperty to check for the presence of the following properties of a JS object property descriptor in the order enumerable, configurable, value, writable, get, set. If any two incompatible properties are found to be “had properties,” an error is thrown. It seems unfortunate that this works: ```js function breakEveryPropertyDescriptor() { Object.prototype.value = undefined; Object.prototype.get = undefined; } ``` But this has been mentioned before. I found this prior discussion: https://esdiscuss.org/topic/topropertydescriptor-hasproperty-hasownproperty As explained there, one can address this, if sufficiently paranoid, by only ever passing in null-prototype objects (or objects with a prototype you control) as JS-object property descriptors. It gets a bit more complex. FromPropertyDescriptor produces JS-object PDs which also inherit from %ObjectPrototype%. Naturally you can perform the own-property check yourself — but it has a consequence I find particularly interesting: ```js const obj1 = {}; const obj2 = new Proxy({}, Reflect); breakEveryPropertyDescriptor(); const pd = Object.assign(Object.create(null), { value: 1 }); try { Reflect.defineProperty(obj1, 'foo', pd); console.log('successful on obj1'); } catch { console.log('unsuccessful on obj1'); } try { Reflect.defineProperty(obj2, 'foo', pd); console.log('successful on obj2'); } catch { console.log('unsuccessful on obj2'); } ``` In the Proxy case, the input JS PD object is converted to an internal Property Descriptor (`{ value: 1 }`) and then back to a fresh JS object (now with %ObjectPrototype%) — and then back to an internal Property Descriptor again. But it doesn’t successfully round trip! At this point the result of HasProperty(Obj, "get") changes from what it would have been if the PD had not passed through `Reflect.defineProperty`, and the descriptor has become `{ value: 1, get: undefined }`. My understanding was that using `Reflect` as your handler object should mean the behavior of all the trapped operations should be the same as it would have been for an ordinary object. This may be an incorrect assumption on my part (are there other examples like this?), but it does seem desirable for that to be true. I’m curious about whether changes to these algorithms to alter this behavior in some way would be web-safe. The obvious solution would be switching HasProperty to HasOwnProperty in ToPropertyDescriptor and/or creating objects that inherit from null in FromPropertyDescriptor. But I’m guessing those changes would not be safe. In any case, I wanted to share the non-parity edge case with Reflect.defineProperty since maybe this is not already a known quirk.
valentinium at gmail.com (2018-09-04T07:35:24.153Z)
The following instrinsic functions invoke the ToPropertyDescriptor internal operation to convert JS-object property descriptors into "Property Descriptors" (spec-internal object type): - `Reflect.defineProperty` - `Object.create` - `Object.defineProperty` - `Object.defineProperties` - a `Proxy` instance if its handler includes `getOwnPropertyDescriptor` The ToPropertyDescriptor operation uses HasProperty to check for and the presence of the following properties of a JS object property descriptor in the order enumerable, configurable, value, writable, get, set. If any two incompatible properties are found to be “had properties,” an error is thrown. It seems unfortunate that this works: ```js function breakEveryPropertyDescriptor() { Object.prototype.value = undefined; Object.prototype.get = undefined; } ``` But this has been mentioned before. I found this prior discussion: https://esdiscuss.org/topic/topropertydescriptor-hasproperty-hasownproperty As explained there, one can address this, if sufficiently paranoid, by only ever passing in null-prototype objects (or objects with a prototype you control) as JS-object property descriptors. It gets a bit more complex. FromPropertyDescriptor produces JS-object PDs which also inherit from %ObjectPrototype%. Naturally you can perform the own-property check yourself — but it has a consequence I find particularly interesting: ```js const obj1 = {}; const obj2 = new Proxy({}, Reflect); breakEveryPropertyDescriptor(); const pd = Object.assign(Object.create(null), { value: 1 }); try { Reflect.defineProperty(obj1, 'foo', pd); console.log('successful on obj1'); } catch { console.log('unsuccessful on obj1'); } try { Reflect.defineProperty(obj2, 'foo', pd); console.log('successful on obj2'); } catch { console.log('unsuccessful on obj2'); } ``` In the Proxy case, the input JS PD object is converted to an internal Property Descriptor (`{ value: 1 }`) and then back to a fresh JS object (now with %ObjectPrototype%) — and then back to an internal Property Descriptor again. But it doesn’t successfully round trip! At this point the result of HasProperty(Obj, "get") changes from what it would have been if the PD had not passed through `Reflect.defineProperty`, and the descriptor has become `{ value: 1, get: undefined }`. My understanding was that using `Reflect` as your handler object should mean the behavior of all the trapped operations should be the same as it would have been for an ordinary object. This may be an incorrect assumption on my part (are there other examples like this?), but it does seem desirable for that to be true. I’m curious about whether changes to these algorithms to alter this behavior in some way would be web-safe. The obvious solution would be switching HasProperty to HasOwnProperty in ToPropertyDescriptor and/or creating objects that inherit from null in FromPropertyDescriptor. But I’m guessing those changes would not be safe. In any case, I wanted to share the non-parity edge case with Reflect.defineProperty in case this was not already a known quirk.
valentinium at gmail.com (2018-09-04T07:34:21.430Z)
The following instrinsic functions invoke the ToPropertyDescriptor internal operation to convert JS-object property descriptors into "Property Descriptors" (spec-internal object type): - `Reflect.defineProperty` - `Object.create` - `Object.defineProperty` - `Object.defineProperties` - a `Proxy` instance if its handler includes `getOwnPropertyDescriptor` The ToPropertyDescriptor operation uses HasProperty to check for and the presence of the following properties of a JS object property descriptor in the order enumerable, configurable, value, writable, get, set. If any two incompatible properties are found to be “had properties,” an error is thrown. It seems unfortunate that this works: ```js function breakEveryPropertyDescriptor() { Object.prototype.value = undefined; Object.prototype.get = undefined; } ``` But this has been mentioned before. I found this prior discussion: https://esdiscuss.org/topic/topropertydescriptor-hasproperty-hasownproperty As explained there, one can address this, if sufficiently paranoid, by only ever passing in null-prototype objects (or objects with a prototype you control) as JS-object property descriptors. It gets a bit more complex. FromPropertyDescriptor produces JS-object PDs which also inherit from %ObjectPrototype%. Naturally you can perform the own-property check yourself — but it has a consequence I find particularly interesting: ```js const obj1 = {}; const obj2 = new Proxy({}, Reflect); breakEveryPropertyDescriptor(); const pd = Object.assign(Object.create(null), { value: 1 }); try { Reflect.defineProperty(obj1, 'foo', pd); console.log('successful on obj1'); } catch { console.log('unsuccessful on obj1'); } try { Reflect.defineProperty(obj2, 'foo', pd); console.log('successful on obj2'); } catch { console.log('unsuccessful on obj2'); } ``` In the Proxy case, the input JS PD object is converted to an internal Property Descriptor (`{ value: 1 }`) and then back to a fresh JS object (now with %ObjectPrototype%) — and then back to an internal Property Descriptor again. But it doesn’t successfully round trip! At this point the result of HasProperty(Obj, "get") changes from what it would have been if the PD had not passed through `Reflect.defineProperty`, and the descriptor has become `{ value: 1, get: undefined }`. My understanding was that, in theory, using `Reflect` as your handler object should mean the behavior of all the trapped operations should be the same as it would have been for an ordinary object. This may be an incorrect assumption on my part (are there other examples like this?), but it does seem desirable for that to be true. I’m curious about whether changes to these algorithms to alter this behavior in some way would be web-safe. The obvious solution would be switching HasProperty to HasOwnProperty in ToPropertyDescriptor and/or creating objects that inherit from null in FromPropertyDescriptor. But I’m guessing those changes would not be safe. In any case, I wanted to share the non-parity edge case with Reflect.defineProperty in case this was not already a known quirk.
valentinium at gmail.com (2018-09-04T07:33:44.419Z)
The following instrinsic functions invoke the ToPropertyDescriptor internal operation to convert JS-object property descriptors into "Property Descriptors" (spec-internal object type): - `Reflect.defineProperty` - `Object.create` - `Object.defineProperty` - `Object.defineProperties` - a `Proxy` instance if its handler includes `getOwnPropertyDescriptor` The ToPropertyDescriptor operation uses HasProperty to check for and the presence of the following properties of a JS object property descriptor in the order enumerable, configurable, value, writable, get, set. If any two incompatible properties are found to be “had properties,” an error is thrown. It seems unfortunate that this works: ```js function breakEveryPropertyDescriptor() { Object.prototype.value = undefined; Object.prototype.get = undefined; } ``` But this has been mentioned before. I found this prior discussion: https://esdiscuss.org/topic/topropertydescriptor-hasproperty-hasownproperty As explained there, one can address this, if sufficiently paranoid, by only ever passing in null-prototype objects (or objects with a prototype you control) as JS-object property descriptors. It gets a bit more complex. FromPropertyDescriptor produces JS-object PDs which also inherit from %ObjectPrototype%. Naturally you can perform the own-property check yourself — but it has a consequence I find particularly interesting: ```js const obj1 = {}; const obj2 = new Proxy({}, Reflect); breakEveryPropertyDescriptor(); const pd = Object.assign(Object.create(null), { value: 1 }); try { Reflect.defineProperty(obj1, 'foo', pd); console.log('successful on obj1'); } catch { console.log('unsuccessful on obj1'); } try { Reflect.defineProperty(obj2, 'foo', pd); console.log('successful on obj2'); } catch { console.log('unsuccessful on obj2'); } ``` In the Proxy case, the input JS PD object is converted to an internal Property Descriptor (`{ value: 1 }`) and then back to a fresh JS object (now with %ObjectPrototype%) — and then back to an internal Property Descriptor again. But it doesn’t successfully round trip! At this the result of HasProperty(Obj, "get") changes from what it would have been if the PD had not passed through Reflect.defineProperty, and the descriptor has become `{ value: 1, get: undefined }`. My understanding was that, in theory, using `Reflect` as your handler object should mean the behavior of all the trapped operations should be the same as it would have been for an ordinary object. This may be an incorrect assumption on my part (are there other examples like this?), but it does seem desirable for that to be true. I’m curious about whether changes to these algorithms to alter this behavior in some way would be web-safe. The obvious solution would be switching HasProperty to HasOwnProperty in ToPropertyDescriptor and/or creating objects that inherit from null in FromPropertyDescriptor. But I’m guessing those changes would not be safe. In any case, I wanted to share the non-parity edge case with Reflect.defineProperty in case this was not already a known quirk.