From a0253af8c1d5e7374d102800d540a014060c9901 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sun, 30 Jan 2022 10:41:07 -0500 Subject: [PATCH] LibJS: Generalize Intl.NumberFormat to operate on Value types Intl.NumberFormat is meant to format both Number and BigInt types. To prepare for formatting BigInt types, this generalizes our NumberFormat implementation to operate on Value instances rather than doubles. All arithmetic is moved to static helpers that can now be updated with BigInt semantics. --- .../LibJS/Runtime/Intl/DateTimeFormat.cpp | 6 +- .../LibJS/Runtime/Intl/NumberFormat.cpp | 191 ++++++++++++------ .../LibJS/Runtime/Intl/NumberFormat.h | 22 +- .../Runtime/Intl/NumberFormatFunction.cpp | 3 +- .../Runtime/Intl/NumberFormatPrototype.cpp | 2 +- .../LibJS/Runtime/Intl/RelativeTimeFormat.cpp | 2 +- .../LibJS/Runtime/NumberPrototype.cpp | 3 +- 7 files changed, 150 insertions(+), 79 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp b/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp index fc46ee611b..157c6e0ec6 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp @@ -899,7 +899,7 @@ ThrowCompletionOr> format_date_time_pattern(GlobalObjec value = floor(value * pow(10, static_cast(*fractional_second_digits) - 3)); // iii. Let fv be FormatNumeric(nf3, v). - auto formatted_value = format_numeric(*number_format3, value); + auto formatted_value = format_numeric(global_object, *number_format3, Value(value)); // iv. Append a new Record { [[Type]]: "fractionalSecond", [[Value]]: fv } as the last element of result. result.append({ "fractionalSecond"sv, move(formatted_value) }); @@ -983,13 +983,13 @@ ThrowCompletionOr> format_date_time_pattern(GlobalObjec // viii. If f is "numeric", then case Unicode::CalendarPatternStyle::Numeric: // 1. Let fv be FormatNumeric(nf, v). - formatted_value = format_numeric(*number_format, value); + formatted_value = format_numeric(global_object, *number_format, Value(value)); break; // ix. Else if f is "2-digit", then case Unicode::CalendarPatternStyle::TwoDigit: // 1. Let fv be FormatNumeric(nf2, v). - formatted_value = format_numeric(*number_format2, value); + formatted_value = format_numeric(global_object, *number_format2, Value(value)); // 2. If the "length" property of fv is greater than 2, let fv be the substring of fv containing the last two characters. // NOTE: The first length check here isn't enough, but lets us avoid UTF-16 transcoding when the formatted value is ASCII. diff --git a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp index 2adbe53e79..d12071745f 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp @@ -247,9 +247,81 @@ StringView NumberFormat::sign_display_string() const } } -static ALWAYS_INLINE int log10floor(double value) +static ALWAYS_INLINE int log10floor(Value number) { - return static_cast(floor(log10(value))); + if (number.is_number()) + return static_cast(floor(log10(number.as_double()))); + VERIFY_NOT_REACHED(); +} + +static Value multiply(GlobalObject&, Value lhs, i64 rhs) +{ + if (lhs.is_number()) + return Value(lhs.as_double() * rhs); + VERIFY_NOT_REACHED(); +} + +static Value divide(GlobalObject&, Value lhs, i64 rhs) +{ + if (lhs.is_number()) + return Value(lhs.as_double() / rhs); + VERIFY_NOT_REACHED(); +} + +static ALWAYS_INLINE Value multiply_by_power(GlobalObject& global_object, Value number, i64 exponent) +{ + if (exponent < 0) + return divide(global_object, number, pow(10, -exponent)); + return multiply(global_object, number, pow(10, exponent)); +} + +static ALWAYS_INLINE Value divide_by_power(GlobalObject& global_object, Value number, i64 exponent) +{ + if (exponent < 0) + return multiply(global_object, number, pow(10, -exponent)); + return divide(global_object, number, pow(10, exponent)); +} + +static ALWAYS_INLINE Value rounded(Value number) +{ + if (number.is_number()) + return Value(round(number.as_double())); + VERIFY_NOT_REACHED(); +} + +static ALWAYS_INLINE bool is_zero(Value number) +{ + if (number.is_number()) + return number.as_double() == 0.0; + VERIFY_NOT_REACHED(); +} + +static ALWAYS_INLINE bool is_greater_than(Value number, i64 rhs) +{ + if (number.is_number()) + return number.as_double() > rhs; + VERIFY_NOT_REACHED(); +} + +static ALWAYS_INLINE bool is_greater_than_or_equal(Value number, i64 rhs) +{ + if (number.is_number()) + return number.as_double() >= rhs; + VERIFY_NOT_REACHED(); +} + +static ALWAYS_INLINE bool is_less_than(Value number, i64 rhs) +{ + if (number.is_number()) + return number.as_double() < rhs; + VERIFY_NOT_REACHED(); +} + +static ALWAYS_INLINE String number_to_string(Value number) +{ + if (number.is_number()) + return number.to_string_without_side_effects(); + VERIFY_NOT_REACHED(); } // 15.1.1 SetNumberFormatDigitOptions ( intlObj, options, mnfdDefault, mxfdDefault, notation ), https://tc39.es/ecma402/#sec-setnfdigitoptions @@ -496,15 +568,15 @@ int currency_digits(StringView currency) } // 15.1.5 FormatNumericToString ( intlObject, x ), https://tc39.es/ecma402/#sec-formatnumberstring -FormatResult format_numeric_to_string(NumberFormatBase& intl_object, double number) +FormatResult format_numeric_to_string(GlobalObject& global_object, NumberFormatBase& intl_object, Value number) { // 1. If x < 0 or x is -0š”½, let isNegative be true; else let isNegative be false. - bool is_negative = (number < 0.0) || Value(number).is_negative_zero(); + bool is_negative = is_less_than(number, 0) || number.is_negative_zero(); // 2. If isNegative, then if (is_negative) { // a. Let x be -x. - number *= -1; + number = multiply(global_object, number, -1); } RawFormatResult result {}; @@ -513,25 +585,25 @@ FormatResult format_numeric_to_string(NumberFormatBase& intl_object, double numb // 3. If intlObject.[[RoundingType]] is significantDigits, then case NumberFormatBase::RoundingType::SignificantDigits: // a. Let result be ToRawPrecision(x, intlObject.[[MinimumSignificantDigits]], intlObject.[[MaximumSignificantDigits]]). - result = to_raw_precision(number, intl_object.min_significant_digits(), intl_object.max_significant_digits()); + result = to_raw_precision(global_object, number, intl_object.min_significant_digits(), intl_object.max_significant_digits()); break; // 4. Else if intlObject.[[RoundingType]] is fractionDigits, then case NumberFormatBase::RoundingType::FractionDigits: // a. Let result be ToRawFixed(x, intlObject.[[MinimumFractionDigits]], intlObject.[[MaximumFractionDigits]]). - result = to_raw_fixed(number, intl_object.min_fraction_digits(), intl_object.max_fraction_digits()); + result = to_raw_fixed(global_object, number, intl_object.min_fraction_digits(), intl_object.max_fraction_digits()); break; // 5. Else, case NumberFormatBase::RoundingType::CompactRounding: // a. Assert: intlObject.[[RoundingType]] is compactRounding. // b. Let result be ToRawPrecision(x, 1, 2). - result = to_raw_precision(number, 1, 2); + result = to_raw_precision(global_object, number, 1, 2); // c. If result.[[IntegerDigitsCount]] > 1, then if (result.digits > 1) { // i. Let result be ToRawFixed(x, 0, 0). - result = to_raw_fixed(number, 0, 0); + result = to_raw_fixed(global_object, number, 0, 0); } break; @@ -564,7 +636,7 @@ FormatResult format_numeric_to_string(NumberFormatBase& intl_object, double numb // 11. If isNegative, then if (is_negative) { // a. Let x be -x. - number *= -1; + number = multiply(global_object, number, -1); } // 12. Return the Record { [[RoundedNumber]]: x, [[FormattedString]]: string }. @@ -572,7 +644,7 @@ FormatResult format_numeric_to_string(NumberFormatBase& intl_object, double numb } // 15.1.6 PartitionNumberPattern ( numberFormat, x ), https://tc39.es/ecma402/#sec-partitionnumberpattern -Vector partition_number_pattern(NumberFormat& number_format, double number) +Vector partition_number_pattern(GlobalObject& global_object, NumberFormat& number_format, Value number) { // 1. Let exponent be 0. int exponent = 0; @@ -580,17 +652,17 @@ Vector partition_number_pattern(NumberFormat& number_format, d String formatted_string; // 2. If x is NaN, then - if (Value(number).is_nan()) { + if (number.is_nan()) { // a. Let n be an implementation- and locale-dependent (ILD) String value indicating the NaN value. formatted_string = Unicode::get_number_system_symbol(number_format.data_locale(), number_format.numbering_system(), Unicode::NumericSymbol::NaN).value_or("NaN"sv); } // 3. Else if x is +āˆž, then - else if (Value(number).is_positive_infinity()) { + else if (number.is_positive_infinity()) { // a. Let n be an ILD String value indicating positive infinity. formatted_string = Unicode::get_number_system_symbol(number_format.data_locale(), number_format.numbering_system(), Unicode::NumericSymbol::Infinity).value_or("infinity"sv); } // 4. Else if x is -āˆž, then - else if (Value(number).is_negative_infinity()) { + else if (number.is_negative_infinity()) { // a. Let n be an ILD String value indicating negative infinity. // NOTE: The CLDR does not contain unique strings for negative infinity. The negative sign will // be inserted by the pattern returned from GetNumberFormatPattern. @@ -600,16 +672,16 @@ Vector partition_number_pattern(NumberFormat& number_format, d else { // a. If numberFormat.[[Style]] is "percent", let x be 100 Ɨ x. if (number_format.style() == NumberFormat::Style::Percent) - number = number * 100; + number = multiply(global_object, number, 100); // b. Let exponent be ComputeExponent(numberFormat, x). - exponent = compute_exponent(number_format, number); + exponent = compute_exponent(global_object, number_format, number); // c. Let x be x Ɨ 10^(-exponent). - number *= pow(10, -exponent); + number = multiply_by_power(global_object, number, -exponent); // d. Let formatNumberResult be FormatNumericToString(numberFormat, x). - auto format_number_result = format_numeric_to_string(number_format, number); + auto format_number_result = format_numeric_to_string(global_object, number_format, number); // e. Let n be formatNumberResult.[[FormattedString]]. formatted_string = move(format_number_result.formatted_string); @@ -645,7 +717,7 @@ Vector partition_number_pattern(NumberFormat& number_format, d // c. Else if p is equal to "number", then else if (part == "number"sv) { // i. Let notationSubParts be PartitionNotationSubPattern(numberFormat, x, n, exponent). - auto notation_sub_parts = partition_notation_sub_pattern(number_format, number, formatted_string, exponent); + auto notation_sub_parts = partition_notation_sub_pattern(global_object, number_format, number, formatted_string, exponent); // ii. Append all elements of notationSubParts to result. result.extend(move(notation_sub_parts)); } @@ -746,7 +818,7 @@ static Vector separate_integer_into_groups(Unicode::NumberGroupings } // 15.1.7 PartitionNotationSubPattern ( numberFormat, x, n, exponent ), https://tc39.es/ecma402/#sec-partitionnotationsubpattern -Vector partition_notation_sub_pattern(NumberFormat& number_format, double number, String formatted_string, int exponent) +Vector partition_notation_sub_pattern(GlobalObject& global_object, NumberFormat& number_format, Value number, String formatted_string, int exponent) { // 1. Let result be a new empty List. Vector result; @@ -756,12 +828,12 @@ Vector partition_notation_sub_pattern(NumberFormat& number_for return {}; // 2. If x is NaN, then - if (Value(number).is_nan()) { + if (number.is_nan()) { // a. Append a new Record { [[Type]]: "nan", [[Value]]: n } as the last element of result. result.append({ "nan"sv, move(formatted_string) }); } // 3. Else if x is a non-finite Number, then - else if (!Value(number).is_finite_number()) { + else if (!number.is_finite_number()) { // a. Append a new Record { [[Type]]: "infinity", [[Value]]: n } as the last element of result. result.append({ "infinity"sv, move(formatted_string) }); } @@ -827,7 +899,7 @@ Vector partition_notation_sub_pattern(NumberFormat& number_for // // See: https://github.com/tc39/proposal-intl-numberformat-v3/issues/3 if (number_format.has_compact_format()) - use_grouping = number >= 10'000; + use_grouping = is_greater_than_or_equal(number, 10'000); // 6. If the numberFormat.[[UseGrouping]] is true, then if (use_grouping) { @@ -908,7 +980,7 @@ Vector partition_notation_sub_pattern(NumberFormat& number_for // 2. Let exponentResult be ToRawFixed(exponent, 1, 0, 0). // Note: See the implementation of ToRawFixed for why we do not pass the 1. - auto exponent_result = to_raw_fixed(exponent, 0, 0); + auto exponent_result = to_raw_fixed(global_object, Value(exponent), 0, 0); // FIXME: The spec does not say to do this, but all of major engines perform this replacement. // Without this, formatting with non-Latin numbering systems will produce non-localized results. @@ -933,11 +1005,11 @@ Vector partition_notation_sub_pattern(NumberFormat& number_for } // 15.1.8 FormatNumeric ( numberFormat, x ), https://tc39.es/ecma402/#sec-formatnumber -String format_numeric(NumberFormat& number_format, double number) +String format_numeric(GlobalObject& global_object, NumberFormat& number_format, Value number) { // 1. Let parts be ? PartitionNumberPattern(numberFormat, x). // Note: Our implementation of PartitionNumberPattern does not throw. - auto parts = partition_number_pattern(number_format, number); + auto parts = partition_number_pattern(global_object, number_format, number); // 2. Let result be the empty String. StringBuilder result; @@ -953,13 +1025,13 @@ String format_numeric(NumberFormat& number_format, double number) } // 15.1.9 FormatNumericToParts ( numberFormat, x ), https://tc39.es/ecma402/#sec-formatnumbertoparts -Array* format_numeric_to_parts(GlobalObject& global_object, NumberFormat& number_format, double number) +Array* format_numeric_to_parts(GlobalObject& global_object, NumberFormat& number_format, Value number) { auto& vm = global_object.vm(); // 1. Let parts be ? PartitionNumberPattern(numberFormat, x). // Note: Our implementation of PartitionNumberPattern does not throw. - auto parts = partition_number_pattern(number_format, number); + auto parts = partition_number_pattern(global_object, number_format, number); // 2. Let result be ArrayCreate(0). auto* result = MUST(Array::create(global_object, 0)); @@ -1012,7 +1084,7 @@ static String cut_trailing_zeroes(StringView string, int cut) } // 15.1.10 ToRawPrecision ( x, minPrecision, maxPrecision ), https://tc39.es/ecma402/#sec-torawprecision -RawFormatResult to_raw_precision(double number, int min_precision, int max_precision) +RawFormatResult to_raw_precision(GlobalObject& global_object, Value number, int min_precision, int max_precision) { RawFormatResult result {}; @@ -1024,7 +1096,7 @@ RawFormatResult to_raw_precision(double number, int min_precision, int max_preci int exponent = 0; // 3. If x = 0, then - if (number == 0.0) { + if (is_zero(number)) { // a. Let m be the String consisting of p occurrences of the character "0". result.formatted_string = String::repeated('0', precision); @@ -1032,7 +1104,7 @@ RawFormatResult to_raw_precision(double number, int min_precision, int max_preci exponent = 0; // c. Let xFinal be 0. - result.rounded_number = 0; + result.rounded_number = Value(0); } // 4. Else, else { @@ -1044,15 +1116,13 @@ RawFormatResult to_raw_precision(double number, int min_precision, int max_preci // a. Let e and n be integers such that 10^(p–1) ≤ n < 10^p and for which n Ɨ 10^(e–p+1) – x is as close to zero as possible. // If there are two such sets of e and n, pick the e and n for which n Ɨ 10^(e–p+1) is larger. exponent = log10floor(number); - - double power = pow(10, exponent - precision + 1); - double n = round(number / power); + auto n = rounded(divide_by_power(global_object, number, exponent - precision + 1)); // b. Let m be the String consisting of the digits of the decimal representation of n (in order, with no leading zeroes). - result.formatted_string = Value(n).to_string_without_side_effects(); + result.formatted_string = number_to_string(n); // c. Let xFinal be n Ɨ 10^(e–p+1). - result.rounded_number = n * power; + result.rounded_number = multiply_by_power(global_object, n, exponent - precision + 1); } // 5. If e ≄ p–1, then @@ -1105,7 +1175,7 @@ RawFormatResult to_raw_precision(double number, int min_precision, int max_preci // 15.1.11 ToRawFixed ( x, minInteger, minFraction, maxFraction ), https://tc39.es/ecma402/#sec-torawfixed // NOTE: The spec has a mistake here. The minInteger parameter is unused and is not provided by FormatNumericToString. -RawFormatResult to_raw_fixed(double number, int min_fraction, int max_fraction) +RawFormatResult to_raw_fixed(GlobalObject& global_object, Value number, int min_fraction, int max_fraction) { RawFormatResult result {}; @@ -1115,16 +1185,14 @@ RawFormatResult to_raw_fixed(double number, int min_fraction, int max_fraction) // 2. Let f be maxFraction. int fraction = max_fraction; - double power = pow(10, fraction); - // 3. Let n be an integer for which the exact mathematical value of n / 10^f – x is as close to zero as possible. If there are two such n, pick the larger n. - double n = round(number * power); + auto n = rounded(multiply_by_power(global_object, number, fraction)); // 4. Let xFinal be n / 10^f. - result.rounded_number = n / power; + result.rounded_number = divide_by_power(global_object, n, fraction); // 5. If n = 0, let m be the String "0". Otherwise, let m be the String consisting of the digits of the decimal representation of n (in order, with no leading zeroes). - result.formatted_string = n == 0.0 ? String("0"sv) : Value(n).to_string_without_side_effects(); + result.formatted_string = is_zero(n) ? String("0"sv) : number_to_string(n); // 6. If f ≠ 0, then if (fraction != 0) { @@ -1245,8 +1313,14 @@ ThrowCompletionOr set_number_format_unit_options(GlobalObject& global_obje } // 15.1.14 GetNumberFormatPattern ( numberFormat, x ), https://tc39.es/ecma402/#sec-getnumberformatpattern -Optional> get_number_format_pattern(NumberFormat& number_format, double number, Unicode::NumberFormat& found_pattern) +Optional> get_number_format_pattern(NumberFormat& number_format, Value number, Unicode::NumberFormat& found_pattern) { + auto as_number = [&]() { + if (number.is_number()) + return number.as_double(); + VERIFY_NOT_REACHED(); + }; + // 1. Let localeData be %NumberFormat%.[[LocaleData]]. // 2. Let dataLocale be numberFormat.[[DataLocale]]. // 3. Let dataLocaleData be localeData.[[]]. @@ -1272,7 +1346,7 @@ Optional> get_number_format_pattern(NumberFormat& nu // e. Let patterns be patterns.[[]]. // f. Let patterns be patterns.[[]]. auto formats = Unicode::get_unit_formats(number_format.data_locale(), number_format.unit(), number_format.unit_display()); - patterns = Unicode::select_pattern_with_plurality(formats, number); + patterns = Unicode::select_pattern_with_plurality(formats, as_number()); break; } @@ -1292,7 +1366,7 @@ Optional> get_number_format_pattern(NumberFormat& nu if (number_format.currency_display() == NumberFormat::CurrencyDisplay::Name) { auto formats = Unicode::get_compact_number_system_formats(number_format.data_locale(), number_format.numbering_system(), Unicode::CompactNumberFormatType::CurrencyUnit); - auto maybe_patterns = Unicode::select_pattern_with_plurality(formats, number); + auto maybe_patterns = Unicode::select_pattern_with_plurality(formats, as_number()); if (maybe_patterns.has_value()) { patterns = maybe_patterns.release_value(); break; @@ -1326,10 +1400,9 @@ Optional> get_number_format_pattern(NumberFormat& nu StringView pattern; - Value number_value(number); - bool is_positive_zero = number_value.is_positive_zero(); - bool is_negative_zero = number_value.is_negative_zero(); - bool is_nan = number_value.is_nan(); + bool is_positive_zero = number.is_positive_zero(); + bool is_negative_zero = number.is_negative_zero(); + bool is_nan = number.is_nan(); // 11. Let signDisplay be numberFormat.[[SignDisplay]]. switch (number_format.sign_display()) { @@ -1342,7 +1415,7 @@ Optional> get_number_format_pattern(NumberFormat& nu // 13. Else if signDisplay is "auto", then case NumberFormat::SignDisplay::Auto: // a. If x is 0 or x > 0 or x is NaN, then - if (is_positive_zero || (number > 0) || is_nan) { + if (is_positive_zero || is_greater_than(number, 0) || is_nan) { // i. Let pattern be patterns.[[zeroPattern]]. pattern = patterns->zero_format; } @@ -1356,7 +1429,7 @@ Optional> get_number_format_pattern(NumberFormat& nu // 14. Else if signDisplay is "always", then case NumberFormat::SignDisplay::Always: // a. If x is 0 or x > 0 or x is NaN, then - if (is_positive_zero || (number > 0) || is_nan) { + if (is_positive_zero || is_greater_than(number, 0) || is_nan) { // i. Let pattern be patterns.[[positivePattern]]. pattern = patterns->positive_format; } @@ -1376,7 +1449,7 @@ Optional> get_number_format_pattern(NumberFormat& nu pattern = patterns->zero_format; } // c. Else if x > 0, then - else if (number > 0) { + else if (is_greater_than(number, 0)) { // i. Let pattern be patterns.[[positivePattern]]. pattern = patterns->positive_format; } @@ -1445,18 +1518,18 @@ Optional get_notation_sub_pattern(NumberFormat& number_format, int e } // 15.1.16 ComputeExponent ( numberFormat, x ), https://tc39.es/ecma402/#sec-computeexponent -int compute_exponent(NumberFormat& number_format, double number) +int compute_exponent(GlobalObject& global_object, NumberFormat& number_format, Value number) { // 1. If x = 0, then - if (number == 0.0) { + if (is_zero(number)) { // a. Return 0. return 0; } // 2. If x < 0, then - if (number < 0) { + if (is_less_than(number, 0)) { // a. Let x = -x. - number *= -1; + number = multiply(global_object, number, -1); } // 3. Let magnitude be the base 10 logarithm of x rounded down to the nearest integer. @@ -1466,13 +1539,13 @@ int compute_exponent(NumberFormat& number_format, double number) int exponent = compute_exponent_for_magnitude(number_format, magnitude); // 5. Let x be x Ɨ 10^(-exponent). - number *= pow(10, -exponent); + number = multiply_by_power(global_object, number, -exponent); // 6. Let formatNumberResult be FormatNumericToString(numberFormat, x). - auto format_number_result = format_numeric_to_string(number_format, number); + auto format_number_result = format_numeric_to_string(global_object, number_format, number); // 7. If formatNumberResult.[[RoundedNumber]] = 0, then - if (format_number_result.rounded_number == 0) { + if (is_zero(format_number_result.rounded_number)) { // a. Return exponent. return exponent; } diff --git a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.h b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.h index 516834510b..a61cca1ae3 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.h +++ b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.h @@ -212,8 +212,8 @@ private: }; struct FormatResult { - String formatted_string; // [[FormattedString]] - double rounded_number { 0.0 }; // [[RoundedNumber]] + String formatted_string; // [[FormattedString]] + Value rounded_number { 0.0 }; // [[RoundedNumber]] }; struct RawFormatResult : public FormatResult { @@ -223,17 +223,17 @@ struct RawFormatResult : public FormatResult { ThrowCompletionOr set_number_format_digit_options(GlobalObject& global_object, NumberFormatBase& intl_object, Object const& options, int default_min_fraction_digits, int default_max_fraction_digits, NumberFormat::Notation notation); ThrowCompletionOr initialize_number_format(GlobalObject& global_object, NumberFormat& number_format, Value locales_value, Value options_value); int currency_digits(StringView currency); -FormatResult format_numeric_to_string(NumberFormatBase& intl_object, double number); -Vector partition_number_pattern(NumberFormat& number_format, double number); -Vector partition_notation_sub_pattern(NumberFormat& number_format, double number, String formatted_string, int exponent); -String format_numeric(NumberFormat& number_format, double number); -Array* format_numeric_to_parts(GlobalObject& global_object, NumberFormat& number_format, double number); -RawFormatResult to_raw_precision(double number, int min_precision, int max_precision); -RawFormatResult to_raw_fixed(double number, int min_fraction, int max_fraction); +FormatResult format_numeric_to_string(GlobalObject& global_object, NumberFormatBase& intl_object, Value number); +Vector partition_number_pattern(GlobalObject& global_object, NumberFormat& number_format, Value number); +Vector partition_notation_sub_pattern(GlobalObject& global_object, NumberFormat& number_format, Value number, String formatted_string, int exponent); +String format_numeric(GlobalObject& global_object, NumberFormat& number_format, Value number); +Array* format_numeric_to_parts(GlobalObject& global_object, NumberFormat& number_format, Value number); +RawFormatResult to_raw_precision(GlobalObject& global_object, Value number, int min_precision, int max_precision); +RawFormatResult to_raw_fixed(GlobalObject& global_object, Value number, int min_fraction, int max_fraction); ThrowCompletionOr set_number_format_unit_options(GlobalObject& global_object, NumberFormat& intl_object, Object const& options); -Optional> get_number_format_pattern(NumberFormat& number_format, double number, Unicode::NumberFormat& found_pattern); +Optional> get_number_format_pattern(NumberFormat& number_format, Value number, Unicode::NumberFormat& found_pattern); Optional get_notation_sub_pattern(NumberFormat& number_format, int exponent); -int compute_exponent(NumberFormat& number_format, double number); +int compute_exponent(GlobalObject& global_object, NumberFormat& number_format, Value number); int compute_exponent_for_magnitude(NumberFormat& number_format, int magnitude); } diff --git a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormatFunction.cpp b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormatFunction.cpp index e758aa9d9f..1a9dc04829 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormatFunction.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormatFunction.cpp @@ -50,8 +50,7 @@ ThrowCompletionOr NumberFormatFunction::call() // 5. Return ? FormatNumeric(nf, x). // Note: Our implementation of FormatNumeric does not throw. - auto formatted = format_numeric(m_number_format, value.as_double()); - + auto formatted = format_numeric(global_object, m_number_format, value); return js_string(vm, move(formatted)); } diff --git a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormatPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormatPrototype.cpp index bcc02b7e79..be9e1de020 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormatPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormatPrototype.cpp @@ -76,7 +76,7 @@ JS_DEFINE_NATIVE_FUNCTION(NumberFormatPrototype::format_to_parts) // 4. Return ? FormatNumericToParts(nf, x). // Note: Our implementation of FormatNumericToParts does not throw. - return format_numeric_to_parts(global_object, *number_format, value.as_double()); + return format_numeric_to_parts(global_object, *number_format, value); } // 15.4.5 Intl.NumberFormat.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.numberformat.prototype.resolvedoptions diff --git a/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.cpp b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.cpp index 95722547fa..b93f92153c 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.cpp @@ -249,7 +249,7 @@ ThrowCompletionOr> partition_relative_time_patt auto patterns = find_patterns_for_tense_or_number(tense); // 20. Let fv be ! PartitionNumberPattern(relativeTimeFormat.[[NumberFormat]], value). - auto value_partitions = partition_number_pattern(relative_time_format.number_format(), value); + auto value_partitions = partition_number_pattern(global_object, relative_time_format.number_format(), Value(value)); // 21. Let pr be ! ResolvePlural(relativeTimeFormat.[[PluralRules]], value). // 22. Let pattern be po.[[]]. diff --git a/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp b/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp index 58b79ef83c..c7f75a8b79 100644 --- a/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp @@ -295,8 +295,7 @@ JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_locale_string) // 3. Return ? FormatNumeric(numberFormat, x). // Note: Our implementation of FormatNumeric does not throw. - auto formatted = Intl::format_numeric(*number_format, number_value.as_double()); - + auto formatted = Intl::format_numeric(global_object, *number_format, number_value); return js_string(vm, move(formatted)); }