diff options
author | Moonchild <moonchild@palemoon.org> | 2023-10-19 10:21:54 +0200 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2023-10-19 15:19:55 +0200 |
commit | 6c6c16b7f012b301af8ebc1aa0808017d631b56d (patch) | |
tree | 7de17fdbf2790abd643974f10a4178d723974f9f | |
parent | 6ad6cc9c2d245be3e0ed7526b09f26b8cd5854a7 (diff) | |
download | uxp-6c6c16b7f012b301af8ebc1aa0808017d631b56d.tar.gz |
Issue #2350 - Part 1: Apply "minimumIntegerDigits" to number-formatters
with fraction-digits.
Implements the changes to `SetNumberFormatDigitOptions` and
`FormatNumberToString`, except for the support of the new
"compact-rounding" mode.
This also includes a few minor fixes for edge cases (like -0 vs. 0)
-rw-r--r-- | js/src/builtin/intl/CommonFunctions.js | 22 | ||||
-rw-r--r-- | js/src/builtin/intl/NumberFormat.cpp | 9 | ||||
-rw-r--r-- | js/src/builtin/intl/NumberFormat.js | 107 | ||||
-rw-r--r-- | js/src/builtin/intl/PluralRules.cpp | 10 | ||||
-rw-r--r-- | js/src/builtin/intl/PluralRules.js | 32 |
5 files changed, 111 insertions, 69 deletions
diff --git a/js/src/builtin/intl/CommonFunctions.js b/js/src/builtin/intl/CommonFunctions.js index 80a35f1851..69b96d6db1 100644 --- a/js/src/builtin/intl/CommonFunctions.js +++ b/js/src/builtin/intl/CommonFunctions.js @@ -698,29 +698,13 @@ function DefaultNumberOption(value, minimum, maximum, fallback) { * 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.12. */ 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"); - - // Step 1. - var value = options[property]; - - // 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. - return 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. // TODO: Can we add support to clone symbols? 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..60d423efd9 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,75 @@ 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, mnfdDefault);
+
+ // Step 12.c.
+ const mxfdActualDefault = std_Math_max(mnfd, mxfdDefault);
+
+ // Step 12.d.
+ mxfd = DefaultNumberOption(mxfd, mnfd, 20, mxfdActualDefault);
+
+ // Step 12.e.
+ lazyData.minimumFractionDigits = mnfd;
+
+ // Step 12.f.
+ 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 +338,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 +564,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 +578,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");
|