-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Relax [[DefineOwnProperty]] on module namespace exotic objects. #858
Conversation
…ects. [[DefineOwnProperty]] on module namespace exotic objects used to always return false. With this change, it returns true if, intuitively, applying the given property descriptor wouldn't change anything. This is consistent with how [[DefineOwnProperty]] behaves for other kinds of objects. - Special case for symbols. - Make OrdinaryDefineOwnProperty use IsExtensible.
Affected test262 tests: language/module-code/namespace/internals/define-own-property |
Note: Since [[GetOwnProperty]] can throw (if the property has not yet been initialized), it matters in which step we call it. In my change it's the very first step, but it may also make sense to do it as late as possible. |
spec.html
Outdated
1. Let _current_ be ? _O_.[[GetOwnProperty]](_P_). | ||
1. If _current_ is *undefined*, return *false*. | ||
1. If IsAccessorDescriptor(_Desc_) is *true*, return *false*. | ||
1. If _Desc_.[[Writable]] is present and has value *false*, return *false*. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this perhaps be cleaner (and independently useful) if there was an IsSameDescriptor
abstract operation? It might also be helpful in cleaning up ValidateAndApplyPropertyDescriptor
as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It wasn't clear to me how to simplify things but in the meantime I think I found a way to factor out the common logic (something like IsSameDescriptor doesn't cut it, I believe). Will update the PR soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had started writing this down but then felt that it doesn't really help a lot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
¯\_(ツ)_/¯ up to you, but it feels like being able to have an algorithm step that's effectively "if descriptorA == descriptorB, then" would be valuable - even if it's only used in a few places.
This change seems reasonable to me. |
@GeorgNeis this change has consensus, but is blocked on tests. Are you planning to make the relevant updates to language/module-code/namespace/internals/define-own-property.js? |
I can do that. |
@GeorgNeis excellent! |
spec.html
Outdated
@@ -8274,7 +8274,14 @@ | |||
<h1>[[DefineOwnProperty]] ( _P_, _Desc_ )</h1> | |||
<p>When the [[DefineOwnProperty]] internal method of a module namespace exotic object _O_ is called with property key _P_ and Property Descriptor _Desc_, the following steps are taken:</p> | |||
<emu-alg> | |||
1. Return *false*. | |||
1. Let _current_ be ? _O_.[[GetOwnProperty]](_P_). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first step needs to be If Type(P) is Symbol, return OrdinaryDefineOwnProperty(O, P, Desc).
, because the symbol properties of module namespace objects have different property descriptor attributes compared to the export properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Meh, I just noticed there is an issue when the current definition of OrdinaryDefineOwnProperty is directly called:
OrdinaryDefineOwnProperty(...)
directly accesses the [[Extensible]] internal slot instead of calling [[IsExtensible]], and module namespace objects don't have an [[Extensible]] internal slot.
So we've got two choices:
- Either change
OrdinaryDefineOwnProperty(...)
to call [[IsExtensible]] instead of directly accessing [[Extensible]]. - Or add an [[Extensible]] internal slot to module namespace objects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thing the first alternative is better. Providing alternative essential internal method definitions was intended to be the manner to encode such values that apply to all instance of a specific kind of exotic object.
I dislike the direction the second alternative is going. The [[Extensible]] internal slot is one of the defining characteristics of an ordinary object. Namespace objects are about as unordinary as you can get. Adding slots simply to allow use of Ordinary* internal methods on such non-ordinary objects weakens the concept of an ordinary object within the spec.
In ES6, Module namespace objects originally did not have a [[Prototype]] slot for similar reasons. Instead, it had a [[GetPrototypeOf]] internal method that always returned null. For some reason, this has changed to eliminate eliminate the exotic [[GetPrototypeOf]] and add a [[Prototype]] slot that always "contains the value null". I suspect this was done for a similar reason to choice 2 above. That change should probably be reverted.
In both cases, these sounds like changes that were simply made to facilitate pseudo-code reuse, but I think such changes detract rather than add to the clarity of the spec. Internal slots are instance specific, stateful and generally mutable. Their presence should imply at the very least that the state may differ among different instances of that kind of exotic object. When a value is immutable and constant that is no need to have instance specific slot to hold the constant value. The better way is to provide exotic internal methods (such as [[isExtensible]] or [[GetPrototypeOf]]) that directly use the constant. Such internal methods are conceptually shared among of instance of the exotic object type.
What more does this need to be able to merge into the spec? |
At least the issue noted in https://github.com/tc39/ecma262/pull/858/files/5de505fcb88ceb61ba3778c18edb0ce2cb3e55ee..0c201ef1b82bd744e10dd5c7c8341d2175442002#r136673470 still needs to be fixed. |
Sorry, I had missed that. Diff now includes the (tiny) generalization of OrdinaryDefineOwnProperty. |
Anything still missing before this change can be applied? FWIW Firefox implements the proposed semantics since Firefox 57 (https://bugzilla.mozilla.org/show_bug.cgi?id=1391773). |
Now that we have tests, consensus on spec text, and a shipping implementation, it seems like time to land this patch. |
LGTM but might want to match existing spec wording : https://tc39.github.io/ecma262/#sec-integer-indexed-exotic-objects-defineownproperty-p-desc |
OK, is this change ready to merge now? |
7858e9b
to
e4bfa86
Compare
[[DefineOwnProperty]] on module namespace exotic objects used to always
return false. With this change, it returns true if, intuitively,
applying the given property descriptor wouldn't change anything. This
is consistent with how [[DefineOwnProperty]] behaves for other kinds of
objects.