summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoonchild <moonchild@palemoon.org>2023-10-20 20:57:39 +0000
committerMoonchild <moonchild@palemoon.org>2023-10-20 20:57:39 +0000
commit93823836d7d6642befc538b13b7e6af911570696 (patch)
tree493a9813b5c0c7914c1030457d4ab2ebce8d5cdc
parent1e3301e987a738e18ae943c19f176a39c4a03e15 (diff)
parent3e71cd6b93edaf7362e2635a38675bb843cd402a (diff)
downloaduxp-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.js68
-rw-r--r--js/src/builtin/intl/NumberFormat.cpp9
-rw-r--r--js/src/builtin/intl/NumberFormat.js122
-rw-r--r--js/src/builtin/intl/PluralRules.cpp10
-rw-r--r--js/src/builtin/intl/PluralRules.js32
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");