diff options
author | Moonchild <moonchild@palemoon.org> | 2023-10-20 20:57:39 +0000 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2023-10-20 20:57:39 +0000 |
commit | 93823836d7d6642befc538b13b7e6af911570696 (patch) | |
tree | 493a9813b5c0c7914c1030457d4ab2ebce8d5cdc | |
parent | 1e3301e987a738e18ae943c19f176a39c4a03e15 (diff) | |
parent | 3e71cd6b93edaf7362e2635a38675bb843cd402a (diff) | |
download | uxp-93823836d7d6642befc538b13b7e6af911570696.tar.gz |
Merge pull request 'Implement some NumberFormat spec updates' (#2352) from 2350-numberformat-work into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/2352
-rw-r--r-- | js/src/builtin/intl/CommonFunctions.js | 68 | ||||
-rw-r--r-- | js/src/builtin/intl/NumberFormat.cpp | 9 | ||||
-rw-r--r-- | js/src/builtin/intl/NumberFormat.js | 122 | ||||
-rw-r--r-- | js/src/builtin/intl/PluralRules.cpp | 10 | ||||
-rw-r--r-- | js/src/builtin/intl/PluralRules.js | 32 |
5 files changed, 172 insertions, 69 deletions
diff --git a/js/src/builtin/intl/CommonFunctions.js b/js/src/builtin/intl/CommonFunctions.js index effabf020f..69b96d6db1 100644 --- a/js/src/builtin/intl/CommonFunctions.js +++ b/js/src/builtin/intl/CommonFunctions.js @@ -647,33 +647,63 @@ function GetOption(options, property, type, values, fallback) { return fallback; } + /** - * Extracts a property value from the provided options object, converts it to a - * Number value, checks whether it is in the allowed range, and fills in a - * fallback value if necessary. + * The abstract operation DefaultNumberOption converts value to a Number value, + * checks whether it is in the allowed range, and fills in a fallback value if + * necessary. * - * Spec: ECMAScript Internationalization API Specification, 9.2.10. + * Spec: ECMAScript Internationalization API Specification, 9.2.11. */ -function GetNumberOption(options, property, minimum, maximum, fallback) { - assert(typeof minimum === "number", "GetNumberOption"); - assert(typeof maximum === "number", "GetNumberOption"); - assert(fallback === undefined || (fallback >= minimum && fallback <= maximum), "GetNumberOption"); +function DefaultNumberOption(value, minimum, maximum, fallback) { + assert( + typeof minimum === "number" && (minimum | 0) === minimum, + "DefaultNumberOption" + ); + assert( + typeof maximum === "number" && (maximum | 0) === maximum, + "DefaultNumberOption" + ); + assert( + fallback === undefined || + (typeof fallback === "number" && (fallback | 0) === fallback), + "DefaultNumberOption" + ); + assert( + fallback === undefined || (minimum <= fallback && fallback <= maximum), + "DefaultNumberOption" + ); + + // Step 1. + if (value === undefined) { + return fallback; + } - // Step 1. - var value = options[property]; + // Step 2. + value = ToNumber(value); - // Step 2. - if (value !== undefined) { - value = ToNumber(value); - if (Number_isNaN(value) || value < minimum || value > maximum) - ThrowRangeError(JSMSG_INVALID_DIGITS_VALUE, value); - return std_Math_floor(value); - } + // Step 3. + if (Number_isNaN(value) || value < minimum || value > maximum) { + ThrowRangeError(JSMSG_INVALID_DIGITS_VALUE, value); + } - // Step 3. - return fallback; + // Step 4. + // Apply bitwise-or to convert -0 to +0 per ES2017, 5.2 and to ensure the + // result is an int32 value. + return std_Math_floor(value) | 0; } +/** + * Extracts a property value from the provided options object, converts it to a + * Number value, checks whether it is in the allowed range, and fills in a + * fallback value if necessary. + * + * Spec: ECMAScript Internationalization API Specification, 9.2.12. + */ +function GetNumberOption(options, property, minimum, maximum, fallback) { + // Steps 1-3. + return DefaultNumberOption(options[property], minimum, maximum, fallback); +} // Symbols in the self-hosting compartment can't be cloned, use a separate // object to hold the actual symbol value. diff --git a/js/src/builtin/intl/NumberFormat.cpp b/js/src/builtin/intl/NumberFormat.cpp index 8820166f56..a1997c136e 100644 --- a/js/src/builtin/intl/NumberFormat.cpp +++ b/js/src/builtin/intl/NumberFormat.cpp @@ -353,10 +353,6 @@ NewUNumberFormat(JSContext* cx, Handle<NumberFormatObject*> numberFormat) return nullptr;
uMaximumSignificantDigits = value.toInt32();
} else {
- if (!GetProperty(cx, internals, internals, cx->names().minimumIntegerDigits,
- &value))
- return nullptr;
- uMinimumIntegerDigits = AssertedCast<uint32_t>(value.toInt32());
if (!GetProperty(cx, internals, internals, cx->names().minimumFractionDigits,
&value))
return nullptr;
@@ -367,6 +363,11 @@ NewUNumberFormat(JSContext* cx, Handle<NumberFormatObject*> numberFormat) uMaximumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
}
+ if (!GetProperty(cx, internals, internals, cx->names().minimumIntegerDigits,
+ &value))
+ return nullptr;
+ uMinimumIntegerDigits = AssertedCast<uint32_t>(value.toInt32());
+
if (!GetProperty(cx, internals, internals, cx->names().useGrouping, &value))
return nullptr;
uUseGrouping = value.toBoolean();
diff --git a/js/src/builtin/intl/NumberFormat.js b/js/src/builtin/intl/NumberFormat.js index 261bff1dc6..85fac41e01 100644 --- a/js/src/builtin/intl/NumberFormat.js +++ b/js/src/builtin/intl/NumberFormat.js @@ -55,8 +55,14 @@ function resolveNumberFormatInternals(lazyNumberFormatData) { // Step 22.
internalProps.minimumIntegerDigits = lazyNumberFormatData.minimumIntegerDigits;
- internalProps.minimumFractionDigits = lazyNumberFormatData.minimumFractionDigits;
- internalProps.maximumFractionDigits = lazyNumberFormatData.maximumFractionDigits;
+
+ if ("minimumFractionDigits" in lazyNumberFormatData) {
+ // Note: Intl.NumberFormat.prototype.resolvedOptions() exposes the
+ // actual presence (versus undefined-ness) of these properties.
+ assert("maximumFractionDigits" in lazyNumberFormatData, "min/max frac digits mismatch");
+ internalProps.minimumFractionDigits = lazyNumberFormatData.minimumFractionDigits;
+ internalProps.maximumFractionDigits = lazyNumberFormatData.maximumFractionDigits;
+ }
if ("minimumSignificantDigits" in lazyNumberFormatData) {
// Note: Intl.NumberFormat.prototype.resolvedOptions() exposes the
@@ -122,34 +128,90 @@ function UnwrapNumberFormat(nf, methodName) { *
* Spec: ECMAScript Internationalization API Specification, 11.1.1.
*/
-function SetNumberFormatDigitOptions(lazyData, options, mnfdDefault) {
+function SetNumberFormatDigitOptions(lazyData, options, mnfdDefault, mxfdDefault) {
// We skip step 1 because we set the properties on a lazyData object.
// Steps 2-4.
assert(IsObject(options), "SetNumberFormatDigitOptions");
assert(typeof mnfdDefault === "number", "SetNumberFormatDigitOptions");
- // Steps 5-8.
- const mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
- const mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault);
- const mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20);
+ assert(typeof mxfdDefault === "number", "SetNumberFormatDigitOptions");
+ assert(mnfdDefault <= mxfdDefault, "SetNumberFormatDigitOptions");
- // Steps 9-10.
+ // Steps 5-9.
+ const mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
+ let mnfd = options.minimumFractionDigits;
+ let mxfd = options.maximumFractionDigits;
let mnsd = options.minimumSignificantDigits;
let mxsd = options.maximumSignificantDigits;
- // Steps 9-11.
+ // Step 10.
lazyData.minimumIntegerDigits = mnid;
- lazyData.minimumFractionDigits = mnfd;
- lazyData.maximumFractionDigits = mxfd;
- // Step 12.
+ // Step 11.
if (mnsd !== undefined || mxsd !== undefined) {
- mnsd = GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1);
- mxsd = GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 21);
+ // Step 11.a (Omitted).
+
+ // Step 11.b.
+ mnsd = DefaultNumberOption(mnsd, 1, 21, 1);
+
+ // Step 11.c.
+ mxsd = DefaultNumberOption(mxsd, mnsd, 21, 21);
+
+ // Step 11.d.
lazyData.minimumSignificantDigits = mnsd;
+
+ // Step 11.e.
lazyData.maximumSignificantDigits = mxsd;
}
+
+ // Step 12.
+ else if (mnfd !== undefined || mxfd !== undefined) {
+ // Step 12.a (Omitted).
+
+ // Step 12.b.
+ mnfd = DefaultNumberOption(mnfd, 0, 20, undefined);
+
+ // Step 12.c.
+ mxfd = DefaultNumberOption(mxfd, 0, 20, undefined);
+
+ // Steps 12.d-e.
+ // Inlined DefaultNumberOption, only the fallback case applies here.
+ if (mnfd === undefined) {
+ assert(mxfd !== undefined, "mxfd isn't undefined when mnfd is undefined");
+ mnfd = std_Math_min(mnfdDefault, mxfd);
+ }
+
+ // Step 12.f.
+ // Inlined DefaultNumberOption, only the fallback case applies here.
+ else if (mxfd === undefined) {
+ mxfd = std_Math_max(mxfdDefault, mnfd);
+ }
+
+ // Step 12.g.
+ else if (mnfd > mxfd) {
+ ThrowRangeError(JSMSG_INVALID_DIGITS_VALUE, mxfd);
+ }
+
+ // Step 12.h.
+ lazyData.minimumFractionDigits = mnfd;
+
+ // Step 12.i.
+ lazyData.maximumFractionDigits = mxfd;
+ }
+
+ // Step 13 (TODO: Not yet implemented).
+
+ // Step 14.
+ else {
+ // Step 14.a (Omitted).
+
+ // Step 14.b.
+ lazyData.minimumFractionDigits = mnfdDefault;
+
+ // Step 14.c.
+ lazyData.maximumFractionDigits = mxfdDefault;
+ }
}
/**
@@ -291,19 +353,16 @@ function InitializeNumberFormat(numberFormat, thisValue, locales, options) { if (s === "currency")
lazyNumberFormatData.currencyDisplay = cd;
- // Steps 20-22.
- SetNumberFormatDigitOptions(lazyNumberFormatData, options, s === "currency" ? cDigits: 0);
-
- // Step 25.
- if (lazyNumberFormatData.maximumFractionDigits === undefined) {
- let mxfdDefault = s === "currency"
- ? cDigits
- : s === "percent"
- ? 0
- : 3;
- lazyNumberFormatData.maximumFractionDigits =
- std_Math_max(lazyNumberFormatData.minimumFractionDigits, mxfdDefault);
+ // Steps 22-25.
+ var mnfdDefault, mxfdDefault;
+ if (s === "currency") {
+ mnfdDefault = cDigits;
+ mxfdDefault = cDigits;
+ } else {
+ mnfdDefault = 0;
+ mxfdDefault = s === "percent" ? 0 : 3;
}
+ SetNumberFormatDigitOptions(lazyNumberFormatData, options, mnfdDefault, mxfdDefault);
// Steps 23.
var g = GetOption(options, "useGrouping", "boolean", undefined, true);
@@ -520,8 +579,6 @@ function Intl_NumberFormat_resolvedOptions() { numberingSystem: internals.numberingSystem,
style: internals.style,
minimumIntegerDigits: internals.minimumIntegerDigits,
- minimumFractionDigits: internals.minimumFractionDigits,
- maximumFractionDigits: internals.maximumFractionDigits,
useGrouping: internals.useGrouping
};
@@ -536,6 +593,15 @@ function Intl_NumberFormat_resolvedOptions() { _DefineDataProperty(result, "currencyDisplay", internals.currencyDisplay);
}
+ // Min/Max fraction digits are either both present or not present at all.
+ assert(hasOwn("minimumFractionDigits", internals) ===
+ hasOwn("maximumFractionDigits", internals),
+ "minimumFractionDigits is present iff maximumFractionDigits is present");
+ if (hasOwn("minimumFractionDigits", internals)) {
+ _DefineDataProperty(result, "minimumFractionDigits", internals.minimumFractionDigits);
+ _DefineDataProperty(result, "maximumFractionDigits", internals.maximumFractionDigits);
+ }
+
// Min/Max significant digits are either both present or not at all.
assert(hasOwn("minimumSignificantDigits", internals) ===
hasOwn("maximumSignificantDigits", internals),
diff --git a/js/src/builtin/intl/PluralRules.cpp b/js/src/builtin/intl/PluralRules.cpp index ce2f3c3893..dc27b330e9 100644 --- a/js/src/builtin/intl/PluralRules.cpp +++ b/js/src/builtin/intl/PluralRules.cpp @@ -224,11 +224,6 @@ NewUNumberFormatForPluralRules(JSContext* cx, Handle<PluralRulesObject*> pluralR return nullptr;
uMaximumSignificantDigits = value.toInt32();
} else {
- if (!GetProperty(cx, internals, internals, cx->names().minimumIntegerDigits,
- &value))
- return nullptr;
- uMinimumIntegerDigits = AssertedCast<uint32_t>(value.toInt32());
-
if (!GetProperty(cx, internals, internals, cx->names().minimumFractionDigits,
&value))
return nullptr;
@@ -240,6 +235,11 @@ NewUNumberFormatForPluralRules(JSContext* cx, Handle<PluralRulesObject*> pluralR uMaximumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
}
+ if (!GetProperty(cx, internals, internals, cx->names().minimumIntegerDigits,
+ &value))
+ return nullptr;
+ uMinimumIntegerDigits = AssertedCast<uint32_t>(value.toInt32());
+
UErrorCode status = U_ZERO_ERROR;
UNumberFormat* nf = unum_open(UNUM_DECIMAL, nullptr, 0, IcuLocale(locale.ptr()), nullptr, &status);
if (U_FAILURE(status)) {
diff --git a/js/src/builtin/intl/PluralRules.js b/js/src/builtin/intl/PluralRules.js index eb19bf067e..ae6a97f0ae 100644 --- a/js/src/builtin/intl/PluralRules.js +++ b/js/src/builtin/intl/PluralRules.js @@ -53,8 +53,12 @@ function resolvePluralRulesInternals(lazyPluralRulesData) { // Step 9.
internalProps.minimumIntegerDigits = lazyPluralRulesData.minimumIntegerDigits;
- internalProps.minimumFractionDigits = lazyPluralRulesData.minimumFractionDigits;
- internalProps.maximumFractionDigits = lazyPluralRulesData.maximumFractionDigits;
+
+ if ("minimumFractionDigits" in lazyPluralRulesData) {
+ assert("maximumFractionDigits" in lazyPluralRulesData, "min/max frac digits mismatch");
+ internalProps.minimumFractionDigits = lazyPluralRulesData.minimumFractionDigits;
+ internalProps.maximumFractionDigits = lazyPluralRulesData.maximumFractionDigits;
+ }
if ("minimumSignificantDigits" in lazyPluralRulesData) {
assert("maximumSignificantDigits" in lazyPluralRulesData, "min/max sig digits mismatch");
@@ -149,14 +153,8 @@ function InitializePluralRules(pluralRules, locales, options) { const type = GetOption(options, "type", "string", ["cardinal", "ordinal"], "cardinal");
lazyPluralRulesData.type = type;
- // Step 9.
- SetNumberFormatDigitOptions(lazyPluralRulesData, options, 0);
-
- // Step 12.
- if (lazyPluralRulesData.maximumFractionDigits === undefined) {
- lazyPluralRulesData.maximumFractionDigits =
- std_Math_max(lazyPluralRulesData.minimumFractionDigits, 3);
- }
+ // Steps 11-12.
+ SetNumberFormatDigitOptions(lazyPluralRulesData, options, 0, 3);
// Step 15.
//
@@ -246,11 +244,19 @@ function Intl_PluralRules_resolvedOptions() { type: internals.type,
pluralCategories: callFunction(std_Array_slice, internals.pluralCategories, 0),
minimumIntegerDigits: internals.minimumIntegerDigits,
- minimumFractionDigits: internals.minimumFractionDigits,
- maximumFractionDigits: internals.maximumFractionDigits,
};
- // Min/Max significant digits are either both present or not at all.
+ // Min/Max fraction digits are either both present or not present at all.
+ assert(hasOwn("minimumFractionDigits", internals) ===
+ hasOwn("maximumFractionDigits", internals),
+ "minimumFractionDigits is present iff maximumFractionDigits is present");
+
+ if (hasOwn("minimumFractionDigits", internals)) {
+ _DefineDataProperty(result, "minimumFractionDigits", internals.minimumFractionDigits);
+ _DefineDataProperty(result, "maximumFractionDigits", internals.maximumFractionDigits);
+ }
+
+ // Min/Max significant digits are either both present or not present at all.
assert(hasOwn("minimumSignificantDigits", internals) ===
hasOwn("maximumSignificantDigits", internals),
"minimumSignificantDigits is present iff maximumSignificantDigits is present");
|