Commit d43080a7 authored by Frank Tang's avatar Frank Tang Committed by V8 LUCI CQ

[intl] Fix ResolvedOptions to output.

In v3 we allow both significant digits and fraction digits to be set in some conditions.
Also fix the case in v2 we didn't handle "precision-integer" with currency format.

Related spec text:
https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/diff.html#sec-intl.numberformat.prototype.resolvedoptions
https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/diff.html#sec-setnfdigitoptions

Bug: v8:11544
Change-Id: I89c147dcc7803eae7aad2a380e85d1d877e30370
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3615217
Commit-Queue: Frank Tang <ftang@chromium.org>
Reviewed-by: 's avatarShu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80322}
parent 71768085
......@@ -713,11 +713,29 @@ int32_t JSNumberFormat::MinimumIntegerDigitsFromSkeleton(
// 123
// 4567
// Set The minimum as 3 and maximum as 7.
// We also treat the following special cases as both minimum and maximum are 0
// while there are no . in the skeleton:
// 1. While there are "precision-integer" in the skeleton.
// 2. While there are "precision-increment/" in the skeleton but no . after it.
// Examples:
// "currency/JPY precision-integer rounding-mode-half-up"
// "precision-increment/2 rounding-mode-half-up"
bool JSNumberFormat::FractionDigitsFromSkeleton(
const icu::UnicodeString& skeleton, int32_t* minimum, int32_t* maximum) {
icu::UnicodeString search(".");
int32_t index = skeleton.indexOf(search);
if (index < 0) return false;
int32_t index = skeleton.indexOf(".");
if (index < 0) {
// https://unicode-org.github.io/icu/userguide/format_parse/numbers/skeletons.html#precision
// Note that the stem . is considered valid and is equivalent to
// precision-integer.
// Also, if there are "precision-increment/" but no "." we consider both
// minimum and maximum fraction digits as 0.
if (skeleton.indexOf("precision-integer") >= 0 ||
skeleton.indexOf("precision-increment/") >= 0) {
*minimum = *maximum = 0;
return true;
}
return false;
}
*minimum = 0;
index++; // skip the '.'
while (index < skeleton.length() && IsDecimalDigit(skeleton[index])) {
......@@ -742,8 +760,7 @@ bool JSNumberFormat::FractionDigitsFromSkeleton(
// Set The minimum as 5 and maximum as 12.
bool JSNumberFormat::SignificantDigitsFromSkeleton(
const icu::UnicodeString& skeleton, int32_t* minimum, int32_t* maximum) {
icu::UnicodeString search("@");
int32_t index = skeleton.indexOf(search);
int32_t index = skeleton.indexOf("@");
if (index < 0) return false;
*minimum = 1;
index++; // skip the first '@'
......@@ -1010,27 +1027,31 @@ Handle<JSObject> JSNumberFormat::ResolvedOptions(
Just(kDontThrow))
.FromJust());
int32_t minimum = 0, maximum = 0;
if (SignificantDigitsFromSkeleton(skeleton, &minimum, &maximum)) {
int32_t mnsd = 0, mxsd = 0, mnfd = 0, mxfd = 0;
bool has_significant_digits =
SignificantDigitsFromSkeleton(skeleton, &mnsd, &mxsd);
if (has_significant_digits) {
CHECK(JSReceiver::CreateDataProperty(
isolate, options, factory->minimumSignificantDigits_string(),
factory->NewNumberFromInt(minimum), Just(kDontThrow))
factory->NewNumberFromInt(mnsd), Just(kDontThrow))
.FromJust());
CHECK(JSReceiver::CreateDataProperty(
isolate, options, factory->maximumSignificantDigits_string(),
factory->NewNumberFromInt(maximum), Just(kDontThrow))
.FromJust());
} else {
FractionDigitsFromSkeleton(skeleton, &minimum, &maximum);
CHECK(JSReceiver::CreateDataProperty(
isolate, options, factory->minimumFractionDigits_string(),
factory->NewNumberFromInt(minimum), Just(kDontThrow))
.FromJust());
CHECK(JSReceiver::CreateDataProperty(
isolate, options, factory->maximumFractionDigits_string(),
factory->NewNumberFromInt(maximum), Just(kDontThrow))
factory->NewNumberFromInt(mxsd), Just(kDontThrow))
.FromJust());
}
if ((FLAG_harmony_intl_number_format_v3 || !has_significant_digits)) {
if (FractionDigitsFromSkeleton(skeleton, &mnfd, &mxfd)) {
CHECK(JSReceiver::CreateDataProperty(
isolate, options, factory->minimumFractionDigits_string(),
factory->NewNumberFromInt(mnfd), Just(kDontThrow))
.FromJust());
CHECK(JSReceiver::CreateDataProperty(
isolate, options, factory->maximumFractionDigits_string(),
factory->NewNumberFromInt(mxfd), Just(kDontThrow))
.FromJust());
}
}
if (FLAG_harmony_intl_number_format_v3) {
CHECK(JSReceiver::CreateDataProperty(
......
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Test v3 condiction
// Flags: --harmony-intl-number-format-v3
function assertPresentOfDigits(
options, expectSignificant, expectFractional, user_message) {
if (expectSignificant) {
assertNotUndefined(options.minimumSignificantDigits, user_message);
assertNotUndefined(options.maximumSignificantDigits, user_message);
} else {
assertEquals(undefined, options.minimumSignificantDigits, user_message);
assertEquals(undefined, options.maximumSignificantDigits, user_message);
}
if (expectFractional) {
assertNotUndefined(options.minimumFractionDigits, user_message);
assertNotUndefined(options.maximumFractionDigits, user_message);
} else {
assertEquals(undefined, options.minimumFractionDigits, user_message);
assertEquals(undefined, options.maximumFractionDigits, user_message);
}
}
// Should contain ONLY significant digits (both v2 and v3)
let options = new Intl.NumberFormat("und",
{ maximumSignificantDigits: 3 }).resolvedOptions();
assertPresentOfDigits(options, true, false,
"maximumSignificantDigits: 3");
// Should contain ONLY fraction digits (both v2 and v3)
options = new Intl.NumberFormat("und",
{ maximumFractionDigits: 3 }).resolvedOptions();
assertPresentOfDigits(options, false, true,
"maximumFractionDigits: 3");
// in v2, this should NOT have EITHER significant nor fraction digits
// in v3, should contain BOTH significant and fraction digits, plus
// roundingPriority
options = new Intl.NumberFormat("und",
{ notation: "compact" }).resolvedOptions();
assertPresentOfDigits(options, true, true, "notation: 'compact'");
// in v2, should contain ONLY significant digits
// in v3, should contain BOTH significant and fraction digits, plus
// roundingPriority
options = new Intl.NumberFormat("und",
{ maximumSignificantDigits: 3, maximumFractionDigits: 3,
roundingPriority: "morePrecision" }).resolvedOptions();
assertPresentOfDigits(options, true, true, "roundingPriority: 'morePrecision'");
// Should contain ONLY fraction digits (both v2 and v3)
options = new Intl.NumberFormat('en',
{ style: 'currency', currency: 'USD' }).resolvedOptions();
assertPresentOfDigits(options, false, true,
"style: 'currency', currency: 'USD'");
// Should contain ONLY fraction digits (both v2 and v3)
options = new Intl.NumberFormat('en',
{ style: 'currency', currency: 'JPY' }).resolvedOptions();
assertPresentOfDigits(options, false, true,
"style: 'currency', currency: 'JPY'");
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Test v2 condiction
// Flags: --no-harmony-intl-number-format-v3
function assertPresentOfDigits(
options, expectSignificant, expectFractional, user_message) {
if (expectSignificant) {
assertNotUndefined(options.minimumSignificantDigits, user_message);
assertNotUndefined(options.maximumSignificantDigits, user_message);
} else {
assertEquals(undefined, options.minimumSignificantDigits, user_message);
assertEquals(undefined, options.maximumSignificantDigits, user_message);
}
if (expectFractional) {
assertNotUndefined(options.minimumFractionDigits, user_message);
assertNotUndefined(options.maximumFractionDigits, user_message);
} else {
assertEquals(undefined, options.minimumFractionDigits, user_message);
assertEquals(undefined, options.maximumFractionDigits, user_message);
}
}
// Should contain ONLY significant digits (both v2 and v3)
let options = new Intl.NumberFormat("und",
{ maximumSignificantDigits: 3 }).resolvedOptions();
assertPresentOfDigits(options, true, false,
"maximumSignificantDigits: 3");
// Should contain ONLY fraction digits (both v2 and v3)
options = new Intl.NumberFormat("und",
{ maximumFractionDigits: 3 }).resolvedOptions();
assertPresentOfDigits(options, false, true,
"maximumFractionDigits: 3");
// in v2, this should NOT have EITHER significant nor fraction digits
// in v3, should contain BOTH significant and fraction digits, plus
// roundingPriority
options = new Intl.NumberFormat("und",
{ notation: "compact" }).resolvedOptions();
assertPresentOfDigits(options, false, false, "notation: 'compact'");
// in v2, should contain ONLY significant digits
// in v3, should contain BOTH significant and fraction digits, plus
// roundingPriority
options = new Intl.NumberFormat("und",
{ maximumSignificantDigits: 3, maximumFractionDigits: 3,
roundingPriority: "morePrecision" }).resolvedOptions();
assertPresentOfDigits(options, true, false,
"roundingPriority: 'morePrecision'");
// Should contain ONLY fraction digits (both v2 and v3)
options = new Intl.NumberFormat('en',
{ style: 'currency', currency: 'USD' }).resolvedOptions();
assertPresentOfDigits(options, false, true,
"style: 'currency', currency: 'USD'");
// Should contain ONLY fraction digits (both v2 and v3)
options = new Intl.NumberFormat('en',
{ style: 'currency', currency: 'JPY' }).resolvedOptions();
assertPresentOfDigits(options, false, true,
"style: 'currency', currency: 'JPY'");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment