mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-09 01:08:39 +09:00
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)
This commit is contained in:
parent
735bb78c28
commit
be81bae5b7
5 changed files with 111 additions and 69 deletions
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,33 +128,74 @@ 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 11.
|
||||
if (mnsd !== undefined || mxsd !== undefined) {
|
||||
// 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.
|
||||
if (mnsd !== undefined || mxsd !== undefined) {
|
||||
mnsd = GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1);
|
||||
mxsd = GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 21);
|
||||
lazyData.minimumSignificantDigits = mnsd;
|
||||
lazyData.maximumSignificantDigits = mxsd;
|
||||
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),
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue