[Intl] Part 1 of NumberFormat v3

Implement ALL in NumberFormat v3 except:
* Add PluralRules.prototype.selectRange
* Add NumberFormat.prototype.formatRange(ToParts)?
(which will be reviewed in later CLs)
* Change NumberFormat.prototpe.resolvedOptions

https://github.com/tc39/proposal-intl-numberformat-v3

https://chromestatus.com/guide/edit/5707621009981440

Design Doc: https://docs.google.com/document/d/19jAogPBb6W4Samt8NWGZKu47iv0_KoQhBvLgQH3xvr8/edit

Bug: v8:10776
Change-Id: I1acf833ec25fb05437cb0b21c5510bb99d1c4583
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3405649
Reviewed-by: Shu-yu Guo <syg@chromium.org>
Commit-Queue: Frank Tang <ftang@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78878}
This commit is contained in:
Frank Tang 2022-01-31 17:26:09 -08:00 committed by V8 LUCI CQ
parent cdb20294b4
commit 250b2e2972
20 changed files with 979 additions and 431 deletions

View File

@ -85,8 +85,15 @@ BUILTIN(NumberFormatPrototypeFormatToParts) {
Handle<Object> x; Handle<Object> x;
if (args.length() >= 2) { if (args.length() >= 2) {
Handle<Object> value = args.at(1);
if (FLAG_harmony_intl_number_format_v3) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, x,
Intl::ToIntlMathematicalValueAsNumberBigIntOrString(isolate, value));
} else {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x, ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x,
Object::ToNumeric(isolate, args.at(1))); Object::ToNumeric(isolate, value));
}
} else { } else {
x = isolate->factory()->nan_value(); x = isolate->factory()->nan_value();
} }
@ -501,8 +508,14 @@ BUILTIN(NumberFormatInternalFormatNumber) {
// 4. Let x be ? ToNumeric(value). // 4. Let x be ? ToNumeric(value).
Handle<Object> numeric_obj; Handle<Object> numeric_obj;
if (FLAG_harmony_intl_number_format_v3) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, numeric_obj,
Intl::ToIntlMathematicalValueAsNumberBigIntOrString(isolate, value));
} else {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, numeric_obj, ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, numeric_obj,
Object::ToNumeric(isolate, value)); Object::ToNumeric(isolate, value));
}
icu::number::LocalizedNumberFormatter* icu_localized_number_formatter = icu::number::LocalizedNumberFormatter* icu_localized_number_formatter =
number_format->icu_number_formatter().raw(); number_format->icu_number_formatter().raw();
@ -902,20 +915,18 @@ BUILTIN(PluralRulesPrototypeResolvedOptions) {
BUILTIN(PluralRulesPrototypeSelect) { BUILTIN(PluralRulesPrototypeSelect) {
HandleScope scope(isolate); HandleScope scope(isolate);
// 1. Let pr be the this value. // 1. 1. Let pr be the this value.
// 2. If Type(pr) is not Object, throw a TypeError exception. // 2. Perform ? RequireInternalSlot(pr, [[InitializedPluralRules]]).
// 3. If pr does not have an [[InitializedPluralRules]] internal slot, throw a
// TypeError exception.
CHECK_RECEIVER(JSPluralRules, plural_rules, CHECK_RECEIVER(JSPluralRules, plural_rules,
"Intl.PluralRules.prototype.select"); "Intl.PluralRules.prototype.select");
// 4. Let n be ? ToNumber(value). // 3. Let n be ? ToNumber(value).
Handle<Object> number = args.atOrUndefined(isolate, 1); Handle<Object> number = args.atOrUndefined(isolate, 1);
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number, ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number,
Object::ToNumber(isolate, number)); Object::ToNumber(isolate, number));
double number_double = number->Number(); double number_double = number->Number();
// 5. Return ? ResolvePlural(pr, n). // 4. Return ! ResolvePlural(pr, n).
RETURN_RESULT_OR_FAILURE(isolate, JSPluralRules::ResolvePlural( RETURN_RESULT_OR_FAILURE(isolate, JSPluralRules::ResolvePlural(
isolate, plural_rules, number_double)); isolate, plural_rules, number_double));
} }

View File

@ -311,7 +311,9 @@ DEFINE_BOOL(harmony_shipping, true, "enable all shipped harmony features")
V(harmony_array_grouping, "harmony array grouping") V(harmony_array_grouping, "harmony array grouping")
#ifdef V8_INTL_SUPPORT #ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) HARMONY_INPROGRESS_BASE(V) #define HARMONY_INPROGRESS(V) \
HARMONY_INPROGRESS_BASE(V) \
V(harmony_intl_number_format_v3, "Intl.NumberFormat v3")
#else #else
#define HARMONY_INPROGRESS(V) HARMONY_INPROGRESS_BASE(V) #define HARMONY_INPROGRESS(V) HARMONY_INPROGRESS_BASE(V)
#endif #endif

View File

@ -4404,6 +4404,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_error_cause)
#ifdef V8_INTL_SUPPORT #ifdef V8_INTL_SUPPORT
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_intl_best_fit_matcher) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_intl_best_fit_matcher)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_intl_number_format_v3)
#endif // V8_INTL_SUPPORT #endif // V8_INTL_SUPPORT
#undef EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE #undef EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE

View File

@ -78,6 +78,7 @@
V(_, minusSign_string, "minusSign") \ V(_, minusSign_string, "minusSign") \
V(_, nan_string, "nan") \ V(_, nan_string, "nan") \
V(_, narrowSymbol_string, "narrowSymbol") \ V(_, narrowSymbol_string, "narrowSymbol") \
V(_, negative_string, "negative") \
V(_, never_string, "never") \ V(_, never_string, "never") \
V(_, none_string, "none") \ V(_, none_string, "none") \
V(_, notation_string, "notation") \ V(_, notation_string, "notation") \

View File

@ -1522,151 +1522,195 @@ Maybe<Intl::NumberFormatDigitOptions> Intl::SetNumberFormatDigitOptions(
return Nothing<NumberFormatDigitOptions>(); return Nothing<NumberFormatDigitOptions>();
} }
int mnfd = 0;
int mxfd = 0;
Handle<Object> mnfd_obj;
Handle<Object> mxfd_obj;
// 6. Let mnfd be ? Get(options, "minimumFractionDigits"). // 6. Let mnfd be ? Get(options, "minimumFractionDigits").
Handle<String> mnfd_str = factory->minimumFractionDigits_string(); Handle<Object> mnfd_obj;
ASSIGN_RETURN_ON_EXCEPTION_VALUE( ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, mnfd_obj, JSReceiver::GetProperty(isolate, options, mnfd_str), isolate, mnfd_obj,
JSReceiver::GetProperty(isolate, options,
factory->minimumFractionDigits_string()),
Nothing<NumberFormatDigitOptions>()); Nothing<NumberFormatDigitOptions>());
// 8. Let mxfd be ? Get(options, "maximumFractionDigits"). // 7. Let mxfd be ? Get(options, "maximumFractionDigits").
Handle<String> mxfd_str = factory->maximumFractionDigits_string(); Handle<Object> mxfd_obj;
ASSIGN_RETURN_ON_EXCEPTION_VALUE( ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, mxfd_obj, JSReceiver::GetProperty(isolate, options, mxfd_str), isolate, mxfd_obj,
JSReceiver::GetProperty(isolate, options,
factory->maximumFractionDigits_string()),
Nothing<NumberFormatDigitOptions>()); Nothing<NumberFormatDigitOptions>());
// 9. Let mnsd be ? Get(options, "minimumSignificantDigits"). // 8. Let mnsd be ? Get(options, "minimumSignificantDigits").
Handle<Object> mnsd_obj; Handle<Object> mnsd_obj;
Handle<String> mnsd_str = factory->minimumSignificantDigits_string();
ASSIGN_RETURN_ON_EXCEPTION_VALUE( ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, mnsd_obj, JSReceiver::GetProperty(isolate, options, mnsd_str), isolate, mnsd_obj,
JSReceiver::GetProperty(isolate, options,
factory->minimumSignificantDigits_string()),
Nothing<NumberFormatDigitOptions>()); Nothing<NumberFormatDigitOptions>());
// 10. Let mxsd be ? Get(options, "maximumSignificantDigits"). // 9. Let mxsd be ? Get(options, "maximumSignificantDigits").
Handle<Object> mxsd_obj; Handle<Object> mxsd_obj;
Handle<String> mxsd_str = factory->maximumSignificantDigits_string();
ASSIGN_RETURN_ON_EXCEPTION_VALUE( ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, mxsd_obj, JSReceiver::GetProperty(isolate, options, mxsd_str), isolate, mxsd_obj,
JSReceiver::GetProperty(isolate, options,
factory->maximumSignificantDigits_string()),
Nothing<NumberFormatDigitOptions>()); Nothing<NumberFormatDigitOptions>());
// 11. Set intlObj.[[MinimumIntegerDigits]] to mnid. digit_options.rounding_priority = RoundingPriority::kAuto;
digit_options.minimum_integer_digits = mnid;
// 12. Set intlObj.[[MinimumFractionDigits]] to mnfd.
digit_options.minimum_fraction_digits = mnfd;
// 13. Set intlObj.[[MaximumFractionDigits]] to mxfd.
digit_options.maximum_fraction_digits = mxfd;
// 14. If mnsd is not undefined or mxsd is not undefined, then
if (!mnsd_obj->IsUndefined(isolate) || !mxsd_obj->IsUndefined(isolate)) {
// 14. a. Let mnsd be ? DefaultNumberOption(mnsd, 1, 21, 1).
int mnsd;
if (!DefaultNumberOption(isolate, mnsd_obj, 1, 21, 1, mnsd_str).To(&mnsd)) {
return Nothing<NumberFormatDigitOptions>();
}
// 14. b. Let mxsd be ? DefaultNumberOption(mxsd, mnsd, 21, 21).
int mxsd;
if (!DefaultNumberOption(isolate, mxsd_obj, mnsd, 21, 21, mxsd_str)
.To(&mxsd)) {
return Nothing<NumberFormatDigitOptions>();
}
// 14. c. Set intlObj.[[MinimumSignificantDigits]] to mnsd.
digit_options.minimum_significant_digits = mnsd;
// 14. d. Set intlObj.[[MaximumSignificantDigits]] to mxsd.
digit_options.maximum_significant_digits = mxsd;
} else {
digit_options.minimum_significant_digits = 0; digit_options.minimum_significant_digits = 0;
digit_options.maximum_significant_digits = 0; digit_options.maximum_significant_digits = 0;
// 15. Else If mnfd is not undefined or mxfd is not undefined, then // 10. Set intlObj.[[MinimumIntegerDigits]] to mnid.
if (!mnfd_obj->IsUndefined(isolate) || !mxfd_obj->IsUndefined(isolate)) { digit_options.minimum_integer_digits = mnid;
int specified_mnfd;
int specified_mxfd;
// a. Let _specifiedMnfd_ be ? DefaultNumberOption(_mnfd_, 0, 20, if (FLAG_harmony_intl_number_format_v3) {
// *undefined*). // 11. Let roundingPriority be ? GetOption(options, "roundingPriority",
// "string", « "auto", "morePrecision", "lessPrecision" », "auto").
Maybe<RoundingPriority> maybe_rounding_priority =
GetStringOption<RoundingPriority>(
isolate, options, "roundingPriority", "SetNumberFormatDigitOptions",
{"auto", "morePrecision", "lessPrecision"},
{RoundingPriority::kAuto, RoundingPriority::kMorePrecision,
RoundingPriority::kLessPrecision},
RoundingPriority::kAuto);
MAYBE_RETURN(maybe_rounding_priority, Nothing<NumberFormatDigitOptions>());
digit_options.rounding_priority = maybe_rounding_priority.FromJust();
}
// 12. If mnsd is not undefined or mxsd is not undefined, then
// a. Set hasSd to true.
// 13. Else,
// a. Set hasSd to false.
bool has_sd =
(!mnsd_obj->IsUndefined(isolate)) || (!mxsd_obj->IsUndefined(isolate));
// 14. If mnfd is not undefined or mxfd is not undefined, then
// a. Set hasFd to true.
// 15. Else,
// a. Set hasFd to false.
bool has_fd =
(!mnfd_obj->IsUndefined(isolate)) || (!mxfd_obj->IsUndefined(isolate));
// 17. If hasSd or roundingPriority is not "auto", set needSd to true; else,
// set needSd to false.
bool need_sd =
has_sd || (RoundingPriority::kAuto != digit_options.rounding_priority);
// 18. If ( not hasSd and (hasFd or notation is not "compact") ) or
// roundingPriority is not "auto", then a. Set needFd to true.
// 19. Else,
// a. Set needFd to false.
bool need_fd = ((!has_sd) && (has_fd || !notation_is_compact)) ||
(RoundingPriority::kAuto != digit_options.rounding_priority);
// 20. If needSd, then
if (need_sd) {
// 20.b If hasSd, then
if (has_sd) {
// 20.b.i Let mnsd be ? DefaultNumberOption(mnsd, 1, 21, 1).
int mnsd;
if (!DefaultNumberOption(isolate, mnsd_obj, 1, 21, 1,
factory->minimumSignificantDigits_string())
.To(&mnsd)) {
return Nothing<NumberFormatDigitOptions>();
}
// 20.b.ii Let mxsd be ? DefaultNumberOption(mxsd, mnsd, 21, 21).
int mxsd;
if (!DefaultNumberOption(isolate, mxsd_obj, mnsd, 21, 21,
factory->maximumSignificantDigits_string())
.To(&mxsd)) {
return Nothing<NumberFormatDigitOptions>();
}
// 20.b.iii Set intlObj.[[MinimumSignificantDigits]] to mnsd.
digit_options.minimum_significant_digits = mnsd;
// 20.b.iv Set intlObj.[[MaximumSignificantDigits]] to mxsd.
digit_options.maximum_significant_digits = mxsd;
} else {
// 20.c Else
// 20.c.i Set intlObj.[[MinimumSignificantDigits]] to 1.
digit_options.minimum_significant_digits = 1;
// 20.c.ii Set intlObj.[[MaximumSignificantDigits]] to 21.
digit_options.maximum_significant_digits = 21;
}
}
// 21. If needFd, then
if (need_fd) {
// 21.a If hasFd, then
if (has_fd) {
Handle<String> mnfd_str = factory->minimumFractionDigits_string();
Handle<String> mxfd_str = factory->maximumFractionDigits_string();
// 21.a.i Let mnfd be ? DefaultNumberOption(mnfd, 0, 20, undefined).
int mnfd;
if (!DefaultNumberOption(isolate, mnfd_obj, 0, 20, -1, mnfd_str) if (!DefaultNumberOption(isolate, mnfd_obj, 0, 20, -1, mnfd_str)
.To(&specified_mnfd)) {
return Nothing<NumberFormatDigitOptions>();
}
Handle<Object> specifiedMnfd_obj;
if (specified_mnfd < 0) {
specifiedMnfd_obj = factory->undefined_value();
} else {
specifiedMnfd_obj = handle(Smi::FromInt(specified_mnfd), isolate);
}
// b. Let _specifiedMxfd_ be ? DefaultNumberOption(_mxfd_, 0, 20,
// *undefined*).
if (!DefaultNumberOption(isolate, mxfd_obj, 0, 20, -1, mxfd_str)
.To(&specified_mxfd)) {
return Nothing<NumberFormatDigitOptions>();
}
Handle<Object> specifiedMxfd_obj;
if (specified_mxfd < 0) {
specifiedMxfd_obj = factory->undefined_value();
} else {
specifiedMxfd_obj = handle(Smi::FromInt(specified_mxfd), isolate);
}
// c. If _specifiedMxfd_ is not *undefined*, set _mnfdDefault_ to
// min(_mnfdDefault_, _specifiedMxfd_).
if (specified_mxfd >= 0) {
mnfd_default = std::min(mnfd_default, specified_mxfd);
}
// d. Set _mnfd_ to ! DefaultNumberOption(_specifiedMnfd_, 0, 20,
// _mnfdDefault_).
if (!DefaultNumberOption(isolate, specifiedMnfd_obj, 0, 20, mnfd_default,
mnfd_str)
.To(&mnfd)) { .To(&mnfd)) {
return Nothing<NumberFormatDigitOptions>(); return Nothing<NumberFormatDigitOptions>();
} }
// 21.a.ii Let mxfd be ? DefaultNumberOption(mxfd, 0, 20, undefined).
// e. Set _mxfd_ to ! DefaultNumberOption(_specifiedMxfd_, 0, 20, int mxfd;
// max(_mxfdDefault_, _mnfd_)). if (!DefaultNumberOption(isolate, mxfd_obj, 0, 20, -1, mxfd_str)
if (!DefaultNumberOption(isolate, specifiedMxfd_obj, 0, 20,
std::max(mxfd_default, mnfd), mxfd_str)
.To(&mxfd)) { .To(&mxfd)) {
return Nothing<NumberFormatDigitOptions>(); return Nothing<NumberFormatDigitOptions>();
} }
// 21.a.iii If mnfd is undefined, set mnfd to min(mnfdDefault, mxfd).
// f. If _mnfd_ is greater than _mxfd_, throw a *RangeError* exception. if (mnfd_obj->IsUndefined(isolate)) {
if (mnfd > mxfd) { mnfd = std::min(mnfd_default, mxfd);
} else if (mxfd_obj->IsUndefined(isolate)) {
// 21.a.iv Else if mxfd is undefined, set mxfd to max(mxfdDefault,
// mnfd).
mxfd = std::max(mxfd_default, mnfd);
} else if (mnfd > mxfd) {
// 21.a.v Else if mnfd is greater than mxfd, throw a RangeError
// exception.
THROW_NEW_ERROR_RETURN_VALUE( THROW_NEW_ERROR_RETURN_VALUE(
isolate, isolate,
NewRangeError(MessageTemplate::kPropertyValueOutOfRange, mxfd_str), NewRangeError(MessageTemplate::kPropertyValueOutOfRange, mxfd_str),
Nothing<NumberFormatDigitOptions>()); Nothing<NumberFormatDigitOptions>());
} }
// 21.a.vi Set intlObj.[[MinimumFractionDigits]] to mnfd.
// g. Set intlObj.[[MinimumFractionDigits]] to mnfd.
digit_options.minimum_fraction_digits = mnfd; digit_options.minimum_fraction_digits = mnfd;
// 21.a.vii Set intlObj.[[MaximumFractionDigits]] to mxfd.
// h. Set intlObj.[[MaximumFractionDigits]] to mxfd.
digit_options.maximum_fraction_digits = mxfd; digit_options.maximum_fraction_digits = mxfd;
// Else If intlObj.[[Notation]] is "compact", then } else { // 17.b Else
} else if (notation_is_compact) { // 21.b.i Set intlObj.[[MinimumFractionDigits]] to mnfdDefault.
// a. Set intlObj.[[RoundingType]] to "compact-rounding".
// Set minimum_significant_digits to -1 to represent roundingtype is
// "compact-rounding".
digit_options.minimum_significant_digits = -1;
// 17. Else,
} else {
// 17. b. Set intlObj.[[MinimumFractionDigits]] to mnfdDefault.
digit_options.minimum_fraction_digits = mnfd_default; digit_options.minimum_fraction_digits = mnfd_default;
// 21.b.ii Set intlObj.[[MaximumFractionDigits]] to mxfdDefault.
// 17. c. Set intlObj.[[MaximumFractionDigits]] to mxfdDefault.
digit_options.maximum_fraction_digits = mxfd_default; digit_options.maximum_fraction_digits = mxfd_default;
} }
} }
// 22. If needSd or needFd, then
if (need_sd || need_fd) {
// a. If roundingPriority is "morePrecision", then
if (digit_options.rounding_priority == RoundingPriority::kMorePrecision) {
// i. Set intlObj.[[RoundingType]] to morePrecision.
digit_options.rounding_type = RoundingType::kMorePrecision;
// b. Else if roundingPriority is "lessPrecision", then
} else if (digit_options.rounding_priority ==
RoundingPriority::kLessPrecision) {
// i. Set intlObj.[[RoundingType]] to lessPrecision.
digit_options.rounding_type = RoundingType::kLessPrecision;
// c. Else if hasSd, then
} else if (has_sd) {
// i. Set intlObj.[[RoundingType]] to significantDigits.
digit_options.rounding_type = RoundingType::kSignificantDigits;
// d. Else,
} else {
// i.Set intlObj.[[RoundingType]] to fractionDigits.
digit_options.rounding_type = RoundingType::kFractionDigits;
}
// 23. Else
} else {
// a. Set intlObj.[[RoundingType]] to morePrecision.
digit_options.rounding_type = RoundingType::kMorePrecision;
// b. Set intlObj.[[MinimumFractionDigits]] to 0.
digit_options.minimum_fraction_digits = 0;
// c. Set intlObj.[[MaximumFractionDigits]] to 0.
digit_options.maximum_fraction_digits = 0;
// d. Set intlObj.[[MinimumSignificantDigits]] to 1.
digit_options.minimum_significant_digits = 1;
// e. Set intlObj.[[MaximumSignificantDigits]] to 2.
digit_options.maximum_significant_digits = 2;
}
return Just(digit_options); return Just(digit_options);
} }
@ -2644,22 +2688,22 @@ const std::set<std::string>& Intl::GetAvailableLocalesForDateFormat() {
return available_locales.Pointer()->Get(); return available_locales.Pointer()->Get();
} }
constexpr uint16_t kInfinityChar = 0x221e;
Handle<String> Intl::NumberFieldToType(Isolate* isolate, Handle<String> Intl::NumberFieldToType(Isolate* isolate,
Handle<Object> numeric_obj, const NumberFormatSpan& part,
int32_t field_id) { const icu::UnicodeString& text,
DCHECK(numeric_obj->IsNumeric()); bool is_nan) {
switch (static_cast<UNumberFormatFields>(field_id)) { switch (static_cast<UNumberFormatFields>(part.field_id)) {
case UNUM_INTEGER_FIELD: case UNUM_INTEGER_FIELD:
if (numeric_obj->IsBigInt()) { if (is_nan) return isolate->factory()->nan_string();
// Neither NaN nor Infinite could be stored into BigInt if (text.charAt(part.begin_pos) == kInfinityChar ||
// so just return integer. // en-US-POSIX output "INF" for Infinity
return isolate->factory()->integer_string(); (part.end_pos - part.begin_pos == 3 &&
} else { text.tempSubString(part.begin_pos, 3) == "INF")) {
double number = numeric_obj->Number();
if (std::isfinite(number)) return isolate->factory()->integer_string();
if (std::isnan(number)) return isolate->factory()->nan_string();
return isolate->factory()->infinity_string(); return isolate->factory()->infinity_string();
} }
return isolate->factory()->integer_string();
case UNUM_FRACTION_FIELD: case UNUM_FRACTION_FIELD:
return isolate->factory()->fraction_string(); return isolate->factory()->fraction_string();
case UNUM_DECIMAL_SEPARATOR_FIELD: case UNUM_DECIMAL_SEPARATOR_FIELD:
@ -2671,15 +2715,9 @@ Handle<String> Intl::NumberFieldToType(Isolate* isolate,
case UNUM_PERCENT_FIELD: case UNUM_PERCENT_FIELD:
return isolate->factory()->percentSign_string(); return isolate->factory()->percentSign_string();
case UNUM_SIGN_FIELD: case UNUM_SIGN_FIELD:
if (numeric_obj->IsBigInt()) { return (text.charAt(part.begin_pos) == '+')
Handle<BigInt> big_int = Handle<BigInt>::cast(numeric_obj); ? isolate->factory()->plusSign_string()
return big_int->IsNegative() ? isolate->factory()->minusSign_string() : isolate->factory()->minusSign_string();
: isolate->factory()->plusSign_string();
} else {
double number = numeric_obj->Number();
return std::signbit(number) ? isolate->factory()->minusSign_string()
: isolate->factory()->plusSign_string();
}
case UNUM_EXPONENT_SYMBOL_FIELD: case UNUM_EXPONENT_SYMBOL_FIELD:
return isolate->factory()->exponentSeparator_string(); return isolate->factory()->exponentSeparator_string();
@ -2856,5 +2894,29 @@ Maybe<bool> Intl::GetTimeZoneIndex(Isolate* isolate, Handle<String> identifier,
UNREACHABLE(); UNREACHABLE();
} }
// #sec-tointlmathematicalvalue
MaybeHandle<Object> Intl::ToIntlMathematicalValueAsNumberBigIntOrString(
Isolate* isolate, Handle<Object> input) {
// Strings are used to preserve arbitrary precision decimals, and are passed
// through to ICU.
if (input->IsNumber() || input->IsBigInt() || input->IsString())
return input; // Shortcut.
// TODO(ftang) revisit the following later.
if (input->IsOddball()) {
return Oddball::ToNumber(isolate, Handle<Oddball>::cast(input));
}
if (input->IsSymbol()) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToNumber),
Object);
}
ASSIGN_RETURN_ON_EXCEPTION(
isolate, input,
JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(input),
ToPrimitiveHint::kNumber),
Object);
return input;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8

View File

@ -35,6 +35,19 @@ class UnicodeString;
namespace v8 { namespace v8 {
namespace internal { namespace internal {
struct NumberFormatSpan {
int32_t field_id;
int32_t begin_pos;
int32_t end_pos;
NumberFormatSpan() = default;
NumberFormatSpan(int32_t field_id, int32_t begin_pos, int32_t end_pos)
: field_id(field_id), begin_pos(begin_pos), end_pos(end_pos) {}
};
V8_EXPORT_PRIVATE std::vector<NumberFormatSpan> FlattenRegionsToParts(
std::vector<NumberFormatSpan>* regions);
template <typename T> template <typename T>
class Handle; class Handle;
class JSCollator; class JSCollator;
@ -115,6 +128,21 @@ class Intl {
Isolate* isolate, Handle<Object> num, Handle<Object> locales, Isolate* isolate, Handle<Object> num, Handle<Object> locales,
Handle<Object> options, const char* method_name); Handle<Object> options, const char* method_name);
// [[RoundingPriority]] is one of the String values "auto", "morePrecision",
// or "lessPrecision", specifying the rounding priority for the number.
enum class RoundingPriority {
kAuto,
kMorePrecision,
kLessPrecision,
};
enum class RoundingType {
kFractionDigits,
kSignificantDigits,
kMorePrecision,
kLessPrecision,
};
// ecma402/#sec-setnfdigitoptions // ecma402/#sec-setnfdigitoptions
struct NumberFormatDigitOptions { struct NumberFormatDigitOptions {
int minimum_integer_digits; int minimum_integer_digits;
@ -122,6 +150,8 @@ class Intl {
int maximum_fraction_digits; int maximum_fraction_digits;
int minimum_significant_digits; int minimum_significant_digits;
int maximum_significant_digits; int maximum_significant_digits;
RoundingPriority rounding_priority;
RoundingType rounding_type;
}; };
V8_WARN_UNUSED_RESULT static Maybe<NumberFormatDigitOptions> V8_WARN_UNUSED_RESULT static Maybe<NumberFormatDigitOptions>
SetNumberFormatDigitOptions(Isolate* isolate, Handle<JSReceiver> options, SetNumberFormatDigitOptions(Isolate* isolate, Handle<JSReceiver> options,
@ -143,8 +173,9 @@ class Intl {
// Helper function to convert number field id to type string. // Helper function to convert number field id to type string.
static Handle<String> NumberFieldToType(Isolate* isolate, static Handle<String> NumberFieldToType(Isolate* isolate,
Handle<Object> numeric_obj, const NumberFormatSpan& part,
int32_t field_id); const icu::UnicodeString& text,
bool is_nan);
// A helper function to implement formatToParts which add element to array as // A helper function to implement formatToParts which add element to array as
// $array[$index] = { type: $field_type_string, value: $value } // $array[$index] = { type: $field_type_string, value: $value }
@ -312,6 +343,16 @@ class Intl {
V8_WARN_UNUSED_RESULT static MaybeHandle<String> CanonicalizeTimeZoneName( V8_WARN_UNUSED_RESULT static MaybeHandle<String> CanonicalizeTimeZoneName(
Isolate* isolate, Handle<String> identifier); Isolate* isolate, Handle<String> identifier);
// ecma402/#sec-coerceoptionstoobject
V8_WARN_UNUSED_RESULT static MaybeHandle<JSReceiver> CoerceOptionsToObject(
Isolate* isolate, Handle<Object> options, const char* service);
// #sec-tointlmathematicalvalue
// The implementation preserve the Object in String, BigInt or Number
V8_WARN_UNUSED_RESULT static MaybeHandle<Object>
ToIntlMathematicalValueAsNumberBigIntOrString(Isolate* isolate,
Handle<Object> input);
}; };
} // namespace internal } // namespace internal

View File

@ -18,10 +18,8 @@
#include "src/objects/objects-inl.h" #include "src/objects/objects-inl.h"
#include "src/objects/option-utils.h" #include "src/objects/option-utils.h"
#include "unicode/currunit.h" #include "unicode/currunit.h"
#include "unicode/decimfmt.h"
#include "unicode/locid.h" #include "unicode/locid.h"
#include "unicode/numberformatter.h" #include "unicode/numberformatter.h"
#include "unicode/numfmt.h"
#include "unicode/numsys.h" #include "unicode/numsys.h"
#include "unicode/ucurr.h" #include "unicode/ucurr.h"
#include "unicode/uloc.h" #include "unicode/uloc.h"
@ -98,6 +96,38 @@ enum class SignDisplay {
ALWAYS, ALWAYS,
NEVER, NEVER,
EXCEPT_ZERO, EXCEPT_ZERO,
NEGATIVE,
};
// [[RoundingMode]] is one of the String values "ceil", "floor", "expand",
// "trunc", "halfCeil", "halfFloor", "halfExpand", "halfTrunc", or "halfEven",
// specifying the rounding strategy for the number.
enum class RoundingMode {
CEIL,
FLOOR,
EXPAND,
TRUNC,
HALF_CEIL,
HALF_FLOOR,
HALF_EXPAND,
HALF_TRUNC,
HALF_EVEN,
};
// [[TrailingZeroDisplay]] is one of the String values "auto" or
// "stripIfInteger", specifying the strategy for displaying trailing zeros on
// whole number.
enum class TrailingZeroDisplay {
AUTO,
STRIP_IF_INTEGER,
};
// [[UseGrouping]] is ....
enum class UseGrouping {
OFF,
MIN2,
AUTO,
ALWAYS,
}; };
UNumberUnitWidth ToUNumberUnitWidth(CurrencyDisplay currency_display) { UNumberUnitWidth ToUNumberUnitWidth(CurrencyDisplay currency_display) {
@ -147,6 +177,12 @@ UNumberSignDisplay ToUNumberSignDisplay(SignDisplay sign_display,
} }
DCHECK(currency_sign == CurrencySign::STANDARD); DCHECK(currency_sign == CurrencySign::STANDARD);
return UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO; return UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO;
case SignDisplay::NEGATIVE:
if (currency_sign == CurrencySign::ACCOUNTING) {
return UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_NEGATIVE;
}
DCHECK(currency_sign == CurrencySign::STANDARD);
return UNumberSignDisplay::UNUM_SIGN_NEGATIVE;
} }
} }
@ -170,6 +206,43 @@ icu::number::Notation ToICUNotation(Notation notation,
} }
} }
UNumberFormatRoundingMode ToUNumberFormatRoundingMode(
RoundingMode rounding_mode) {
switch (rounding_mode) {
case RoundingMode::CEIL:
return UNumberFormatRoundingMode::UNUM_ROUND_CEILING;
case RoundingMode::FLOOR:
return UNumberFormatRoundingMode::UNUM_ROUND_FLOOR;
case RoundingMode::EXPAND:
return UNumberFormatRoundingMode::UNUM_ROUND_UP;
case RoundingMode::TRUNC:
return UNumberFormatRoundingMode::UNUM_ROUND_DOWN;
case RoundingMode::HALF_CEIL:
return UNumberFormatRoundingMode::UNUM_ROUND_HALF_CEILING;
case RoundingMode::HALF_FLOOR:
return UNumberFormatRoundingMode::UNUM_ROUND_HALF_FLOOR;
case RoundingMode::HALF_EXPAND:
return UNumberFormatRoundingMode::UNUM_ROUND_HALFUP;
case RoundingMode::HALF_TRUNC:
return UNumberFormatRoundingMode::UNUM_ROUND_HALFDOWN;
case RoundingMode::HALF_EVEN:
return UNumberFormatRoundingMode::UNUM_ROUND_HALFEVEN;
}
}
UNumberGroupingStrategy ToUNumberGroupingStrategy(UseGrouping use_grouping) {
switch (use_grouping) {
case UseGrouping::OFF:
return UNumberGroupingStrategy::UNUM_GROUPING_OFF;
case UseGrouping::MIN2:
return UNumberGroupingStrategy::UNUM_GROUPING_MIN2;
case UseGrouping::AUTO:
return UNumberGroupingStrategy::UNUM_GROUPING_AUTO;
case UseGrouping::ALWAYS:
return UNumberGroupingStrategy::UNUM_GROUPING_ON_ALIGNED;
}
}
std::map<const std::string, icu::MeasureUnit> CreateUnitMap() { std::map<const std::string, icu::MeasureUnit> CreateUnitMap() {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
int32_t total = icu::MeasureUnit::getAvailable(nullptr, 0, status); int32_t total = icu::MeasureUnit::getAvailable(nullptr, 0, status);
@ -462,6 +535,13 @@ Handle<String> SignDisplayString(Isolate* isolate,
skeleton.indexOf("sign-except-zero") >= 0) { skeleton.indexOf("sign-except-zero") >= 0) {
return ReadOnlyRoots(isolate).exceptZero_string_handle(); return ReadOnlyRoots(isolate).exceptZero_string_handle();
} }
// Ex: skeleton as
// ".### rounding-mode-half-up sign-negative" or
// "currency/TWD .00 rounding-mode-half-up sign-accounting-negative"
if (skeleton.indexOf("sign-accounting-negative") >= 0 ||
skeleton.indexOf("sign-negative") >= 0) {
return ReadOnlyRoots(isolate).negative_string_handle();
}
return ReadOnlyRoots(isolate).auto_string_handle(); return ReadOnlyRoots(isolate).auto_string_handle();
} }
@ -505,7 +585,7 @@ bool JSNumberFormat::FractionDigitsFromSkeleton(
if (index < 0) return false; if (index < 0) return false;
*minimum = 0; *minimum = 0;
index++; // skip the '.' index++; // skip the '.'
while (index < skeleton.length() && skeleton[index] == '0') { while (index < skeleton.length() && IsDecimalDigit(skeleton[index])) {
(*minimum)++; (*minimum)++;
index++; index++;
} }
@ -607,21 +687,16 @@ Style StyleFromSkeleton(const icu::UnicodeString& skeleton) {
return Style::DECIMAL; return Style::DECIMAL;
} }
} // anonymous namespace icu::number::UnlocalizedNumberFormatter SetDigitOptionsToFormatterV2(
const icu::number::UnlocalizedNumberFormatter& settings,
icu::number::LocalizedNumberFormatter
JSNumberFormat::SetDigitOptionsToFormatter(
const icu::number::LocalizedNumberFormatter& icu_number_formatter,
const Intl::NumberFormatDigitOptions& digit_options) { const Intl::NumberFormatDigitOptions& digit_options) {
icu::number::LocalizedNumberFormatter result = icu_number_formatter; icu::number::UnlocalizedNumberFormatter result = settings;
if (digit_options.minimum_integer_digits > 1) { if (digit_options.minimum_integer_digits > 1) {
result = result.integerWidth(icu::number::IntegerWidth::zeroFillTo( result = result.integerWidth(icu::number::IntegerWidth::zeroFillTo(
digit_options.minimum_integer_digits)); digit_options.minimum_integer_digits));
} }
// Value -1 of minimum_significant_digits represent the roundingtype is if (digit_options.rounding_type == Intl::RoundingType::kMorePrecision) {
// "compact-rounding".
if (digit_options.minimum_significant_digits < 0) {
return result; return result;
} }
icu::number::Precision precision = icu::number::Precision precision =
@ -636,6 +711,70 @@ JSNumberFormat::SetDigitOptionsToFormatter(
return result.precision(precision); return result.precision(precision);
} }
icu::number::UnlocalizedNumberFormatter SetDigitOptionsToFormatterV3(
const icu::number::UnlocalizedNumberFormatter& settings,
const Intl::NumberFormatDigitOptions& digit_options, int rounding_increment,
JSNumberFormat::ShowTrailingZeros trailing_zeros) {
icu::number::UnlocalizedNumberFormatter result = settings;
if (digit_options.minimum_integer_digits > 1) {
result = result.integerWidth(icu::number::IntegerWidth::zeroFillTo(
digit_options.minimum_integer_digits));
}
icu::number::Precision precision = icu::number::Precision::unlimited();
bool relaxed = false;
switch (digit_options.rounding_type) {
case Intl::RoundingType::kSignificantDigits:
precision = icu::number::Precision::minMaxSignificantDigits(
digit_options.minimum_significant_digits,
digit_options.maximum_significant_digits);
break;
case Intl::RoundingType::kFractionDigits:
precision = icu::number::Precision::minMaxFraction(
digit_options.minimum_fraction_digits,
digit_options.maximum_fraction_digits);
break;
case Intl::RoundingType::kMorePrecision:
relaxed = true;
V8_FALLTHROUGH;
case Intl::RoundingType::kLessPrecision:
precision =
icu::number::Precision::minMaxFraction(
digit_options.minimum_fraction_digits,
digit_options.maximum_fraction_digits)
.withSignificantDigits(digit_options.minimum_significant_digits,
digit_options.maximum_significant_digits,
relaxed ? UNUM_ROUNDING_PRIORITY_RELAXED
: UNUM_ROUNDING_PRIORITY_STRICT);
break;
}
if (rounding_increment != 1) {
double icu_increment = rounding_increment *
std::pow(10, -digit_options.maximum_fraction_digits);
precision = ::icu::number::Precision::increment(icu_increment)
.withMinFraction(digit_options.minimum_fraction_digits);
}
if (trailing_zeros == JSNumberFormat::ShowTrailingZeros::kHide) {
precision = precision.trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE);
}
return result.precision(precision);
}
} // anonymous namespace
icu::number::UnlocalizedNumberFormatter
JSNumberFormat::SetDigitOptionsToFormatter(
const icu::number::UnlocalizedNumberFormatter& settings,
const Intl::NumberFormatDigitOptions& digit_options, int rounding_increment,
JSNumberFormat::ShowTrailingZeros trailing_zeros) {
if (FLAG_harmony_intl_number_format_v3) {
return SetDigitOptionsToFormatterV3(settings, digit_options,
rounding_increment, trailing_zeros);
} else {
return SetDigitOptionsToFormatterV2(settings, digit_options);
}
}
// static // static
// ecma402 #sec-intl.numberformat.prototype.resolvedoptions // ecma402 #sec-intl.numberformat.prototype.resolvedoptions
Handle<JSObject> JSNumberFormat::ResolvedOptions( Handle<JSObject> JSNumberFormat::ResolvedOptions(
@ -662,12 +801,19 @@ Handle<JSObject> JSNumberFormat::ResolvedOptions(
// [[Style]] "style" // [[Style]] "style"
// [[Currency]] "currency" // [[Currency]] "currency"
// [[CurrencyDisplay]] "currencyDisplay" // [[CurrencyDisplay]] "currencyDisplay"
// [[CurrencySign]] "currencySign"
// [[Unit]] "unit"
// [[UnitDisplay]] "unitDisplay"
// [[MinimumIntegerDigits]] "minimumIntegerDigits" // [[MinimumIntegerDigits]] "minimumIntegerDigits"
// [[MinimumFractionDigits]] "minimumFractionDigits" // [[MinimumFractionDigits]] "minimumFractionDigits"
// [[MaximumFractionDigits]] "maximumFractionDigits" // [[MaximumFractionDigits]] "maximumFractionDigits"
// [[MinimumSignificantDigits]] "minimumSignificantDigits" // [[MinimumSignificantDigits]] "minimumSignificantDigits"
// [[MaximumSignificantDigits]] "maximumSignificantDigits" // [[MaximumSignificantDigits]] "maximumSignificantDigits"
// [[UseGrouping]] "useGrouping" // [[UseGrouping]] "useGrouping"
// [[Notation]] "notation"
// [[CompactDisplay]] "compactDisplay"
// [[SignDisplay]] "signDisplay"
CHECK(JSReceiver::CreateDataProperty(isolate, options, CHECK(JSReceiver::CreateDataProperty(isolate, options,
factory->locale_string(), locale, factory->locale_string(), locale,
Just(kDontThrow)) Just(kDontThrow))
@ -752,6 +898,7 @@ Handle<JSObject> JSNumberFormat::ResolvedOptions(
factory->ToBoolean(UseGroupingFromSkeleton(skeleton)), factory->ToBoolean(UseGroupingFromSkeleton(skeleton)),
Just(kDontThrow)) Just(kDontThrow))
.FromJust()); .FromJust());
Notation notation = NotationFromSkeleton(skeleton); Notation notation = NotationFromSkeleton(skeleton);
CHECK(JSReceiver::CreateDataProperty( CHECK(JSReceiver::CreateDataProperty(
isolate, options, factory->notation_string(), isolate, options, factory->notation_string(),
@ -800,6 +947,14 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::UnwrapNumberFormat(
return Handle<JSNumberFormat>::cast(object); return Handle<JSNumberFormat>::cast(object);
} }
// 22. is in « 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500,
// 5000 »
bool IsValidRoundingIncrement(int value) {
return value == 1 || value == 2 || value == 5 || value == 10 || value == 20 ||
value == 25 || value == 50 || value == 100 || value == 200 ||
value == 250 || value == 500 || value == 1000 || value == 2000 ||
value == 2500 || value == 5000;
}
// static // static
MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate, MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
Handle<Map> map, Handle<Map> map,
@ -821,28 +976,28 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
isolate, options, CoerceOptionsToObject(isolate, options_obj, service), isolate, options, CoerceOptionsToObject(isolate, options_obj, service),
JSNumberFormat); JSNumberFormat);
// 4. Let opt be a new Record. // 3. Let opt be a new Record.
// 5. Let matcher be ? GetOption(options, "localeMatcher", "string", « // 4. Let matcher be ? GetOption(options, "localeMatcher", "string", «
// "lookup", "best fit" », "best fit"). // "lookup", "best fit" », "best fit").
// 6. Set opt.[[localeMatcher]] to matcher. // 5. Set opt.[[localeMatcher]] to matcher.
Maybe<Intl::MatcherOption> maybe_locale_matcher = Maybe<Intl::MatcherOption> maybe_locale_matcher =
Intl::GetLocaleMatcher(isolate, options, service); Intl::GetLocaleMatcher(isolate, options, service);
MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSNumberFormat>()); MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSNumberFormat>());
Intl::MatcherOption matcher = maybe_locale_matcher.FromJust(); Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
std::unique_ptr<char[]> numbering_system_str = nullptr; std::unique_ptr<char[]> numbering_system_str = nullptr;
// 7. Let _numberingSystem_ be ? GetOption(_options_, `"numberingSystem"`, // 6. Let _numberingSystem_ be ? GetOption(_options_, `"numberingSystem"`,
// `"string"`, *undefined*, *undefined*). // `"string"`, *undefined*, *undefined*).
Maybe<bool> maybe_numberingSystem = Intl::GetNumberingSystem( Maybe<bool> maybe_numberingSystem = Intl::GetNumberingSystem(
isolate, options, service, &numbering_system_str); isolate, options, service, &numbering_system_str);
// 8. If _numberingSystem_ is not *undefined*, then // 7. If _numberingSystem_ is not *undefined*, then
// a. If _numberingSystem_ does not match the // 8. If _numberingSystem_ does not match the
// `(3*8alphanum) *("-" (3*8alphanum))` sequence, throw a *RangeError* // `(3*8alphanum) *("-" (3*8alphanum))` sequence, throw a *RangeError*
// exception. // exception.
MAYBE_RETURN(maybe_numberingSystem, MaybeHandle<JSNumberFormat>()); MAYBE_RETURN(maybe_numberingSystem, MaybeHandle<JSNumberFormat>());
// 7. Let localeData be %NumberFormat%.[[LocaleData]]. // 9. Let localeData be %NumberFormat%.[[LocaleData]].
// 8. Let r be ResolveLocale(%NumberFormat%.[[AvailableLocales]], // 10. Let r be ResolveLocale(%NumberFormat%.[[AvailableLocales]],
// requestedLocales, opt, %NumberFormat%.[[RelevantExtensionKeys]], // requestedLocales, opt, %NumberFormat%.[[RelevantExtensionKeys]],
// localeData). // localeData).
std::set<std::string> relevant_extension_keys{"nu"}; std::set<std::string> relevant_extension_keys{"nu"};
@ -882,21 +1037,20 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
// 11. Let dataLocale be r.[[dataLocale]]. // 11. Let dataLocale be r.[[dataLocale]].
icu::number::LocalizedNumberFormatter icu_number_formatter = icu::number::UnlocalizedNumberFormatter settings =
icu::number::NumberFormatter::withLocale(icu_locale) icu::number::UnlocalizedNumberFormatter().roundingMode(UNUM_ROUND_HALFUP);
.roundingMode(UNUM_ROUND_HALFUP);
// For 'latn' numbering system, skip the adoptSymbols which would cause // For 'latn' numbering system, skip the adoptSymbols which would cause
// 10.1%-13.7% of regression of JSTests/Intl-NewIntlNumberFormat // 10.1%-13.7% of regression of JSTests/Intl-NewIntlNumberFormat
// See crbug/1052751 so we skip calling adoptSymbols and depending on the // See crbug/1052751 so we skip calling adoptSymbols and depending on the
// default instead. // default instead.
if (!numbering_system.empty() && numbering_system != "latn") { if (!numbering_system.empty() && numbering_system != "latn") {
icu_number_formatter = icu_number_formatter.adoptSymbols( settings = settings.adoptSymbols(icu::NumberingSystem::createInstanceByName(
icu::NumberingSystem::createInstanceByName(numbering_system.c_str(), numbering_system.c_str(), status));
status));
CHECK(U_SUCCESS(status)); CHECK(U_SUCCESS(status));
} }
// ==== Start SetNumberFormatUnitOptions ====
// 3. Let style be ? GetOption(options, "style", "string", « "decimal", // 3. Let style be ? GetOption(options, "style", "string", « "decimal",
// "percent", "currency", "unit" », "decimal"). // "percent", "currency", "unit" », "decimal").
@ -1029,15 +1183,14 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
Intl::ToString(isolate, currency_ustr), Intl::ToString(isolate, currency_ustr),
JSNumberFormat); JSNumberFormat);
icu_number_formatter = icu_number_formatter.unit( settings =
icu::CurrencyUnit(currency_ustr.getBuffer(), status)); settings.unit(icu::CurrencyUnit(currency_ustr.getBuffer(), status));
CHECK(U_SUCCESS(status)); CHECK(U_SUCCESS(status));
// 14.c Set intlObj.[[CurrencyDisplay]] to currencyDisplay. // 14.c Set intlObj.[[CurrencyDisplay]] to currencyDisplay.
// The default unitWidth is SHORT in ICU and that mapped from // The default unitWidth is SHORT in ICU and that mapped from
// Symbol so we can skip the setting for optimization. // Symbol so we can skip the setting for optimization.
if (currency_display != CurrencyDisplay::SYMBOL) { if (currency_display != CurrencyDisplay::SYMBOL) {
icu_number_formatter = icu_number_formatter.unitWidth( settings = settings.unitWidth(ToUNumberUnitWidth(currency_display));
ToUNumberUnitWidth(currency_display));
} }
CHECK(U_SUCCESS(status)); CHECK(U_SUCCESS(status));
} }
@ -1051,27 +1204,27 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
icu::MeasureUnit none = icu::MeasureUnit(); icu::MeasureUnit none = icu::MeasureUnit();
// 13.b Set intlObj.[[Unit]] to unit. // 13.b Set intlObj.[[Unit]] to unit.
if (unit_pair.first != none) { if (unit_pair.first != none) {
icu_number_formatter = icu_number_formatter.unit(unit_pair.first); settings = settings.unit(unit_pair.first);
} }
if (unit_pair.second != none) { if (unit_pair.second != none) {
icu_number_formatter = icu_number_formatter.perUnit(unit_pair.second); settings = settings.perUnit(unit_pair.second);
} }
// The default unitWidth is SHORT in ICU and that mapped from // The default unitWidth is SHORT in ICU and that mapped from
// Symbol so we can skip the setting for optimization. // Symbol so we can skip the setting for optimization.
if (unit_display != UnitDisplay::SHORT) { if (unit_display != UnitDisplay::SHORT) {
icu_number_formatter = settings = settings.unitWidth(ToUNumberUnitWidth(unit_display));
icu_number_formatter.unitWidth(ToUNumberUnitWidth(unit_display));
} }
} }
// === End of SetNumberFormatUnitOptions
if (style == Style::PERCENT) { if (style == Style::PERCENT) {
icu_number_formatter = settings = settings.unit(icu::MeasureUnit::getPercent())
icu_number_formatter.unit(icu::MeasureUnit::getPercent())
.scale(icu::number::Scale::powerOfTen(2)); .scale(icu::number::Scale::powerOfTen(2));
} }
// 23. If style is "currency", then // 16. If style is "currency", then
int mnfd_default, mxfd_default; int mnfd_default, mxfd_default;
if (style == Style::CURRENCY) { if (style == Style::CURRENCY) {
// b. Let cDigits be CurrencyDigits(currency). // b. Let cDigits be CurrencyDigits(currency).
@ -1080,7 +1233,7 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
// d. Let mxfdDefault be cDigits. // d. Let mxfdDefault be cDigits.
mnfd_default = c_digits; mnfd_default = c_digits;
mxfd_default = c_digits; mxfd_default = c_digits;
// 24. Else, // 17. Else,
} else { } else {
// a. Let mnfdDefault be 0. // a. Let mnfdDefault be 0.
mnfd_default = 0; mnfd_default = 0;
@ -1096,7 +1249,7 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
} }
Notation notation = Notation::STANDARD; Notation notation = Notation::STANDARD;
// 25. Let notation be ? GetOption(options, "notation", "string", « // 18. Let notation be ? GetOption(options, "notation", "string", «
// "standard", "scientific", "engineering", "compact" », "standard"). // "standard", "scientific", "engineering", "compact" », "standard").
Maybe<Notation> maybe_notation = GetStringOption<Notation>( Maybe<Notation> maybe_notation = GetStringOption<Notation>(
isolate, options, "notation", service, isolate, options, "notation", service,
@ -1105,9 +1258,10 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
Notation::COMPACT}, Notation::COMPACT},
Notation::STANDARD); Notation::STANDARD);
MAYBE_RETURN(maybe_notation, MaybeHandle<JSNumberFormat>()); MAYBE_RETURN(maybe_notation, MaybeHandle<JSNumberFormat>());
// 19. Set numberFormat.[[Notation]] to notation.
notation = maybe_notation.FromJust(); notation = maybe_notation.FromJust();
// 27. Perform ? SetNumberFormatDigitOptions(numberFormat, options, // 20. Perform ? SetNumberFormatDigitOptions(numberFormat, options,
// mnfdDefault, mxfdDefault). // mnfdDefault, mxfdDefault).
Maybe<Intl::NumberFormatDigitOptions> maybe_digit_options = Maybe<Intl::NumberFormatDigitOptions> maybe_digit_options =
Intl::SetNumberFormatDigitOptions(isolate, options, mnfd_default, Intl::SetNumberFormatDigitOptions(isolate, options, mnfd_default,
@ -1115,10 +1269,58 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
notation == Notation::COMPACT); notation == Notation::COMPACT);
MAYBE_RETURN(maybe_digit_options, Handle<JSNumberFormat>()); MAYBE_RETURN(maybe_digit_options, Handle<JSNumberFormat>());
Intl::NumberFormatDigitOptions digit_options = maybe_digit_options.FromJust(); Intl::NumberFormatDigitOptions digit_options = maybe_digit_options.FromJust();
icu_number_formatter = JSNumberFormat::SetDigitOptionsToFormatter(
icu_number_formatter, digit_options);
// 28. Let compactDisplay be ? GetOption(options, "compactDisplay", if (FLAG_harmony_intl_number_format_v3) {
// 21. Let roundingIncrement be ? GetNumberOption(options,
// "roundingIncrement,", 1, 5000, 1).
int rounding_increment = 1;
Maybe<int> maybe_rounding_increment = GetNumberOption(
isolate, options, factory->roundingIncrement_string(), 1, 5000, 1);
MAYBE_RETURN(maybe_rounding_increment, MaybeHandle<JSNumberFormat>());
CHECK(maybe_rounding_increment.To(&rounding_increment));
// 22. If roundingIncrement is not in « 1, 2, 5, 10, 20, 25, 50, 100, 200,
// 250, 500, 1000, 2000, 2500, 5000 », throw a RangeError exception.
if (!IsValidRoundingIncrement(rounding_increment)) {
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kPropertyValueOutOfRange,
factory->roundingIncrement_string()),
JSNumberFormat);
}
// 23. If roundingIncrement is not 1 and numberFormat.[[RoundingType]] is
// not fractionDigits, throw a RangeError exception.
if (rounding_increment != 1 &&
digit_options.rounding_type != Intl::RoundingType::kFractionDigits) {
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kPropertyValueOutOfRange,
factory->roundingIncrement_string()),
JSNumberFormat);
}
// 24. Set _numberFormat.[[RoundingIncrement]] to roundingIncrement.
// 25. Let trailingZeroDisplay be ? GetOption(options,
// "trailingZeroDisplay", "string", « "auto", "stripIfInteger" », "auto").
Maybe<TrailingZeroDisplay> maybe_trailing_zero_display =
GetStringOption<TrailingZeroDisplay>(
isolate, options, "trailingZeroDisplay", service,
{"auto", "stripIfInteger"},
{TrailingZeroDisplay::AUTO, TrailingZeroDisplay::STRIP_IF_INTEGER},
TrailingZeroDisplay::AUTO);
MAYBE_RETURN(maybe_trailing_zero_display, MaybeHandle<JSNumberFormat>());
TrailingZeroDisplay trailing_zero_display =
maybe_trailing_zero_display.FromJust();
// 26. Set numberFormat.[[TrailingZeroDisplay]] to trailingZeroDisplay.
settings = SetDigitOptionsToFormatterV3(
settings, digit_options, rounding_increment,
trailing_zero_display == TrailingZeroDisplay::STRIP_IF_INTEGER
? ShowTrailingZeros::kHide
: ShowTrailingZeros::kShow);
} else {
settings = SetDigitOptionsToFormatterV2(settings, digit_options);
}
// 27. Let compactDisplay be ? GetOption(options, "compactDisplay",
// "string", « "short", "long" », "short"). // "string", « "short", "long" », "short").
Maybe<CompactDisplay> maybe_compact_display = GetStringOption<CompactDisplay>( Maybe<CompactDisplay> maybe_compact_display = GetStringOption<CompactDisplay>(
isolate, options, "compactDisplay", service, {"short", "long"}, isolate, options, "compactDisplay", service, {"short", "long"},
@ -1126,13 +1328,13 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
MAYBE_RETURN(maybe_compact_display, MaybeHandle<JSNumberFormat>()); MAYBE_RETURN(maybe_compact_display, MaybeHandle<JSNumberFormat>());
CompactDisplay compact_display = maybe_compact_display.FromJust(); CompactDisplay compact_display = maybe_compact_display.FromJust();
// 26. Set numberFormat.[[Notation]] to notation.
// The default notation in ICU is Simple, which mapped from STANDARD // The default notation in ICU is Simple, which mapped from STANDARD
// so we can skip setting it. // so we can skip setting it.
if (notation != Notation::STANDARD) { if (notation != Notation::STANDARD) {
icu_number_formatter = settings = settings.notation(ToICUNotation(notation, compact_display));
icu_number_formatter.notation(ToICUNotation(notation, compact_display));
} }
if (!FLAG_harmony_intl_number_format_v3) {
// 30. Let useGrouping be ? GetOption(options, "useGrouping", "boolean", // 30. Let useGrouping be ? GetOption(options, "useGrouping", "boolean",
// undefined, true). // undefined, true).
bool use_grouping = true; bool use_grouping = true;
@ -1141,18 +1343,58 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
MAYBE_RETURN(found_use_grouping, MaybeHandle<JSNumberFormat>()); MAYBE_RETURN(found_use_grouping, MaybeHandle<JSNumberFormat>());
// 31. Set numberFormat.[[UseGrouping]] to useGrouping. // 31. Set numberFormat.[[UseGrouping]] to useGrouping.
if (!use_grouping) { if (!use_grouping) {
icu_number_formatter = icu_number_formatter.grouping( settings = settings.grouping(UNumberGroupingStrategy::UNUM_GROUPING_OFF);
UNumberGroupingStrategy::UNUM_GROUPING_OFF); }
settings = JSNumberFormat::SetDigitOptionsToFormatter(
settings, digit_options, 1, ShowTrailingZeros::kShow);
} else {
// 28. Let defaultUseGrouping be "auto".
UseGrouping default_use_grouping = UseGrouping::AUTO;
// 29. If notation is "compact", then
if (notation == Notation::COMPACT) {
// a. Set numberFormat.[[CompactDisplay]] to compactDisplay.
// Done in above together
// b. Set defaultUseGrouping to "min2".
default_use_grouping = UseGrouping::MIN2;
}
// 30. Let useGrouping be ? GetStringOrBooleanOption(options, "useGrouping",
// « "min2", "auto", "always" », "always", false, defaultUseGrouping).
Maybe<UseGrouping> maybe_use_grouping =
GetStringOrBooleanOption<UseGrouping>(
isolate, options, "useGrouping", service,
{"min2", "auto", "always"},
{UseGrouping::MIN2, UseGrouping::AUTO, UseGrouping::ALWAYS},
UseGrouping::ALWAYS, // trueValue
UseGrouping::OFF, // falseValue
default_use_grouping); // fallbackValue
MAYBE_RETURN(maybe_use_grouping, MaybeHandle<JSNumberFormat>());
UseGrouping use_grouping = maybe_use_grouping.FromJust();
// 31. Set numberFormat.[[UseGrouping]] to useGrouping.
if (use_grouping != UseGrouping::AUTO) {
settings = settings.grouping(ToUNumberGroupingStrategy(use_grouping));
}
} }
// 32. Let signDisplay be ? GetOption(options, "signDisplay", "string", « // 32. Let signDisplay be ? GetOption(options, "signDisplay", "string", «
// "auto", "never", "always", "exceptZero" », "auto"). // "auto", "never", "always", "exceptZero", "negative" », "auto").
Maybe<SignDisplay> maybe_sign_display = GetStringOption<SignDisplay>( Maybe<SignDisplay> maybe_sign_display = Nothing<SignDisplay>();
if (FLAG_harmony_intl_number_format_v3) {
maybe_sign_display = GetStringOption<SignDisplay>(
isolate, options, "signDisplay", service,
{"auto", "never", "always", "exceptZero", "negative"},
{SignDisplay::AUTO, SignDisplay::NEVER, SignDisplay::ALWAYS,
SignDisplay::EXCEPT_ZERO, SignDisplay::NEGATIVE},
SignDisplay::AUTO);
} else {
maybe_sign_display = GetStringOption<SignDisplay>(
isolate, options, "signDisplay", service, isolate, options, "signDisplay", service,
{"auto", "never", "always", "exceptZero"}, {"auto", "never", "always", "exceptZero"},
{SignDisplay::AUTO, SignDisplay::NEVER, SignDisplay::ALWAYS, {SignDisplay::AUTO, SignDisplay::NEVER, SignDisplay::ALWAYS,
SignDisplay::EXCEPT_ZERO}, SignDisplay::EXCEPT_ZERO},
SignDisplay::AUTO); SignDisplay::AUTO);
}
MAYBE_RETURN(maybe_sign_display, MaybeHandle<JSNumberFormat>()); MAYBE_RETURN(maybe_sign_display, MaybeHandle<JSNumberFormat>());
SignDisplay sign_display = maybe_sign_display.FromJust(); SignDisplay sign_display = maybe_sign_display.FromJust();
@ -1162,8 +1404,27 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
// under that values for optimization. // under that values for optimization.
if (sign_display != SignDisplay::AUTO || if (sign_display != SignDisplay::AUTO ||
currency_sign != CurrencySign::STANDARD) { currency_sign != CurrencySign::STANDARD) {
icu_number_formatter = icu_number_formatter.sign( settings = settings.sign(ToUNumberSignDisplay(sign_display, currency_sign));
ToUNumberSignDisplay(sign_display, currency_sign)); }
if (FLAG_harmony_intl_number_format_v3) {
// X. Let roundingMode be ? GetOption(options, "roundingMode", "string",
// « "ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor",
// "halfExpand", "halfTrunc", "halfEven" »,
// "halfExpand").
Maybe<RoundingMode> maybe_rounding_mode = GetStringOption<RoundingMode>(
isolate, options, "roundingMode", service,
{"ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor",
"halfExpand", "halfTrunc", "halfEven"},
{RoundingMode::CEIL, RoundingMode::FLOOR, RoundingMode::EXPAND,
RoundingMode::TRUNC, RoundingMode::HALF_CEIL, RoundingMode::HALF_FLOOR,
RoundingMode::HALF_EXPAND, RoundingMode::HALF_TRUNC,
RoundingMode::HALF_EVEN},
RoundingMode::HALF_EXPAND);
MAYBE_RETURN(maybe_rounding_mode, MaybeHandle<JSNumberFormat>());
RoundingMode rounding_mode = maybe_rounding_mode.FromJust();
settings =
settings.roundingMode(ToUNumberFormatRoundingMode(rounding_mode));
} }
// 25. Let dataLocaleData be localeData.[[<dataLocale>]]. // 25. Let dataLocaleData be localeData.[[<dataLocale>]].
@ -1180,6 +1441,9 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
// 30. Set numberFormat.[[NegativePattern]] to // 30. Set numberFormat.[[NegativePattern]] to
// stylePatterns.[[negativePattern]]. // stylePatterns.[[negativePattern]].
// //
icu::number::LocalizedNumberFormatter icu_number_formatter =
settings.locale(icu_locale);
Handle<Managed<icu::number::LocalizedNumberFormatter>> Handle<Managed<icu::number::LocalizedNumberFormatter>>
managed_number_formatter = managed_number_formatter =
Managed<icu::number::LocalizedNumberFormatter>::FromRawPtr( Managed<icu::number::LocalizedNumberFormatter>::FromRawPtr(
@ -1200,6 +1464,7 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
} }
namespace { namespace {
Maybe<bool> IcuFormatNumber( Maybe<bool> IcuFormatNumber(
Isolate* isolate, Isolate* isolate,
const icu::number::LocalizedNumberFormatter& number_format, const icu::number::LocalizedNumberFormatter& number_format,
@ -1212,14 +1477,41 @@ Maybe<bool> IcuFormatNumber(
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, big_int_string, ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, big_int_string,
BigInt::ToString(isolate, big_int), BigInt::ToString(isolate, big_int),
Nothing<bool>()); Nothing<bool>());
big_int_string = String::Flatten(isolate, big_int_string);
DisallowGarbageCollection no_gc;
const String::FlatContent& flat = big_int_string->GetFlatContent(no_gc);
int32_t length = big_int_string->length();
DCHECK(flat.IsOneByte());
const char* char_buffer =
reinterpret_cast<const char*>(flat.ToOneByteVector().begin());
*formatted = number_format.formatDecimal({char_buffer, length}, status);
} else {
if (FLAG_harmony_intl_number_format_v3 && numeric_obj->IsString()) {
Handle<String> string =
String::Flatten(isolate, Handle<String>::cast(numeric_obj));
DisallowGarbageCollection no_gc;
const String::FlatContent& flat = string->GetFlatContent(no_gc);
int32_t length = string->length();
if (flat.IsOneByte()) {
const char* char_buffer =
reinterpret_cast<const char*>(flat.ToOneByteVector().begin());
*formatted = number_format.formatDecimal({char_buffer, length}, status);
} else {
// We may have two bytes string such as "漢 123456789".substring(2)
// The value will be "123456789" only in ASCII range, but encoded
// in two bytes string.
// ICU accepts UTF8 string, so if the source is two-byte encoded,
// copy into a UTF8 string via ToCString.
*formatted = number_format.formatDecimal( *formatted = number_format.formatDecimal(
{big_int_string->ToCString().get(), big_int_string->length()}, status); {string->ToCString().get(), string->length()}, status);
}
} else { } else {
double number = numeric_obj->IsNaN() double number = numeric_obj->IsNaN()
? std::numeric_limits<double>::quiet_NaN() ? std::numeric_limits<double>::quiet_NaN()
: numeric_obj->Number(); : numeric_obj->Number();
*formatted = number_format.formatDouble(number, status); *formatted = number_format.formatDouble(number, status);
} }
}
if (U_FAILURE(status)) { if (U_FAILURE(status)) {
// This happen because of icu data trimming trim out "unit". // This happen because of icu data trimming trim out "unit".
// See https://bugs.chromium.org/p/v8/issues/detail?id=8641 // See https://bugs.chromium.org/p/v8/issues/detail?id=8641
@ -1229,28 +1521,6 @@ Maybe<bool> IcuFormatNumber(
return Just(true); return Just(true);
} }
} // namespace
MaybeHandle<String> JSNumberFormat::FormatNumeric(
Isolate* isolate,
const icu::number::LocalizedNumberFormatter& number_format,
Handle<Object> numeric_obj) {
DCHECK(numeric_obj->IsNumeric());
icu::number::FormattedNumber formatted;
Maybe<bool> maybe_format =
IcuFormatNumber(isolate, number_format, numeric_obj, &formatted);
MAYBE_RETURN(maybe_format, Handle<String>());
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString result = formatted.toString(status);
if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), String);
}
return Intl::ToString(isolate, result);
}
namespace {
bool cmp_NumberFormatSpan(const NumberFormatSpan& a, bool cmp_NumberFormatSpan(const NumberFormatSpan& a,
const NumberFormatSpan& b) { const NumberFormatSpan& b) {
// Regions that start earlier should be encountered earlier. // Regions that start earlier should be encountered earlier.
@ -1359,17 +1629,15 @@ std::vector<NumberFormatSpan> FlattenRegionsToParts(
} }
namespace { namespace {
Maybe<int> ConstructParts(Isolate* isolate, Maybe<int> ConstructParts(Isolate* isolate, icu::FormattedValue* formatted,
icu::number::FormattedNumber* formatted,
Handle<JSArray> result, int start_index, Handle<JSArray> result, int start_index,
Handle<Object> numeric_obj, bool style_is_unit) { bool style_is_unit, bool is_nan) {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString formatted_text = formatted->toString(status); icu::UnicodeString formatted_text = formatted->toString(status);
if (U_FAILURE(status)) { if (U_FAILURE(status)) {
THROW_NEW_ERROR_RETURN_VALUE( THROW_NEW_ERROR_RETURN_VALUE(
isolate, NewTypeError(MessageTemplate::kIcuError), Nothing<int>()); isolate, NewTypeError(MessageTemplate::kIcuError), Nothing<int>());
} }
DCHECK(numeric_obj->IsNumeric());
int32_t length = formatted_text.length(); int32_t length = formatted_text.length();
int index = start_index; int index = start_index;
if (length == 0) return Just(index); if (length == 0) return Just(index);
@ -1380,7 +1648,6 @@ Maybe<int> ConstructParts(Isolate* isolate,
// there's another field with exactly the same begin and end as this backdrop, // there's another field with exactly the same begin and end as this backdrop,
// in which case the backdrop's field_id of -1 will give it lower priority. // in which case the backdrop's field_id of -1 will give it lower priority.
regions.push_back(NumberFormatSpan(-1, 0, formatted_text.length())); regions.push_back(NumberFormatSpan(-1, 0, formatted_text.length()));
{ {
icu::ConstrainedFieldPosition cfp; icu::ConstrainedFieldPosition cfp;
cfp.constrainCategory(UFIELD_CATEGORY_NUMBER); cfp.constrainCategory(UFIELD_CATEGORY_NUMBER);
@ -1402,7 +1669,7 @@ Maybe<int> ConstructParts(Isolate* isolate,
field_type_string = isolate->factory()->unit_string(); field_type_string = isolate->factory()->unit_string();
} else { } else {
field_type_string = field_type_string =
Intl::NumberFieldToType(isolate, numeric_obj, part.field_id); Intl::NumberFieldToType(isolate, part, formatted_text, is_nan);
} }
} }
Handle<String> substring; Handle<String> substring;
@ -1410,6 +1677,7 @@ Maybe<int> ConstructParts(Isolate* isolate,
isolate, substring, isolate, substring,
Intl::ToString(isolate, formatted_text, part.begin_pos, part.end_pos), Intl::ToString(isolate, formatted_text, part.begin_pos, part.end_pos),
Nothing<int>()); Nothing<int>());
Intl::AddElement(isolate, result, index, field_type_string, substring); Intl::AddElement(isolate, result, index, field_type_string, substring);
++index; ++index;
} }
@ -1417,13 +1685,54 @@ Maybe<int> ConstructParts(Isolate* isolate,
return Just(index); return Just(index);
} }
MaybeHandle<String> FormatToString(Isolate* isolate,
icu::FormattedValue* formatted,
const icu::number::LocalizedNumberFormatter*,
bool) {
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString result = formatted->toString(status);
if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), String);
}
return Intl::ToString(isolate, result);
}
MaybeHandle<JSArray> FormatToJSArray(
Isolate* isolate, icu::FormattedValue* formatted,
const icu::number::LocalizedNumberFormatter* nfmt, bool is_nan) {
UErrorCode status = U_ZERO_ERROR;
bool is_unit = Style::UNIT == StyleFromSkeleton(nfmt->toSkeleton(status));
CHECK(U_SUCCESS(status));
Factory* factory = isolate->factory();
Handle<JSArray> result = factory->NewJSArray(0);
Maybe<int> maybe_format_to_parts =
ConstructParts(isolate, formatted, result, 0, is_unit, is_nan);
MAYBE_RETURN(maybe_format_to_parts, Handle<JSArray>());
return result;
}
} // namespace } // namespace
MaybeHandle<String> JSNumberFormat::FormatNumeric(
Isolate* isolate,
const icu::number::LocalizedNumberFormatter& number_format,
Handle<Object> numeric_obj) {
DCHECK(numeric_obj->IsNumeric() || FLAG_harmony_intl_number_format_v3);
icu::number::FormattedNumber formatted;
Maybe<bool> maybe_format =
IcuFormatNumber(isolate, number_format, numeric_obj, &formatted);
MAYBE_RETURN(maybe_format, Handle<String>());
return FormatToString(isolate, &formatted, &number_format,
numeric_obj->IsNaN());
}
MaybeHandle<JSArray> JSNumberFormat::FormatToParts( MaybeHandle<JSArray> JSNumberFormat::FormatToParts(
Isolate* isolate, Handle<JSNumberFormat> number_format, Isolate* isolate, Handle<JSNumberFormat> number_format,
Handle<Object> numeric_obj) { Handle<Object> numeric_obj) {
CHECK(numeric_obj->IsNumeric()); CHECK(numeric_obj->IsNumeric() || FLAG_harmony_intl_number_format_v3);
Factory* factory = isolate->factory();
icu::number::LocalizedNumberFormatter* fmt = icu::number::LocalizedNumberFormatter* fmt =
number_format->icu_number_formatter().raw(); number_format->icu_number_formatter().raw();
CHECK_NOT_NULL(fmt); CHECK_NOT_NULL(fmt);
@ -1432,18 +1741,8 @@ MaybeHandle<JSArray> JSNumberFormat::FormatToParts(
Maybe<bool> maybe_format = Maybe<bool> maybe_format =
IcuFormatNumber(isolate, *fmt, numeric_obj, &formatted); IcuFormatNumber(isolate, *fmt, numeric_obj, &formatted);
MAYBE_RETURN(maybe_format, Handle<JSArray>()); MAYBE_RETURN(maybe_format, Handle<JSArray>());
UErrorCode status = U_ZERO_ERROR;
bool style_is_unit = return FormatToJSArray(isolate, &formatted, fmt, numeric_obj->IsNaN());
Style::UNIT == StyleFromSkeleton(fmt->toSkeleton(status));
CHECK(U_SUCCESS(status));
Handle<JSArray> result = factory->NewJSArray(0);
Maybe<int> maybe_format_to_parts = ConstructParts(
isolate, &formatted, result, 0, numeric_obj, style_is_unit);
MAYBE_RETURN(maybe_format_to_parts, Handle<JSArray>());
return result;
} }
namespace { namespace {

View File

@ -26,6 +26,7 @@ namespace U_ICU_NAMESPACE {
class UnicodeString; class UnicodeString;
namespace number { namespace number {
class LocalizedNumberFormatter; class LocalizedNumberFormatter;
class UnlocalizedNumberFormatter;
} // namespace number } // namespace number
} // namespace U_ICU_NAMESPACE } // namespace U_ICU_NAMESPACE
@ -68,9 +69,13 @@ class JSNumberFormat
int32_t* minimum, int32_t* maximum); int32_t* minimum, int32_t* maximum);
static bool SignificantDigitsFromSkeleton(const icu::UnicodeString& skeleton, static bool SignificantDigitsFromSkeleton(const icu::UnicodeString& skeleton,
int32_t* minimum, int32_t* maximum); int32_t* minimum, int32_t* maximum);
static icu::number::LocalizedNumberFormatter SetDigitOptionsToFormatter(
const icu::number::LocalizedNumberFormatter& icu_number_formatter, enum class ShowTrailingZeros { kShow, kHide };
const Intl::NumberFormatDigitOptions& digit_options);
static icu::number::UnlocalizedNumberFormatter SetDigitOptionsToFormatter(
const icu::number::UnlocalizedNumberFormatter& settings,
const Intl::NumberFormatDigitOptions& digit_options,
int rounding_increment, ShowTrailingZeros show);
DECL_PRINTER(JSNumberFormat) DECL_PRINTER(JSNumberFormat)
@ -80,19 +85,6 @@ class JSNumberFormat
TQ_OBJECT_CONSTRUCTORS(JSNumberFormat) TQ_OBJECT_CONSTRUCTORS(JSNumberFormat)
}; };
struct NumberFormatSpan {
int32_t field_id;
int32_t begin_pos;
int32_t end_pos;
NumberFormatSpan() = default;
NumberFormatSpan(int32_t field_id, int32_t begin_pos, int32_t end_pos)
: field_id(field_id), begin_pos(begin_pos), end_pos(end_pos) {}
};
V8_EXPORT_PRIVATE std::vector<NumberFormatSpan> FlattenRegionsToParts(
std::vector<NumberFormatSpan>* regions);
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8

View File

@ -115,21 +115,19 @@ MaybeHandle<JSPluralRules> JSPluralRules::New(Isolate* isolate, Handle<Map> map,
Handle<String> locale_str = Handle<String> locale_str =
isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str()); isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
icu::number::LocalizedNumberFormatter icu_number_formatter = icu::Locale icu_locale = r.icu_locale;
icu::number::NumberFormatter::withLocale(r.icu_locale) icu::number::UnlocalizedNumberFormatter settings =
.roundingMode(UNUM_ROUND_HALFUP); icu::number::UnlocalizedNumberFormatter().roundingMode(UNUM_ROUND_HALFUP);
std::unique_ptr<icu::PluralRules> icu_plural_rules; std::unique_ptr<icu::PluralRules> icu_plural_rules;
bool success = bool success =
CreateICUPluralRules(isolate, r.icu_locale, type, &icu_plural_rules); CreateICUPluralRules(isolate, r.icu_locale, type, &icu_plural_rules);
if (!success || icu_plural_rules.get() == nullptr) { if (!success || icu_plural_rules.get() == nullptr) {
// Remove extensions and try again. // Remove extensions and try again.
icu::Locale no_extension_locale(r.icu_locale.getBaseName()); icu::Locale no_extension_locale(icu_locale.getBaseName());
success = CreateICUPluralRules(isolate, no_extension_locale, type, success = CreateICUPluralRules(isolate, no_extension_locale, type,
&icu_plural_rules); &icu_plural_rules);
icu_number_formatter = icu_locale = no_extension_locale;
icu::number::NumberFormatter::withLocale(no_extension_locale)
.roundingMode(UNUM_ROUND_HALFUP);
if (!success || icu_plural_rules.get() == nullptr) { if (!success || icu_plural_rules.get() == nullptr) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError), THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),
@ -142,8 +140,11 @@ MaybeHandle<JSPluralRules> JSPluralRules::New(Isolate* isolate, Handle<Map> map,
Intl::SetNumberFormatDigitOptions(isolate, options, 0, 3, false); Intl::SetNumberFormatDigitOptions(isolate, options, 0, 3, false);
MAYBE_RETURN(maybe_digit_options, MaybeHandle<JSPluralRules>()); MAYBE_RETURN(maybe_digit_options, MaybeHandle<JSPluralRules>());
Intl::NumberFormatDigitOptions digit_options = maybe_digit_options.FromJust(); Intl::NumberFormatDigitOptions digit_options = maybe_digit_options.FromJust();
icu_number_formatter = JSNumberFormat::SetDigitOptionsToFormatter( settings = JSNumberFormat::SetDigitOptionsToFormatter(
icu_number_formatter, digit_options); settings, digit_options, 1, JSNumberFormat::ShowTrailingZeros::kShow);
icu::number::LocalizedNumberFormatter icu_number_formatter =
settings.locale(icu_locale);
Handle<Managed<icu::PluralRules>> managed_plural_rules = Handle<Managed<icu::PluralRules>> managed_plural_rules =
Managed<icu::PluralRules>::FromUniquePtr(isolate, 0, Managed<icu::PluralRules>::FromUniquePtr(isolate, 0,

View File

@ -343,9 +343,9 @@ template <typename T>
MaybeHandle<T> FormatCommon( MaybeHandle<T> FormatCommon(
Isolate* isolate, Handle<JSRelativeTimeFormat> format, Isolate* isolate, Handle<JSRelativeTimeFormat> format,
Handle<Object> value_obj, Handle<Object> unit_obj, const char* func_name, Handle<Object> value_obj, Handle<Object> unit_obj, const char* func_name,
const std::function< MaybeHandle<T> (*formatToResult)(Isolate*,
MaybeHandle<T>(Isolate*, const icu::FormattedRelativeDateTime&, const icu::FormattedRelativeDateTime&,
Handle<Object>, Handle<String>)>& formatToResult) { Handle<String>, bool)) {
// 3. Let value be ? ToNumber(value). // 3. Let value be ? ToNumber(value).
Handle<Object> value; Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(isolate, value, ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
@ -382,13 +382,13 @@ MaybeHandle<T> FormatCommon(
if (U_FAILURE(status)) { if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), T); THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), T);
} }
return formatToResult(isolate, formatted, value, return formatToResult(isolate, formatted, UnitAsString(isolate, unit_enum),
UnitAsString(isolate, unit_enum)); value->IsNaN());
} }
MaybeHandle<String> FormatToString( MaybeHandle<String> FormatToString(
Isolate* isolate, const icu::FormattedRelativeDateTime& formatted, Isolate* isolate, const icu::FormattedRelativeDateTime& formatted,
Handle<Object> value, Handle<String> unit) { Handle<String> unit, bool is_nan) {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString result = formatted.toString(status); icu::UnicodeString result = formatted.toString(status);
if (U_FAILURE(status)) { if (U_FAILURE(status)) {
@ -411,21 +411,22 @@ Maybe<bool> AddLiteral(Isolate* isolate, Handle<JSArray> array,
Maybe<bool> AddUnit(Isolate* isolate, Handle<JSArray> array, Maybe<bool> AddUnit(Isolate* isolate, Handle<JSArray> array,
const icu::UnicodeString& string, int32_t index, const icu::UnicodeString& string, int32_t index,
int32_t start, int32_t limit, int32_t field_id, const NumberFormatSpan& part, Handle<String> unit,
Handle<Object> value, Handle<String> unit) { bool is_nan) {
Handle<String> substring; Handle<String> substring;
ASSIGN_RETURN_ON_EXCEPTION_VALUE( ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, substring, Intl::ToString(isolate, string, start, limit), isolate, substring,
Intl::ToString(isolate, string, part.begin_pos, part.end_pos),
Nothing<bool>()); Nothing<bool>());
Intl::AddElement(isolate, array, index, Intl::AddElement(isolate, array, index,
Intl::NumberFieldToType(isolate, value, field_id), substring, Intl::NumberFieldToType(isolate, part, string, is_nan),
isolate->factory()->unit_string(), unit); substring, isolate->factory()->unit_string(), unit);
return Just(true); return Just(true);
} }
MaybeHandle<JSArray> FormatToJSArray( MaybeHandle<JSArray> FormatToJSArray(
Isolate* isolate, const icu::FormattedRelativeDateTime& formatted, Isolate* isolate, const icu::FormattedRelativeDateTime& formatted,
Handle<Object> value, Handle<String> unit) { Handle<String> unit, bool is_nan) {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString string = formatted.toString(status); icu::UnicodeString string = formatted.toString(status);
@ -457,19 +458,23 @@ MaybeHandle<JSArray> FormatToJSArray(
for (auto start_limit : groups) { for (auto start_limit : groups) {
if (start_limit.first > start) { if (start_limit.first > start) {
Maybe<bool> maybe_added = Maybe<bool> maybe_added =
AddUnit(isolate, array, string, index++, start, AddUnit(isolate, array, string, index++,
start_limit.first, field, value, unit); NumberFormatSpan(field, start, start_limit.first), unit,
is_nan);
MAYBE_RETURN(maybe_added, Handle<JSArray>()); MAYBE_RETURN(maybe_added, Handle<JSArray>());
maybe_added = AddUnit(isolate, array, string, index++, maybe_added =
start_limit.first, start_limit.second, AddUnit(isolate, array, string, index++,
UNUM_GROUPING_SEPARATOR_FIELD, value, unit); NumberFormatSpan(UNUM_GROUPING_SEPARATOR_FIELD,
start_limit.first, start_limit.second),
unit, is_nan);
MAYBE_RETURN(maybe_added, Handle<JSArray>()); MAYBE_RETURN(maybe_added, Handle<JSArray>());
start = start_limit.second; start = start_limit.second;
} }
} }
} }
Maybe<bool> maybe_added = AddUnit(isolate, array, string, index++, start, Maybe<bool> maybe_added =
limit, field, value, unit); AddUnit(isolate, array, string, index++,
NumberFormatSpan(field, start, limit), unit, is_nan);
MAYBE_RETURN(maybe_added, Handle<JSArray>()); MAYBE_RETURN(maybe_added, Handle<JSArray>());
previous_end = limit; previous_end = limit;
} }

View File

@ -65,6 +65,75 @@ V8_WARN_UNUSED_RESULT static Maybe<T> GetStringOption(
return Just(default_value); return Just(default_value);
} }
// A helper template to get string from option into a enum.
// The enum in the enum_values is the corresponding value to the strings
// in the str_values. If the option does not contains name,
// default_value will be return.
template <typename T>
V8_WARN_UNUSED_RESULT static Maybe<T> GetStringOrBooleanOption(
Isolate* isolate, Handle<JSReceiver> options, const char* property,
const char* method, const std::vector<const char*>& str_values,
const std::vector<T>& enum_values, T true_value, T false_value,
T fallback_value) {
DCHECK_EQ(str_values.size(), enum_values.size());
Handle<String> property_str =
isolate->factory()->NewStringFromAsciiChecked(property);
// 1. Let value be ? Get(options, property).
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, value,
Object::GetPropertyOrElement(isolate, options, property_str),
Nothing<T>());
// 2. If value is undefined, then return fallback.
if (value->IsUndefined(isolate)) {
return Just(fallback_value);
}
// 3. If value is true, then return trueValue.
if (value->IsTrue(isolate)) {
return Just(true_value);
}
// 4. Let valueBoolean be ToBoolean(value).
bool valueBoolean = value->BooleanValue(isolate);
// 5. If valueBoolean is false, then return valueBoolean.
if (!valueBoolean) {
return Just(false_value);
}
Handle<String> value_str;
// 6. Let value be ? ToString(value).
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, value_str, Object::ToString(isolate, value), Nothing<T>());
// 7. If values does not contain an element equal to value, throw a
// RangeError exception.
// 8. Return value.
value_str = String::Flatten(isolate, value_str);
DisallowGarbageCollection no_gc;
const String::FlatContent& flat = value_str->GetFlatContent(no_gc);
int32_t length = value_str->length();
for (size_t i = 0; i < str_values.size(); i++) {
if (static_cast<int32_t>(strlen(str_values.at(i))) == length) {
if (flat.IsOneByte()) {
if (CompareCharsEqual(str_values.at(i), flat.ToOneByteVector().begin(),
length)) {
return Just(enum_values[i]);
}
} else {
if (CompareCharsEqual(str_values.at(i), flat.ToUC16Vector().begin(),
length)) {
return Just(enum_values[i]);
}
}
}
}
THROW_NEW_ERROR_RETURN_VALUE(
isolate,
NewRangeError(MessageTemplate::kValueOutOfRange, value,
isolate->factory()->NewStringFromAsciiChecked(method),
property_str),
Nothing<T>());
}
// ECMA402 9.2.10. GetOption( options, property, type, values, fallback) // ECMA402 9.2.10. GetOption( options, property, type, values, fallback)
// ecma402/#sec-getoption // ecma402/#sec-getoption
// //

View File

@ -30,6 +30,7 @@
[ALWAYS, { [ALWAYS, {
# TODO(ftang,jshin): The following test is flaky. # TODO(ftang,jshin): The following test is flaky.
'overrides/caching': [PASS, FAIL], 'overrides/caching': [PASS, FAIL],
'number-format/rounding-increment-resolved-match-v3': [FAIL],
}], # ALWAYS }], # ALWAYS
################################################################################ ################################################################################

View File

@ -0,0 +1,13 @@
// Copyright 2021 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.
// Flags: --harmony-intl-number-format-v3
let validRoundingIncrements = [
1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500, 5000];
validRoundingIncrements.forEach(function(roundingIncrement) {
let nf = new Intl.NumberFormat(undefined, {roundingIncrement});
assertEquals(roundingIncrement, nf.resolvedOptions().roundingIncrement);
});

View File

@ -0,0 +1,23 @@
// Copyright 2021 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.
// Flags: --harmony-intl-number-format-v3
let validRoundingIncrements = [
1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500, 5000];
let invalidRoundingIncrements = [
-1, -5, 0, 3, 1001, 1100, 5500, 10000, 20000, 25000, 100000, 200000, 500005, 10000000
];
validRoundingIncrements.forEach(function(roundingIncrement) {
assertDoesNotThrow(() => {
new Intl.NumberFormat(undefined, {roundingIncrement})});
});
invalidRoundingIncrements.forEach(function(roundingIncrement) {
assertThrows(() => {
let nf = new Intl.NumberFormat(undefined, {roundingIncrement})},
RangeError);
});

View File

@ -0,0 +1,23 @@
// Copyright 2021 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.
// Flags: --harmony-intl-number-format-v3
let penny = new Intl.NumberFormat(
"en", { minimumFractionDigits: 2, maximumFractionDigits: 2, roundingIncrement: 1 });
let nickel = new Intl.NumberFormat(
"en", { minimumFractionDigits: 2, maximumFractionDigits: 2, roundingIncrement: 5 });
let dime = new Intl.NumberFormat(
"en", { minimumFractionDigits: 2, maximumFractionDigits: 2, roundingIncrement: 10 });
// https://necs.com/knowledgebase/sysprefs_prc_mod_roundmeth.htm
assertEquals("10.15", penny.format(10.154));
assertEquals("10.16", penny.format(10.155));
assertEquals("10.10", nickel.format(10.124));
assertEquals("10.15", nickel.format(10.125));
assertEquals("10.40", dime.format(10.444));
// mistake in the above page, the result should be 10.40 not 10.50
// assertEquals("10.50", dime.format(10.445));
assertEquals("10.40", dime.format(10.445));
assertEquals("10.50", dime.format(10.45));

View File

@ -0,0 +1,29 @@
// Copyright 2021 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.
//
// Flags: --harmony-intl-number-format-v3
// Test default.
let nf = new Intl.NumberFormat();
assertEquals("auto", nf.resolvedOptions().signDisplay);
nf = new Intl.NumberFormat("en");
assertEquals("auto", nf.resolvedOptions().signDisplay);
const testData = [
["auto", "-123", "-0", "0", "123"],
["always", "-123", "-0", "+0", "+123"],
["never", "123", "0", "0", "123"],
["exceptZero", "-123", "0", "0", "+123"],
["negative", "-123", "0", "0", "123"],
];
for (const [signDisplay, neg, negZero, zero, pos] of testData) {
nf = new Intl.NumberFormat("en", {signDisplay});
assertEquals(signDisplay, nf.resolvedOptions().signDisplay);
assertEquals(neg, nf.format(-123));
assertEquals(negZero, nf.format(-0));
assertEquals(zero, nf.format(0));
assertEquals(pos, nf.format(123));
}

View File

@ -0,0 +1,24 @@
// Copyright 2021 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.
// Flags: --harmony-intl-number-format-v3
let defaultFmt = new Intl.NumberFormat("en",
{ minimumFractionDigits: 2, maximumFractionDigits: 2 });
let autoFmt = new Intl.NumberFormat("en",
{ minimumFractionDigits: 2, maximumFractionDigits: 2,
trailingZeroDisplay: 'auto'});
let stripIfIntegerFmt = new Intl.NumberFormat("en",
{ minimumFractionDigits: 2, maximumFractionDigits: 2,
trailingZeroDisplay: 'stripIfInteger'});
assertEquals("3.14", defaultFmt.format(3.1411));
assertEquals("3.14", autoFmt.format(3.1411));
assertEquals("3.14", stripIfIntegerFmt.format(3.1411));
assertEquals("3.00", defaultFmt.format(3.001411));
assertEquals("3.00", autoFmt.format(3.001411));
assertEquals("3", stripIfIntegerFmt.format(3.001411));
assertEquals("3.00", defaultFmt.format(2.999411));
assertEquals("3.00", autoFmt.format(2.999411));
assertEquals("3", stripIfIntegerFmt.format(2.999411));

View File

@ -2578,66 +2578,11 @@
# https://bugs.chromium.org/p/v8/issues/detail?id=10776 # https://bugs.chromium.org/p/v8/issues/detail?id=10776
'intl402/NumberFormat/constructor-options-roundingMode-invalid': [FAIL],
'intl402/NumberFormat/constructor-options-throwing-getters-rounding-mode': [FAIL],
'intl402/NumberFormat/constructor-signDisplay-negative': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-mode-ceil': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-mode-expand': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-mode-floor': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-mode-half-ceil': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-mode-half-even': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-mode-half-floor': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-mode-half-trunc': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-mode-trunc': [FAIL],
'intl402/NumberFormat/prototype/format/signDisplay-negative-currency-de-DE': [FAIL],
'intl402/NumberFormat/prototype/format/signDisplay-negative-currency-en-US': [FAIL],
'intl402/NumberFormat/prototype/format/signDisplay-negative-currency-ja-JP': [FAIL],
'intl402/NumberFormat/prototype/format/signDisplay-negative-currency-ko-KR': [FAIL],
'intl402/NumberFormat/prototype/format/signDisplay-negative-currency-zh-TW': [FAIL],
'intl402/NumberFormat/prototype/format/signDisplay-negative-de-DE': [FAIL],
'intl402/NumberFormat/prototype/format/signDisplay-negative-en-US': [FAIL],
'intl402/NumberFormat/prototype/format/signDisplay-negative-ja-JP': [FAIL],
'intl402/NumberFormat/prototype/format/signDisplay-negative-ko-KR': [FAIL],
'intl402/NumberFormat/prototype/format/signDisplay-negative-zh-TW': [FAIL],
'intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-currency-de-DE': [FAIL],
'intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-currency-en-US': [FAIL],
'intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-currency-ja-JP': [FAIL],
'intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-currency-ko-KR': [FAIL],
'intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-currency-zh-TW': [FAIL],
'intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-de-DE': [FAIL],
'intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-en-US': [FAIL],
'intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-ja-JP': [FAIL],
'intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-ko-KR': [FAIL],
'intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-zh-TW': [FAIL],
'intl402/NumberFormat/prototype/resolvedOptions/roundingMode': [FAIL],
'intl402/NumberFormat/prototype/format/useGrouping-extended-de-DE': [FAIL],
'intl402/NumberFormat/prototype/format/useGrouping-extended-en-IN': [FAIL],
'intl402/NumberFormat/prototype/format/useGrouping-extended-en-US': [FAIL],
'intl402/NumberFormat/test-option-useGrouping-extended': [FAIL],
'intl402/NumberFormat/prototype/format/value-decimal-string': [FAIL],
'intl402/NumberFormat/constructor-options-throwing-getters-rounding-increment': [FAIL],
'intl402/NumberFormat/constructor-options-throwing-getters-rounding-priority': [FAIL],
'intl402/NumberFormat/constructor-options-throwing-getters-trailing-zero-display': [FAIL],
'intl402/NumberFormat/constructor-roundingIncrement': [FAIL], 'intl402/NumberFormat/constructor-roundingIncrement': [FAIL],
'intl402/NumberFormat/constructor-roundingIncrement-invalid': [FAIL], 'intl402/NumberFormat/test-option-roundingPriority': [FAIL],
'intl402/NumberFormat/constructor-trailingZeroDisplay': [FAIL], # NumberFormat.prototype.format
'intl402/NumberFormat/constructor-trailingZeroDisplay-invalid': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-1000': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-100': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-10': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-2000': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-200': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-20': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-2500': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-250': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-25': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-2': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-5000': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-500': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-50': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-5': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-priority-less-precision': [FAIL], 'intl402/NumberFormat/prototype/format/format-rounding-priority-less-precision': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-priority-more-precision': [FAIL], # NumberFormat.prototype.formatRange
'intl402/NumberFormat/prototype/formatRange/builtin': [FAIL], 'intl402/NumberFormat/prototype/formatRange/builtin': [FAIL],
'intl402/NumberFormat/prototype/formatRange/en-US': [FAIL], 'intl402/NumberFormat/prototype/formatRange/en-US': [FAIL],
'intl402/NumberFormat/prototype/formatRange/invoked-as-func': [FAIL], 'intl402/NumberFormat/prototype/formatRange/invoked-as-func': [FAIL],
@ -2646,6 +2591,8 @@
'intl402/NumberFormat/prototype/formatRange/nan-arguments-throws': [FAIL], 'intl402/NumberFormat/prototype/formatRange/nan-arguments-throws': [FAIL],
'intl402/NumberFormat/prototype/formatRange/prop-desc': [FAIL], 'intl402/NumberFormat/prototype/formatRange/prop-desc': [FAIL],
'intl402/NumberFormat/prototype/formatRange/pt-PT': [FAIL], 'intl402/NumberFormat/prototype/formatRange/pt-PT': [FAIL],
'intl402/NumberFormat/prototype/formatRange/x-greater-than-y-throws': [FAIL],
# NumberFormat.prototype.formatRangeToParts
'intl402/NumberFormat/prototype/formatRangeToParts/builtin': [FAIL], 'intl402/NumberFormat/prototype/formatRangeToParts/builtin': [FAIL],
'intl402/NumberFormat/prototype/formatRangeToParts/en-US': [FAIL], 'intl402/NumberFormat/prototype/formatRangeToParts/en-US': [FAIL],
'intl402/NumberFormat/prototype/formatRangeToParts/invoked-as-func': [FAIL], 'intl402/NumberFormat/prototype/formatRangeToParts/invoked-as-func': [FAIL],
@ -2654,19 +2601,22 @@
'intl402/NumberFormat/prototype/formatRangeToParts/nan-arguments-throws': [FAIL], 'intl402/NumberFormat/prototype/formatRangeToParts/nan-arguments-throws': [FAIL],
'intl402/NumberFormat/prototype/formatRangeToParts/prop-desc': [FAIL], 'intl402/NumberFormat/prototype/formatRangeToParts/prop-desc': [FAIL],
'intl402/NumberFormat/prototype/formatRangeToParts/x-greater-than-y-throws': [FAIL], 'intl402/NumberFormat/prototype/formatRangeToParts/x-greater-than-y-throws': [FAIL],
'intl402/NumberFormat/prototype/formatRange/x-greater-than-y-throws': [FAIL],
# NumberFormat.prototype.resolvedOptions
'intl402/NumberFormat/constructor-trailingZeroDisplay': [FAIL],
'intl402/NumberFormat/prototype/resolvedOptions/basic': [FAIL], 'intl402/NumberFormat/prototype/resolvedOptions/basic': [FAIL],
'intl402/NumberFormat/test-option-roundingPriority': [FAIL], 'intl402/NumberFormat/prototype/resolvedOptions/roundingMode': [FAIL],
'intl402/NumberFormat/test-option-useGrouping': [FAIL], 'intl402/NumberFormat/test-option-useGrouping': [FAIL],
'intl402/NumberFormat/test-option-useGrouping-extended': [FAIL],
# PluralRules.prototype.selectRange
'intl402/PluralRules/prototype/selectRange/default-en-us': [FAIL], 'intl402/PluralRules/prototype/selectRange/default-en-us': [FAIL],
'intl402/PluralRules/prototype/selectRange/invoked-as-func': [FAIL], 'intl402/PluralRules/prototype/selectRange/invoked-as-func': [FAIL],
'intl402/PluralRules/prototype/selectRange/length': [FAIL], 'intl402/PluralRules/prototype/selectRange/length': [FAIL],
'intl402/PluralRules/prototype/selectRange/name': [FAIL], 'intl402/PluralRules/prototype/selectRange/name': [FAIL],
'intl402/PluralRules/prototype/selectRange/nan-arguments-throws': [FAIL],
'intl402/PluralRules/prototype/selectRange/prop-desc': [FAIL], 'intl402/PluralRules/prototype/selectRange/prop-desc': [FAIL],
'intl402/PluralRules/prototype/selectRange/nan-arguments-throws': [FAIL],
'intl402/PluralRules/prototype/selectRange/x-greater-than-y-throws': [FAIL], 'intl402/PluralRules/prototype/selectRange/x-greater-than-y-throws': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=11660 # https://bugs.chromium.org/p/v8/issues/detail?id=11660
'intl402/DurationFormat/prototype/prototype_attributes': [FAIL], 'intl402/DurationFormat/prototype/prototype_attributes': [FAIL],
'intl402/DurationFormat/prototype/toStringTag': [FAIL], 'intl402/DurationFormat/prototype/toStringTag': [FAIL],

View File

@ -46,6 +46,7 @@ from testrunner.outproc import test262
FEATURE_FLAGS = { FEATURE_FLAGS = {
'Intl.Locale-info': '--harmony_intl_locale_info', 'Intl.Locale-info': '--harmony_intl_locale_info',
'Intl-enumeration': '--harmony_intl_enumeration', 'Intl-enumeration': '--harmony_intl_enumeration',
'Intl.NumberFormat-v3': '--harmony_intl_number_format_v3',
'Symbol.prototype.description': '--harmony-symbol-description', 'Symbol.prototype.description': '--harmony-symbol-description',
'FinalizationRegistry': '--harmony-weak-refs-with-cleanup-some', 'FinalizationRegistry': '--harmony-weak-refs-with-cleanup-some',
'WeakRef': '--harmony-weak-refs-with-cleanup-some', 'WeakRef': '--harmony-weak-refs-with-cleanup-some',

View File

@ -371,76 +371,76 @@ KNOWN_MAPS = {
("read_only_space", 0x033d9): (131, "BasicBlockCountersMarkerMap"), ("read_only_space", 0x033d9): (131, "BasicBlockCountersMarkerMap"),
("read_only_space", 0x0341d): (147, "ArrayBoilerplateDescriptionMap"), ("read_only_space", 0x0341d): (147, "ArrayBoilerplateDescriptionMap"),
("read_only_space", 0x0351d): (161, "InterceptorInfoMap"), ("read_only_space", 0x0351d): (161, "InterceptorInfoMap"),
("read_only_space", 0x05e25): (132, "PromiseFulfillReactionJobTaskMap"), ("read_only_space", 0x05e39): (132, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x05e4d): (133, "PromiseRejectReactionJobTaskMap"), ("read_only_space", 0x05e61): (133, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x05e75): (134, "CallableTaskMap"), ("read_only_space", 0x05e89): (134, "CallableTaskMap"),
("read_only_space", 0x05e9d): (135, "CallbackTaskMap"), ("read_only_space", 0x05eb1): (135, "CallbackTaskMap"),
("read_only_space", 0x05ec5): (136, "PromiseResolveThenableJobTaskMap"), ("read_only_space", 0x05ed9): (136, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x05eed): (139, "FunctionTemplateInfoMap"), ("read_only_space", 0x05f01): (139, "FunctionTemplateInfoMap"),
("read_only_space", 0x05f15): (140, "ObjectTemplateInfoMap"), ("read_only_space", 0x05f29): (140, "ObjectTemplateInfoMap"),
("read_only_space", 0x05f3d): (141, "AccessCheckInfoMap"), ("read_only_space", 0x05f51): (141, "AccessCheckInfoMap"),
("read_only_space", 0x05f65): (142, "AccessorInfoMap"), ("read_only_space", 0x05f79): (142, "AccessorInfoMap"),
("read_only_space", 0x05f8d): (143, "AccessorPairMap"), ("read_only_space", 0x05fa1): (143, "AccessorPairMap"),
("read_only_space", 0x05fb5): (144, "AliasedArgumentsEntryMap"), ("read_only_space", 0x05fc9): (144, "AliasedArgumentsEntryMap"),
("read_only_space", 0x05fdd): (145, "AllocationMementoMap"), ("read_only_space", 0x05ff1): (145, "AllocationMementoMap"),
("read_only_space", 0x06005): (148, "AsmWasmDataMap"), ("read_only_space", 0x06019): (148, "AsmWasmDataMap"),
("read_only_space", 0x0602d): (149, "AsyncGeneratorRequestMap"), ("read_only_space", 0x06041): (149, "AsyncGeneratorRequestMap"),
("read_only_space", 0x06055): (150, "BreakPointMap"), ("read_only_space", 0x06069): (150, "BreakPointMap"),
("read_only_space", 0x0607d): (151, "BreakPointInfoMap"), ("read_only_space", 0x06091): (151, "BreakPointInfoMap"),
("read_only_space", 0x060a5): (152, "CachedTemplateObjectMap"), ("read_only_space", 0x060b9): (152, "CachedTemplateObjectMap"),
("read_only_space", 0x060cd): (154, "CallSiteInfoMap"), ("read_only_space", 0x060e1): (154, "CallSiteInfoMap"),
("read_only_space", 0x060f5): (155, "ClassPositionsMap"), ("read_only_space", 0x06109): (155, "ClassPositionsMap"),
("read_only_space", 0x0611d): (156, "DebugInfoMap"), ("read_only_space", 0x06131): (156, "DebugInfoMap"),
("read_only_space", 0x06145): (158, "ErrorStackDataMap"), ("read_only_space", 0x06159): (158, "ErrorStackDataMap"),
("read_only_space", 0x0616d): (160, "FunctionTemplateRareDataMap"), ("read_only_space", 0x06181): (160, "FunctionTemplateRareDataMap"),
("read_only_space", 0x06195): (162, "InterpreterDataMap"), ("read_only_space", 0x061a9): (162, "InterpreterDataMap"),
("read_only_space", 0x061bd): (163, "ModuleRequestMap"), ("read_only_space", 0x061d1): (163, "ModuleRequestMap"),
("read_only_space", 0x061e5): (164, "PromiseCapabilityMap"), ("read_only_space", 0x061f9): (164, "PromiseCapabilityMap"),
("read_only_space", 0x0620d): (165, "PromiseReactionMap"), ("read_only_space", 0x06221): (165, "PromiseReactionMap"),
("read_only_space", 0x06235): (166, "PropertyDescriptorObjectMap"), ("read_only_space", 0x06249): (166, "PropertyDescriptorObjectMap"),
("read_only_space", 0x0625d): (167, "PrototypeInfoMap"), ("read_only_space", 0x06271): (167, "PrototypeInfoMap"),
("read_only_space", 0x06285): (168, "RegExpBoilerplateDescriptionMap"), ("read_only_space", 0x06299): (168, "RegExpBoilerplateDescriptionMap"),
("read_only_space", 0x062ad): (169, "ScriptMap"), ("read_only_space", 0x062c1): (169, "ScriptMap"),
("read_only_space", 0x062d5): (170, "ScriptOrModuleMap"), ("read_only_space", 0x062e9): (170, "ScriptOrModuleMap"),
("read_only_space", 0x062fd): (171, "SourceTextModuleInfoEntryMap"), ("read_only_space", 0x06311): (171, "SourceTextModuleInfoEntryMap"),
("read_only_space", 0x06325): (172, "StackFrameInfoMap"), ("read_only_space", 0x06339): (172, "StackFrameInfoMap"),
("read_only_space", 0x0634d): (173, "TemplateObjectDescriptionMap"), ("read_only_space", 0x06361): (173, "TemplateObjectDescriptionMap"),
("read_only_space", 0x06375): (174, "Tuple2Map"), ("read_only_space", 0x06389): (174, "Tuple2Map"),
("read_only_space", 0x0639d): (175, "WasmContinuationObjectMap"), ("read_only_space", 0x063b1): (175, "WasmContinuationObjectMap"),
("read_only_space", 0x063c5): (176, "WasmExceptionTagMap"), ("read_only_space", 0x063d9): (176, "WasmExceptionTagMap"),
("read_only_space", 0x063ed): (177, "WasmIndirectFunctionTableMap"), ("read_only_space", 0x06401): (177, "WasmIndirectFunctionTableMap"),
("read_only_space", 0x06415): (196, "SloppyArgumentsElementsMap"), ("read_only_space", 0x06429): (196, "SloppyArgumentsElementsMap"),
("read_only_space", 0x0643d): (231, "DescriptorArrayMap"), ("read_only_space", 0x06451): (231, "DescriptorArrayMap"),
("read_only_space", 0x06465): (219, "UncompiledDataWithoutPreparseDataMap"), ("read_only_space", 0x06479): (219, "UncompiledDataWithoutPreparseDataMap"),
("read_only_space", 0x0648d): (217, "UncompiledDataWithPreparseDataMap"), ("read_only_space", 0x064a1): (217, "UncompiledDataWithPreparseDataMap"),
("read_only_space", 0x064b5): (220, "UncompiledDataWithoutPreparseDataWithJobMap"), ("read_only_space", 0x064c9): (220, "UncompiledDataWithoutPreparseDataWithJobMap"),
("read_only_space", 0x064dd): (218, "UncompiledDataWithPreparseDataAndJobMap"), ("read_only_space", 0x064f1): (218, "UncompiledDataWithPreparseDataAndJobMap"),
("read_only_space", 0x06505): (250, "OnHeapBasicBlockProfilerDataMap"), ("read_only_space", 0x06519): (250, "OnHeapBasicBlockProfilerDataMap"),
("read_only_space", 0x0652d): (197, "TurbofanBitsetTypeMap"), ("read_only_space", 0x06541): (197, "TurbofanBitsetTypeMap"),
("read_only_space", 0x06555): (201, "TurbofanUnionTypeMap"), ("read_only_space", 0x06569): (201, "TurbofanUnionTypeMap"),
("read_only_space", 0x0657d): (200, "TurbofanRangeTypeMap"), ("read_only_space", 0x06591): (200, "TurbofanRangeTypeMap"),
("read_only_space", 0x065a5): (198, "TurbofanHeapConstantTypeMap"), ("read_only_space", 0x065b9): (198, "TurbofanHeapConstantTypeMap"),
("read_only_space", 0x065cd): (199, "TurbofanOtherNumberConstantTypeMap"), ("read_only_space", 0x065e1): (199, "TurbofanOtherNumberConstantTypeMap"),
("read_only_space", 0x065f5): (246, "InternalClassMap"), ("read_only_space", 0x06609): (246, "InternalClassMap"),
("read_only_space", 0x0661d): (257, "SmiPairMap"), ("read_only_space", 0x06631): (257, "SmiPairMap"),
("read_only_space", 0x06645): (256, "SmiBoxMap"), ("read_only_space", 0x06659): (256, "SmiBoxMap"),
("read_only_space", 0x0666d): (225, "ExportedSubClassBaseMap"), ("read_only_space", 0x06681): (225, "ExportedSubClassBaseMap"),
("read_only_space", 0x06695): (226, "ExportedSubClassMap"), ("read_only_space", 0x066a9): (226, "ExportedSubClassMap"),
("read_only_space", 0x066bd): (202, "AbstractInternalClassSubclass1Map"), ("read_only_space", 0x066d1): (202, "AbstractInternalClassSubclass1Map"),
("read_only_space", 0x066e5): (203, "AbstractInternalClassSubclass2Map"), ("read_only_space", 0x066f9): (203, "AbstractInternalClassSubclass2Map"),
("read_only_space", 0x0670d): (195, "InternalClassWithSmiElementsMap"), ("read_only_space", 0x06721): (195, "InternalClassWithSmiElementsMap"),
("read_only_space", 0x06735): (247, "InternalClassWithStructElementsMap"), ("read_only_space", 0x06749): (247, "InternalClassWithStructElementsMap"),
("read_only_space", 0x0675d): (227, "ExportedSubClass2Map"), ("read_only_space", 0x06771): (227, "ExportedSubClass2Map"),
("read_only_space", 0x06785): (258, "SortStateMap"), ("read_only_space", 0x06799): (258, "SortStateMap"),
("read_only_space", 0x067ad): (146, "AllocationSiteWithWeakNextMap"), ("read_only_space", 0x067c1): (146, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x067d5): (146, "AllocationSiteWithoutWeakNextMap"), ("read_only_space", 0x067e9): (146, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x067fd): (137, "LoadHandler1Map"), ("read_only_space", 0x06811): (137, "LoadHandler1Map"),
("read_only_space", 0x06825): (137, "LoadHandler2Map"), ("read_only_space", 0x06839): (137, "LoadHandler2Map"),
("read_only_space", 0x0684d): (137, "LoadHandler3Map"), ("read_only_space", 0x06861): (137, "LoadHandler3Map"),
("read_only_space", 0x06875): (138, "StoreHandler0Map"), ("read_only_space", 0x06889): (138, "StoreHandler0Map"),
("read_only_space", 0x0689d): (138, "StoreHandler1Map"), ("read_only_space", 0x068b1): (138, "StoreHandler1Map"),
("read_only_space", 0x068c5): (138, "StoreHandler2Map"), ("read_only_space", 0x068d9): (138, "StoreHandler2Map"),
("read_only_space", 0x068ed): (138, "StoreHandler3Map"), ("read_only_space", 0x06901): (138, "StoreHandler3Map"),
("map_space", 0x02149): (1057, "ExternalMap"), ("map_space", 0x02149): (1057, "ExternalMap"),
("map_space", 0x02171): (2114, "JSMessageObjectMap"), ("map_space", 0x02171): (2114, "JSMessageObjectMap"),
} }