mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-29 18:47:31 +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
|
* Number value, checks whether it is in the allowed range, and fills in a
|
||||||
* fallback value if necessary.
|
* 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) {
|
function GetNumberOption(options, property, minimum, maximum, fallback) {
|
||||||
assert(typeof minimum === "number", "GetNumberOption");
|
// Steps 1-3.
|
||||||
assert(typeof maximum === "number", "GetNumberOption");
|
return DefaultNumberOption(options[property], minimum, maximum, fallback);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Symbols in the self-hosting compartment can't be cloned, use a separate
|
// Symbols in the self-hosting compartment can't be cloned, use a separate
|
||||||
// object to hold the actual symbol value.
|
// object to hold the actual symbol value.
|
||||||
// TODO: Can we add support to clone symbols?
|
// TODO: Can we add support to clone symbols?
|
||||||
|
|
|
||||||
|
|
@ -353,10 +353,6 @@ NewUNumberFormat(JSContext* cx, Handle<NumberFormatObject*> numberFormat)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
uMaximumSignificantDigits = value.toInt32();
|
uMaximumSignificantDigits = value.toInt32();
|
||||||
} else {
|
} 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,
|
if (!GetProperty(cx, internals, internals, cx->names().minimumFractionDigits,
|
||||||
&value))
|
&value))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -367,6 +363,11 @@ NewUNumberFormat(JSContext* cx, Handle<NumberFormatObject*> numberFormat)
|
||||||
uMaximumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
|
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))
|
if (!GetProperty(cx, internals, internals, cx->names().useGrouping, &value))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
uUseGrouping = value.toBoolean();
|
uUseGrouping = value.toBoolean();
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,14 @@ function resolveNumberFormatInternals(lazyNumberFormatData) {
|
||||||
|
|
||||||
// Step 22.
|
// Step 22.
|
||||||
internalProps.minimumIntegerDigits = lazyNumberFormatData.minimumIntegerDigits;
|
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) {
|
if ("minimumSignificantDigits" in lazyNumberFormatData) {
|
||||||
// Note: Intl.NumberFormat.prototype.resolvedOptions() exposes the
|
// Note: Intl.NumberFormat.prototype.resolvedOptions() exposes the
|
||||||
|
|
@ -122,33 +128,74 @@ function UnwrapNumberFormat(nf, methodName) {
|
||||||
*
|
*
|
||||||
* Spec: ECMAScript Internationalization API Specification, 11.1.1.
|
* 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.
|
// We skip step 1 because we set the properties on a lazyData object.
|
||||||
|
|
||||||
// Steps 2-4.
|
// Steps 2-4.
|
||||||
assert(IsObject(options), "SetNumberFormatDigitOptions");
|
assert(IsObject(options), "SetNumberFormatDigitOptions");
|
||||||
assert(typeof mnfdDefault === "number", "SetNumberFormatDigitOptions");
|
assert(typeof mnfdDefault === "number", "SetNumberFormatDigitOptions");
|
||||||
|
|
||||||
// Steps 5-8.
|
assert(typeof mxfdDefault === "number", "SetNumberFormatDigitOptions");
|
||||||
const mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
|
assert(mnfdDefault <= mxfdDefault, "SetNumberFormatDigitOptions");
|
||||||
const mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault);
|
|
||||||
const mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20);
|
|
||||||
|
|
||||||
// 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 mnsd = options.minimumSignificantDigits;
|
||||||
let mxsd = options.maximumSignificantDigits;
|
let mxsd = options.maximumSignificantDigits;
|
||||||
|
|
||||||
// Steps 9-11.
|
// Step 10.
|
||||||
lazyData.minimumIntegerDigits = mnid;
|
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.
|
// Step 12.
|
||||||
if (mnsd !== undefined || mxsd !== undefined) {
|
else if (mnfd !== undefined || mxfd !== undefined) {
|
||||||
mnsd = GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1);
|
// Step 12.a (Omitted).
|
||||||
mxsd = GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 21);
|
|
||||||
lazyData.minimumSignificantDigits = mnsd;
|
// Step 12.b.
|
||||||
lazyData.maximumSignificantDigits = mxsd;
|
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")
|
if (s === "currency")
|
||||||
lazyNumberFormatData.currencyDisplay = cd;
|
lazyNumberFormatData.currencyDisplay = cd;
|
||||||
|
|
||||||
// Steps 20-22.
|
// Steps 22-25.
|
||||||
SetNumberFormatDigitOptions(lazyNumberFormatData, options, s === "currency" ? cDigits: 0);
|
var mnfdDefault, mxfdDefault;
|
||||||
|
if (s === "currency") {
|
||||||
// Step 25.
|
mnfdDefault = cDigits;
|
||||||
if (lazyNumberFormatData.maximumFractionDigits === undefined) {
|
mxfdDefault = cDigits;
|
||||||
let mxfdDefault = s === "currency"
|
} else {
|
||||||
? cDigits
|
mnfdDefault = 0;
|
||||||
: s === "percent"
|
mxfdDefault = s === "percent" ? 0 : 3;
|
||||||
? 0
|
|
||||||
: 3;
|
|
||||||
lazyNumberFormatData.maximumFractionDigits =
|
|
||||||
std_Math_max(lazyNumberFormatData.minimumFractionDigits, mxfdDefault);
|
|
||||||
}
|
}
|
||||||
|
SetNumberFormatDigitOptions(lazyNumberFormatData, options, mnfdDefault, mxfdDefault);
|
||||||
|
|
||||||
// Steps 23.
|
// Steps 23.
|
||||||
var g = GetOption(options, "useGrouping", "boolean", undefined, true);
|
var g = GetOption(options, "useGrouping", "boolean", undefined, true);
|
||||||
|
|
@ -520,8 +564,6 @@ function Intl_NumberFormat_resolvedOptions() {
|
||||||
numberingSystem: internals.numberingSystem,
|
numberingSystem: internals.numberingSystem,
|
||||||
style: internals.style,
|
style: internals.style,
|
||||||
minimumIntegerDigits: internals.minimumIntegerDigits,
|
minimumIntegerDigits: internals.minimumIntegerDigits,
|
||||||
minimumFractionDigits: internals.minimumFractionDigits,
|
|
||||||
maximumFractionDigits: internals.maximumFractionDigits,
|
|
||||||
useGrouping: internals.useGrouping
|
useGrouping: internals.useGrouping
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -536,6 +578,15 @@ function Intl_NumberFormat_resolvedOptions() {
|
||||||
_DefineDataProperty(result, "currencyDisplay", internals.currencyDisplay);
|
_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.
|
// Min/Max significant digits are either both present or not at all.
|
||||||
assert(hasOwn("minimumSignificantDigits", internals) ===
|
assert(hasOwn("minimumSignificantDigits", internals) ===
|
||||||
hasOwn("maximumSignificantDigits", internals),
|
hasOwn("maximumSignificantDigits", internals),
|
||||||
|
|
|
||||||
|
|
@ -224,11 +224,6 @@ NewUNumberFormatForPluralRules(JSContext* cx, Handle<PluralRulesObject*> pluralR
|
||||||
return nullptr;
|
return nullptr;
|
||||||
uMaximumSignificantDigits = value.toInt32();
|
uMaximumSignificantDigits = value.toInt32();
|
||||||
} else {
|
} 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,
|
if (!GetProperty(cx, internals, internals, cx->names().minimumFractionDigits,
|
||||||
&value))
|
&value))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -240,6 +235,11 @@ NewUNumberFormatForPluralRules(JSContext* cx, Handle<PluralRulesObject*> pluralR
|
||||||
uMaximumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
|
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;
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
UNumberFormat* nf = unum_open(UNUM_DECIMAL, nullptr, 0, IcuLocale(locale.ptr()), nullptr, &status);
|
UNumberFormat* nf = unum_open(UNUM_DECIMAL, nullptr, 0, IcuLocale(locale.ptr()), nullptr, &status);
|
||||||
if (U_FAILURE(status)) {
|
if (U_FAILURE(status)) {
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,12 @@ function resolvePluralRulesInternals(lazyPluralRulesData) {
|
||||||
|
|
||||||
// Step 9.
|
// Step 9.
|
||||||
internalProps.minimumIntegerDigits = lazyPluralRulesData.minimumIntegerDigits;
|
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) {
|
if ("minimumSignificantDigits" in lazyPluralRulesData) {
|
||||||
assert("maximumSignificantDigits" in lazyPluralRulesData, "min/max sig digits mismatch");
|
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");
|
const type = GetOption(options, "type", "string", ["cardinal", "ordinal"], "cardinal");
|
||||||
lazyPluralRulesData.type = type;
|
lazyPluralRulesData.type = type;
|
||||||
|
|
||||||
// Step 9.
|
// Steps 11-12.
|
||||||
SetNumberFormatDigitOptions(lazyPluralRulesData, options, 0);
|
SetNumberFormatDigitOptions(lazyPluralRulesData, options, 0, 3);
|
||||||
|
|
||||||
// Step 12.
|
|
||||||
if (lazyPluralRulesData.maximumFractionDigits === undefined) {
|
|
||||||
lazyPluralRulesData.maximumFractionDigits =
|
|
||||||
std_Math_max(lazyPluralRulesData.minimumFractionDigits, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 15.
|
// Step 15.
|
||||||
//
|
//
|
||||||
|
|
@ -246,11 +244,19 @@ function Intl_PluralRules_resolvedOptions() {
|
||||||
type: internals.type,
|
type: internals.type,
|
||||||
pluralCategories: callFunction(std_Array_slice, internals.pluralCategories, 0),
|
pluralCategories: callFunction(std_Array_slice, internals.pluralCategories, 0),
|
||||||
minimumIntegerDigits: internals.minimumIntegerDigits,
|
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) ===
|
assert(hasOwn("minimumSignificantDigits", internals) ===
|
||||||
hasOwn("maximumSignificantDigits", internals),
|
hasOwn("maximumSignificantDigits", internals),
|
||||||
"minimumSignificantDigits is present iff maximumSignificantDigits is present");
|
"minimumSignificantDigits is present iff maximumSignificantDigits is present");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue