May the defineProperty method of a proxy handler throw a TypeError?

# David Flanagan (13 years ago)

I'm using a proxy to implement the DOM NodeList interface. A NodeList object is array-like and has read-only, non-configurable array index properties.

In my handler's set() and delete() methods, I just return false if an attempt is made to set or delete an indexed property.

The defineProperty() method is not parallel to set() and delete(), however. I can't just return false, since the return value is ignored.
And I can't tell from the proposal whether I am allowed to throw a TypeError from this method. In getOwnPropertyDescriptor() I know that I have to lie and return a descriptor with configurable:true for the indexed properties. So in defineProperty() should I just silently ignore any attempts to set an indexed property, or should I actively reject those attempts with a TypeError?

# Tom Van Cutsem (13 years ago)

The short answer: if you also define a 'set' trap, throwing a TypeError from defineProperty() to signal rejection is appropriate.

The longer answer:

defineProperty() is based on [[DefineOwnProperty]] (ES5 section 8.12.9) whose rejection behavior depends on an explicit 'Throw' parameter, just like [[Put]] and [[Delete]]. Why then, do set() and delete() return a boolean success flag to determine their rejection behavior, while defineProperty() does not?

I believe that, until now, we were convinced that in the case of proxies, defineProperty() actually does not depend on strict mode, because the built-in Object.defineProperty and Object.defineProperties methods (ES5 section 15.2.3.6-7) call the built-in [[DefineOwnProperty]] method with its 3rd argument 'Throw' set unconditionally to 'true', meaning it should never reject silently, independent of the strictness of the code.

However, checking other parts of the spec where [[DefineOwnProperty]] is invoked, I now notice it's also invoked by [[Put]], which passes its own 'Throw' argument to [[DefineOwnProperty]], and this time, the 'Throw' argument is dependent on strict mode. This complicates matters because of derived traps.

If a proxy handler does not define a 'set' trap (which is derived), the proxy implementation will fall back on the fundamental defineProperty() trap, whose rejection behavior in that context now should depend on strict mode. However, the current defineProperty() API doesn't allow the programmer to express this.

I see two options here:

  1. modify the defineProperty() trap such that it also returns a boolean flag to indicate rejection, like set() and delete(). This is still possible as defineProperty() currently has no useful return value.

  2. specify unambiguously that defineProperty() on proxies should always reject explicitly by throwing a TypeError, even if the defineProperty() trap was called as part of the default 'set' trap. This can be justified if we specify that the default 'set' trap invokes the 'defineProperty' trap as if by calling "Object.defineProperty", which is specified to never reject silently.

Option #1 would most closely follow the current ES5 spec, but would disallow the default 'set' trap behavior from being written in Javascript itself, since it's impossible to specify the value of the 'Throw' parameter in Javascript.

Option #2 is in line with how the current default 'set' behavior is specified at < harmony:proxies#trap_defaults>.

Cheers, Tom

2011/5/25 David Flanagan <dflanagan at mozilla.com>

# Sean Eagan (13 years ago)

Is think it would beneficial to replace all this.* calls in the default trap implementations with Object.* methods and syntax that subsequently triggers the this.* calls. This would guarantee that none of the internal object semantics are being missed, and implementations could still optimize just as easily.

# David Flanagan (13 years ago)

On 5/26/11 6:36 AM, Tom Van Cutsem wrote:

Hi David,

The short answer: if you also define a 'set' trap, throwing a TypeError from defineProperty() to signal rejection is appropriate

Thanks. I was worried that defineProperty() was required to honor property changes since getOwnPropertyDescriptor() is required to say that the properties are configurable. (I've read the justification for that in the proposal multiple times, but I've never been able to understand it...)

# David Flanagan (13 years ago)

On 5/26/11 6:51 AM, Sean Eagan wrote:

Is think it would beneficial to replace all this.* calls in the default trap implementations with Object.* methods and syntax that subsequently triggers the this.* calls. This would guarantee that none of the internal object semantics are being missed, and implementations could still optimize just as easily.

With specification text "as if by calling the primordial Object.* function"? So that user modifications to Object.* are not observable?

Wouldn't that mean that a pure-JavaScript implementation of the default traps would not be possible from a module that could not run at interpreter startup? (That may not be a goal that anyone has, of course.)

Personally, I think that proxies would be easier to reason about if no distinction was made between fundamental and derived traps. I'd put that distinction in an abstract Proxy.Handler class and then rename the current Proxy.Handler to Proxy.ForwardingHandler.

# David Flanagan (13 years ago)

On 5/26/11 9:35 AM, David Flanagan wrote:

On 5/26/11 6:36 AM, Tom Van Cutsem wrote:

Hi David,

The short answer: if you also define a 'set' trap, throwing a TypeError from defineProperty() to signal rejection is appropriate

Thanks. I was worried that defineProperty() was required to honor property changes since getOwnPropertyDescriptor() is required to say that the properties are configurable. (I've read the justification for that in the proposal multiple times, but I've never been able to understand it...)

Responding to my own message... I think I now understand the configurable property requirement of getOwnPropertyDescriptor. Is it for conformance with these paragraphs from §8.6.2 of the ECMA-262 spec?

# David Bruant (13 years ago)

Le 26/05/2011 19:10, David Flanagan a écrit :

On 5/26/11 9:35 AM, David Flanagan wrote:

On 5/26/11 6:36 AM, Tom Van Cutsem wrote:

Hi David,

The short answer: if you also define a 'set' trap, throwing a TypeError from defineProperty() to signal rejection is appropriate

Thanks. I was worried that defineProperty() was required to honor property changes since getOwnPropertyDescriptor() is required to say that the properties are configurable. (I've read the justification for that in the proposal multiple times, but I've never been able to understand it...)

Responding to my own message... I think I now understand the configurable property requirement of getOwnPropertyDescriptor. Is it for conformance with these paragraphs from §8.6.2 of the ECMA-262 spec?

Yes it is. In a way, this is the equivalent of what happens for Object.freeze. If you don't want to honor what it says, you can still throw an error saying you don't want to.

# David Bruant (13 years ago)

Le 26/05/2011 18:44, David Flanagan a écrit :

On 5/26/11 6:51 AM, Sean Eagan wrote:

Is think it would beneficial to replace all this.* calls in the default trap implementations with Object.* methods and syntax that subsequently triggers the this.* calls. This would guarantee that none of the internal object semantics are being missed, and implementations could still optimize just as easily.

We have been discussing whether this.trap was equivalent to Object.trap (shorthand notation) and the conclusion was that there was no way to distinguish both cases. I think there actually is. If, in a handler, I do "this.defineProperty(23, {value:1})", there is no conversion of the first argument to a string while there is if I internally call "Object.defineProperty(proxy, 23, {value:1})" There are probably other such cases.

And I agree with Sean. this.trap calls should be replaced with Object.* calls

With specification text "as if by calling the primordial Object.* function"? So that user modifications to Object.* are not observable?

I would argue so. (by the way, ES5 terminology says "built-in" where you say "primordial")

Wouldn't that mean that a pure-JavaScript implementation of the default traps would not be possible from a module that could not run at interpreter startup? (That may not be a goal that anyone has, of course.)

Is it a goal at all? There are plenty of things that are not possible anymore after one starts messing around with built-in. I wouldn't be shocked by adding this one. I will always be in favor that derived traps should be implementable with pure JS in a fresh environment, but when the environment isn't fresh, it don't think it's worth worrying about such a guarantee.

Personally, I think that proxies would be easier to reason about if no distinction was made between fundamental and derived traps.

Based on my experience with proxies, there are very few cases which really require to re-implement all traps. Reimplementing the fundamentals and letting the engine handle inheritance + hasOwn trap is handy.

I'd put that distinction in an abstract Proxy.Handler class and then rename the current Proxy.Handler to Proxy.ForwardingHandler.

What do you call a "class" is is a constructor with some arguments? Could you provide a code snippet of how you'd see it work.

Also, I think that this strawman (strawman:derived_traps_forwarding_handler) is a first step toward what you're asking. I've also raised the idea of renaming what is currently called Proxy.Handler. It's still in discussion I think. I remember that Tom had an idea, but I don't remember which.

# Andreas Rossberg (13 years ago)

On a somewhat related note, I recently noticed that the semantics of [[GetProperty]] and [[GetOwnProperty]], according to harmony:proxies_semantics, contain a possible "reject" step, which doesn't seem to be well-defined given that these method have no Throw parameter either.

# David Flanagan (13 years ago)

On 5/26/11 10:46 AM, David Bruant wrote:

Le 26/05/2011 18:44, David Flanagan a écrit :

I'd put that distinction in an abstract Proxy.Handler class and then rename the current Proxy.Handler to Proxy.ForwardingHandler. What do you call a "class" is is a constructor with some arguments? Could you provide a code snippet of how you'd see it work.

All I really mean is that Proxy.Handler should be an object suitable for use as a handler prototype. Rather than calling it Proxy.Handler, maybe Proxy.DerivedTraps would be better. Then, using Allen's proposed <| operator you could do:

p = Proxy.create(Proxy.DerivedTraps <| { // fundamental traps, and optionally some derived traps defined here });

# David Bruant (13 years ago)

Le 26/05/2011 22:19, David Flanagan a écrit :

On 5/26/11 10:46 AM, David Bruant wrote:

Le 26/05/2011 18:44, David Flanagan a écrit :

I'd put that distinction in an abstract Proxy.Handler class and then rename the current Proxy.Handler to Proxy.ForwardingHandler. What do you call a "class" is is a constructor with some arguments? Could you provide a code snippet of how you'd see it work. All I really mean is that Proxy.Handler should be an object suitable for use as a handler prototype.

Are you suggesting a "generic" Proxy.Handler (or Proxy.DerivedTraps) or something targeted for the forwarding handler use case. For the forwarding handler use case, I think that [1] addresses what you suggest. For a generic handler object with generic methods, I think it's pointless, because there is no such thing like a generic handler method. However, if you have examples of what you think would be suitable for generic handler methods, any handler object could inherit from, please do share code snippets

Rather than calling it Proxy.Handler, maybe Proxy.DerivedTraps would be better. Then, using Allen's proposed <| operator you could do:

p = Proxy.create(Proxy.DerivedTraps <| { // fundamental traps, and optionally some derived traps defined here });

Functionally, the current proposal is doing this (without the explicit inheritance) with shorter syntax.

David

[1] strawman:derived_traps_forwarding_handler

# Tom Van Cutsem (13 years ago)

2011/5/26 David Bruant <david.bruant at labri.fr>

Le 26/05/2011 18:44, David Flanagan a écrit :

On 5/26/11 6:51 AM, Sean Eagan wrote:

Is think it would beneficial to replace all this.* calls in the default trap implementations with Object.* methods and syntax that subsequently triggers the this.* calls. This would guarantee that none of the internal object semantics are being missed, and implementations could still optimize just as easily. We have been discussing whether this.trap was equivalent to Object.trap (shorthand notation) and the conclusion was that there was no way to distinguish both cases. I think there actually is. If, in a handler, I do "this.defineProperty(23, {value:1})", there is no conversion of the first argument to a string while there is if I internally call "Object.defineProperty(proxy, 23, {value:1})" There are probably other such cases.

And I agree with Sean. this.trap calls should be replaced with Object.* calls

The current API doesn't allow this as changing |this.| to |Object.| requires the handler to have access to the |proxy| in all traps. But if < strawman:handler_access_to_proxy> is

accepted, this seems like a good suggestion.

With specification text "as if by calling the primordial Object.* function"? So that user modifications to Object.* are not observable? I would argue so. (by the way, ES5 terminology says "built-in" where you say "primordial")

Wouldn't that mean that a pure-JavaScript implementation of the default traps would not be possible from a module that could not run at interpreter startup? (That may not be a goal that anyone has, of course.) Is it a goal at all? There are plenty of things that are not possible anymore after one starts messing around with built-in. I wouldn't be shocked by adding this one. I will always be in favor that derived traps should be implementable with pure JS in a fresh environment, but when the environment isn't fresh, it don't think it's worth worrying about such a guarantee.

I agree. I think it would be good to specify the built-in implementation of derived traps to always invoke the built-in Object.* methods. This allows engines to fully optimize. It is still possible for JS programmers to faithfully implement the default behavior in JS, given a consistent environment (consistent meaning here that Object.* methods may even have been monkey-patched, but remain faithful to their ES5 semantics).

Personally, I think that proxies would be easier to reason about if no distinction was made between fundamental and derived traps. Based on my experience with proxies, there are very few cases which really require to re-implement all traps. Reimplementing the fundamentals and letting the engine handle inheritance + hasOwn trap is handy.

I'd put that distinction in an abstract Proxy.Handler class and then rename the current Proxy.Handler to Proxy.ForwardingHandler. What do you call a "class" is is a constructor with some arguments? Could you provide a code snippet of how you'd see it work.

Also, I think that this strawman ( strawman:derived_traps_forwarding_handler ) is a first step toward what you're asking. I've also raised the idea of renaming what is currently called Proxy.Handler. It's still in discussion I think. I remember that Tom had an idea, but I don't remember which.

My latest suggestion was to rename Proxy.Handler to Proxy.Forwarder (Proxy.ForwardingHandler is a mouthful).

The idea of the cited strawman would be that Proxy.Forwarder inherits from Proxy.BaseForwarder. BaseForwarder implements a forwarding implementation of all the fundamental traps. Forwarder then adds a forwarding implementation for the derived traps. Programmers can then create handlers that inherit from either one of these prototypal handlers, and override only a select number of traps, relying on the inherited implementation for the other traps. This seems to be in line with what David F. is proposing.

# Tom Van Cutsem (13 years ago)

Good catch. I count 5 occurrences of 'reject' on the page, 4 of these are called when the corresponding trap is not defined on the handler. For these cases, we should probably throw a TypeError, like we do for other traps (like for fix() in Object.freeze and getOwnPropertyNames in Object.getOwnPropertyNames).

For [[DefineOwnProperty]] and [[Delete]] I think we are unnecessarily conflating this error condition with the notion of 'rejection'. And as you note, for [[GetProperty]] and [[GetOwnProperty]] it isn't even well-defined.

2011/5/26 Andreas Rossberg <rossberg at google.com>

# David Flanagan (13 years ago)

I'd like to follow up on this message from last month. Has any decision been made about how a defineProperty() trap should reject the property definition attempt? §4.6.4 of the latest WebIDL draft specifies a [[DefineOwnProperty]] algorithm with a Reject step that is dependent on the Throw argument. And right now it can't really be implemented with a Proxy in a way that will work correctly when the proxy is used in non-strict code.

Tom suggested that defineProperty could return false or throw a TypeError to reject. I assume that the proxy implementation would then do the right thing (return false or throw TypeError) based on the value of the Throw argument.

# David Bruant (13 years ago)

Le 26/05/2011 15:36, Tom Van Cutsem a écrit :

Hi David,

The short answer: if you also define a 'set' trap, throwing a TypeError from defineProperty() to signal rejection is appropriate.

The longer answer:

defineProperty() is based on [[DefineOwnProperty]] (ES5 section 8.12.9) whose rejection behavior depends on an explicit 'Throw' parameter, just like [[Put]] and [[Delete]]. Why then, do set() and delete() return a boolean success flag to determine their rejection behavior, while defineProperty() does not?

I believe that, until now, we were convinced that in the case of proxies, defineProperty() actually does not depend on strict mode, because the built-in Object.defineProperty and Object.defineProperties methods (ES5 section 15.2.3.6-7) call the built-in [[DefineOwnProperty]] method with its 3rd argument 'Throw' set unconditionally to 'true', meaning it should never reject silently, independent of the strictness of the code.

However, checking other parts of the spec where [[DefineOwnProperty]] is invoked, I now notice it's also invoked by [[Put]], which passes its own 'Throw' argument to [[DefineOwnProperty]], and this time, the 'Throw' argument is dependent on strict mode. This complicates matters because of derived traps.

If a proxy handler does not define a 'set' trap (which is derived), the proxy implementation will fall back on the fundamental defineProperty() trap, whose rejection behavior in that context now should depend on strict mode. However, the current defineProperty() API doesn't allow the programmer to express this.

I see two options here:

  1. modify the defineProperty() trap such that it also returns a boolean flag to indicate rejection, like set() and delete(). This is still possible as defineProperty() currently has no useful return value.

  2. specify unambiguously that defineProperty() on proxies should always reject explicitly by throwing a TypeError, even if the defineProperty() trap was called as part of the default 'set' trap. This can be justified if we specify that the default 'set' trap invokes the 'defineProperty' trap as if by calling "Object.defineProperty", which is specified to never reject silently.

Option #1 would most closely follow the current ES5 spec, but would disallow the default 'set' trap behavior from being written in Javascript itself, since it's impossible to specify the value of the 'Throw' parameter in Javascript.

Option #2 is in line with how the current default 'set' behavior is specified at harmony:proxies#trap_defaults.

What about adding a "throw" argument to the defineProperty trap (and any trap for which the equivalent internal method has a "throw" argument) ? The engine would set the boolean value correctly depending on strict mode or any relevant parameter. Trap writers can adapt their behavior based on this boolean.

# David Flanagan (13 years ago)

On 6/17/11 3:05 PM, David Bruant wrote:

What about adding a "throw" argument to the defineProperty trap (and any trap for which the equivalent internal method has a "throw" argument) ? The engine would set the boolean value correctly depending on strict mode or any relevant parameter. Trap writers can adapt their behavior based on this boolean.

David

harmony:proxies_semantics already defines appropriate Reject behavior (return false or throw) for the [[Put]] and [[Delete]] operations based on the return value of the put() and delete() traps. [[DefineOwnProperty]] is the only one that does not allow it.

Given that the fixed properties proposal is adding a return value to defineProperty, perhaps returning false would be a suitable way to reject a property definition.

# Mark S. Miller (13 years ago)

On Fri, Jun 17, 2011 at 3:47 PM, David Flanagan <dflanagan at mozilla.com>wrote:

On 6/17/11 3:05 PM, David Bruant wrote:

What about adding a "throw" argument to the defineProperty trap (and any trap for which the equivalent internal method has a "throw" argument) ? The engine would set the boolean value correctly depending on strict mode or any relevant parameter. Trap writers can adapt their behavior based on this boolean.

David

**doku.php?id=harmony:proxies_**semanticsharmony:proxies_semanticsalready defines appropriate Reject behavior (return false or throw) for the [[Put]] and [[Delete]] operations based on the return value of the put() and delete() traps.

Yes, the reason is that a JavaScript callee cannot sense the strictness of its caller.

# David Bruant (13 years ago)

Le 18/06/2011 00:47, David Flanagan a écrit :

On 6/17/11 3:05 PM, David Bruant wrote:

What about adding a "throw" argument to the defineProperty trap (and any trap for which the equivalent internal method has a "throw" argument) ? The engine would set the boolean value correctly depending on strict mode or any relevant parameter. Trap writers can adapt their behavior based on this boolean.

David harmony:proxies_semantics already defines appropriate Reject behavior (return false or throw) for the [[Put]] and [[Delete]] operations based on the return value of the put() and delete() traps. [[DefineOwnProperty]] is the only one that does not allow it.

Given that the fixed properties proposal is adding a return value to defineProperty, perhaps returning false would be a suitable way to reject a property definition.

I would see this as a weird semantic collision. If I return undefined, should it be considered as a falsy value or an inappropriate property descriptor? If I return {} or [], is it a truthy value or an invalid property descriptor? I would be much more in favor of traps closely following internal methods signature, meaning add "Throw" arguments and let trap writers decide of what they want to do with it. Following internal method signature sounds a safer way to get around what you need to do. Currently, the "Throw" argument seems bound to strictness (needs to be proven, i do not know ES5 by heart), but who knows what it's going to be used for in the future (what other contextual information will influence its value)?


delete(a,Throw){ if(Throw) throw new Error(); else return false; }

// VS

delete(a){ return false; }

I think it's more clear for the reader that an error can be thrown from the former case. The latter case requires implicit knowledge and may be harder to debug. Of course, this is very subjective as I have never had the occasion to debug such a code.

# Mark S. Miller (13 years ago)

On Fri, Jun 17, 2011 at 4:20 PM, David Bruant <david.bruant at labri.fr> wrote: [...]

I would be much more in favor of traps closely following internal methods signature, meaning add "Throw" arguments and let trap writers decide of what they want to do with it. Following internal method signature sounds a safer way to get around what you need to do.

I agree with the general idea. But rather than make the traps conform to leaky internal spec methods, let's refactor the internal spec method signatures to conform to the less leaky signatures we've already achieved for the traps < harmony:proxy_defaulthandler#alternative_implementation_for_default_set_trap

.

In particular, everywhere we're currently passing in a boolean Throw argument (which does in fact always indicate strictness) we should instead be returning a boolean. Then, only the top level interface between special forms and internal methods need be concerned to turn a failure (false) into either a silent failure or a throw, depending on the strictness of the special form. All other internal methods need only propagate such failure reports back accurately.

# Tom Van Cutsem (13 years ago)

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

Le 18/06/2011 00:47, David Flanagan a écrit :

Given that the fixed properties proposal is adding a return value to defineProperty, perhaps returning false would be a suitable way to reject a property definition.

Yes, I think such an API could work.

I would see this as a weird semantic collision. If I return undefined, should it be considered as a falsy value or an inappropriate property descriptor? If I return {} or [], is it a truthy value or an invalid property descriptor?

  • |undefined| is falsy, so interpret as a reject.
  • {} is a valid, empty property descriptor.
  • For [], or any other non-falsy, non-object value either one could 1) ignore such values, 2) interpret them as 'rejects', or 3) interpret them as illegal property descriptors, which should trigger a TypeError. I would opt for 3), as if the internal Proxy code does:

let res = handler.defineProperty(...); if (res) { desc = ToPropertyDescriptor(res); // throws TypeError if res is a non-Object value ... } else { reject(); }

But before continuing this discussion, I'd like to take measure of how important it actually is for the Proxy API to be able to faithfully emulate ES5.1 reject behavior in non-strict mode. Say some non-strict ES5.1 code is executing Object.defineProperty(...) on a proxy and it throws a TypeError rather than failing silently. Is that a big deal or just a minor annoyance? As Mark recently mentioned, to ES5.1 code, proxies are like host objects, which may already trigger such behavior anyway. OTOH, it might preclude the use of Proxies to transparently wrap existing ES5.1 objects.

# Mark S. Miller (13 years ago)

On Sun, Jun 19, 2011 at 8:22 AM, Tom Van Cutsem <tomvc.be at gmail.com> wrote:

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

Le 18/06/2011 00:47, David Flanagan a écrit :

Given that the fixed properties proposal is adding a return value to defineProperty, perhaps returning false would be a suitable way to reject a property definition.

Yes, I think such an API could work.

I would see this as a weird semantic collision. If I return undefined, should it be considered as a falsy value or an inappropriate property descriptor? If I return {} or [], is it a truthy value or an invalid property descriptor?

  • |undefined| is falsy, so interpret as a reject.
  • {} is a valid, empty property descriptor.
  • For [], or any other non-falsy, non-object value either one could 1) ignore such values, 2) interpret them as 'rejects', or 3) interpret them as illegal property descriptors, which should trigger a TypeError. I would opt for 3), as if the internal Proxy code does:

let res = handler.defineProperty(...); if (res) { desc = ToPropertyDescriptor(res); // throws TypeError if res is a non-Object value ... } else { reject(); }

But before continuing this discussion, I'd like to take measure of how important it actually is for the Proxy API to be able to faithfully emulate ES5.1 reject behavior in non-strict mode. Say some non-strict ES5.1 code is executing Object.defineProperty(...) on a proxy and it throws a TypeError rather than failing silently. Is that a big deal or just a minor annoyance?

There might be a misunderstanding here. If ES5.1 strict or non-strict code explicitly calls Object.defineProperty(...) then (15.2.3.6 step 4) the [[DefineOwnProperty]] method is called with a Throw argument of true. So a failed explicit call to Object.defineProperty(...) always throws, regardless of the strictness of its caller.

To do otherwise would be to make the dynamic scoping mistake, since Object.defineProperty is first class, not a special form.

# Tom Van Cutsem (13 years ago)

2011/6/19 Mark S. Miller <erights at google.com>

On Sun, Jun 19, 2011 at 8:22 AM, Tom Van Cutsem <tomvc.be at gmail.com>wrote:

But before continuing this discussion, I'd like to take measure of how important it actually is for the Proxy API to be able to faithfully emulate ES5.1 reject behavior in non-strict mode. Say some non-strict ES5.1 code is executing Object.defineProperty(...) on a proxy and it throws a TypeError rather than failing silently. Is that a big deal or just a minor annoyance?

There might be a misunderstanding here. If ES5.1 strict or non-strict code explicitly calls Object.defineProperty(...) then (15.2.3.6 step 4) the [[DefineOwnProperty]] method is called with a Throw argument of true. So a failed explicit call to Object.defineProperty(...) always throws, regardless of the strictness of its caller.

To do otherwise would be to make the dynamic scoping mistake, since Object.defineProperty is first class, not a special form.

You're right, the problem is not with Object.defineProperty but rather with [[Put]] / the set trap (since it defaults to [[DefineOwnProperty]] / the defineProperty trap, and it is dependent on the strictness of the caller). But my question remains: how important is it for a Proxy to be able to faithfully emulate the reject behavior of assignment in non-strict code? If it is important then we should probably have the defineProperty trap return a success value, just like delete or set.

# David Flanagan (13 years ago)

On 6/20/11 1:13 AM, Tom Van Cutsem wrote [esdiscuss/2011-June/015352]:

But my question remains: how important is it for a Proxy to be able to faithfully emulate the reject behavior of assignment in non-strict code? If it is important then we should probably have the defineProperty trap return a success value, just like delete or set.

No one seems to have answered the question. Maybe that means it is not so important. The one data point I'd like to add is that WebIDL (§4.7.3 and elsewhere) is written to use the ECMA-262 Reject operation. If Proxy can't correctly emulate reject behavior in non-strict code, then I can't correctly implement the DOM in JavaScript.

On 6/17/11, Cameron McCormack wrote [lists.w3.org/Archives/Public/public-script-coord/2011AprJun/0209.html]:

It seems like proxies should allow throwing/ignoring based on strict modeness of the caller. I’ll leave the requirements to Reject in there for now.

Since WebIDL is in last-call status, this is probably a good time to decide whether the proxy proposal should change to allow faithful emulation of Reject, or whether WebIDL should change to throw a TypeError instead of using Reject...

# Mark S. Miller (13 years ago)

On Mon, Aug 8, 2011 at 2:20 PM, David Flanagan <dflanagan at mozilla.com>wrote: [...]

Since WebIDL is in last-call status, this is probably a good time to decide whether the proxy proposal should change to allow faithful emulation of Reject, or whether WebIDL should change to throw a TypeError instead of using Reject...

If there are no show-stopping legacy compat constraints forcing us to specify Reject, I prefer that we specify these to throw a TypeError.

# Cameron McCormack (13 years ago)

On 9/08/11 9:40 AM, Mark S. Miller wrote:

If there are no show-stopping legacy compat constraints forcing us to specify Reject, I prefer that we specify these to throw a TypeError.

I can't say for sure, but I would be surprised if this didn't cause problems, given that it introduces exception throwing where currently assignment to non-writable properties of objects that need to be implemented as proxies (like NodeList) is just ignored.

# Mark S. Miller (13 years ago)

On Wed, Aug 10, 2011 at 7:20 PM, Cameron McCormack <cam at mcc.id.au> wrote:

On 9/08/11 9:40 AM, Mark S. Miller wrote:

If there are no show-stopping legacy compat constraints forcing us to specify Reject, I prefer that we specify these to throw a TypeError.

I can't say for sure, but I would be surprised if this didn't cause problems, given that it introduces exception throwing where currently assignment to non-writable properties of objects that need to be implemented as proxies (like NodeList) is just ignored.

Ok. If we decide not to, then I think it is important that proxies be able to faithfully emulate ES5 failed "Reject" semantics, so that ES-next code can fully implement a conformant DOM.

# Tom Van Cutsem (13 years ago)

2011/8/11 Mark S. Miller <erights at google.com>

On Wed, Aug 10, 2011 at 7:20 PM, Cameron McCormack <cam at mcc.id.au> wrote:

On 9/08/11 9:40 AM, Mark S. Miller wrote:

If there are no show-stopping legacy compat constraints forcing us to specify Reject, I prefer that we specify these to throw a TypeError.

I can't say for sure, but I would be surprised if this didn't cause problems, given that it introduces exception throwing where currently assignment to non-writable properties of objects that need to be implemented as proxies (like NodeList) is just ignored.

Ok. If we decide not to, then I think it is important that proxies be able to faithfully emulate ES5 failed "Reject" semantics, so that ES-next code can fully implement a conformant DOM.

Noted. I'll write up a small strawman to change the signature of 'defineProperty' to return a boolean success value. This change should be fully compatible with the existing API, as the return value of the 'defineProperty' trap is currently ignored.

# Tom Van Cutsem (13 years ago)

2011/8/11 Tom Van Cutsem <tomvc.be at gmail.com>

Noted. I'll write up a small strawman to change the signature of 'defineProperty' to return a boolean success value. This change should be fully compatible with the existing API, as the return value of the 'defineProperty' trap is currently ignored.

For completeness: it's here: < strawman:defineproperty_reject_behavior

One upwards-compatibility hazard I see is that all current Proxy code that does not explicitly return a value from its |defineProperty| trap, implicitly returns |undefined|, which is coerced to |false|. That is probably not the behavior that was intended.

So, to future-proof if this strawman is accepted, I think it's best to let existing |defineProperty| traps |return true;|.

# Cameron McCormack (13 years ago)

On 15/08/11 8:58 PM, Tom Van Cutsem wrote:

So, to future-proof if this strawman is accepted, I think it's best to let existing |defineProperty| traps |return true;|.

By that do you mean interpret an undefined return value as meaning "don't throw"? Sounds reasonable.

# Tom Van Cutsem (13 years ago)

2011/8/15 Cameron McCormack <cam at mcc.id.au>

On 15/08/11 8:58 PM, Tom Van Cutsem wrote:

So, to future-proof if this strawman is accepted, I think it's best to let existing |defineProperty| traps |return true;|.

By that do you mean interpret an undefined return value as meaning "don't throw"? Sounds reasonable.

No, that's not what I meant. We could consider it, but then the same meaning should apply to the 'set' and 'delete' traps.

What I meant was that existing Proxy code (in which defineProperty doesn't need to return a boolean success value) is not upwards-compatible with this strawman. If a defineProperty trap does not return a value (or returns undefined), under this strawman that indicates a failed defineProperty invocation, which is most probably not what the original code intended. If proxy authors want to anticipate acceptance of this strawman, they can do so by having defineProperty traps |return true;| already. We need to discuss this change at the next TC39 meeting, but I don't currently see any reason why it would not be accepted into the proxies proposal.