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

[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/+/3405649Reviewed-by: 's avatarShu-yu Guo <syg@chromium.org>
Commit-Queue: Frank Tang <ftang@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78878}
parent cdb20294
......@@ -85,8 +85,15 @@ BUILTIN(NumberFormatPrototypeFormatToParts) {
Handle<Object> x;
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,
Object::ToNumeric(isolate, args.at(1)));
Object::ToNumeric(isolate, value));
}
} else {
x = isolate->factory()->nan_value();
}
......@@ -501,8 +508,14 @@ BUILTIN(NumberFormatInternalFormatNumber) {
// 4. Let x be ? ToNumeric(value).
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,
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<Object> 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));
}
......
......@@ -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
......
......@@ -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
......
......@@ -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") \
......
......@@ -1522,151 +1522,195 @@ Maybe<Intl::NumberFormatDigitOptions> Intl::SetNumberFormatDigitOptions(
return Nothing<NumberFormatDigitOptions>();
}
int mnfd = 0;
int mxfd = 0;
Handle<Object> mnfd_obj;
Handle<Object> mxfd_obj;
// 6. Let mnfd be ? Get(options, "minimumFractionDigits").
Handle<String> mnfd_str = factory->minimumFractionDigits_string();
Handle<Object> 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<NumberFormatDigitOptions>());
// 8. Let mxfd be ? Get(options, "maximumFractionDigits").
Handle<String> mxfd_str = factory->maximumFractionDigits_string();
// 7. Let mxfd be ? Get(options, "maximumFractionDigits").
Handle<Object> 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<NumberFormatDigitOptions>());
// 9. Let mnsd be ? Get(options, "minimumSignificantDigits").
// 8. Let mnsd be ? Get(options, "minimumSignificantDigits").
Handle<Object> mnsd_obj;
Handle<String> 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<NumberFormatDigitOptions>());
// 10. Let mxsd be ? Get(options, "maximumSignificantDigits").
// 9. Let mxsd be ? Get(options, "maximumSignificantDigits").
Handle<Object> mxsd_obj;
Handle<String> 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<NumberFormatDigitOptions>());
// 11. Set intlObj.[[MinimumIntegerDigits]] to mnid.
digit_options.minimum_integer_digits = mnid;
// 12. Set intlObj.[[MinimumFractionDigits]] to mnfd.
digit_options.minimum_fraction_digits = mnfd;
digit_options.rounding_priority = RoundingPriority::kAuto;
digit_options.minimum_significant_digits = 0;
digit_options.maximum_significant_digits = 0;
// 13. Set intlObj.[[MaximumFractionDigits]] to mxfd.
digit_options.maximum_fraction_digits = mxfd;
// 10. Set intlObj.[[MinimumIntegerDigits]] to mnid.
digit_options.minimum_integer_digits = mnid;
// 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).
if (FLAG_harmony_intl_number_format_v3) {
// 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, mnsd_str).To(&mnsd)) {
if (!DefaultNumberOption(isolate, mnsd_obj, 1, 21, 1,
factory->minimumSignificantDigits_string())
.To(&mnsd)) {
return Nothing<NumberFormatDigitOptions>();
}
// 14. b. Let mxsd be ? DefaultNumberOption(mxsd, mnsd, 21, 21).
// 20.b.ii Let mxsd be ? DefaultNumberOption(mxsd, mnsd, 21, 21).
int mxsd;
if (!DefaultNumberOption(isolate, mxsd_obj, mnsd, 21, 21, mxsd_str)
if (!DefaultNumberOption(isolate, mxsd_obj, mnsd, 21, 21,
factory->maximumSignificantDigits_string())
.To(&mxsd)) {
return Nothing<NumberFormatDigitOptions>();
}
// 14. c. Set intlObj.[[MinimumSignificantDigits]] to mnsd.
// 20.b.iii Set intlObj.[[MinimumSignificantDigits]] to mnsd.
digit_options.minimum_significant_digits = mnsd;
// 14. d. Set intlObj.[[MaximumSignificantDigits]] to mxsd.
// 20.b.iv 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*).
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);
// 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;
}
// 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)
// 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)
.To(&mnfd)) {
return Nothing<NumberFormatDigitOptions>();
}
// 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<NumberFormatDigitOptions>();
}
// 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<NumberFormatDigitOptions>());
}
// 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<std::string>& Intl::GetAvailableLocalesForDateFormat() {
return available_locales.Pointer()->Get();
}
constexpr uint16_t kInfinityChar = 0x221e;
Handle<String> Intl::NumberFieldToType(Isolate* isolate,
Handle<Object> numeric_obj,
int32_t field_id) {
DCHECK(numeric_obj->IsNumeric());
switch (static_cast<UNumberFormatFields>(field_id)) {
const NumberFormatSpan& part,
const icu::UnicodeString& text,
bool is_nan) {
switch (static_cast<UNumberFormatFields>(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<String> Intl::NumberFieldToType(Isolate* isolate,
case UNUM_PERCENT_FIELD:
return isolate->factory()->percentSign_string();
case UNUM_SIGN_FIELD:
if (numeric_obj->IsBigInt()) {
Handle<BigInt> big_int = Handle<BigInt>::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<bool> Intl::GetTimeZoneIndex(Isolate* isolate, Handle<String> identifier,
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 v8
......@@ -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<NumberFormatSpan> FlattenRegionsToParts(
std::vector<NumberFormatSpan>* regions);
template <typename T>
class Handle;
class JSCollator;
......@@ -115,6 +128,21 @@ class Intl {
Isolate* isolate, Handle<Object> num, Handle<Object> locales,
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
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<NumberFormatDigitOptions>
SetNumberFormatDigitOptions(Isolate* isolate, Handle<JSReceiver> options,
......@@ -143,8 +173,9 @@ class Intl {
// Helper function to convert number field id to type string.
static Handle<String> NumberFieldToType(Isolate* isolate,
Handle<Object> 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<String> CanonicalizeTimeZoneName(
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
......
......@@ -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<const std::string, icu::MeasureUnit> CreateUnitMap() {
UErrorCode status = U_ZERO_ERROR;
int32_t total = icu::MeasureUnit::getAvailable(nullptr, 0, status);
......@@ -462,6 +535,13 @@ Handle<String> 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<JSObject> JSNumberFormat::ResolvedOptions(
......@@ -662,12 +801,19 @@ Handle<JSObject> 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<JSObject> 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> JSNumberFormat::UnwrapNumberFormat(
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
MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
Handle<Map> map,
......@@ -821,28 +976,28 @@ MaybeHandle<JSNumberFormat> 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<Intl::MatcherOption> maybe_locale_matcher =
Intl::GetLocaleMatcher(isolate, options, service);
MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSNumberFormat>());
Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
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*).
Maybe<bool> 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<JSNumberFormat>());
// 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<std::string> relevant_extension_keys{"nu"};
......@@ -882,21 +1037,20 @@ MaybeHandle<JSNumberFormat> 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> 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> 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())
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> 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> 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<Notation> maybe_notation = GetStringOption<Notation>(
isolate, options, "notation", service,
......@@ -1105,9 +1258,10 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
Notation::COMPACT},
Notation::STANDARD);
MAYBE_RETURN(maybe_notation, MaybeHandle<JSNumberFormat>());
// 19. Set numberFormat.[[Notation]] to notation.
notation = maybe_notation.FromJust();
// 27. Perform ? SetNumberFormatDigitOptions(numberFormat, options,
// 20. Perform ? SetNumberFormatDigitOptions(numberFormat, options,
// mnfdDefault, mxfdDefault).
Maybe<Intl::NumberFormatDigitOptions> maybe_digit_options =
Intl::SetNumberFormatDigitOptions(isolate, options, mnfd_default,
......@@ -1115,10 +1269,58 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
notation == Notation::COMPACT);
MAYBE_RETURN(maybe_digit_options, Handle<JSNumberFormat>());
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").
Maybe<CompactDisplay> maybe_compact_display = GetStringOption<CompactDisplay>(
isolate, options, "compactDisplay", service, {"short", "long"},
......@@ -1126,13 +1328,13 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
MAYBE_RETURN(maybe_compact_display, MaybeHandle<JSNumberFormat>());
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));
}
if (!FLAG_harmony_intl_number_format_v3) {
// 30. Let useGrouping be ? GetOption(options, "useGrouping", "boolean",
// undefined, true).
bool use_grouping = true;
......@@ -1141,18 +1343,58 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
MAYBE_RETURN(found_use_grouping, MaybeHandle<JSNumberFormat>());
// 31. Set numberFormat.[[UseGrouping]] to useGrouping.
if (!use_grouping) {
icu_number_formatter = icu_number_formatter.grouping(
UNumberGroupingStrategy::UNUM_GROUPING_OFF);
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<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", «
// "auto", "never", "always", "exceptZero" », "auto").
Maybe<SignDisplay> maybe_sign_display = GetStringOption<SignDisplay>(
// "auto", "never", "always", "exceptZero", "negative" », "auto").
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,
{"auto", "never", "always", "exceptZero"},
{SignDisplay::AUTO, SignDisplay::NEVER, SignDisplay::ALWAYS,
SignDisplay::EXCEPT_ZERO},
SignDisplay::AUTO);
}
MAYBE_RETURN(maybe_sign_display, MaybeHandle<JSNumberFormat>());
SignDisplay sign_display = maybe_sign_display.FromJust();
......@@ -1162,8 +1404,27 @@ MaybeHandle<JSNumberFormat> 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<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>]].
......@@ -1180,6 +1441,9 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
// 30. Set numberFormat.[[NegativePattern]] to
// stylePatterns.[[negativePattern]].
//
icu::number::LocalizedNumberFormatter icu_number_formatter =
settings.locale(icu_locale);
Handle<Managed<icu::number::LocalizedNumberFormatter>>
managed_number_formatter =
Managed<icu::number::LocalizedNumberFormatter>::FromRawPtr(
......@@ -1200,6 +1464,7 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate,
}
namespace {
Maybe<bool> IcuFormatNumber(
Isolate* isolate,
const icu::number::LocalizedNumberFormatter& number_format,
......@@ -1212,14 +1477,41 @@ Maybe<bool> IcuFormatNumber(
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, big_int_string,
BigInt::ToString(isolate, big_int),
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(
{big_int_string->ToCString().get(), big_int_string->length()}, status);
{string->ToCString().get(), string->length()}, status);
}
} else {
double number = numeric_obj->IsNaN()
? std::numeric_limits<double>::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".
// See https://bugs.chromium.org/p/v8/issues/detail?id=8641
......@@ -1229,28 +1521,6 @@ Maybe<bool> IcuFormatNumber(
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,
const NumberFormatSpan& b) {
// Regions that start earlier should be encountered earlier.
......@@ -1359,17 +1629,15 @@ std::vector<NumberFormatSpan> FlattenRegionsToParts(
}
namespace {
Maybe<int> ConstructParts(Isolate* isolate,
icu::number::FormattedNumber* formatted,
Maybe<int> ConstructParts(Isolate* isolate, icu::FormattedValue* formatted,
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;
icu::UnicodeString formatted_text = formatted->toString(status);
if (U_FAILURE(status)) {
THROW_NEW_ERROR_RETURN_VALUE(
isolate, NewTypeError(MessageTemplate::kIcuError), Nothing<int>());
}
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<int> 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<int> 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<String> substring;
......@@ -1410,6 +1677,7 @@ Maybe<int> ConstructParts(Isolate* isolate,
isolate, substring,
Intl::ToString(isolate, formatted_text, part.begin_pos, part.end_pos),
Nothing<int>());
Intl::AddElement(isolate, result, index, field_type_string, substring);
++index;
}
......@@ -1417,13 +1685,54 @@ Maybe<int> ConstructParts(Isolate* isolate,
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
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(
Isolate* isolate, Handle<JSNumberFormat> number_format,
Handle<Object> 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<JSArray> JSNumberFormat::FormatToParts(
Maybe<bool> maybe_format =
IcuFormatNumber(isolate, *fmt, numeric_obj, &formatted);
MAYBE_RETURN(maybe_format, Handle<JSArray>());
UErrorCode status = U_ZERO_ERROR;
bool style_is_unit =
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;
return FormatToJSArray(isolate, &formatted, fmt, numeric_obj->IsNaN());
}
namespace {
......
......@@ -26,6 +26,7 @@ namespace U_ICU_NAMESPACE {
class UnicodeString;
namespace number {
class LocalizedNumberFormatter;
class UnlocalizedNumberFormatter;
} // namespace number
} // namespace U_ICU_NAMESPACE
......@@ -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<NumberFormatSpan> FlattenRegionsToParts(
std::vector<NumberFormatSpan>* regions);
} // namespace internal
} // namespace v8
......
......@@ -115,21 +115,19 @@ MaybeHandle<JSPluralRules> JSPluralRules::New(Isolate* isolate, Handle<Map> map,
Handle<String> 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::PluralRules> 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> JSPluralRules::New(Isolate* isolate, Handle<Map> map,
Intl::SetNumberFormatDigitOptions(isolate, options, 0, 3, false);
MAYBE_RETURN(maybe_digit_options, MaybeHandle<JSPluralRules>());
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<icu::PluralRules>> managed_plural_rules =
Managed<icu::PluralRules>::FromUniquePtr(isolate, 0,
......
......@@ -343,9 +343,9 @@ template <typename T>
MaybeHandle<T> FormatCommon(
Isolate* isolate, Handle<JSRelativeTimeFormat> format,
Handle<Object> value_obj, Handle<Object> unit_obj, const char* func_name,
const std::function<
MaybeHandle<T>(Isolate*, const icu::FormattedRelativeDateTime&,
Handle<Object>, Handle<String>)>& formatToResult) {
MaybeHandle<T> (*formatToResult)(Isolate*,
const icu::FormattedRelativeDateTime&,
Handle<String>, bool)) {
// 3. Let value be ? ToNumber(value).
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
......@@ -382,13 +382,13 @@ MaybeHandle<T> 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<String> FormatToString(
Isolate* isolate, const icu::FormattedRelativeDateTime& formatted,
Handle<Object> value, Handle<String> unit) {
Handle<String> unit, bool is_nan) {
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString result = formatted.toString(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,
const icu::UnicodeString& string, int32_t index,
int32_t start, int32_t limit, int32_t field_id,
Handle<Object> value, Handle<String> unit) {
const NumberFormatSpan& part, Handle<String> unit,
bool is_nan) {
Handle<String> 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<bool>());
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<JSArray> FormatToJSArray(
Isolate* isolate, const icu::FormattedRelativeDateTime& formatted,
Handle<Object> value, Handle<String> unit) {
Handle<String> unit, bool is_nan) {
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString string = formatted.toString(status);
......@@ -457,19 +458,23 @@ MaybeHandle<JSArray> FormatToJSArray(
for (auto start_limit : groups) {
if (start_limit.first > start) {
Maybe<bool> 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<JSArray>());
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<JSArray>());
start = start_limit.second;
}
}
}
Maybe<bool> maybe_added = AddUnit(isolate, array, string, index++, start,
limit, field, value, unit);
Maybe<bool> maybe_added =
AddUnit(isolate, array, string, index++,
NumberFormatSpan(field, start, limit), unit, is_nan);
MAYBE_RETURN(maybe_added, Handle<JSArray>());
previous_end = limit;
}
......
......@@ -65,6 +65,75 @@ V8_WARN_UNUSED_RESULT static Maybe<T> 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 <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/#sec-getoption
//
......
......@@ -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
################################################################################
......
// 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);
});
// 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);
});
// 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));
// 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));
}
// 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));
......@@ -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],
......
......@@ -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',
......
......@@ -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"),
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment