소스 검색

LibJS+LibLocale: Port Intl.NumberFormat to String

Timothy Flynn 2 년 전
부모
커밋
0c2efa285a

+ 3 - 3
Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp

@@ -624,7 +624,7 @@ ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(VM& vm, Dat
             auto formatted_value = MUST_OR_THROW_OOM(format_numeric(vm, *number_format3, Value(value)));
 
             // iv. Append a new Record { [[Type]]: "fractionalSecond", [[Value]]: fv } as the last element of result.
-            result.append({ "fractionalSecond"sv, TRY_OR_THROW_OOM(vm, String::from_deprecated_string(formatted_value)) });
+            result.append({ "fractionalSecond"sv, move(formatted_value) });
         }
 
         // d. Else if p is equal to "dayPeriod", then
@@ -705,13 +705,13 @@ ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(VM& vm, Dat
             // viii. If f is "numeric", then
             case ::Locale::CalendarPatternStyle::Numeric:
                 // 1. Let fv be FormatNumeric(nf, v).
-                formatted_value = MUST_OR_THROW_OOM(format_numeric(vm, *number_format, Value(value)));
+                formatted_value = MUST_OR_THROW_OOM(format_numeric(vm, *number_format, Value(value))).to_deprecated_string();
                 break;
 
             // ix. Else if f is "2-digit", then
             case ::Locale::CalendarPatternStyle::TwoDigit:
                 // 1. Let fv be FormatNumeric(nf2, v).
-                formatted_value = MUST_OR_THROW_OOM(format_numeric(vm, *number_format2, Value(value)));
+                formatted_value = MUST_OR_THROW_OOM(format_numeric(vm, *number_format2, Value(value))).to_deprecated_string();
 
                 // 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.

+ 1 - 1
Userland/Libraries/LibJS/Runtime/Intl/DurationFormat.cpp

@@ -448,7 +448,7 @@ ThrowCompletionOr<Vector<PatternPartition>> partition_duration_format_pattern(VM
                 auto number = MUST_OR_THROW_OOM(format_numeric(vm, *number_format, MathematicalValue(value)));
 
                 // 5. Append the new Record { [[Type]]: unit, [[Value]]: num} to the end of result.
-                result.append({ unit, TRY_OR_THROW_OOM(vm, String::from_deprecated_string(number)) });
+                result.append({ unit, move(number) });
 
                 // 6. If unit is "hours" or "minutes", then
                 if (unit.is_one_of("hours"sv, "minutes"sv)) {

+ 8 - 4
Userland/Libraries/LibJS/Runtime/Intl/MathematicalValue.cpp

@@ -293,12 +293,16 @@ bool MathematicalValue::is_zero() const
         [](auto) { return false; });
 }
 
-DeprecatedString MathematicalValue::to_deprecated_string() const
+ThrowCompletionOr<String> MathematicalValue::to_string(VM& vm) const
 {
     return m_value.visit(
-        [](double value) { return number_to_deprecated_string(value, NumberToStringMode::WithoutExponent); },
-        [](Crypto::SignedBigInteger const& value) { return value.to_base_deprecated(10); },
-        [](auto) -> DeprecatedString { VERIFY_NOT_REACHED(); });
+        [&](double value) {
+            return number_to_string(vm, value, NumberToStringMode::WithoutExponent);
+        },
+        [&](Crypto::SignedBigInteger const& value) -> ThrowCompletionOr<String> {
+            return TRY_OR_THROW_OOM(vm, value.to_base(10));
+        },
+        [&](auto) -> ThrowCompletionOr<String> { VERIFY_NOT_REACHED(); });
 }
 
 Value MathematicalValue::to_value(VM& vm) const

+ 2 - 1
Userland/Libraries/LibJS/Runtime/Intl/MathematicalValue.h

@@ -7,6 +7,7 @@
 #pragma once
 
 #include <AK/Checked.h>
+#include <AK/String.h>
 #include <AK/Variant.h>
 #include <LibCrypto/BigInt/SignedBigInteger.h>
 #include <LibJS/Runtime/Value.h>
@@ -87,7 +88,7 @@ public:
     bool is_positive() const;
     bool is_zero() const;
 
-    DeprecatedString to_deprecated_string() const;
+    ThrowCompletionOr<String> to_string(VM&) const;
     Value to_value(VM&) const;
 
 private:

+ 110 - 95
Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp

@@ -14,6 +14,7 @@
 #include <LibJS/Runtime/Intl/NumberFormat.h>
 #include <LibJS/Runtime/Intl/NumberFormatFunction.h>
 #include <LibJS/Runtime/Intl/PluralRules.h>
+#include <LibJS/Runtime/ThrowableStringBuilder.h>
 #include <LibUnicode/CurrencyCode.h>
 #include <math.h>
 #include <stdlib.h>
@@ -377,7 +378,7 @@ int currency_digits(StringView currency)
 
 // 15.5.3 FormatNumericToString ( intlObject, x ), https://tc39.es/ecma402/#sec-formatnumberstring
 // 1.5.3 FormatNumericToString ( intlObject, x ), https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/proposed.html#sec-formatnumberstring
-FormatResult format_numeric_to_string(NumberFormatBase const& intl_object, MathematicalValue number)
+ThrowCompletionOr<FormatResult> format_numeric_to_string(VM& vm, NumberFormatBase const& intl_object, MathematicalValue number)
 {
     bool is_negative = false;
 
@@ -416,23 +417,23 @@ FormatResult format_numeric_to_string(NumberFormatBase const& intl_object, Mathe
     // 4. If intlObject.[[RoundingType]] is significantDigits, then
     case NumberFormatBase::RoundingType::SignificantDigits:
         // a. Let result be ToRawPrecision(x, intlObject.[[MinimumSignificantDigits]], intlObject.[[MaximumSignificantDigits]], unsignedRoundingMode).
-        result = to_raw_precision(number, intl_object.min_significant_digits(), intl_object.max_significant_digits(), unsigned_rounding_mode);
+        result = MUST_OR_THROW_OOM(to_raw_precision(vm, number, intl_object.min_significant_digits(), intl_object.max_significant_digits(), unsigned_rounding_mode));
         break;
 
     // 5. Else if intlObject.[[RoundingType]] is fractionDigits, then
     case NumberFormatBase::RoundingType::FractionDigits:
         // a. Let result be ToRawFixed(x, intlObject.[[MinimumFractionDigits]], intlObject.[[MaximumFractionDigits]], intlObject.[[RoundingIncrement]], unsignedRoundingMode).
-        result = to_raw_fixed(number, intl_object.min_fraction_digits(), intl_object.max_fraction_digits(), intl_object.rounding_increment(), unsigned_rounding_mode);
+        result = MUST_OR_THROW_OOM(to_raw_fixed(vm, number, intl_object.min_fraction_digits(), intl_object.max_fraction_digits(), intl_object.rounding_increment(), unsigned_rounding_mode));
         break;
 
     // 6. Else,
     case NumberFormatBase::RoundingType::MorePrecision:
     case NumberFormatBase::RoundingType::LessPrecision: {
         // a. Let sResult be ToRawPrecision(x, intlObject.[[MinimumSignificantDigits]], intlObject.[[MaximumSignificantDigits]], unsignedRoundingMode).
-        auto significant_result = to_raw_precision(number, intl_object.min_significant_digits(), intl_object.max_significant_digits(), unsigned_rounding_mode);
+        auto significant_result = MUST_OR_THROW_OOM(to_raw_precision(vm, number, intl_object.min_significant_digits(), intl_object.max_significant_digits(), unsigned_rounding_mode));
 
         // b. Let fResult be ToRawFixed(x, intlObject.[[MinimumFractionDigits]], intlObject.[[MaximumFractionDigits]], intlObject.[[RoundingIncrement]], unsignedRoundingMode).
-        auto fraction_result = to_raw_fixed(number, intl_object.min_fraction_digits(), intl_object.max_fraction_digits(), intl_object.rounding_increment(), unsigned_rounding_mode);
+        auto fraction_result = MUST_OR_THROW_OOM(to_raw_fixed(vm, number, intl_object.min_fraction_digits(), intl_object.max_fraction_digits(), intl_object.rounding_increment(), unsigned_rounding_mode));
 
         // c. If intlObj.[[RoundingType]] is morePrecision, then
         if (intl_object.rounding_type() == NumberFormatBase::RoundingType::MorePrecision) {
@@ -480,9 +481,9 @@ FormatResult format_numeric_to_string(NumberFormatBase const& intl_object, Mathe
     // 9. If intlObject.[[TrailingZeroDisplay]] is "stripIfInteger" and x modulo 1 = 0, then
     if ((intl_object.trailing_zero_display() == NumberFormat::TrailingZeroDisplay::StripIfInteger) && number.modulo_is_zero(1)) {
         // a. If string contains ".", then
-        if (auto index = string.find('.'); index.has_value()) {
+        if (auto index = string.find_byte_offset('.'); index.has_value()) {
             // i. Set string to the substring of string from index 0 to the index of ".".
-            string = string.substring(0, *index);
+            string = TRY_OR_THROW_OOM(vm, string.substring_from_byte_offset(0, *index));
         }
     }
 
@@ -495,10 +496,10 @@ FormatResult format_numeric_to_string(NumberFormatBase const& intl_object, Mathe
     // 12. If int < minInteger, then
     if (digits < min_integer) {
         // a. Let forwardZeros be the String consisting of minInteger - int occurrences of the code unit 0x0030 (DIGIT ZERO).
-        auto forward_zeros = DeprecatedString::repeated('0', min_integer - digits);
+        auto forward_zeros = TRY_OR_THROW_OOM(vm, String::repeated('0', min_integer - digits));
 
         // b. Set string to the string-concatenation of forwardZeros and string.
-        string = DeprecatedString::formatted("{}{}", forward_zeros, string);
+        string = TRY_OR_THROW_OOM(vm, String::formatted("{}{}", forward_zeros, string));
     }
 
     // 13. If isNegative and x is 0, then
@@ -513,7 +514,7 @@ FormatResult format_numeric_to_string(NumberFormatBase const& intl_object, Mathe
     }
 
     // 15. Return the Record { [[RoundedNumber]]: x, [[FormattedString]]: string }.
-    return { move(string), move(number) };
+    return FormatResult { move(string), move(number) };
 }
 
 // 15.5.4 PartitionNumberPattern ( numberFormat, x ), https://tc39.es/ecma402/#sec-partitionnumberpattern
@@ -523,24 +524,27 @@ ThrowCompletionOr<Vector<PatternPartition>> partition_number_pattern(VM& vm, Num
     // 1. Let exponent be 0.
     int exponent = 0;
 
-    DeprecatedString formatted_string;
+    String formatted_string;
 
     // 2. If x is not-a-number, then
     if (number.is_nan()) {
         // a. Let n be an implementation- and locale-dependent (ILD) String value indicating the NaN value.
-        formatted_string = ::Locale::get_number_system_symbol(number_format.data_locale(), number_format.numbering_system(), ::Locale::NumericSymbol::NaN).value_or("NaN"sv);
+        auto symbol = ::Locale::get_number_system_symbol(number_format.data_locale(), number_format.numbering_system(), ::Locale::NumericSymbol::NaN).value_or("NaN"sv);
+        formatted_string = TRY_OR_THROW_OOM(vm, String::from_utf8(symbol));
     }
     // 3. Else if x is positive-infinity, then
     else if (number.is_positive_infinity()) {
         // a. Let n be an ILD String value indicating positive infinity.
-        formatted_string = ::Locale::get_number_system_symbol(number_format.data_locale(), number_format.numbering_system(), ::Locale::NumericSymbol::Infinity).value_or("infinity"sv);
+        auto symbol = ::Locale::get_number_system_symbol(number_format.data_locale(), number_format.numbering_system(), ::Locale::NumericSymbol::Infinity).value_or("infinity"sv);
+        formatted_string = TRY_OR_THROW_OOM(vm, String::from_utf8(symbol));
     }
     // 4. Else if x is negative-infinity, then
     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.
-        formatted_string = ::Locale::get_number_system_symbol(number_format.data_locale(), number_format.numbering_system(), ::Locale::NumericSymbol::Infinity).value_or("infinity"sv);
+        auto symbol = ::Locale::get_number_system_symbol(number_format.data_locale(), number_format.numbering_system(), ::Locale::NumericSymbol::Infinity).value_or("infinity"sv);
+        formatted_string = TRY_OR_THROW_OOM(vm, String::from_utf8(symbol));
     }
     // 5. Else,
     else {
@@ -554,14 +558,14 @@ ThrowCompletionOr<Vector<PatternPartition>> partition_number_pattern(VM& vm, Num
                 number = number.multiplied_by(100);
 
             // iii. Let exponent be ComputeExponent(numberFormat, x).
-            exponent = compute_exponent(number_format, number);
+            exponent = MUST_OR_THROW_OOM(compute_exponent(vm, number_format, number));
 
             // iv. Let x be x × 10^-exponent.
             number = number.multiplied_by_power(-exponent);
         }
 
         // b. Let formatNumberResult be FormatNumericToString(numberFormat, x).
-        auto format_number_result = format_numeric_to_string(number_format, move(number));
+        auto format_number_result = MUST_OR_THROW_OOM(format_numeric_to_string(vm, number_format, move(number)));
 
         // c. Let n be formatNumberResult.[[FormattedString]].
         formatted_string = move(format_number_result.formatted_string);
@@ -573,7 +577,7 @@ ThrowCompletionOr<Vector<PatternPartition>> partition_number_pattern(VM& vm, Num
     ::Locale::NumberFormat found_pattern {};
 
     // 6. Let pattern be GetNumberFormatPattern(numberFormat, x).
-    auto pattern = get_number_format_pattern(vm, number_format, number, found_pattern);
+    auto pattern = MUST_OR_THROW_OOM(get_number_format_pattern(vm, number_format, number, found_pattern));
     if (!pattern.has_value())
         return Vector<PatternPartition> {};
 
@@ -716,7 +720,7 @@ static Vector<StringView> separate_integer_into_groups(::Locale::NumberGroupings
 
 // 15.5.5 PartitionNotationSubPattern ( numberFormat, x, n, exponent ), https://tc39.es/ecma402/#sec-partitionnotationsubpattern
 // 1.5.5 PartitionNotationSubPattern ( numberFormat, x, n, exponent ), https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/proposed.html#sec-partitionnotationsubpattern
-ThrowCompletionOr<Vector<PatternPartition>> partition_notation_sub_pattern(VM& vm, NumberFormat& number_format, MathematicalValue const& number, DeprecatedString formatted_string, int exponent)
+ThrowCompletionOr<Vector<PatternPartition>> partition_notation_sub_pattern(VM& vm, NumberFormat& number_format, MathematicalValue const& number, String formatted_string, int exponent)
 {
     // 1. Let result be a new empty List.
     Vector<PatternPartition> result;
@@ -728,12 +732,12 @@ ThrowCompletionOr<Vector<PatternPartition>> partition_notation_sub_pattern(VM& v
     // 2. If x is NaN, then
     if (number.is_nan()) {
         // a. Append a new Record { [[Type]]: "nan", [[Value]]: n } as the last element of result.
-        result.append({ "nan"sv, TRY_OR_THROW_OOM(vm, String::from_deprecated_string(formatted_string)) });
+        result.append({ "nan"sv, move(formatted_string) });
     }
     // 3. Else if x is a non-finite Number, then
     else if (number.is_positive_infinity() || number.is_negative_infinity()) {
         // a. Append a new Record { [[Type]]: "infinity", [[Value]]: n } as the last element of result.
-        result.append({ "infinity"sv, TRY_OR_THROW_OOM(vm, String::from_deprecated_string(formatted_string)) });
+        result.append({ "infinity"sv, move(formatted_string) });
     }
     // 4. Else,
     else {
@@ -773,10 +777,10 @@ ThrowCompletionOr<Vector<PatternPartition>> partition_notation_sub_pattern(VM& v
                 //         iv. Set position to position + 1.
                 //     g. Set n to transliterated.
                 // 2. Else use an implementation dependent algorithm to map n to the appropriate representation of n in the given numbering system.
-                formatted_string = ::Locale::replace_digits_for_number_system(number_format.numbering_system(), formatted_string);
+                formatted_string = TRY_OR_THROW_OOM(vm, ::Locale::replace_digits_for_number_system(number_format.numbering_system(), formatted_string));
 
                 // 3. Let decimalSepIndex be StringIndexOf(n, ".", 0).
-                auto decimal_sep_index = formatted_string.find('.');
+                auto decimal_sep_index = formatted_string.find_byte_offset('.');
 
                 StringView integer;
                 Optional<StringView> fraction;
@@ -784,14 +788,15 @@ ThrowCompletionOr<Vector<PatternPartition>> partition_notation_sub_pattern(VM& v
                 // 4. If decimalSepIndex > 0, then
                 if (decimal_sep_index.has_value() && (*decimal_sep_index > 0)) {
                     // a. Let integer be the substring of n from position 0, inclusive, to position decimalSepIndex, exclusive.
-                    integer = formatted_string.substring_view(0, *decimal_sep_index);
+                    integer = formatted_string.bytes_as_string_view().substring_view(0, *decimal_sep_index);
+
                     // b. Let fraction be the substring of n from position decimalSepIndex, exclusive, to the end of n.
-                    fraction = formatted_string.substring_view(*decimal_sep_index + 1);
+                    fraction = formatted_string.bytes_as_string_view().substring_view(*decimal_sep_index + 1);
                 }
                 // 5. Else,
                 else {
                     // a. Let integer be n.
-                    integer = formatted_string;
+                    integer = formatted_string.bytes_as_string_view();
                     // b. Let fraction be undefined.
                 }
 
@@ -874,14 +879,14 @@ ThrowCompletionOr<Vector<PatternPartition>> partition_notation_sub_pattern(VM& v
 
                 // 2. Let exponentResult be ToRawFixed(exponent, 0, 0, 1, undefined).
                 auto exponent_value = MathematicalValue { static_cast<double>(exponent) };
-                auto exponent_result = to_raw_fixed(exponent_value, 0, 0, 1, {});
+                auto exponent_result = MUST_OR_THROW_OOM(to_raw_fixed(vm, exponent_value, 0, 0, 1, {}));
 
                 // 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.
-                exponent_result.formatted_string = ::Locale::replace_digits_for_number_system(number_format.numbering_system(), exponent_result.formatted_string);
+                exponent_result.formatted_string = TRY_OR_THROW_OOM(vm, ::Locale::replace_digits_for_number_system(number_format.numbering_system(), exponent_result.formatted_string));
 
                 // 3. Append a new Record { [[Type]]: "exponentInteger", [[Value]]: exponentResult.[[FormattedString]] } as the last element of result.
-                result.append({ "exponentInteger"sv, TRY_OR_THROW_OOM(vm, String::from_deprecated_string(exponent_result.formatted_string)) });
+                result.append({ "exponentInteger"sv, move(exponent_result.formatted_string) });
             }
             // viii. Else,
             else {
@@ -899,22 +904,22 @@ ThrowCompletionOr<Vector<PatternPartition>> partition_notation_sub_pattern(VM& v
 }
 
 // 15.5.6 FormatNumeric ( numberFormat, x ), https://tc39.es/ecma402/#sec-formatnumber
-ThrowCompletionOr<DeprecatedString> format_numeric(VM& vm, NumberFormat& number_format, MathematicalValue number)
+ThrowCompletionOr<String> format_numeric(VM& vm, NumberFormat& number_format, MathematicalValue number)
 {
     // 1. Let parts be ? PartitionNumberPattern(numberFormat, x).
     auto parts = TRY(partition_number_pattern(vm, number_format, move(number)));
 
     // 2. Let result be the empty String.
-    StringBuilder result;
+    ThrowableStringBuilder result(vm);
 
     // 3. For each Record { [[Type]], [[Value]] } part in parts, do
     for (auto& part : parts) {
         // a. Set result to the string-concatenation of result and part.[[Value]].
-        result.append(move(part.value));
+        TRY(result.append(part.value));
     }
 
     // 4. Return result.
-    return result.build();
+    return result.to_string();
 }
 
 // 15.5.7 FormatNumericToParts ( numberFormat, x ), https://tc39.es/ecma402/#sec-formatnumbertoparts
@@ -953,7 +958,7 @@ ThrowCompletionOr<Array*> format_numeric_to_parts(VM& vm, NumberFormat& number_f
     return result.ptr();
 }
 
-static DeprecatedString cut_trailing_zeroes(StringView string, int cut)
+static ErrorOr<String> cut_trailing_zeroes(StringView string, int cut)
 {
     // These steps are exactly the same between ToRawPrecision and ToRawFixed.
 
@@ -972,7 +977,7 @@ static DeprecatedString cut_trailing_zeroes(StringView string, int cut)
         string = string.substring_view(0, string.length() - 1);
     }
 
-    return string.to_deprecated_string();
+    return String::from_utf8(string);
 }
 
 enum class PreferredResult {
@@ -980,15 +985,16 @@ enum class PreferredResult {
     GreaterThanNumber,
 };
 
+struct RawPrecisionResult {
+    MathematicalValue number;
+    int exponent { 0 };
+    MathematicalValue rounded;
+};
+
 // ToRawPrecisionFn, https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/proposed.html#eqn-ToRawPrecisionFn
-static auto to_raw_precision_function(MathematicalValue const& number, int precision, PreferredResult mode)
+static ThrowCompletionOr<RawPrecisionResult> to_raw_precision_function(VM& vm, MathematicalValue const& number, int precision, PreferredResult mode)
 {
-    struct {
-        MathematicalValue number;
-        int exponent { 0 };
-        MathematicalValue rounded;
-    } result {};
-
+    RawPrecisionResult result {};
     result.exponent = number.logarithmic_floor();
 
     if (number.is_number()) {
@@ -1008,8 +1014,8 @@ static auto to_raw_precision_function(MathematicalValue const& number, int preci
         result.number = number.divided_by_power(result.exponent - precision);
 
         // FIXME: Can we do this without string conversion?
-        auto digits = result.number.to_deprecated_string();
-        auto digit = digits.substring_view(digits.length() - 1);
+        auto digits = MUST_OR_THROW_OOM(result.number.to_string(vm));
+        auto digit = digits.bytes_as_string_view().substring_view(digits.bytes_as_string_view().length() - 1);
 
         result.number = result.number.divided_by(10);
 
@@ -1023,7 +1029,7 @@ static auto to_raw_precision_function(MathematicalValue const& number, int preci
 
 // 15.5.8 ToRawPrecision ( x, minPrecision, maxPrecision ), https://tc39.es/ecma402/#sec-torawprecision
 // 1.5.8 ToRawPrecision ( x, minPrecision, maxPrecision, unsignedRoundingMode ), https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/proposed.html#sec-torawprecision
-RawFormatResult to_raw_precision(MathematicalValue const& number, int min_precision, int max_precision, Optional<NumberFormat::UnsignedRoundingMode> const& unsigned_rounding_mode)
+ThrowCompletionOr<RawFormatResult> to_raw_precision(VM& vm, MathematicalValue const& number, int min_precision, int max_precision, Optional<NumberFormat::UnsignedRoundingMode> const& unsigned_rounding_mode)
 {
     RawFormatResult result {};
 
@@ -1034,7 +1040,7 @@ RawFormatResult to_raw_precision(MathematicalValue const& number, int min_precis
     // 2. If x = 0, then
     if (number.is_zero()) {
         // a. Let m be the String consisting of p occurrences of the code unit 0x0030 (DIGIT ZERO).
-        result.formatted_string = DeprecatedString::repeated('0', precision);
+        result.formatted_string = TRY_OR_THROW_OOM(vm, String::repeated('0', precision));
 
         // b. Let e be 0.
         exponent = 0;
@@ -1045,10 +1051,10 @@ RawFormatResult to_raw_precision(MathematicalValue const& number, int min_precis
     // 3. Else,
     else {
         // a. Let n1 and e1 each be an integer and r1 a mathematical value, with r1 = ToRawPrecisionFn(n1, e1, p), such that r1 ≤ x and r1 is maximized.
-        auto [number1, exponent1, rounded1] = to_raw_precision_function(number, precision, PreferredResult::LessThanNumber);
+        auto [number1, exponent1, rounded1] = MUST_OR_THROW_OOM(to_raw_precision_function(vm, number, precision, PreferredResult::LessThanNumber));
 
         // b. Let n2 and e2 each be an integer and r2 a mathematical value, with r2 = ToRawPrecisionFn(n2, e2, p), such that r2 ≥ x and r2 is minimized.
-        auto [number2, exponent2, rounded2] = to_raw_precision_function(number, precision, PreferredResult::GreaterThanNumber);
+        auto [number2, exponent2, rounded2] = MUST_OR_THROW_OOM(to_raw_precision_function(vm, number, precision, PreferredResult::GreaterThanNumber));
 
         // c. Let r be ApplyUnsignedRoundingMode(x, r1, r2, unsignedRoundingMode).
         auto rounded = apply_unsigned_rounding_mode(number, rounded1, rounded2, unsigned_rounding_mode);
@@ -1079,16 +1085,17 @@ RawFormatResult to_raw_precision(MathematicalValue const& number, int min_precis
         }
 
         // f. 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.to_deprecated_string();
+        result.formatted_string = MUST_OR_THROW_OOM(n.to_string(vm));
     }
 
     // 4. If e ≥ (p – 1), then
     if (exponent >= (precision - 1)) {
         // a. Set m to the string-concatenation of m and e - p + 1 occurrences of the code unit 0x0030 (DIGIT ZERO).
-        result.formatted_string = DeprecatedString::formatted(
-            "{}{}",
-            result.formatted_string,
-            DeprecatedString::repeated('0', exponent - precision + 1));
+        result.formatted_string = TRY_OR_THROW_OOM(vm,
+            String::formatted(
+                "{}{}",
+                result.formatted_string,
+                TRY_OR_THROW_OOM(vm, String::repeated('0', exponent - precision + 1))));
 
         // b. Let int be e + 1.
         result.digits = exponent + 1;
@@ -1096,10 +1103,11 @@ RawFormatResult to_raw_precision(MathematicalValue const& number, int min_precis
     // 5. Else if e ≥ 0, then
     else if (exponent >= 0) {
         // a. Set m to the string-concatenation of the first e + 1 code units of m, the code unit 0x002E (FULL STOP), and the remaining p - (e + 1) code units of m.
-        result.formatted_string = DeprecatedString::formatted(
-            "{}.{}",
-            result.formatted_string.substring_view(0, exponent + 1),
-            result.formatted_string.substring_view(exponent + 1));
+        result.formatted_string = TRY_OR_THROW_OOM(vm,
+            String::formatted(
+                "{}.{}",
+                result.formatted_string.bytes_as_string_view().substring_view(0, exponent + 1),
+                result.formatted_string.bytes_as_string_view().substring_view(exponent + 1)));
 
         // b. Let int be e + 1.
         result.digits = exponent + 1;
@@ -1108,10 +1116,11 @@ RawFormatResult to_raw_precision(MathematicalValue const& number, int min_precis
     else {
         // a. Assert: e < 0.
         // b. Set m to the string-concatenation of "0.", -(e + 1) occurrences of the code unit 0x0030 (DIGIT ZERO), and m.
-        result.formatted_string = DeprecatedString::formatted(
-            "0.{}{}",
-            DeprecatedString::repeated('0', -1 * (exponent + 1)),
-            result.formatted_string);
+        result.formatted_string = TRY_OR_THROW_OOM(vm,
+            String::formatted(
+                "0.{}{}",
+                TRY_OR_THROW_OOM(vm, String::repeated('0', -1 * (exponent + 1))),
+                result.formatted_string));
 
         // c. Let int be 1.
         result.digits = 1;
@@ -1123,7 +1132,7 @@ RawFormatResult to_raw_precision(MathematicalValue const& number, int min_precis
         int cut = max_precision - min_precision;
 
         // Steps 8b-8c are implemented by cut_trailing_zeroes.
-        result.formatted_string = cut_trailing_zeroes(result.formatted_string, cut);
+        result.formatted_string = TRY_OR_THROW_OOM(vm, cut_trailing_zeroes(result.formatted_string, cut));
     }
 
     // 8. Return the Record { [[FormattedString]]: m, [[RoundedNumber]]: xFinal, [[IntegerDigitsCount]]: int, [[RoundingMagnitude]]: e–p+1 }.
@@ -1131,13 +1140,15 @@ RawFormatResult to_raw_precision(MathematicalValue const& number, int min_precis
     return result;
 }
 
+struct RawFixedResult {
+    MathematicalValue number;
+    MathematicalValue rounded;
+};
+
 // ToRawFixedFn, https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/proposed.html#eqn-ToRawFixedFn
-static auto to_raw_fixed_function(MathematicalValue const& number, int fraction, int rounding_increment, PreferredResult mode)
+static ThrowCompletionOr<RawFixedResult> to_raw_fixed_function(VM& vm, MathematicalValue const& number, int fraction, int rounding_increment, PreferredResult mode)
 {
-    struct {
-        MathematicalValue number;
-        MathematicalValue rounded;
-    } result {};
+    RawFixedResult result {};
 
     if (number.is_number()) {
         result.number = number.multiplied_by_power(fraction);
@@ -1156,8 +1167,8 @@ static auto to_raw_fixed_function(MathematicalValue const& number, int fraction,
         result.number = number.multiplied_by_power(fraction - 1);
 
         // FIXME: Can we do this without string conversion?
-        auto digits = result.number.to_deprecated_string();
-        auto digit = digits.substring_view(digits.length() - 1);
+        auto digits = MUST_OR_THROW_OOM(result.number.to_string(vm));
+        auto digit = digits.bytes_as_string_view().substring_view(digits.bytes_as_string_view().length() - 1);
 
         result.number = result.number.multiplied_by(10);
 
@@ -1182,7 +1193,7 @@ static auto to_raw_fixed_function(MathematicalValue const& number, int fraction,
 
 // 15.5.9 ToRawFixed ( x, minInteger, minFraction, maxFraction ), https://tc39.es/ecma402/#sec-torawfixed
 // 1.5.9 ToRawFixed ( x, minFraction, maxFraction, roundingIncrement, unsignedRoundingMode ), https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/proposed.html#sec-torawfixed
-RawFormatResult to_raw_fixed(MathematicalValue const& number, int min_fraction, int max_fraction, int rounding_increment, Optional<NumberFormat::UnsignedRoundingMode> const& unsigned_rounding_mode)
+ThrowCompletionOr<RawFormatResult> to_raw_fixed(VM& vm, MathematicalValue const& number, int min_fraction, int max_fraction, int rounding_increment, Optional<NumberFormat::UnsignedRoundingMode> const& unsigned_rounding_mode)
 {
     RawFormatResult result {};
 
@@ -1190,10 +1201,10 @@ RawFormatResult to_raw_fixed(MathematicalValue const& number, int min_fraction,
     int fraction = max_fraction;
 
     // 2. Let n1 be an integer and r1 a mathematical value, with r1 = ToRawFixedFn(n1, f), such that n1 modulo roundingIncrement = 0, r1 ≤ x, and r1 is maximized.
-    auto [number1, rounded1] = to_raw_fixed_function(number, fraction, rounding_increment, PreferredResult::LessThanNumber);
+    auto [number1, rounded1] = MUST_OR_THROW_OOM(to_raw_fixed_function(vm, number, fraction, rounding_increment, PreferredResult::LessThanNumber));
 
     // 3. Let n2 be an integer and r2 a mathematical value, with r2 = ToRawFixedFn(n2, f), such that n2 modulo roundingIncrement = 0, r2 ≥ x, and r2 is minimized.
-    auto [number2, rounded2] = to_raw_fixed_function(number, fraction, rounding_increment, PreferredResult::GreaterThanNumber);
+    auto [number2, rounded2] = MUST_OR_THROW_OOM(to_raw_fixed_function(vm, number, fraction, rounding_increment, PreferredResult::GreaterThanNumber));
 
     // 4. Let r be ApplyUnsignedRoundingMode(x, r1, r2, unsignedRoundingMode).
     auto rounded = apply_unsigned_rounding_mode(number, rounded1, rounded2, unsigned_rounding_mode);
@@ -1218,45 +1229,47 @@ RawFormatResult to_raw_fixed(MathematicalValue const& number, int min_fraction,
     }
 
     // 7. If n = 0, let m be "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.is_zero() ? DeprecatedString("0"sv) : n.to_deprecated_string();
+    result.formatted_string = n.is_zero()
+        ? String::from_utf8_short_string("0"sv)
+        : MUST_OR_THROW_OOM(n.to_string(vm));
 
     // 8. If f ≠ 0, then
     if (fraction != 0) {
         // a. Let k be the length of m.
-        auto decimals = result.formatted_string.length();
+        auto decimals = result.formatted_string.bytes_as_string_view().length();
 
         // b. If k ≤ f, then
         if (decimals <= static_cast<size_t>(fraction)) {
             // i. Let z be the String value consisting of f + 1 - k occurrences of the code unit 0x0030 (DIGIT ZERO).
-            auto zeroes = DeprecatedString::repeated('0', fraction + 1 - decimals);
+            auto zeroes = TRY_OR_THROW_OOM(vm, String::repeated('0', fraction + 1 - decimals));
 
             // ii. Let m be the string-concatenation of z and m.
-            result.formatted_string = DeprecatedString::formatted("{}{}", zeroes, result.formatted_string);
+            result.formatted_string = TRY_OR_THROW_OOM(vm, String::formatted("{}{}", zeroes, result.formatted_string));
 
             // iii. Let k be f + 1.
             decimals = fraction + 1;
         }
 
         // c. Let a be the first k - f code units of m, and let b be the remaining f code units of m.
-        auto a = result.formatted_string.substring_view(0, decimals - fraction);
-        auto b = result.formatted_string.substring_view(decimals - fraction, fraction);
+        auto a = result.formatted_string.bytes_as_string_view().substring_view(0, decimals - fraction);
+        auto b = result.formatted_string.bytes_as_string_view().substring_view(decimals - fraction, fraction);
 
         // d. Let m be the string-concatenation of a, ".", and b.
-        result.formatted_string = DeprecatedString::formatted("{}.{}", a, b);
+        result.formatted_string = TRY_OR_THROW_OOM(vm, String::formatted("{}.{}", a, b));
 
         // e. Let int be the length of a.
         result.digits = a.length();
     }
     // 9. Else, let int be the length of m.
     else {
-        result.digits = result.formatted_string.length();
+        result.digits = result.formatted_string.bytes_as_string_view().length();
     }
 
     // 10. Let cut be maxFraction – minFraction.
     int cut = max_fraction - min_fraction;
 
     // Steps 11-12 are implemented by cut_trailing_zeroes.
-    result.formatted_string = cut_trailing_zeroes(result.formatted_string, cut);
+    result.formatted_string = TRY_OR_THROW_OOM(vm, cut_trailing_zeroes(result.formatted_string, cut));
 
     // 13. Return the Record { [[FormattedString]]: m, [[RoundedNumber]]: xFinal, [[IntegerDigitsCount]]: int, [[RoundingMagnitude]]: –f }.
     result.rounding_magnitude = -fraction;
@@ -1265,7 +1278,7 @@ RawFormatResult to_raw_fixed(MathematicalValue const& number, int min_fraction,
 
 // 15.5.11 GetNumberFormatPattern ( numberFormat, x ), https://tc39.es/ecma402/#sec-getnumberformatpattern
 // 1.5.11 GetNumberFormatPattern ( numberFormat, x ), https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/proposed.html#sec-getnumberformatpattern
-Optional<Variant<StringView, DeprecatedString>> get_number_format_pattern(VM& vm, NumberFormat& number_format, MathematicalValue const& number, ::Locale::NumberFormat& found_pattern)
+ThrowCompletionOr<Optional<Variant<StringView, String>>> get_number_format_pattern(VM& vm, NumberFormat& number_format, MathematicalValue const& number, ::Locale::NumberFormat& found_pattern)
 {
     // 1. Let localeData be %NumberFormat%.[[LocaleData]].
     // 2. Let dataLocale be numberFormat.[[DataLocale]].
@@ -1292,7 +1305,7 @@ Optional<Variant<StringView, DeprecatedString>> get_number_format_pattern(VM& vm
         // e. Let patterns be patterns.[[<unit>]].
         // f. Let patterns be patterns.[[<unitDisplay>]].
         auto formats = ::Locale::get_unit_formats(number_format.data_locale(), number_format.unit(), number_format.unit_display());
-        auto plurality = resolve_plural(number_format, ::Locale::PluralForm::Cardinal, number.to_value(vm));
+        auto plurality = MUST_OR_THROW_OOM(resolve_plural(vm, number_format, ::Locale::PluralForm::Cardinal, number.to_value(vm)));
 
         if (auto it = formats.find_if([&](auto& p) { return p.plurality == plurality; }); it != formats.end())
             patterns = move(*it);
@@ -1315,7 +1328,7 @@ Optional<Variant<StringView, DeprecatedString>> get_number_format_pattern(VM& vm
         // Handling of other [[CurrencyDisplay]] options will occur after [[SignDisplay]].
         if (number_format.currency_display() == NumberFormat::CurrencyDisplay::Name) {
             auto formats = ::Locale::get_compact_number_system_formats(number_format.data_locale(), number_format.numbering_system(), ::Locale::CompactNumberFormatType::CurrencyUnit);
-            auto plurality = resolve_plural(number_format, ::Locale::PluralForm::Cardinal, number.to_value(vm));
+            auto plurality = MUST_OR_THROW_OOM(resolve_plural(vm, number_format, ::Locale::PluralForm::Cardinal, number.to_value(vm)));
 
             if (auto it = formats.find_if([&](auto& p) { return p.plurality == plurality; }); it != formats.end()) {
                 patterns = move(*it);
@@ -1346,7 +1359,7 @@ Optional<Variant<StringView, DeprecatedString>> get_number_format_pattern(VM& vm
     }
 
     if (!patterns.has_value())
-        return {};
+        return OptionalNone {};
 
     StringView pattern;
 
@@ -1430,7 +1443,7 @@ Optional<Variant<StringView, DeprecatedString>> get_number_format_pattern(VM& vm
     // we might need to mutate the format pattern to inject a space between the currency display and
     // the currency number.
     if (number_format.style() == NumberFormat::Style::Currency) {
-        auto modified_pattern = ::Locale::augment_currency_format_pattern(number_format.resolve_currency_display(), pattern);
+        auto modified_pattern = TRY_OR_THROW_OOM(vm, ::Locale::augment_currency_format_pattern(number_format.resolve_currency_display(), pattern));
         if (modified_pattern.has_value())
             return modified_pattern.release_value();
     }
@@ -1478,7 +1491,7 @@ Optional<StringView> get_notation_sub_pattern(NumberFormat& number_format, int e
 }
 
 // 15.5.13 ComputeExponent ( numberFormat, x ), https://tc39.es/ecma402/#sec-computeexponent
-int compute_exponent(NumberFormat& number_format, MathematicalValue number)
+ThrowCompletionOr<int> compute_exponent(VM& vm, NumberFormat& number_format, MathematicalValue number)
 {
     // 1. If x = 0, then
     if (number.is_zero()) {
@@ -1502,7 +1515,7 @@ int compute_exponent(NumberFormat& number_format, MathematicalValue number)
     number = number.multiplied_by_power(-exponent);
 
     // 6. Let formatNumberResult be FormatNumericToString(numberFormat, x).
-    auto format_number_result = format_numeric_to_string(number_format, move(number));
+    auto format_number_result = MUST_OR_THROW_OOM(format_numeric_to_string(vm, number_format, move(number)));
 
     // 7. If formatNumberResult.[[RoundedNumber]] = 0, then
     if (format_number_result.rounded_number.is_zero()) {
@@ -1604,7 +1617,7 @@ ThrowCompletionOr<MathematicalValue> to_intl_mathematical_value(VM& vm, Value va
 
     // 3. If Type(primValue) is String,
     // a.     Let str be primValue.
-    auto string = TRY(primitive_value.as_string().deprecated_string());
+    auto string = TRY(primitive_value.as_string().utf8_string());
 
     // Step 4 handled separately by the FIXME above.
 
@@ -1613,7 +1626,7 @@ ThrowCompletionOr<MathematicalValue> to_intl_mathematical_value(VM& vm, Value va
     auto mathematical_value = TRY(primitive_value.to_number(vm)).as_double();
 
     // 7. If mv is 0 and the first non white space code point in str is -, return negative-zero.
-    if (mathematical_value == 0.0 && string.view().trim_whitespace(TrimMode::Left).starts_with('-'))
+    if (mathematical_value == 0.0 && string.bytes_as_string_view().trim_whitespace(TrimMode::Left).starts_with('-'))
         return MathematicalValue::Symbol::NegativeZero;
 
     // 8. If mv is 10^10000 and str contains Infinity, return positive-infinity.
@@ -1770,12 +1783,14 @@ ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_number_range_pat
 
     // 8. Let rangeSeparator be an ILND String value used to separate two numbers.
     auto range_separator_symbol = ::Locale::get_number_system_symbol(number_format.data_locale(), number_format.numbering_system(), ::Locale::NumericSymbol::RangeSeparator).value_or("-"sv);
-    auto range_separator = ::Locale::augment_range_pattern(range_separator_symbol, result.last().value, end_result[0].value);
+    auto range_separator = TRY_OR_THROW_OOM(vm, ::Locale::augment_range_pattern(range_separator_symbol, result.last().value, end_result[0].value));
 
     // 9. Append a new Record { [[Type]]: "literal", [[Value]]: rangeSeparator, [[Source]]: "shared" } element to result.
     PatternPartitionWithSource part;
     part.type = "literal"sv;
-    part.value = TRY_OR_THROW_OOM(vm, String::from_deprecated_string(range_separator.value_or(range_separator_symbol)));
+    part.value = range_separator.has_value()
+        ? range_separator.release_value()
+        : TRY_OR_THROW_OOM(vm, String::from_utf8(range_separator_symbol));
     part.source = "shared"sv;
     result.append(move(part));
 
@@ -1820,22 +1835,22 @@ Vector<PatternPartitionWithSource> collapse_number_range(Vector<PatternPartition
 }
 
 // 1.5.22 FormatNumericRange( numberFormat, x, y ), https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/proposed.html#sec-formatnumericrange
-ThrowCompletionOr<DeprecatedString> format_numeric_range(VM& vm, NumberFormat& number_format, MathematicalValue start, MathematicalValue end)
+ThrowCompletionOr<String> format_numeric_range(VM& vm, NumberFormat& number_format, MathematicalValue start, MathematicalValue end)
 {
     // 1. Let parts be ? PartitionNumberRangePattern(numberFormat, x, y).
     auto parts = TRY(partition_number_range_pattern(vm, number_format, move(start), move(end)));
 
     // 2. Let result be the empty String.
-    StringBuilder result;
+    ThrowableStringBuilder result(vm);
 
     // 3. For each part in parts, do
     for (auto& part : parts) {
         // a. Set result to the string-concatenation of result and part.[[Value]].
-        result.append(move(part.value));
+        TRY(result.append(part.value));
     }
 
     // 4. Return result.
-    return result.build();
+    return result.to_string();
 }
 
 // 1.5.23 FormatNumericRangeToParts( numberFormat, x, y ), https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/proposed.html#sec-formatnumericrangetoparts

+ 10 - 11
Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021-2022, Tim Flynn <trflynn89@serenityos.org>
+ * Copyright (c) 2021-2023, Tim Flynn <trflynn89@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -7,7 +7,6 @@
 #pragma once
 
 #include <AK/Array.h>
-#include <AK/DeprecatedString.h>
 #include <AK/Optional.h>
 #include <AK/String.h>
 #include <LibJS/Runtime/Intl/AbstractOperations.h>
@@ -262,7 +261,7 @@ private:
 };
 
 struct FormatResult {
-    DeprecatedString formatted_string;        // [[FormattedString]]
+    String formatted_string;                  // [[FormattedString]]
     MathematicalValue rounded_number { 0.0 }; // [[RoundedNumber]]
 };
 
@@ -277,16 +276,16 @@ enum class RoundingDecision {
 };
 
 int currency_digits(StringView currency);
-FormatResult format_numeric_to_string(NumberFormatBase const& intl_object, MathematicalValue number);
+ThrowCompletionOr<FormatResult> format_numeric_to_string(VM&, NumberFormatBase const& intl_object, MathematicalValue number);
 ThrowCompletionOr<Vector<PatternPartition>> partition_number_pattern(VM&, NumberFormat&, MathematicalValue number);
-ThrowCompletionOr<Vector<PatternPartition>> partition_notation_sub_pattern(VM&, NumberFormat&, MathematicalValue const& number, DeprecatedString formatted_string, int exponent);
-ThrowCompletionOr<DeprecatedString> format_numeric(VM&, NumberFormat&, MathematicalValue number);
+ThrowCompletionOr<Vector<PatternPartition>> partition_notation_sub_pattern(VM&, NumberFormat&, MathematicalValue const& number, String formatted_string, int exponent);
+ThrowCompletionOr<String> format_numeric(VM&, NumberFormat&, MathematicalValue number);
 ThrowCompletionOr<Array*> format_numeric_to_parts(VM&, NumberFormat&, MathematicalValue number);
-RawFormatResult to_raw_precision(MathematicalValue const& number, int min_precision, int max_precision, Optional<NumberFormat::UnsignedRoundingMode> const& unsigned_rounding_mode);
-RawFormatResult to_raw_fixed(MathematicalValue const& number, int min_fraction, int max_fraction, int rounding_increment, Optional<NumberFormat::UnsignedRoundingMode> const& unsigned_rounding_mode);
-Optional<Variant<StringView, DeprecatedString>> get_number_format_pattern(VM&, NumberFormat&, MathematicalValue const& number, ::Locale::NumberFormat& found_pattern);
+ThrowCompletionOr<RawFormatResult> to_raw_precision(VM&, MathematicalValue const& number, int min_precision, int max_precision, Optional<NumberFormat::UnsignedRoundingMode> const& unsigned_rounding_mode);
+ThrowCompletionOr<RawFormatResult> to_raw_fixed(VM&, MathematicalValue const& number, int min_fraction, int max_fraction, int rounding_increment, Optional<NumberFormat::UnsignedRoundingMode> const& unsigned_rounding_mode);
+ThrowCompletionOr<Optional<Variant<StringView, String>>> get_number_format_pattern(VM&, NumberFormat&, MathematicalValue const& number, ::Locale::NumberFormat& found_pattern);
 Optional<StringView> get_notation_sub_pattern(NumberFormat&, int exponent);
-int compute_exponent(NumberFormat&, MathematicalValue number);
+ThrowCompletionOr<int> compute_exponent(VM&, NumberFormat&, MathematicalValue number);
 int compute_exponent_for_magnitude(NumberFormat&, int magnitude);
 ThrowCompletionOr<MathematicalValue> to_intl_mathematical_value(VM&, Value value);
 NumberFormat::UnsignedRoundingMode get_unsigned_rounding_mode(NumberFormat::RoundingMode, bool is_negative);
@@ -294,7 +293,7 @@ RoundingDecision apply_unsigned_rounding_mode(MathematicalValue const& x, Mathem
 ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_number_range_pattern(VM&, NumberFormat&, MathematicalValue start, MathematicalValue end);
 ThrowCompletionOr<Vector<PatternPartitionWithSource>> format_approximately(VM&, NumberFormat&, Vector<PatternPartitionWithSource> result);
 Vector<PatternPartitionWithSource> collapse_number_range(Vector<PatternPartitionWithSource> result);
-ThrowCompletionOr<DeprecatedString> format_numeric_range(VM&, NumberFormat&, MathematicalValue start, MathematicalValue end);
+ThrowCompletionOr<String> format_numeric_range(VM&, NumberFormat&, MathematicalValue start, MathematicalValue end);
 ThrowCompletionOr<Array*> format_numeric_range_to_parts(VM&, NumberFormat&, MathematicalValue start, MathematicalValue end);
 
 }

+ 6 - 6
Userland/Libraries/LibJS/Runtime/Intl/PluralRules.cpp

@@ -92,13 +92,13 @@ PluralRules::PluralRules(Object& prototype)
 }
 
 // 16.5.3 ResolvePlural ( pluralRules, n ), https://tc39.es/ecma402/#sec-resolveplural
-::Locale::PluralCategory resolve_plural(PluralRules const& plural_rules, Value number)
+ThrowCompletionOr<::Locale::PluralCategory> resolve_plural(VM& vm, PluralRules const& plural_rules, Value number)
 {
-    return resolve_plural(plural_rules, plural_rules.type(), number);
+    return resolve_plural(vm, plural_rules, plural_rules.type(), number);
 }
 
 // Non-standard overload of ResolvePlural to allow using the AO without an Intl.PluralRules object.
-::Locale::PluralCategory resolve_plural(NumberFormatBase const& number_format, ::Locale::PluralForm type, Value number)
+ThrowCompletionOr<::Locale::PluralCategory> resolve_plural(VM& vm, NumberFormatBase const& number_format, ::Locale::PluralForm type, Value number)
 {
     // 1. Assert: Type(pluralRules) is Object.
     // 2. Assert: pluralRules has an [[InitializedPluralRules]] internal slot.
@@ -116,7 +116,7 @@ PluralRules::PluralRules(Object& prototype)
     // 6. Let type be pluralRules.[[Type]].
 
     // 7. Let res be ! FormatNumericToString(pluralRules, n).
-    auto result = format_numeric_to_string(number_format, number);
+    auto result = MUST_OR_THROW_OOM(format_numeric_to_string(vm, number_format, number));
 
     // 8. Let s be res.[[FormattedString]].
     auto const& string = result.formatted_string;
@@ -149,10 +149,10 @@ ThrowCompletionOr<::Locale::PluralCategory> resolve_plural_range(VM& vm, PluralR
         return vm.throw_completion<RangeError>(ErrorType::IntlNumberIsNaN, "end"sv);
 
     // 6. Let xp be ! ResolvePlural(pluralRules, x).
-    auto start_plurality = resolve_plural(plural_rules, start);
+    auto start_plurality = MUST_OR_THROW_OOM(resolve_plural(vm, plural_rules, start));
 
     // 7. Let yp be ! ResolvePlural(pluralRules, y).
-    auto end_plurality = resolve_plural(plural_rules, end);
+    auto end_plurality = MUST_OR_THROW_OOM(resolve_plural(vm, plural_rules, end));
 
     // 8. Let locale be pluralRules.[[Locale]].
     auto const& locale = plural_rules.locale();

+ 2 - 2
Userland/Libraries/LibJS/Runtime/Intl/PluralRules.h

@@ -32,8 +32,8 @@ private:
 
 ::Locale::PluralOperands get_operands(StringView string);
 ::Locale::PluralCategory plural_rule_select(StringView locale, ::Locale::PluralForm type, Value number, ::Locale::PluralOperands operands);
-::Locale::PluralCategory resolve_plural(PluralRules const&, Value number);
-::Locale::PluralCategory resolve_plural(NumberFormatBase const& number_format, ::Locale::PluralForm type, Value number);
+ThrowCompletionOr<::Locale::PluralCategory> resolve_plural(VM&, PluralRules const&, Value number);
+ThrowCompletionOr<::Locale::PluralCategory> resolve_plural(VM&, NumberFormatBase const& number_format, ::Locale::PluralForm type, Value number);
 ::Locale::PluralCategory plural_rule_select_range(StringView locale, ::Locale::PluralForm, ::Locale::PluralCategory start, ::Locale::PluralCategory end);
 ThrowCompletionOr<::Locale::PluralCategory> resolve_plural_range(VM&, PluralRules const&, Value start, Value end);
 

+ 1 - 1
Userland/Libraries/LibJS/Runtime/Intl/PluralRulesPrototype.cpp

@@ -44,7 +44,7 @@ JS_DEFINE_NATIVE_FUNCTION(PluralRulesPrototype::select)
     auto number = TRY(vm.argument(0).to_number(vm));
 
     // 4. Return ! ResolvePlural(pr, n).
-    auto plurality = resolve_plural(*plural_rules, number);
+    auto plurality = MUST_OR_THROW_OOM(resolve_plural(vm, *plural_rules, number));
     return PrimitiveString::create(vm, ::Locale::plural_category_to_string(plurality));
 }
 

+ 2 - 2
Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.cpp

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
+ * Copyright (c) 2022-2023, Tim Flynn <trflynn89@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -177,7 +177,7 @@ ThrowCompletionOr<Vector<PatternPartitionWithUnit>> partition_relative_time_patt
     auto value_partitions = MUST_OR_THROW_OOM(partition_number_pattern(vm, relative_time_format.number_format(), Value(value)));
 
     // 21. Let pr be ! ResolvePlural(relativeTimeFormat.[[PluralRules]], value).
-    auto plurality = resolve_plural(relative_time_format.plural_rules(), Value(value));
+    auto plurality = MUST_OR_THROW_OOM(resolve_plural(vm, relative_time_format.plural_rules(), Value(value)));
 
     // 22. Let pattern be po.[[<pr>]].
     auto pattern = patterns.find_if([&](auto& p) { return p.plurality == plurality; });

+ 2 - 2
Userland/Libraries/LibLocale/DateTimeFormat.cpp

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021-2022, Tim Flynn <trflynn89@serenityos.org>
+ * Copyright (c) 2021-2023, Tim Flynn <trflynn89@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -290,7 +290,7 @@ static Optional<DeprecatedString> format_time_zone_offset(StringView locale, Cal
     }
 
     // The digits used for hours, minutes and seconds fields in this format are the locale's default decimal digits.
-    auto result = replace_digits_for_number_system(*number_system, builder.build());
+    auto result = replace_digits_for_number_system(*number_system, builder.build()).release_value_but_fixme_should_propagate_errors();
     return formats->gmt_format.replace("{0}"sv, result, ReplaceMode::FirstOnly);
 }
 

+ 15 - 15
Userland/Libraries/LibLocale/NumberFormat.cpp

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
+ * Copyright (c) 2021-2023, Tim Flynn <trflynn89@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -29,7 +29,7 @@ Optional<Span<u32 const>> __attribute__((weak)) get_digits_for_number_system(Str
     return digits.span();
 }
 
-DeprecatedString replace_digits_for_number_system(StringView system, StringView number)
+ErrorOr<String> replace_digits_for_number_system(StringView system, StringView number)
 {
     auto digits = get_digits_for_number_system(system);
     if (!digits.has_value())
@@ -41,13 +41,13 @@ DeprecatedString replace_digits_for_number_system(StringView system, StringView
     for (auto ch : number) {
         if (is_ascii_digit(ch)) {
             u32 digit = digits->at(parse_ascii_digit(ch));
-            builder.append_code_point(digit);
+            TRY(builder.try_append_code_point(digit));
         } else {
-            builder.append(ch);
+            TRY(builder.try_append(ch));
         }
     }
 
-    return builder.build();
+    return builder.to_string();
 }
 
 #if ENABLE_UNICODE_DATA
@@ -64,7 +64,7 @@ static u32 last_code_point(StringView string)
 #endif
 
 // https://www.unicode.org/reports/tr35/tr35-numbers.html#Currencies
-Optional<DeprecatedString> augment_currency_format_pattern([[maybe_unused]] StringView currency_display, [[maybe_unused]] StringView base_pattern)
+ErrorOr<Optional<String>> augment_currency_format_pattern([[maybe_unused]] StringView currency_display, [[maybe_unused]] StringView base_pattern)
 {
 #if ENABLE_UNICODE_DATA
     constexpr auto number_key = "{number}"sv;
@@ -78,7 +78,7 @@ Optional<DeprecatedString> augment_currency_format_pattern([[maybe_unused]] Stri
     VERIFY(currency_index.has_value());
 
     Utf8View utf8_currency_display { currency_display };
-    Optional<DeprecatedString> currency_key_with_spacing;
+    Optional<String> currency_key_with_spacing;
 
     if (*number_index < *currency_index) {
         u32 last_pattern_code_point = last_code_point(base_pattern.substring_view(0, *currency_index));
@@ -87,7 +87,7 @@ Optional<DeprecatedString> augment_currency_format_pattern([[maybe_unused]] Stri
             u32 first_currency_code_point = *utf8_currency_display.begin();
 
             if (!Unicode::code_point_has_general_category(first_currency_code_point, Unicode::GeneralCategory::Symbol))
-                currency_key_with_spacing = DeprecatedString::formatted("{}{}", spacing, currency_key);
+                currency_key_with_spacing = TRY(String::formatted("{}{}", spacing, currency_key));
         }
     } else {
         u32 last_pattern_code_point = last_code_point(base_pattern.substring_view(0, *number_index));
@@ -96,23 +96,23 @@ Optional<DeprecatedString> augment_currency_format_pattern([[maybe_unused]] Stri
             u32 last_currency_code_point = last_code_point(currency_display);
 
             if (!Unicode::code_point_has_general_category(last_currency_code_point, Unicode::GeneralCategory::Symbol))
-                currency_key_with_spacing = DeprecatedString::formatted("{}{}", currency_key, spacing);
+                currency_key_with_spacing = TRY(String::formatted("{}{}", currency_key, spacing));
         }
     }
 
     if (currency_key_with_spacing.has_value())
-        return base_pattern.replace(currency_key, *currency_key_with_spacing, ReplaceMode::FirstOnly);
+        return TRY(TRY(String::from_utf8(base_pattern)).replace(currency_key, *currency_key_with_spacing, ReplaceMode::FirstOnly));
 #endif
 
-    return {};
+    return OptionalNone {};
 }
 
 // https://unicode.org/reports/tr35/tr35-numbers.html#83-range-pattern-processing
-Optional<DeprecatedString> augment_range_pattern([[maybe_unused]] StringView range_separator, [[maybe_unused]] StringView lower, [[maybe_unused]] StringView upper)
+ErrorOr<Optional<String>> augment_range_pattern([[maybe_unused]] StringView range_separator, [[maybe_unused]] StringView lower, [[maybe_unused]] StringView upper)
 {
 #if ENABLE_UNICODE_DATA
     auto range_pattern_with_spacing = [&]() {
-        return DeprecatedString::formatted(" {} ", range_separator);
+        return String::formatted(" {} ", range_separator);
     };
 
     Utf8View utf8_range_separator { range_separator };
@@ -124,7 +124,7 @@ Optional<DeprecatedString> augment_range_pattern([[maybe_unused]] StringView ran
     // 2. If the range pattern does not contain a character having the White_Space binary Unicode property after the {0} or before the {1} placeholders.
     for (auto it = utf8_range_separator.begin(); it != utf8_range_separator.end(); ++it) {
         if (Unicode::code_point_has_property(*it, Unicode::Property::White_Space))
-            return {};
+            return OptionalNone {};
     }
 
     // 1. If the lower string ends with a character other than a digit, or if the upper string begins with a character other than a digit.
@@ -137,7 +137,7 @@ Optional<DeprecatedString> augment_range_pattern([[maybe_unused]] StringView ran
         return range_pattern_with_spacing();
 #endif
 
-    return {};
+    return OptionalNone {};
 }
 
 }

+ 6 - 5
Userland/Libraries/LibLocale/NumberFormat.h

@@ -1,13 +1,14 @@
 /*
- * Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
+ * Copyright (c) 2021-2023, Tim Flynn <trflynn89@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
 #pragma once
 
-#include <AK/DeprecatedString.h>
+#include <AK/Error.h>
 #include <AK/Optional.h>
+#include <AK/String.h>
 #include <AK/StringView.h>
 #include <AK/Vector.h>
 #include <LibLocale/Forward.h>
@@ -64,13 +65,13 @@ Optional<StringView> get_number_system_symbol(StringView locale, StringView syst
 Optional<NumberGroupings> get_number_system_groupings(StringView locale, StringView system);
 
 Optional<Span<u32 const>> get_digits_for_number_system(StringView system);
-DeprecatedString replace_digits_for_number_system(StringView system, StringView number);
+ErrorOr<String> replace_digits_for_number_system(StringView system, StringView number);
 
 Optional<NumberFormat> get_standard_number_system_format(StringView locale, StringView system, StandardNumberFormatType type);
 Vector<NumberFormat> get_compact_number_system_formats(StringView locale, StringView system, CompactNumberFormatType type);
 Vector<NumberFormat> get_unit_formats(StringView locale, StringView unit, Style style);
 
-Optional<DeprecatedString> augment_currency_format_pattern(StringView currency_display, StringView base_pattern);
-Optional<DeprecatedString> augment_range_pattern(StringView range_separator, StringView lower, StringView upper);
+ErrorOr<Optional<String>> augment_currency_format_pattern(StringView currency_display, StringView base_pattern);
+ErrorOr<Optional<String>> augment_range_pattern(StringView range_separator, StringView lower, StringView upper);
 
 }