diff --git a/src/builtins/builtins-intl.cc b/src/builtins/builtins-intl.cc index 1d72a3ae32..fa6ee0a155 100644 --- a/src/builtins/builtins-intl.cc +++ b/src/builtins/builtins-intl.cc @@ -85,8 +85,15 @@ BUILTIN(NumberFormatPrototypeFormatToParts) { Handle x; if (args.length() >= 2) { - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x, - Object::ToNumeric(isolate, args.at(1))); + Handle 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, + Object::ToNumeric(isolate, value)); + } } else { x = isolate->factory()->nan_value(); } @@ -501,8 +508,14 @@ BUILTIN(NumberFormatInternalFormatNumber) { // 4. Let x be ? ToNumeric(value). Handle numeric_obj; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, numeric_obj, - Object::ToNumeric(isolate, value)); + 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, + Object::ToNumeric(isolate, value)); + } icu::number::LocalizedNumberFormatter* icu_localized_number_formatter = number_format->icu_number_formatter().raw(); @@ -902,20 +915,18 @@ BUILTIN(PluralRulesPrototypeResolvedOptions) { BUILTIN(PluralRulesPrototypeSelect) { HandleScope scope(isolate); - // 1. Let pr be the this value. - // 2. If Type(pr) is not Object, throw a TypeError exception. - // 3. If pr does not have an [[InitializedPluralRules]] internal slot, throw a - // TypeError exception. + // 1. 1. Let pr be the this value. + // 2. Perform ? RequireInternalSlot(pr, [[InitializedPluralRules]]). CHECK_RECEIVER(JSPluralRules, plural_rules, "Intl.PluralRules.prototype.select"); - // 4. Let n be ? ToNumber(value). + // 3. Let n be ? ToNumber(value). Handle number = args.atOrUndefined(isolate, 1); ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number, Object::ToNumber(isolate, number)); double number_double = number->Number(); - // 5. Return ? ResolvePlural(pr, n). + // 4. Return ! ResolvePlural(pr, n). RETURN_RESULT_OR_FAILURE(isolate, JSPluralRules::ResolvePlural( isolate, plural_rules, number_double)); } diff --git a/src/flags/flag-definitions.h b/src/flags/flag-definitions.h index ed361e3357..81d8a3cfa9 100644 --- a/src/flags/flag-definitions.h +++ b/src/flags/flag-definitions.h @@ -311,7 +311,9 @@ DEFINE_BOOL(harmony_shipping, true, "enable all shipped harmony features") V(harmony_array_grouping, "harmony array grouping") #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 #define HARMONY_INPROGRESS(V) HARMONY_INPROGRESS_BASE(V) #endif diff --git a/src/init/bootstrapper.cc b/src/init/bootstrapper.cc index 8d07fa80cc..8690c64f92 100644 --- a/src/init/bootstrapper.cc +++ b/src/init/bootstrapper.cc @@ -4404,6 +4404,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_error_cause) #ifdef V8_INTL_SUPPORT EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_intl_best_fit_matcher) +EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_intl_number_format_v3) #endif // V8_INTL_SUPPORT #undef EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE diff --git a/src/init/heap-symbols.h b/src/init/heap-symbols.h index 249f741f41..f1d2786dc1 100644 --- a/src/init/heap-symbols.h +++ b/src/init/heap-symbols.h @@ -78,6 +78,7 @@ V(_, minusSign_string, "minusSign") \ V(_, nan_string, "nan") \ V(_, narrowSymbol_string, "narrowSymbol") \ + V(_, negative_string, "negative") \ V(_, never_string, "never") \ V(_, none_string, "none") \ V(_, notation_string, "notation") \ diff --git a/src/objects/intl-objects.cc b/src/objects/intl-objects.cc index cf3985ecc6..0f10e000e7 100644 --- a/src/objects/intl-objects.cc +++ b/src/objects/intl-objects.cc @@ -1522,151 +1522,195 @@ Maybe Intl::SetNumberFormatDigitOptions( return Nothing(); } - int mnfd = 0; - int mxfd = 0; - Handle mnfd_obj; - Handle mxfd_obj; - // 6. Let mnfd be ? Get(options, "minimumFractionDigits"). - Handle mnfd_str = factory->minimumFractionDigits_string(); + Handle mnfd_obj; 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()); - // 8. Let mxfd be ? Get(options, "maximumFractionDigits"). - Handle mxfd_str = factory->maximumFractionDigits_string(); + // 7. Let mxfd be ? Get(options, "maximumFractionDigits"). + Handle mxfd_obj; 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()); - // 9. Let mnsd be ? Get(options, "minimumSignificantDigits"). + // 8. Let mnsd be ? Get(options, "minimumSignificantDigits"). Handle mnsd_obj; - Handle mnsd_str = factory->minimumSignificantDigits_string(); 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()); - // 10. Let mxsd be ? Get(options, "maximumSignificantDigits"). + // 9. Let mxsd be ? Get(options, "maximumSignificantDigits"). Handle mxsd_obj; - Handle mxsd_str = factory->maximumSignificantDigits_string(); 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()); - // 11. Set intlObj.[[MinimumIntegerDigits]] to mnid. + digit_options.rounding_priority = RoundingPriority::kAuto; + digit_options.minimum_significant_digits = 0; + digit_options.maximum_significant_digits = 0; + + // 10. Set intlObj.[[MinimumIntegerDigits]] to mnid. digit_options.minimum_integer_digits = mnid; - // 12. Set intlObj.[[MinimumFractionDigits]] to mnfd. - digit_options.minimum_fraction_digits = mnfd; + if (FLAG_harmony_intl_number_format_v3) { + // 11. Let roundingPriority be ? GetOption(options, "roundingPriority", + // "string", « "auto", "morePrecision", "lessPrecision" », "auto"). - // 13. Set intlObj.[[MaximumFractionDigits]] to mxfd. - digit_options.maximum_fraction_digits = mxfd; + Maybe maybe_rounding_priority = + GetStringOption( + isolate, options, "roundingPriority", "SetNumberFormatDigitOptions", + {"auto", "morePrecision", "lessPrecision"}, + {RoundingPriority::kAuto, RoundingPriority::kMorePrecision, + RoundingPriority::kLessPrecision}, + RoundingPriority::kAuto); + MAYBE_RETURN(maybe_rounding_priority, Nothing()); + digit_options.rounding_priority = maybe_rounding_priority.FromJust(); + } - // 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(); + // 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(); + } + // 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(); + } + // 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; } + } - // 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(); - } - - // 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.maximum_significant_digits = 0; - - // 15. Else If mnfd is not undefined or mxfd is not undefined, then - if (!mnfd_obj->IsUndefined(isolate) || !mxfd_obj->IsUndefined(isolate)) { - int specified_mnfd; - int specified_mxfd; - - // a. Let _specifiedMnfd_ be ? DefaultNumberOption(_mnfd_, 0, 20, - // *undefined*). + // 21. If needFd, then + if (need_fd) { + // 21.a If hasFd, then + if (has_fd) { + Handle mnfd_str = factory->minimumFractionDigits_string(); + Handle 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) - .To(&specified_mnfd)) { - return Nothing(); - } - Handle 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(); - } - Handle 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)) { return Nothing(); } - - // e. Set _mxfd_ to ! DefaultNumberOption(_specifiedMxfd_, 0, 20, - // max(_mxfdDefault_, _mnfd_)). - if (!DefaultNumberOption(isolate, specifiedMxfd_obj, 0, 20, - std::max(mxfd_default, mnfd), mxfd_str) + // 21.a.ii Let mxfd be ? DefaultNumberOption(mxfd, 0, 20, undefined). + int mxfd; + if (!DefaultNumberOption(isolate, mxfd_obj, 0, 20, -1, mxfd_str) .To(&mxfd)) { return Nothing(); } - - // f. If _mnfd_ is greater than _mxfd_, throw a *RangeError* exception. - if (mnfd > mxfd) { + // 21.a.iii If mnfd is undefined, set mnfd to min(mnfdDefault, mxfd). + if (mnfd_obj->IsUndefined(isolate)) { + 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( isolate, NewRangeError(MessageTemplate::kPropertyValueOutOfRange, mxfd_str), Nothing()); } - - // g. Set intlObj.[[MinimumFractionDigits]] to mnfd. + // 21.a.vi Set intlObj.[[MinimumFractionDigits]] to mnfd. digit_options.minimum_fraction_digits = mnfd; - - // h. Set intlObj.[[MaximumFractionDigits]] to mxfd. + // 21.a.vii Set intlObj.[[MaximumFractionDigits]] to mxfd. digit_options.maximum_fraction_digits = mxfd; - // Else If intlObj.[[Notation]] is "compact", then - } else if (notation_is_compact) { - // 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. + } else { // 17.b Else + // 21.b.i Set intlObj.[[MinimumFractionDigits]] to mnfdDefault. digit_options.minimum_fraction_digits = mnfd_default; - - // 17. c. Set intlObj.[[MaximumFractionDigits]] to mxfdDefault. + // 21.b.ii Set intlObj.[[MaximumFractionDigits]] to mxfdDefault. 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); } @@ -2644,22 +2688,22 @@ const std::set& Intl::GetAvailableLocalesForDateFormat() { return available_locales.Pointer()->Get(); } +constexpr uint16_t kInfinityChar = 0x221e; + Handle Intl::NumberFieldToType(Isolate* isolate, - Handle numeric_obj, - int32_t field_id) { - DCHECK(numeric_obj->IsNumeric()); - switch (static_cast(field_id)) { + const NumberFormatSpan& part, + const icu::UnicodeString& text, + bool is_nan) { + switch (static_cast(part.field_id)) { case UNUM_INTEGER_FIELD: - if (numeric_obj->IsBigInt()) { - // Neither NaN nor Infinite could be stored into BigInt - // so just return integer. - return isolate->factory()->integer_string(); - } else { - double number = numeric_obj->Number(); - if (std::isfinite(number)) return isolate->factory()->integer_string(); - if (std::isnan(number)) return isolate->factory()->nan_string(); + if (is_nan) return isolate->factory()->nan_string(); + if (text.charAt(part.begin_pos) == kInfinityChar || + // en-US-POSIX output "INF" for Infinity + (part.end_pos - part.begin_pos == 3 && + text.tempSubString(part.begin_pos, 3) == "INF")) { return isolate->factory()->infinity_string(); } + return isolate->factory()->integer_string(); case UNUM_FRACTION_FIELD: return isolate->factory()->fraction_string(); case UNUM_DECIMAL_SEPARATOR_FIELD: @@ -2671,15 +2715,9 @@ Handle Intl::NumberFieldToType(Isolate* isolate, case UNUM_PERCENT_FIELD: return isolate->factory()->percentSign_string(); case UNUM_SIGN_FIELD: - if (numeric_obj->IsBigInt()) { - Handle big_int = Handle::cast(numeric_obj); - return big_int->IsNegative() ? 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(); - } + return (text.charAt(part.begin_pos) == '+') + ? isolate->factory()->plusSign_string() + : isolate->factory()->minusSign_string(); case UNUM_EXPONENT_SYMBOL_FIELD: return isolate->factory()->exponentSeparator_string(); @@ -2856,5 +2894,29 @@ Maybe Intl::GetTimeZoneIndex(Isolate* isolate, Handle identifier, UNREACHABLE(); } +// #sec-tointlmathematicalvalue +MaybeHandle Intl::ToIntlMathematicalValueAsNumberBigIntOrString( + Isolate* isolate, Handle 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::cast(input)); + } + if (input->IsSymbol()) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToNumber), + Object); + } + ASSIGN_RETURN_ON_EXCEPTION( + isolate, input, + JSReceiver::ToPrimitive(Handle::cast(input), + ToPrimitiveHint::kNumber), + Object); + return input; +} + } // namespace internal } // namespace v8 diff --git a/src/objects/intl-objects.h b/src/objects/intl-objects.h index 2d38231744..e9b2537b06 100644 --- a/src/objects/intl-objects.h +++ b/src/objects/intl-objects.h @@ -35,6 +35,19 @@ class UnicodeString; namespace v8 { 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 FlattenRegionsToParts( + std::vector* regions); + template class Handle; class JSCollator; @@ -115,6 +128,21 @@ class Intl { Isolate* isolate, Handle num, Handle locales, Handle 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 struct NumberFormatDigitOptions { int minimum_integer_digits; @@ -122,6 +150,8 @@ class Intl { int maximum_fraction_digits; int minimum_significant_digits; int maximum_significant_digits; + RoundingPriority rounding_priority; + RoundingType rounding_type; }; V8_WARN_UNUSED_RESULT static Maybe SetNumberFormatDigitOptions(Isolate* isolate, Handle options, @@ -143,8 +173,9 @@ class Intl { // Helper function to convert number field id to type string. static Handle NumberFieldToType(Isolate* isolate, - Handle numeric_obj, - int32_t field_id); + const NumberFormatSpan& part, + const icu::UnicodeString& text, + bool is_nan); // A helper function to implement formatToParts which add element to array as // $array[$index] = { type: $field_type_string, value: $value } @@ -312,6 +343,16 @@ class Intl { V8_WARN_UNUSED_RESULT static MaybeHandle CanonicalizeTimeZoneName( Isolate* isolate, Handle identifier); + + // ecma402/#sec-coerceoptionstoobject + V8_WARN_UNUSED_RESULT static MaybeHandle CoerceOptionsToObject( + Isolate* isolate, Handle options, const char* service); + + // #sec-tointlmathematicalvalue + // The implementation preserve the Object in String, BigInt or Number + V8_WARN_UNUSED_RESULT static MaybeHandle + ToIntlMathematicalValueAsNumberBigIntOrString(Isolate* isolate, + Handle input); }; } // namespace internal diff --git a/src/objects/js-number-format.cc b/src/objects/js-number-format.cc index cc337a0df2..2d50d8e25f 100644 --- a/src/objects/js-number-format.cc +++ b/src/objects/js-number-format.cc @@ -18,10 +18,8 @@ #include "src/objects/objects-inl.h" #include "src/objects/option-utils.h" #include "unicode/currunit.h" -#include "unicode/decimfmt.h" #include "unicode/locid.h" #include "unicode/numberformatter.h" -#include "unicode/numfmt.h" #include "unicode/numsys.h" #include "unicode/ucurr.h" #include "unicode/uloc.h" @@ -98,6 +96,38 @@ enum class SignDisplay { ALWAYS, NEVER, 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) { @@ -147,6 +177,12 @@ UNumberSignDisplay ToUNumberSignDisplay(SignDisplay sign_display, } DCHECK(currency_sign == CurrencySign::STANDARD); 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 CreateUnitMap() { UErrorCode status = U_ZERO_ERROR; int32_t total = icu::MeasureUnit::getAvailable(nullptr, 0, status); @@ -462,6 +535,13 @@ Handle SignDisplayString(Isolate* isolate, skeleton.indexOf("sign-except-zero") >= 0) { 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(); } @@ -505,7 +585,7 @@ bool JSNumberFormat::FractionDigitsFromSkeleton( if (index < 0) return false; *minimum = 0; index++; // skip the '.' - while (index < skeleton.length() && skeleton[index] == '0') { + while (index < skeleton.length() && IsDecimalDigit(skeleton[index])) { (*minimum)++; index++; } @@ -607,21 +687,16 @@ Style StyleFromSkeleton(const icu::UnicodeString& skeleton) { return Style::DECIMAL; } -} // anonymous namespace - -icu::number::LocalizedNumberFormatter -JSNumberFormat::SetDigitOptionsToFormatter( - const icu::number::LocalizedNumberFormatter& icu_number_formatter, +icu::number::UnlocalizedNumberFormatter SetDigitOptionsToFormatterV2( + const icu::number::UnlocalizedNumberFormatter& settings, const Intl::NumberFormatDigitOptions& digit_options) { - icu::number::LocalizedNumberFormatter result = icu_number_formatter; + icu::number::UnlocalizedNumberFormatter result = settings; if (digit_options.minimum_integer_digits > 1) { result = result.integerWidth(icu::number::IntegerWidth::zeroFillTo( digit_options.minimum_integer_digits)); } - // Value -1 of minimum_significant_digits represent the roundingtype is - // "compact-rounding". - if (digit_options.minimum_significant_digits < 0) { + if (digit_options.rounding_type == Intl::RoundingType::kMorePrecision) { return result; } icu::number::Precision precision = @@ -636,6 +711,70 @@ JSNumberFormat::SetDigitOptionsToFormatter( 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 // ecma402 #sec-intl.numberformat.prototype.resolvedoptions Handle JSNumberFormat::ResolvedOptions( @@ -662,12 +801,19 @@ Handle JSNumberFormat::ResolvedOptions( // [[Style]] "style" // [[Currency]] "currency" // [[CurrencyDisplay]] "currencyDisplay" + // [[CurrencySign]] "currencySign" + // [[Unit]] "unit" + // [[UnitDisplay]] "unitDisplay" // [[MinimumIntegerDigits]] "minimumIntegerDigits" // [[MinimumFractionDigits]] "minimumFractionDigits" // [[MaximumFractionDigits]] "maximumFractionDigits" // [[MinimumSignificantDigits]] "minimumSignificantDigits" // [[MaximumSignificantDigits]] "maximumSignificantDigits" // [[UseGrouping]] "useGrouping" + // [[Notation]] "notation" + // [[CompactDisplay]] "compactDisplay" + // [[SignDisplay]] "signDisplay" + CHECK(JSReceiver::CreateDataProperty(isolate, options, factory->locale_string(), locale, Just(kDontThrow)) @@ -752,6 +898,7 @@ Handle JSNumberFormat::ResolvedOptions( factory->ToBoolean(UseGroupingFromSkeleton(skeleton)), Just(kDontThrow)) .FromJust()); + Notation notation = NotationFromSkeleton(skeleton); CHECK(JSReceiver::CreateDataProperty( isolate, options, factory->notation_string(), @@ -800,6 +947,14 @@ MaybeHandle JSNumberFormat::UnwrapNumberFormat( return Handle::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 MaybeHandle JSNumberFormat::New(Isolate* isolate, Handle map, @@ -821,28 +976,28 @@ MaybeHandle JSNumberFormat::New(Isolate* isolate, isolate, options, CoerceOptionsToObject(isolate, options_obj, service), JSNumberFormat); - // 4. Let opt be a new Record. - // 5. Let matcher be ? GetOption(options, "localeMatcher", "string", « + // 3. Let opt be a new Record. + // 4. Let matcher be ? GetOption(options, "localeMatcher", "string", « // "lookup", "best fit" », "best fit"). - // 6. Set opt.[[localeMatcher]] to matcher. + // 5. Set opt.[[localeMatcher]] to matcher. Maybe maybe_locale_matcher = Intl::GetLocaleMatcher(isolate, options, service); MAYBE_RETURN(maybe_locale_matcher, MaybeHandle()); Intl::MatcherOption matcher = maybe_locale_matcher.FromJust(); std::unique_ptr numbering_system_str = nullptr; - // 7. Let _numberingSystem_ be ? GetOption(_options_, `"numberingSystem"`, + // 6. Let _numberingSystem_ be ? GetOption(_options_, `"numberingSystem"`, // `"string"`, *undefined*, *undefined*). Maybe maybe_numberingSystem = Intl::GetNumberingSystem( isolate, options, service, &numbering_system_str); - // 8. If _numberingSystem_ is not *undefined*, then - // a. If _numberingSystem_ does not match the + // 7. If _numberingSystem_ is not *undefined*, then + // 8. If _numberingSystem_ does not match the // `(3*8alphanum) *("-" (3*8alphanum))` sequence, throw a *RangeError* // exception. MAYBE_RETURN(maybe_numberingSystem, MaybeHandle()); - // 7. Let localeData be %NumberFormat%.[[LocaleData]]. - // 8. Let r be ResolveLocale(%NumberFormat%.[[AvailableLocales]], + // 9. Let localeData be %NumberFormat%.[[LocaleData]]. + // 10. Let r be ResolveLocale(%NumberFormat%.[[AvailableLocales]], // requestedLocales, opt, %NumberFormat%.[[RelevantExtensionKeys]], // localeData). std::set relevant_extension_keys{"nu"}; @@ -882,21 +1037,20 @@ MaybeHandle JSNumberFormat::New(Isolate* isolate, // 11. Let dataLocale be r.[[dataLocale]]. - icu::number::LocalizedNumberFormatter icu_number_formatter = - icu::number::NumberFormatter::withLocale(icu_locale) - .roundingMode(UNUM_ROUND_HALFUP); + icu::number::UnlocalizedNumberFormatter settings = + icu::number::UnlocalizedNumberFormatter().roundingMode(UNUM_ROUND_HALFUP); // For 'latn' numbering system, skip the adoptSymbols which would cause // 10.1%-13.7% of regression of JSTests/Intl-NewIntlNumberFormat // See crbug/1052751 so we skip calling adoptSymbols and depending on the // default instead. if (!numbering_system.empty() && numbering_system != "latn") { - icu_number_formatter = icu_number_formatter.adoptSymbols( - icu::NumberingSystem::createInstanceByName(numbering_system.c_str(), - status)); + settings = settings.adoptSymbols(icu::NumberingSystem::createInstanceByName( + numbering_system.c_str(), status)); CHECK(U_SUCCESS(status)); } + // ==== Start SetNumberFormatUnitOptions ==== // 3. Let style be ? GetOption(options, "style", "string", « "decimal", // "percent", "currency", "unit" », "decimal"). @@ -1029,15 +1183,14 @@ MaybeHandle JSNumberFormat::New(Isolate* isolate, Intl::ToString(isolate, currency_ustr), JSNumberFormat); - icu_number_formatter = icu_number_formatter.unit( - icu::CurrencyUnit(currency_ustr.getBuffer(), status)); + settings = + settings.unit(icu::CurrencyUnit(currency_ustr.getBuffer(), status)); CHECK(U_SUCCESS(status)); // 14.c Set intlObj.[[CurrencyDisplay]] to currencyDisplay. // The default unitWidth is SHORT in ICU and that mapped from // Symbol so we can skip the setting for optimization. if (currency_display != CurrencyDisplay::SYMBOL) { - icu_number_formatter = icu_number_formatter.unitWidth( - ToUNumberUnitWidth(currency_display)); + settings = settings.unitWidth(ToUNumberUnitWidth(currency_display)); } CHECK(U_SUCCESS(status)); } @@ -1051,27 +1204,27 @@ MaybeHandle JSNumberFormat::New(Isolate* isolate, icu::MeasureUnit none = icu::MeasureUnit(); // 13.b Set intlObj.[[Unit]] to unit. 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) { - 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 // Symbol so we can skip the setting for optimization. if (unit_display != UnitDisplay::SHORT) { - icu_number_formatter = - icu_number_formatter.unitWidth(ToUNumberUnitWidth(unit_display)); + settings = settings.unitWidth(ToUNumberUnitWidth(unit_display)); } } + // === End of SetNumberFormatUnitOptions + if (style == Style::PERCENT) { - icu_number_formatter = - icu_number_formatter.unit(icu::MeasureUnit::getPercent()) - .scale(icu::number::Scale::powerOfTen(2)); + settings = settings.unit(icu::MeasureUnit::getPercent()) + .scale(icu::number::Scale::powerOfTen(2)); } - // 23. If style is "currency", then + // 16. If style is "currency", then int mnfd_default, mxfd_default; if (style == Style::CURRENCY) { // b. Let cDigits be CurrencyDigits(currency). @@ -1080,7 +1233,7 @@ MaybeHandle JSNumberFormat::New(Isolate* isolate, // d. Let mxfdDefault be cDigits. mnfd_default = c_digits; mxfd_default = c_digits; - // 24. Else, + // 17. Else, } else { // a. Let mnfdDefault be 0. mnfd_default = 0; @@ -1096,7 +1249,7 @@ MaybeHandle JSNumberFormat::New(Isolate* isolate, } 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"). Maybe maybe_notation = GetStringOption( isolate, options, "notation", service, @@ -1105,9 +1258,10 @@ MaybeHandle JSNumberFormat::New(Isolate* isolate, Notation::COMPACT}, Notation::STANDARD); MAYBE_RETURN(maybe_notation, MaybeHandle()); + // 19. Set numberFormat.[[Notation]] to notation. notation = maybe_notation.FromJust(); - // 27. Perform ? SetNumberFormatDigitOptions(numberFormat, options, + // 20. Perform ? SetNumberFormatDigitOptions(numberFormat, options, // mnfdDefault, mxfdDefault). Maybe maybe_digit_options = Intl::SetNumberFormatDigitOptions(isolate, options, mnfd_default, @@ -1115,10 +1269,58 @@ MaybeHandle JSNumberFormat::New(Isolate* isolate, notation == Notation::COMPACT); MAYBE_RETURN(maybe_digit_options, Handle()); 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 maybe_rounding_increment = GetNumberOption( + isolate, options, factory->roundingIncrement_string(), 1, 5000, 1); + MAYBE_RETURN(maybe_rounding_increment, MaybeHandle()); + 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 maybe_trailing_zero_display = + GetStringOption( + isolate, options, "trailingZeroDisplay", service, + {"auto", "stripIfInteger"}, + {TrailingZeroDisplay::AUTO, TrailingZeroDisplay::STRIP_IF_INTEGER}, + TrailingZeroDisplay::AUTO); + MAYBE_RETURN(maybe_trailing_zero_display, MaybeHandle()); + 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"). Maybe maybe_compact_display = GetStringOption( isolate, options, "compactDisplay", service, {"short", "long"}, @@ -1126,33 +1328,73 @@ MaybeHandle JSNumberFormat::New(Isolate* isolate, MAYBE_RETURN(maybe_compact_display, MaybeHandle()); CompactDisplay compact_display = maybe_compact_display.FromJust(); - // 26. Set numberFormat.[[Notation]] to notation. // The default notation in ICU is Simple, which mapped from STANDARD // so we can skip setting it. if (notation != Notation::STANDARD) { - icu_number_formatter = - icu_number_formatter.notation(ToICUNotation(notation, compact_display)); + settings = settings.notation(ToICUNotation(notation, compact_display)); } - // 30. Let useGrouping be ? GetOption(options, "useGrouping", "boolean", - // undefined, true). - bool use_grouping = true; - Maybe found_use_grouping = - GetBoolOption(isolate, options, "useGrouping", service, &use_grouping); - MAYBE_RETURN(found_use_grouping, MaybeHandle()); - // 31. Set numberFormat.[[UseGrouping]] to useGrouping. - if (!use_grouping) { - icu_number_formatter = icu_number_formatter.grouping( - UNumberGroupingStrategy::UNUM_GROUPING_OFF); + + if (!FLAG_harmony_intl_number_format_v3) { + // 30. Let useGrouping be ? GetOption(options, "useGrouping", "boolean", + // undefined, true). + bool use_grouping = true; + Maybe found_use_grouping = + GetBoolOption(isolate, options, "useGrouping", service, &use_grouping); + MAYBE_RETURN(found_use_grouping, MaybeHandle()); + // 31. Set numberFormat.[[UseGrouping]] to useGrouping. + if (!use_grouping) { + settings = settings.grouping(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 maybe_use_grouping = + GetStringOrBooleanOption( + 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()); + 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", « - // "auto", "never", "always", "exceptZero" », "auto"). - Maybe maybe_sign_display = GetStringOption( - isolate, options, "signDisplay", service, - {"auto", "never", "always", "exceptZero"}, - {SignDisplay::AUTO, SignDisplay::NEVER, SignDisplay::ALWAYS, - SignDisplay::EXCEPT_ZERO}, - SignDisplay::AUTO); + // "auto", "never", "always", "exceptZero", "negative" », "auto"). + Maybe maybe_sign_display = Nothing(); + if (FLAG_harmony_intl_number_format_v3) { + maybe_sign_display = GetStringOption( + 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( + isolate, options, "signDisplay", service, + {"auto", "never", "always", "exceptZero"}, + {SignDisplay::AUTO, SignDisplay::NEVER, SignDisplay::ALWAYS, + SignDisplay::EXCEPT_ZERO}, + SignDisplay::AUTO); + } MAYBE_RETURN(maybe_sign_display, MaybeHandle()); SignDisplay sign_display = maybe_sign_display.FromJust(); @@ -1162,8 +1404,27 @@ MaybeHandle JSNumberFormat::New(Isolate* isolate, // under that values for optimization. if (sign_display != SignDisplay::AUTO || currency_sign != CurrencySign::STANDARD) { - icu_number_formatter = icu_number_formatter.sign( - ToUNumberSignDisplay(sign_display, currency_sign)); + settings = settings.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 maybe_rounding_mode = GetStringOption( + 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()); + RoundingMode rounding_mode = maybe_rounding_mode.FromJust(); + settings = + settings.roundingMode(ToUNumberFormatRoundingMode(rounding_mode)); } // 25. Let dataLocaleData be localeData.[[]]. @@ -1180,6 +1441,9 @@ MaybeHandle JSNumberFormat::New(Isolate* isolate, // 30. Set numberFormat.[[NegativePattern]] to // stylePatterns.[[negativePattern]]. // + icu::number::LocalizedNumberFormatter icu_number_formatter = + settings.locale(icu_locale); + Handle> managed_number_formatter = Managed::FromRawPtr( @@ -1200,6 +1464,7 @@ MaybeHandle JSNumberFormat::New(Isolate* isolate, } namespace { + Maybe IcuFormatNumber( Isolate* isolate, const icu::number::LocalizedNumberFormatter& number_format, @@ -1212,13 +1477,40 @@ Maybe IcuFormatNumber( ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, big_int_string, BigInt::ToString(isolate, big_int), Nothing()); - *formatted = number_format.formatDecimal( - {big_int_string->ToCString().get(), big_int_string->length()}, status); + 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(flat.ToOneByteVector().begin()); + *formatted = number_format.formatDecimal({char_buffer, length}, status); } else { - double number = numeric_obj->IsNaN() - ? std::numeric_limits::quiet_NaN() - : numeric_obj->Number(); - *formatted = number_format.formatDouble(number, status); + if (FLAG_harmony_intl_number_format_v3 && numeric_obj->IsString()) { + Handle string = + String::Flatten(isolate, Handle::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(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( + {string->ToCString().get(), string->length()}, status); + } + } else { + double number = numeric_obj->IsNaN() + ? std::numeric_limits::quiet_NaN() + : numeric_obj->Number(); + *formatted = number_format.formatDouble(number, status); + } } if (U_FAILURE(status)) { // This happen because of icu data trimming trim out "unit". @@ -1229,28 +1521,6 @@ Maybe IcuFormatNumber( return Just(true); } -} // namespace - -MaybeHandle JSNumberFormat::FormatNumeric( - Isolate* isolate, - const icu::number::LocalizedNumberFormatter& number_format, - Handle numeric_obj) { - DCHECK(numeric_obj->IsNumeric()); - - icu::number::FormattedNumber formatted; - Maybe maybe_format = - IcuFormatNumber(isolate, number_format, numeric_obj, &formatted); - MAYBE_RETURN(maybe_format, Handle()); - 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, const NumberFormatSpan& b) { // Regions that start earlier should be encountered earlier. @@ -1359,17 +1629,15 @@ std::vector FlattenRegionsToParts( } namespace { -Maybe ConstructParts(Isolate* isolate, - icu::number::FormattedNumber* formatted, +Maybe ConstructParts(Isolate* isolate, icu::FormattedValue* formatted, Handle result, int start_index, - Handle numeric_obj, bool style_is_unit) { + bool style_is_unit, bool is_nan) { UErrorCode status = U_ZERO_ERROR; icu::UnicodeString formatted_text = formatted->toString(status); if (U_FAILURE(status)) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewTypeError(MessageTemplate::kIcuError), Nothing()); } - DCHECK(numeric_obj->IsNumeric()); int32_t length = formatted_text.length(); int index = start_index; if (length == 0) return Just(index); @@ -1380,7 +1648,6 @@ Maybe ConstructParts(Isolate* isolate, // 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. regions.push_back(NumberFormatSpan(-1, 0, formatted_text.length())); - { icu::ConstrainedFieldPosition cfp; cfp.constrainCategory(UFIELD_CATEGORY_NUMBER); @@ -1402,7 +1669,7 @@ Maybe ConstructParts(Isolate* isolate, field_type_string = isolate->factory()->unit_string(); } else { field_type_string = - Intl::NumberFieldToType(isolate, numeric_obj, part.field_id); + Intl::NumberFieldToType(isolate, part, formatted_text, is_nan); } } Handle substring; @@ -1410,6 +1677,7 @@ Maybe ConstructParts(Isolate* isolate, isolate, substring, Intl::ToString(isolate, formatted_text, part.begin_pos, part.end_pos), Nothing()); + Intl::AddElement(isolate, result, index, field_type_string, substring); ++index; } @@ -1417,13 +1685,54 @@ Maybe ConstructParts(Isolate* isolate, return Just(index); } +MaybeHandle 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 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 result = factory->NewJSArray(0); + Maybe maybe_format_to_parts = + ConstructParts(isolate, formatted, result, 0, is_unit, is_nan); + MAYBE_RETURN(maybe_format_to_parts, Handle()); + return result; +} + } // namespace +MaybeHandle JSNumberFormat::FormatNumeric( + Isolate* isolate, + const icu::number::LocalizedNumberFormatter& number_format, + Handle numeric_obj) { + DCHECK(numeric_obj->IsNumeric() || FLAG_harmony_intl_number_format_v3); + + icu::number::FormattedNumber formatted; + Maybe maybe_format = + IcuFormatNumber(isolate, number_format, numeric_obj, &formatted); + MAYBE_RETURN(maybe_format, Handle()); + + return FormatToString(isolate, &formatted, &number_format, + numeric_obj->IsNaN()); +} + MaybeHandle JSNumberFormat::FormatToParts( Isolate* isolate, Handle number_format, Handle numeric_obj) { - CHECK(numeric_obj->IsNumeric()); - Factory* factory = isolate->factory(); + CHECK(numeric_obj->IsNumeric() || FLAG_harmony_intl_number_format_v3); icu::number::LocalizedNumberFormatter* fmt = number_format->icu_number_formatter().raw(); CHECK_NOT_NULL(fmt); @@ -1432,18 +1741,8 @@ MaybeHandle JSNumberFormat::FormatToParts( Maybe maybe_format = IcuFormatNumber(isolate, *fmt, numeric_obj, &formatted); MAYBE_RETURN(maybe_format, Handle()); - UErrorCode status = U_ZERO_ERROR; - bool style_is_unit = - Style::UNIT == StyleFromSkeleton(fmt->toSkeleton(status)); - CHECK(U_SUCCESS(status)); - - Handle result = factory->NewJSArray(0); - Maybe maybe_format_to_parts = ConstructParts( - isolate, &formatted, result, 0, numeric_obj, style_is_unit); - MAYBE_RETURN(maybe_format_to_parts, Handle()); - - return result; + return FormatToJSArray(isolate, &formatted, fmt, numeric_obj->IsNaN()); } namespace { diff --git a/src/objects/js-number-format.h b/src/objects/js-number-format.h index 38710131d6..0707820cd0 100644 --- a/src/objects/js-number-format.h +++ b/src/objects/js-number-format.h @@ -26,8 +26,9 @@ namespace U_ICU_NAMESPACE { class UnicodeString; namespace number { class LocalizedNumberFormatter; -} // namespace number -} // namespace U_ICU_NAMESPACE +class UnlocalizedNumberFormatter; +} // namespace number +} // namespace U_ICU_NAMESPACE namespace v8 { namespace internal { @@ -68,9 +69,13 @@ class JSNumberFormat int32_t* minimum, int32_t* maximum); static bool SignificantDigitsFromSkeleton(const icu::UnicodeString& skeleton, int32_t* minimum, int32_t* maximum); - static icu::number::LocalizedNumberFormatter SetDigitOptionsToFormatter( - const icu::number::LocalizedNumberFormatter& icu_number_formatter, - const Intl::NumberFormatDigitOptions& digit_options); + + enum class ShowTrailingZeros { kShow, kHide }; + + static icu::number::UnlocalizedNumberFormatter SetDigitOptionsToFormatter( + const icu::number::UnlocalizedNumberFormatter& settings, + const Intl::NumberFormatDigitOptions& digit_options, + int rounding_increment, ShowTrailingZeros show); DECL_PRINTER(JSNumberFormat) @@ -80,19 +85,6 @@ class 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 FlattenRegionsToParts( - std::vector* regions); - } // namespace internal } // namespace v8 diff --git a/src/objects/js-plural-rules.cc b/src/objects/js-plural-rules.cc index ec15bd17cd..e4017abfbf 100644 --- a/src/objects/js-plural-rules.cc +++ b/src/objects/js-plural-rules.cc @@ -115,21 +115,19 @@ MaybeHandle JSPluralRules::New(Isolate* isolate, Handle map, Handle locale_str = isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str()); - icu::number::LocalizedNumberFormatter icu_number_formatter = - icu::number::NumberFormatter::withLocale(r.icu_locale) - .roundingMode(UNUM_ROUND_HALFUP); + icu::Locale icu_locale = r.icu_locale; + icu::number::UnlocalizedNumberFormatter settings = + icu::number::UnlocalizedNumberFormatter().roundingMode(UNUM_ROUND_HALFUP); std::unique_ptr icu_plural_rules; bool success = CreateICUPluralRules(isolate, r.icu_locale, type, &icu_plural_rules); if (!success || icu_plural_rules.get() == nullptr) { // 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, &icu_plural_rules); - icu_number_formatter = - icu::number::NumberFormatter::withLocale(no_extension_locale) - .roundingMode(UNUM_ROUND_HALFUP); + icu_locale = no_extension_locale; if (!success || icu_plural_rules.get() == nullptr) { THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError), @@ -142,8 +140,11 @@ MaybeHandle JSPluralRules::New(Isolate* isolate, Handle map, Intl::SetNumberFormatDigitOptions(isolate, options, 0, 3, false); MAYBE_RETURN(maybe_digit_options, MaybeHandle()); Intl::NumberFormatDigitOptions digit_options = maybe_digit_options.FromJust(); - icu_number_formatter = JSNumberFormat::SetDigitOptionsToFormatter( - icu_number_formatter, digit_options); + settings = JSNumberFormat::SetDigitOptionsToFormatter( + settings, digit_options, 1, JSNumberFormat::ShowTrailingZeros::kShow); + + icu::number::LocalizedNumberFormatter icu_number_formatter = + settings.locale(icu_locale); Handle> managed_plural_rules = Managed::FromUniquePtr(isolate, 0, diff --git a/src/objects/js-relative-time-format.cc b/src/objects/js-relative-time-format.cc index d6a65d95ca..23dfa9587d 100644 --- a/src/objects/js-relative-time-format.cc +++ b/src/objects/js-relative-time-format.cc @@ -343,9 +343,9 @@ template MaybeHandle FormatCommon( Isolate* isolate, Handle format, Handle value_obj, Handle unit_obj, const char* func_name, - const std::function< - MaybeHandle(Isolate*, const icu::FormattedRelativeDateTime&, - Handle, Handle)>& formatToResult) { + MaybeHandle (*formatToResult)(Isolate*, + const icu::FormattedRelativeDateTime&, + Handle, bool)) { // 3. Let value be ? ToNumber(value). Handle value; ASSIGN_RETURN_ON_EXCEPTION(isolate, value, @@ -382,13 +382,13 @@ MaybeHandle FormatCommon( if (U_FAILURE(status)) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), T); } - return formatToResult(isolate, formatted, value, - UnitAsString(isolate, unit_enum)); + return formatToResult(isolate, formatted, UnitAsString(isolate, unit_enum), + value->IsNaN()); } MaybeHandle FormatToString( Isolate* isolate, const icu::FormattedRelativeDateTime& formatted, - Handle value, Handle unit) { + Handle unit, bool is_nan) { UErrorCode status = U_ZERO_ERROR; icu::UnicodeString result = formatted.toString(status); if (U_FAILURE(status)) { @@ -411,21 +411,22 @@ Maybe AddLiteral(Isolate* isolate, Handle array, Maybe AddUnit(Isolate* isolate, Handle array, const icu::UnicodeString& string, int32_t index, - int32_t start, int32_t limit, int32_t field_id, - Handle value, Handle unit) { + const NumberFormatSpan& part, Handle unit, + bool is_nan) { Handle substring; 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()); Intl::AddElement(isolate, array, index, - Intl::NumberFieldToType(isolate, value, field_id), substring, - isolate->factory()->unit_string(), unit); + Intl::NumberFieldToType(isolate, part, string, is_nan), + substring, isolate->factory()->unit_string(), unit); return Just(true); } MaybeHandle FormatToJSArray( Isolate* isolate, const icu::FormattedRelativeDateTime& formatted, - Handle value, Handle unit) { + Handle unit, bool is_nan) { UErrorCode status = U_ZERO_ERROR; icu::UnicodeString string = formatted.toString(status); @@ -457,19 +458,23 @@ MaybeHandle FormatToJSArray( for (auto start_limit : groups) { if (start_limit.first > start) { Maybe maybe_added = - AddUnit(isolate, array, string, index++, start, - start_limit.first, field, value, unit); + AddUnit(isolate, array, string, index++, + NumberFormatSpan(field, start, start_limit.first), unit, + is_nan); MAYBE_RETURN(maybe_added, Handle()); - maybe_added = AddUnit(isolate, array, string, index++, - start_limit.first, start_limit.second, - UNUM_GROUPING_SEPARATOR_FIELD, value, unit); + maybe_added = + AddUnit(isolate, array, string, index++, + NumberFormatSpan(UNUM_GROUPING_SEPARATOR_FIELD, + start_limit.first, start_limit.second), + unit, is_nan); MAYBE_RETURN(maybe_added, Handle()); start = start_limit.second; } } } - Maybe maybe_added = AddUnit(isolate, array, string, index++, start, - limit, field, value, unit); + Maybe maybe_added = + AddUnit(isolate, array, string, index++, + NumberFormatSpan(field, start, limit), unit, is_nan); MAYBE_RETURN(maybe_added, Handle()); previous_end = limit; } diff --git a/src/objects/option-utils.h b/src/objects/option-utils.h index 5bb2c35701..f65ab89ece 100644 --- a/src/objects/option-utils.h +++ b/src/objects/option-utils.h @@ -65,6 +65,75 @@ V8_WARN_UNUSED_RESULT static Maybe GetStringOption( 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 +V8_WARN_UNUSED_RESULT static Maybe GetStringOrBooleanOption( + Isolate* isolate, Handle options, const char* property, + const char* method, const std::vector& str_values, + const std::vector& enum_values, T true_value, T false_value, + T fallback_value) { + DCHECK_EQ(str_values.size(), enum_values.size()); + Handle property_str = + isolate->factory()->NewStringFromAsciiChecked(property); + + // 1. Let value be ? Get(options, property). + Handle value; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, value, + Object::GetPropertyOrElement(isolate, options, property_str), + Nothing()); + // 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 value_str; + // 6. Let value be ? ToString(value). + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, value_str, Object::ToString(isolate, value), Nothing()); + // 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(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()); +} + // ECMA402 9.2.10. GetOption( options, property, type, values, fallback) // ecma402/#sec-getoption // diff --git a/test/intl/intl.status b/test/intl/intl.status index 3304965438..6e6eb6a439 100644 --- a/test/intl/intl.status +++ b/test/intl/intl.status @@ -30,6 +30,7 @@ [ALWAYS, { # TODO(ftang,jshin): The following test is flaky. 'overrides/caching': [PASS, FAIL], + 'number-format/rounding-increment-resolved-match-v3': [FAIL], }], # ALWAYS ################################################################################ diff --git a/test/intl/number-format/rounding-increment-resolved-match-v3.js b/test/intl/number-format/rounding-increment-resolved-match-v3.js new file mode 100644 index 0000000000..c76e48954a --- /dev/null +++ b/test/intl/number-format/rounding-increment-resolved-match-v3.js @@ -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); +}); diff --git a/test/intl/number-format/rounding-increment-v3.js b/test/intl/number-format/rounding-increment-v3.js new file mode 100644 index 0000000000..8b30a7a38b --- /dev/null +++ b/test/intl/number-format/rounding-increment-v3.js @@ -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); +}); diff --git a/test/intl/number-format/rounding-increment-value-v3.js b/test/intl/number-format/rounding-increment-value-v3.js new file mode 100644 index 0000000000..a6693fb155 --- /dev/null +++ b/test/intl/number-format/rounding-increment-value-v3.js @@ -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)); diff --git a/test/intl/number-format/sign-display-v3.js b/test/intl/number-format/sign-display-v3.js new file mode 100644 index 0000000000..6345517ec4 --- /dev/null +++ b/test/intl/number-format/sign-display-v3.js @@ -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)); +} diff --git a/test/intl/number-format/trailing-zero-display-v3.js b/test/intl/number-format/trailing-zero-display-v3.js new file mode 100644 index 0000000000..ef3d03ef6e --- /dev/null +++ b/test/intl/number-format/trailing-zero-display-v3.js @@ -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)); diff --git a/test/test262/test262.status b/test/test262/test262.status index b246395b3e..5ecaa8c506 100644 --- a/test/test262/test262.status +++ b/test/test262/test262.status @@ -2578,66 +2578,11 @@ # 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-invalid': [FAIL], - 'intl402/NumberFormat/constructor-trailingZeroDisplay': [FAIL], - '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/test-option-roundingPriority': [FAIL], + # NumberFormat.prototype.format '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/en-US': [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/prop-desc': [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/en-US': [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/prop-desc': [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/test-option-roundingPriority': [FAIL], + 'intl402/NumberFormat/prototype/resolvedOptions/roundingMode': [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/invoked-as-func': [FAIL], 'intl402/PluralRules/prototype/selectRange/length': [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/nan-arguments-throws': [FAIL], 'intl402/PluralRules/prototype/selectRange/x-greater-than-y-throws': [FAIL], - # https://bugs.chromium.org/p/v8/issues/detail?id=11660 'intl402/DurationFormat/prototype/prototype_attributes': [FAIL], 'intl402/DurationFormat/prototype/toStringTag': [FAIL], diff --git a/test/test262/testcfg.py b/test/test262/testcfg.py index c14b93aedd..63749b0be3 100644 --- a/test/test262/testcfg.py +++ b/test/test262/testcfg.py @@ -46,6 +46,7 @@ from testrunner.outproc import test262 FEATURE_FLAGS = { 'Intl.Locale-info': '--harmony_intl_locale_info', 'Intl-enumeration': '--harmony_intl_enumeration', + 'Intl.NumberFormat-v3': '--harmony_intl_number_format_v3', 'Symbol.prototype.description': '--harmony-symbol-description', 'FinalizationRegistry': '--harmony-weak-refs-with-cleanup-some', 'WeakRef': '--harmony-weak-refs-with-cleanup-some', diff --git a/tools/v8heapconst.py b/tools/v8heapconst.py index 58ddb13c13..ffff328b4d 100644 --- a/tools/v8heapconst.py +++ b/tools/v8heapconst.py @@ -371,76 +371,76 @@ KNOWN_MAPS = { ("read_only_space", 0x033d9): (131, "BasicBlockCountersMarkerMap"), ("read_only_space", 0x0341d): (147, "ArrayBoilerplateDescriptionMap"), ("read_only_space", 0x0351d): (161, "InterceptorInfoMap"), - ("read_only_space", 0x05e25): (132, "PromiseFulfillReactionJobTaskMap"), - ("read_only_space", 0x05e4d): (133, "PromiseRejectReactionJobTaskMap"), - ("read_only_space", 0x05e75): (134, "CallableTaskMap"), - ("read_only_space", 0x05e9d): (135, "CallbackTaskMap"), - ("read_only_space", 0x05ec5): (136, "PromiseResolveThenableJobTaskMap"), - ("read_only_space", 0x05eed): (139, "FunctionTemplateInfoMap"), - ("read_only_space", 0x05f15): (140, "ObjectTemplateInfoMap"), - ("read_only_space", 0x05f3d): (141, "AccessCheckInfoMap"), - ("read_only_space", 0x05f65): (142, "AccessorInfoMap"), - ("read_only_space", 0x05f8d): (143, "AccessorPairMap"), - ("read_only_space", 0x05fb5): (144, "AliasedArgumentsEntryMap"), - ("read_only_space", 0x05fdd): (145, "AllocationMementoMap"), - ("read_only_space", 0x06005): (148, "AsmWasmDataMap"), - ("read_only_space", 0x0602d): (149, "AsyncGeneratorRequestMap"), - ("read_only_space", 0x06055): (150, "BreakPointMap"), - ("read_only_space", 0x0607d): (151, "BreakPointInfoMap"), - ("read_only_space", 0x060a5): (152, "CachedTemplateObjectMap"), - ("read_only_space", 0x060cd): (154, "CallSiteInfoMap"), - ("read_only_space", 0x060f5): (155, "ClassPositionsMap"), - ("read_only_space", 0x0611d): (156, "DebugInfoMap"), - ("read_only_space", 0x06145): (158, "ErrorStackDataMap"), - ("read_only_space", 0x0616d): (160, "FunctionTemplateRareDataMap"), - ("read_only_space", 0x06195): (162, "InterpreterDataMap"), - ("read_only_space", 0x061bd): (163, "ModuleRequestMap"), - ("read_only_space", 0x061e5): (164, "PromiseCapabilityMap"), - ("read_only_space", 0x0620d): (165, "PromiseReactionMap"), - ("read_only_space", 0x06235): (166, "PropertyDescriptorObjectMap"), - ("read_only_space", 0x0625d): (167, "PrototypeInfoMap"), - ("read_only_space", 0x06285): (168, "RegExpBoilerplateDescriptionMap"), - ("read_only_space", 0x062ad): (169, "ScriptMap"), - ("read_only_space", 0x062d5): (170, "ScriptOrModuleMap"), - ("read_only_space", 0x062fd): (171, "SourceTextModuleInfoEntryMap"), - ("read_only_space", 0x06325): (172, "StackFrameInfoMap"), - ("read_only_space", 0x0634d): (173, "TemplateObjectDescriptionMap"), - ("read_only_space", 0x06375): (174, "Tuple2Map"), - ("read_only_space", 0x0639d): (175, "WasmContinuationObjectMap"), - ("read_only_space", 0x063c5): (176, "WasmExceptionTagMap"), - ("read_only_space", 0x063ed): (177, "WasmIndirectFunctionTableMap"), - ("read_only_space", 0x06415): (196, "SloppyArgumentsElementsMap"), - ("read_only_space", 0x0643d): (231, "DescriptorArrayMap"), - ("read_only_space", 0x06465): (219, "UncompiledDataWithoutPreparseDataMap"), - ("read_only_space", 0x0648d): (217, "UncompiledDataWithPreparseDataMap"), - ("read_only_space", 0x064b5): (220, "UncompiledDataWithoutPreparseDataWithJobMap"), - ("read_only_space", 0x064dd): (218, "UncompiledDataWithPreparseDataAndJobMap"), - ("read_only_space", 0x06505): (250, "OnHeapBasicBlockProfilerDataMap"), - ("read_only_space", 0x0652d): (197, "TurbofanBitsetTypeMap"), - ("read_only_space", 0x06555): (201, "TurbofanUnionTypeMap"), - ("read_only_space", 0x0657d): (200, "TurbofanRangeTypeMap"), - ("read_only_space", 0x065a5): (198, "TurbofanHeapConstantTypeMap"), - ("read_only_space", 0x065cd): (199, "TurbofanOtherNumberConstantTypeMap"), - ("read_only_space", 0x065f5): (246, "InternalClassMap"), - ("read_only_space", 0x0661d): (257, "SmiPairMap"), - ("read_only_space", 0x06645): (256, "SmiBoxMap"), - ("read_only_space", 0x0666d): (225, "ExportedSubClassBaseMap"), - ("read_only_space", 0x06695): (226, "ExportedSubClassMap"), - ("read_only_space", 0x066bd): (202, "AbstractInternalClassSubclass1Map"), - ("read_only_space", 0x066e5): (203, "AbstractInternalClassSubclass2Map"), - ("read_only_space", 0x0670d): (195, "InternalClassWithSmiElementsMap"), - ("read_only_space", 0x06735): (247, "InternalClassWithStructElementsMap"), - ("read_only_space", 0x0675d): (227, "ExportedSubClass2Map"), - ("read_only_space", 0x06785): (258, "SortStateMap"), - ("read_only_space", 0x067ad): (146, "AllocationSiteWithWeakNextMap"), - ("read_only_space", 0x067d5): (146, "AllocationSiteWithoutWeakNextMap"), - ("read_only_space", 0x067fd): (137, "LoadHandler1Map"), - ("read_only_space", 0x06825): (137, "LoadHandler2Map"), - ("read_only_space", 0x0684d): (137, "LoadHandler3Map"), - ("read_only_space", 0x06875): (138, "StoreHandler0Map"), - ("read_only_space", 0x0689d): (138, "StoreHandler1Map"), - ("read_only_space", 0x068c5): (138, "StoreHandler2Map"), - ("read_only_space", 0x068ed): (138, "StoreHandler3Map"), + ("read_only_space", 0x05e39): (132, "PromiseFulfillReactionJobTaskMap"), + ("read_only_space", 0x05e61): (133, "PromiseRejectReactionJobTaskMap"), + ("read_only_space", 0x05e89): (134, "CallableTaskMap"), + ("read_only_space", 0x05eb1): (135, "CallbackTaskMap"), + ("read_only_space", 0x05ed9): (136, "PromiseResolveThenableJobTaskMap"), + ("read_only_space", 0x05f01): (139, "FunctionTemplateInfoMap"), + ("read_only_space", 0x05f29): (140, "ObjectTemplateInfoMap"), + ("read_only_space", 0x05f51): (141, "AccessCheckInfoMap"), + ("read_only_space", 0x05f79): (142, "AccessorInfoMap"), + ("read_only_space", 0x05fa1): (143, "AccessorPairMap"), + ("read_only_space", 0x05fc9): (144, "AliasedArgumentsEntryMap"), + ("read_only_space", 0x05ff1): (145, "AllocationMementoMap"), + ("read_only_space", 0x06019): (148, "AsmWasmDataMap"), + ("read_only_space", 0x06041): (149, "AsyncGeneratorRequestMap"), + ("read_only_space", 0x06069): (150, "BreakPointMap"), + ("read_only_space", 0x06091): (151, "BreakPointInfoMap"), + ("read_only_space", 0x060b9): (152, "CachedTemplateObjectMap"), + ("read_only_space", 0x060e1): (154, "CallSiteInfoMap"), + ("read_only_space", 0x06109): (155, "ClassPositionsMap"), + ("read_only_space", 0x06131): (156, "DebugInfoMap"), + ("read_only_space", 0x06159): (158, "ErrorStackDataMap"), + ("read_only_space", 0x06181): (160, "FunctionTemplateRareDataMap"), + ("read_only_space", 0x061a9): (162, "InterpreterDataMap"), + ("read_only_space", 0x061d1): (163, "ModuleRequestMap"), + ("read_only_space", 0x061f9): (164, "PromiseCapabilityMap"), + ("read_only_space", 0x06221): (165, "PromiseReactionMap"), + ("read_only_space", 0x06249): (166, "PropertyDescriptorObjectMap"), + ("read_only_space", 0x06271): (167, "PrototypeInfoMap"), + ("read_only_space", 0x06299): (168, "RegExpBoilerplateDescriptionMap"), + ("read_only_space", 0x062c1): (169, "ScriptMap"), + ("read_only_space", 0x062e9): (170, "ScriptOrModuleMap"), + ("read_only_space", 0x06311): (171, "SourceTextModuleInfoEntryMap"), + ("read_only_space", 0x06339): (172, "StackFrameInfoMap"), + ("read_only_space", 0x06361): (173, "TemplateObjectDescriptionMap"), + ("read_only_space", 0x06389): (174, "Tuple2Map"), + ("read_only_space", 0x063b1): (175, "WasmContinuationObjectMap"), + ("read_only_space", 0x063d9): (176, "WasmExceptionTagMap"), + ("read_only_space", 0x06401): (177, "WasmIndirectFunctionTableMap"), + ("read_only_space", 0x06429): (196, "SloppyArgumentsElementsMap"), + ("read_only_space", 0x06451): (231, "DescriptorArrayMap"), + ("read_only_space", 0x06479): (219, "UncompiledDataWithoutPreparseDataMap"), + ("read_only_space", 0x064a1): (217, "UncompiledDataWithPreparseDataMap"), + ("read_only_space", 0x064c9): (220, "UncompiledDataWithoutPreparseDataWithJobMap"), + ("read_only_space", 0x064f1): (218, "UncompiledDataWithPreparseDataAndJobMap"), + ("read_only_space", 0x06519): (250, "OnHeapBasicBlockProfilerDataMap"), + ("read_only_space", 0x06541): (197, "TurbofanBitsetTypeMap"), + ("read_only_space", 0x06569): (201, "TurbofanUnionTypeMap"), + ("read_only_space", 0x06591): (200, "TurbofanRangeTypeMap"), + ("read_only_space", 0x065b9): (198, "TurbofanHeapConstantTypeMap"), + ("read_only_space", 0x065e1): (199, "TurbofanOtherNumberConstantTypeMap"), + ("read_only_space", 0x06609): (246, "InternalClassMap"), + ("read_only_space", 0x06631): (257, "SmiPairMap"), + ("read_only_space", 0x06659): (256, "SmiBoxMap"), + ("read_only_space", 0x06681): (225, "ExportedSubClassBaseMap"), + ("read_only_space", 0x066a9): (226, "ExportedSubClassMap"), + ("read_only_space", 0x066d1): (202, "AbstractInternalClassSubclass1Map"), + ("read_only_space", 0x066f9): (203, "AbstractInternalClassSubclass2Map"), + ("read_only_space", 0x06721): (195, "InternalClassWithSmiElementsMap"), + ("read_only_space", 0x06749): (247, "InternalClassWithStructElementsMap"), + ("read_only_space", 0x06771): (227, "ExportedSubClass2Map"), + ("read_only_space", 0x06799): (258, "SortStateMap"), + ("read_only_space", 0x067c1): (146, "AllocationSiteWithWeakNextMap"), + ("read_only_space", 0x067e9): (146, "AllocationSiteWithoutWeakNextMap"), + ("read_only_space", 0x06811): (137, "LoadHandler1Map"), + ("read_only_space", 0x06839): (137, "LoadHandler2Map"), + ("read_only_space", 0x06861): (137, "LoadHandler3Map"), + ("read_only_space", 0x06889): (138, "StoreHandler0Map"), + ("read_only_space", 0x068b1): (138, "StoreHandler1Map"), + ("read_only_space", 0x068d9): (138, "StoreHandler2Map"), + ("read_only_space", 0x06901): (138, "StoreHandler3Map"), ("map_space", 0x02149): (1057, "ExternalMap"), ("map_space", 0x02171): (2114, "JSMessageObjectMap"), }