-
Notifications
You must be signed in to change notification settings - Fork 108
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
Require options to be an object #480
Comments
If there's agreement to perform this change, we should definitely also try to apply it to the existing Intl constructors. And only if we detect any web-compat issues, back it out for the affected constructors. Similar to how we managed to restrict the "IntlLegacyConstructedSymbol" hack to just Intl.NumberFormat and Intl.DateTimeFormat, but not Intl.Collator. |
See also: tc39/ecma402#480 Co-Authored-By: Philip Chimento <pchimento@igalia.com>
See also: tc39/ecma402#480 Co-Authored-By: Philip Chimento <pchimento@igalia.com>
There's a way to make this change non-normatively as well. We could change the definition of GetOption but make sure to use ToObject first on all objects passed to existing uses of GetOption. That way, the definition of GetOption could move into 262 when Temporal does, but the behaviour in Intl could remain unchanged. I'll prepare a PR with both the editorial and normative changes, split into two commits, for discussion. |
This changes the behaviour of GetOption, but it has no effect on anything currently in the spec, because currently all values that GetOption sees are the output of either the ToObject or ObjectCreate operations. Therefore the change can be considered editorial. This is in order to later eliminate the boxing behaviour of ToObject in future Intl objects (and in current ones where there is no web-compat issue) and to be able to move GetOption into ECMA-262 in sync with Temporal.
This changes all uses of GetOption to accept undefined and objects, and throw a TypeError on all other types. This is different behaviour from what was previously specified. As an example of what will change, previously, if user code passed a number as an options argument (`new Intl.DateTimeFormt('en', 3)`) the number would be boxed into a Number object which would then be queried for a property which it didn't have. This overwhelmingly indicates a bug in user code, so after this change, that code snippet would throw a TypeError. (This commit is for the purpose of discussion. Some or all of these changes should be removed if they result in web-compat issues.)
This changes all uses of GetOption to accept undefined and objects, and throw a TypeError on all other types. This is different behaviour from what was previously specified. As an example of what will change, previously, if user code passed a number as an options argument (`new Intl.DateTimeFormt('en', 3)`) the number would be boxed into a Number object which would then be queried for a property which it didn't have. This overwhelmingly indicates a bug in user code, so after this change, that code snippet would throw a TypeError. (This commit is for the purpose of discussion. Some or all of these changes should be removed if they result in web-compat issues.)
OK, this is in #493, one non-normative commit followed by a normative commit. |
Another option, which I mentioned in the meeting: Keep <emu-clause id="sec-normalizeoptionsobject" aoid="NormalizeOptionsObject">
<h1>NormalizeOptionsObject ( _options_, _legacyMode_ )</h1>
<p>
The abstract operation NormalizeOptionsObject massages _options_ into an Object to be subsequently passed to GetOption. _legacyMode_ should be *false* except in contexts where it is required for backwards compatibility.
</p>
<emu-alg>
1. If _options_ is *undefined*, then
1. Return ObjectCreate(*null*).
1. If Type(_options_) is Object, then
1. Return _options_.
1. If _legacyMode_, then
1. Return ToObject(_options_).
1. Throw a *TypeError* exception.
</emu-alg>
</emu-clause> Change the 402 call sites as follows: <!-- Current 4 lines, present in several places, preceding calls to GetOption -->
1. If _options_ is *undefined*, then
1. Let _options_ be ObjectCreate(*null*).
1. Else,
1. Let _options_ be ? ToObject(_options_).
<!-- Replace with: -->
1. Let _options_ be NormalizeOptionsObject(_options_, true). All new callers of |
Rather than adding a "legacy mode" into 262, why not have an operation in 402 that calls NormalizeOptionsObject, and if it's abrupt, wraps the original value in ToObject? |
If we don't want the legacy mode in 262, we could do: <!-- 262 -->
<emu-clause id="sec-normalizeoptionsobject" aoid="NormalizeOptionsObject">
<h1>NormalizeOptionsObject ( _options_ )</h1>
<p>
The abstract operation NormalizeOptionsObject massages _options_ into an Object to be subsequently passed to GetOption. It throws a TypeError if _options_ is not undefined and not an Object.
</p>
<emu-alg>
1. If _options_ is *undefined*, then
1. Return ObjectCreate(*null*).
1. If Type(_options_) is Object, then
1. Return _options_.
1. Throw a *TypeError* exception.
</emu-alg>
</emu-clause>
<!-- 402 -->
<emu-clause id="sec-normalizeoptionsobjectlegacy" aoid="NormalizeOptionsObjectLegacy">
<h1>NormalizeOptionsObjectLegacy ( _options_ )</h1>
<p>
The abstract operation NormalizeOptionsObjectLegacy massages _options_ into an Object to be subsequently passed to GetOption. It does not throw an exception, instead making a best-effort attempt at coercing _options_ to an Object. Newer spec code should use ECMA-262's NormalizeOptionsObject instead.
</p>
<emu-alg>
1. If _options_ is *undefined*, then
1. Return ObjectCreate(*null*).
1. If Type(_options_) is Object, then
1. Return _options_.
1. Return ToObject(_options_).
</emu-alg>
</emu-clause> |
NormalizeOptionsObject and NormalizeOptionsObjectLegacy seem reasonable to me. To get things moving, I would propose adding them both to the 402 spec, then we can later move NormalizeOptionsObject and GetOption into 262? |
I support @ptomato's suggestion in #480 (comment) and @sffc proposal in #480 (comment) |
In order to later be able to move GetOption into ECMA-262 in sync with Temporal, we add NormalizeOptionsObject and NormalizeOptionsObjectLegacy as abstract operations. NormalizeOptionsObject and GetOption are intended to be moved into ECMA-262 at the same time, while NormalizeOptionsObjectLegacy is intended to stay in ECMA-402. NormalizeOptionsObjectLegacy describes the somewhat unexpected boxing behaviour when passing a primitive as the options parameter in a formatter constructor. This behaviour may need to be preserved for web-compatibility reasons, but new Intl objects going forward should use the NormalizeOptionsObject behaviour.
I've updated the PR so that it doesn't make any normative changes. Please take a look when you have time! |
See also: tc39/ecma402#480 Co-Authored-By: Philip Chimento <pchimento@igalia.com>
In order to later be able to move GetOption into ECMA-262 in sync with Temporal, we add NormalizeOptionsObject and NormalizeOptionsObjectLegacy as abstract operations. NormalizeOptionsObject and GetOption are intended to be moved into ECMA-262 at the same time, while NormalizeOptionsObjectLegacy is intended to stay in ECMA-402. NormalizeOptionsObjectLegacy describes the somewhat unexpected boxing behaviour when passing a primitive as the options parameter in a formatter constructor. This behaviour may need to be preserved for web-compatibility reasons, but new Intl objects going forward should use the NormalizeOptionsObject behaviour.
See also: tc39/ecma402#480 Co-Authored-By: Philip Chimento <pchimento@igalia.com>
See also: tc39/ecma402#480 Co-Authored-By: Philip Chimento <pchimento@igalia.com>
See also: tc39/ecma402#480 Co-Authored-By: Philip Chimento <pchimento@igalia.com>
See also: tc39/ecma402#480 Co-Authored-By: Philip Chimento <pchimento@igalia.com>
See also: tc39/ecma402#480 Co-Authored-By: Philip Chimento <pchimento@igalia.com>
See also: tc39/ecma402#480 Co-Authored-By: Philip Chimento <pchimento@igalia.com>
In order to later be able to move GetOption into ECMA-262 in sync with Temporal, we add NormalizeOptionsObject and NormalizeOptionsObjectLegacy as abstract operations. NormalizeOptionsObject and GetOption are intended to be moved into ECMA-262 at the same time, while NormalizeOptionsObjectLegacy is intended to stay in ECMA-402. NormalizeOptionsObjectLegacy describes the somewhat unexpected boxing behaviour when passing a primitive as the options parameter in a formatter constructor. This behaviour may need to be preserved for web-compatibility reasons, but new Intl objects going forward should use the NormalizeOptionsObject behaviour.
In order to later be able to move GetOption into ECMA-262 in sync with Temporal, we add GetOptionsObject and CoerceOptionsToObject as abstract operations. GetOptionsObject and GetOption are intended to be moved into ECMA-262 at the same time, while CoerceOptionsToObject is intended to stay in ECMA-402. CoerceOptionsToObject describes the somewhat unexpected boxing behaviour when passing a primitive as the options parameter in a formatter constructor. This behaviour may need to be preserved for web-compatibility reasons, but new Intl objects going forward should use the GetOptionsObject behaviour.
2021-02-11 discussion: https://github.com/tc39/ecma402/blob/master/meetings/notes-2021-02-11.md#require-options-to-be-an-object-480 We agreed to adopt the new stricter GetOptions behavior in newer APIs, but not to change older APIs to avoid breaking the web with what would most likely be benign errors. We should document this decision by ensuring that new constructors use the new behavior. |
Is it not worth some telemetry to figure out if it would break the web? |
I'd be happy if someone can volunteer to do this, but from a prioritization standpoint I don't want this to delay more urgent issues that are on our backlog. |
Could you clearly define which one are part of the "newer APIs" and which one are part of the "older APIs"? The word "newer" and "older" is very ambiguous here. |
Any API introduced to the specification from this point forward, minus possible exceptions for anything that is already mature enough for that change itself to present a risk. |
I filed #537 to track updating new ES 2021 proposals. |
FYI v8 tracking https://bugs.chromium.org/p/v8/issues/detail?id=11466 |
JSC change FYI WebKit/WebKit@51b6659 |
Given that #538 and tc39/proposal-intl-segmenter#135 are merged, is there anything remaining to do on this issue? |
Yes: #480 (comment)
I'm working on similar changes now, and do expect this to close soon. |
See also: tc39/ecma402#480 Co-Authored-By: Philip Chimento <pchimento@igalia.com>
Currently, we coerce the options argument to an object by calling
ToObject
. @justingrant suggested in tc39/proposal-temporal#693 that we should be stricter and throw instead.I don't think we should change older Intl objects, due to backwards compatibility, but we can adopt this pattern in new objects.
The text was updated successfully, but these errors were encountered: