Jelajahi Sumber

LibJS+LibLocale: Change ListFormat to be created once per Intl object

ListFormat was the first formatter I ported to ICU. This patch makes it
match the style of subsequently ported formatters, where we create the
formatter once per Intl object, rather than once per prototype
invocation.
Timothy Flynn 1 tahun lalu
induk
melakukan
de99dd2c89

+ 5 - 5
Userland/Libraries/LibJS/Runtime/Intl/ListFormat.cpp

@@ -22,24 +22,24 @@ ListFormat::ListFormat(Object& prototype)
 }
 }
 
 
 // 13.5.2 CreatePartsFromList ( listFormat, list ), https://tc39.es/ecma402/#sec-createpartsfromlist
 // 13.5.2 CreatePartsFromList ( listFormat, list ), https://tc39.es/ecma402/#sec-createpartsfromlist
-Vector<::Locale::ListFormatPart> create_parts_from_list(ListFormat const& list_format, Vector<String> const& list)
+Vector<::Locale::ListFormat::Partition> create_parts_from_list(ListFormat const& list_format, ReadonlySpan<String> list)
 {
 {
-    return ::Locale::format_list_to_parts(list_format.locale(), list_format.type(), list_format.style(), list);
+    return list_format.formatter().format_to_parts(list);
 }
 }
 
 
 // 13.5.3 FormatList ( listFormat, list ), https://tc39.es/ecma402/#sec-formatlist
 // 13.5.3 FormatList ( listFormat, list ), https://tc39.es/ecma402/#sec-formatlist
-String format_list(ListFormat const& list_format, Vector<String> const& list)
+String format_list(ListFormat const& list_format, ReadonlySpan<String> list)
 {
 {
     // 1. Let parts be ! CreatePartsFromList(listFormat, list).
     // 1. Let parts be ! CreatePartsFromList(listFormat, list).
     // 2. Let result be an empty String.
     // 2. Let result be an empty String.
     // 3. For each Record { [[Type]], [[Value]] } part in parts, do
     // 3. For each Record { [[Type]], [[Value]] } part in parts, do
     //     a. Set result to the string-concatenation of result and part.[[Value]].
     //     a. Set result to the string-concatenation of result and part.[[Value]].
     // 4. Return result.
     // 4. Return result.
-    return ::Locale::format_list(list_format.locale(), list_format.type(), list_format.style(), list);
+    return list_format.formatter().format(list);
 }
 }
 
 
 // 13.5.4 FormatListToParts ( listFormat, list ), https://tc39.es/ecma402/#sec-formatlisttoparts
 // 13.5.4 FormatListToParts ( listFormat, list ), https://tc39.es/ecma402/#sec-formatlisttoparts
-NonnullGCPtr<Array> format_list_to_parts(VM& vm, ListFormat const& list_format, Vector<String> const& list)
+NonnullGCPtr<Array> format_list_to_parts(VM& vm, ListFormat const& list_format, ReadonlySpan<String> list)
 {
 {
     auto& realm = *vm.current_realm();
     auto& realm = *vm.current_realm();
 
 

+ 9 - 3
Userland/Libraries/LibJS/Runtime/Intl/ListFormat.h

@@ -41,17 +41,23 @@ public:
     void set_style(StringView style) { m_style = ::Locale::style_from_string(style); }
     void set_style(StringView style) { m_style = ::Locale::style_from_string(style); }
     StringView style_string() const { return ::Locale::style_to_string(m_style); }
     StringView style_string() const { return ::Locale::style_to_string(m_style); }
 
 
+    ::Locale::ListFormat const& formatter() const { return *m_formatter; }
+    void set_formatter(NonnullOwnPtr<::Locale::ListFormat> formatter) { m_formatter = move(formatter); }
+
 private:
 private:
     explicit ListFormat(Object& prototype);
     explicit ListFormat(Object& prototype);
 
 
     String m_locale;                                                           // [[Locale]]
     String m_locale;                                                           // [[Locale]]
     ::Locale::ListFormatType m_type { ::Locale::ListFormatType::Conjunction }; // [[Type]]
     ::Locale::ListFormatType m_type { ::Locale::ListFormatType::Conjunction }; // [[Type]]
     ::Locale::Style m_style { ::Locale::Style::Long };                         // [[Style]]
     ::Locale::Style m_style { ::Locale::Style::Long };                         // [[Style]]
+
+    // Non-standard. Stores the ICU list formatter for the Intl object's formatting options.
+    OwnPtr<::Locale::ListFormat> m_formatter;
 };
 };
 
 
-Vector<::Locale::ListFormatPart> create_parts_from_list(ListFormat const&, Vector<String> const& list);
-String format_list(ListFormat const&, Vector<String> const& list);
-NonnullGCPtr<Array> format_list_to_parts(VM&, ListFormat const&, Vector<String> const& list);
+Vector<::Locale::ListFormat::Partition> create_parts_from_list(ListFormat const&, ReadonlySpan<String> list);
+String format_list(ListFormat const&, ReadonlySpan<String> list);
+NonnullGCPtr<Array> format_list_to_parts(VM&, ListFormat const&, ReadonlySpan<String> list);
 ThrowCompletionOr<Vector<String>> string_list_from_iterable(VM&, Value iterable);
 ThrowCompletionOr<Vector<String>> string_list_from_iterable(VM&, Value iterable);
 
 
 }
 }

+ 17 - 12
Userland/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2021-2023, Tim Flynn <trflynn89@serenityos.org>
+ * Copyright (c) 2021-2024, Tim Flynn <trflynn89@serenityos.org>
  *
  *
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
@@ -52,7 +52,7 @@ ThrowCompletionOr<NonnullGCPtr<Object>> ListFormatConstructor::construct(Functio
     auto locale_value = vm.argument(0);
     auto locale_value = vm.argument(0);
     auto options_value = vm.argument(1);
     auto options_value = vm.argument(1);
 
 
-    // 2. Let listFormat be ? OrdinaryCreateFromConstructor(NewTarget, "%ListFormat.prototype%", « [[InitializedListFormat]], [[Locale]], [[Type]], [[Style]], [[Templates]] »).
+    // 2. Let listFormat be ? OrdinaryCreateFromConstructor(NewTarget, "%Intl.ListFormat.prototype%", « [[InitializedListFormat]], [[Locale]], [[Type]], [[Style]], [[Templates]] »).
     auto list_format = TRY(ordinary_create_from_constructor<ListFormat>(vm, new_target, &Intrinsics::intl_list_format_prototype));
     auto list_format = TRY(ordinary_create_from_constructor<ListFormat>(vm, new_target, &Intrinsics::intl_list_format_prototype));
 
 
     // 3. Let requestedLocales be ? CanonicalizeLocaleList(locales).
     // 3. Let requestedLocales be ? CanonicalizeLocaleList(locales).
@@ -70,29 +70,34 @@ ThrowCompletionOr<NonnullGCPtr<Object>> ListFormatConstructor::construct(Functio
     // 7. Set opt.[[localeMatcher]] to matcher.
     // 7. Set opt.[[localeMatcher]] to matcher.
     opt.locale_matcher = matcher;
     opt.locale_matcher = matcher;
 
 
-    // 8. Let localeData be %ListFormat%.[[LocaleData]].
-
-    // 9. Let r be ResolveLocale(%ListFormat%.[[AvailableLocales]], requestedLocales, opt, %ListFormat%.[[RelevantExtensionKeys]], localeData).
+    // 8. Let r be ResolveLocale(%Intl.ListFormat%.[[AvailableLocales]], requestedLocales, opt, %Intl.ListFormat%.[[RelevantExtensionKeys]], %Intl.ListFormat%.[[LocaleData]]).
     auto result = resolve_locale(requested_locales, opt, {});
     auto result = resolve_locale(requested_locales, opt, {});
 
 
-    // 10. Set listFormat.[[Locale]] to r.[[locale]].
+    // 9. Set listFormat.[[Locale]] to r.[[Locale]].
     list_format->set_locale(move(result.locale));
     list_format->set_locale(move(result.locale));
 
 
-    // 11. Let type be ? GetOption(options, "type", string, « "conjunction", "disjunction", "unit" », "conjunction").
+    // 10. Let type be ? GetOption(options, "type", string, « "conjunction", "disjunction", "unit" », "conjunction").
     auto type = TRY(get_option(vm, *options, vm.names.type, OptionType::String, { "conjunction"sv, "disjunction"sv, "unit"sv }, "conjunction"sv));
     auto type = TRY(get_option(vm, *options, vm.names.type, OptionType::String, { "conjunction"sv, "disjunction"sv, "unit"sv }, "conjunction"sv));
 
 
-    // 12. Set listFormat.[[Type]] to type.
+    // 11. Set listFormat.[[Type]] to type.
     list_format->set_type(type.as_string().utf8_string_view());
     list_format->set_type(type.as_string().utf8_string_view());
 
 
-    // 13. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow" », "long").
+    // 12. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow" », "long").
     auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv }, "long"sv));
     auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv }, "long"sv));
 
 
-    // 14. Set listFormat.[[Style]] to style.
+    // 13. Set listFormat.[[Style]] to style.
     list_format->set_style(style.as_string().utf8_string_view());
     list_format->set_style(style.as_string().utf8_string_view());
 
 
-    // Note: The remaining steps are skipped in favor of deferring to LibUnicode.
+    // 14. Let resolvedLocaleData be r.[[LocaleData]].
+    // 15. Let dataLocaleTypes be resolvedLocaleData.[[<type>]].
+    // 16. Set listFormat.[[Templates]] to dataLocaleTypes.[[<style>]].
+    auto formatter = ::Locale::ListFormat::create(
+        list_format->locale(),
+        list_format->type(),
+        list_format->style());
+    list_format->set_formatter(move(formatter));
 
 
-    // 19. Return listFormat.
+    // 17. Return listFormat.
     return list_format;
     return list_format;
 }
 }
 
 

+ 63 - 48
Userland/Libraries/LibLocale/ListFormat.cpp

@@ -34,9 +34,8 @@ StringView list_format_type_to_string(ListFormatType list_format_type)
         return "disjunction"sv;
         return "disjunction"sv;
     case ListFormatType::Unit:
     case ListFormatType::Unit:
         return "unit"sv;
         return "unit"sv;
-    default:
-        VERIFY_NOT_REACHED();
     }
     }
+    VERIFY_NOT_REACHED();
 }
 }
 
 
 static constexpr UListFormatterType icu_list_format_type(ListFormatType type)
 static constexpr UListFormatterType icu_list_format_type(ListFormatType type)
@@ -49,7 +48,6 @@ static constexpr UListFormatterType icu_list_format_type(ListFormatType type)
     case ListFormatType::Unit:
     case ListFormatType::Unit:
         return ULISTFMT_TYPE_UNITS;
         return ULISTFMT_TYPE_UNITS;
     }
     }
-
     VERIFY_NOT_REACHED();
     VERIFY_NOT_REACHED();
 }
 }
 
 
@@ -63,7 +61,6 @@ static constexpr UListFormatterWidth icu_list_format_width(Style style)
     case Style::Narrow:
     case Style::Narrow:
         return ULISTFMT_WIDTH_NARROW;
         return ULISTFMT_WIDTH_NARROW;
     }
     }
-
     VERIFY_NOT_REACHED();
     VERIFY_NOT_REACHED();
 }
 }
 
 
@@ -75,70 +72,88 @@ static constexpr StringView icu_list_format_field_to_string(i32 field)
     case ULISTFMT_ELEMENT_FIELD:
     case ULISTFMT_ELEMENT_FIELD:
         return "element"sv;
         return "element"sv;
     }
     }
-
     VERIFY_NOT_REACHED();
     VERIFY_NOT_REACHED();
 }
 }
 
 
-struct FormatResult {
-    icu::FormattedList list;
-    icu::UnicodeString string;
-};
+class ListFormatImpl : public ListFormat {
+public:
+    ListFormatImpl(NonnullOwnPtr<icu::ListFormatter> formatter)
+        : m_formatter(move(formatter))
+    {
+    }
 
 
-static Optional<FormatResult> format_list_impl(StringView locale, ListFormatType type, Style style, ReadonlySpan<String> list)
-{
-    auto locale_data = LocaleData::for_locale(locale);
-    if (!locale_data.has_value())
-        return {};
+    virtual ~ListFormatImpl() override = default;
 
 
-    UErrorCode status = U_ZERO_ERROR;
+    virtual String format(ReadonlySpan<String> list) const override
+    {
+        UErrorCode status = U_ZERO_ERROR;
 
 
-    auto list_formatter = adopt_own(*icu::ListFormatter::createInstance(locale_data->locale(), icu_list_format_type(type), icu_list_format_width(style), status));
-    if (icu_failure(status))
-        return {};
+        auto formatted = format_list_impl(list);
+        if (!formatted.has_value())
+            return {};
 
 
-    auto icu_list = icu_string_list(list);
+        auto formatted_string = formatted->toTempString(status);
+        if (icu_failure(status))
+            return {};
 
 
-    auto formatted_list = list_formatter->formatStringsToValue(icu_list.data(), static_cast<i32>(icu_list.size()), status);
-    if (icu_failure(status))
-        return {};
+        return icu_string_to_string(formatted_string);
+    }
 
 
-    auto formatted_string = formatted_list.toString(status);
-    if (icu_failure(status))
-        return {};
+    virtual Vector<Partition> format_to_parts(ReadonlySpan<String> list) const override
+    {
+        UErrorCode status = U_ZERO_ERROR;
 
 
-    return FormatResult { move(formatted_list), move(formatted_string) };
-}
+        auto formatted = format_list_impl(list);
+        if (!formatted.has_value())
+            return {};
 
 
-String format_list(StringView locale, ListFormatType type, Style style, ReadonlySpan<String> list)
-{
-    auto formatted = format_list_impl(locale, type, style, list);
-    if (!formatted.has_value())
-        return {};
+        auto formatted_string = formatted->toTempString(status);
+        if (icu_failure(status))
+            return {};
 
 
-    return icu_string_to_string(formatted->string);
-}
+        Vector<Partition> result;
 
 
-Vector<ListFormatPart> format_list_to_parts(StringView locale, ListFormatType type, Style style, ReadonlySpan<String> list)
-{
-    UErrorCode status = U_ZERO_ERROR;
+        icu::ConstrainedFieldPosition position;
+        position.constrainCategory(UFIELD_CATEGORY_LIST);
 
 
-    auto formatted = format_list_impl(locale, type, style, list);
-    if (!formatted.has_value())
-        return {};
+        while (static_cast<bool>(formatted->nextPosition(position, status)) && icu_success(status)) {
+            auto type = icu_list_format_field_to_string(position.getField());
+            auto part = formatted_string.tempSubStringBetween(position.getStart(), position.getLimit());
 
 
-    Vector<ListFormatPart> result;
+            result.empend(type, icu_string_to_string(part));
+        }
+
+        return result;
+    }
 
 
-    icu::ConstrainedFieldPosition position;
-    position.constrainCategory(UFIELD_CATEGORY_LIST);
+private:
+    Optional<icu::FormattedList> format_list_impl(ReadonlySpan<String> list) const
+    {
+        UErrorCode status = U_ZERO_ERROR;
 
 
-    while (static_cast<bool>(formatted->list.nextPosition(position, status)) && icu_success(status)) {
-        auto type = icu_list_format_field_to_string(position.getField());
-        auto part = formatted->string.tempSubStringBetween(position.getStart(), position.getLimit());
+        auto icu_list = icu_string_list(list);
 
 
-        result.empend(type, icu_string_to_string(part));
+        auto formatted_list = m_formatter->formatStringsToValue(icu_list.data(), static_cast<i32>(icu_list.size()), status);
+        if (icu_failure(status))
+            return {};
+
+        return formatted_list;
     }
     }
 
 
-    return result;
+    NonnullOwnPtr<icu::ListFormatter> m_formatter;
+};
+
+NonnullOwnPtr<ListFormat> ListFormat::create(StringView locale, ListFormatType type, Style style)
+{
+    UErrorCode status = U_ZERO_ERROR;
+
+    auto locale_data = LocaleData::for_locale(locale);
+    VERIFY(locale_data.has_value());
+
+    auto formatter = adopt_own(*icu::ListFormatter::createInstance(locale_data->locale(), icu_list_format_type(type), icu_list_format_width(style), status));
+    VERIFY(icu_success(status));
+
+    return adopt_own(*new ListFormatImpl(move(formatter)));
 }
 }
 
 
 }
 }

+ 16 - 8
Userland/Libraries/LibLocale/ListFormat.h

@@ -17,16 +17,24 @@ enum class ListFormatType {
     Disjunction,
     Disjunction,
     Unit,
     Unit,
 };
 };
+ListFormatType list_format_type_from_string(StringView);
+StringView list_format_type_to_string(ListFormatType);
 
 
-ListFormatType list_format_type_from_string(StringView list_format_type);
-StringView list_format_type_to_string(ListFormatType list_format_type);
+class ListFormat {
+public:
+    static NonnullOwnPtr<ListFormat> create(StringView locale, ListFormatType, Style);
+    virtual ~ListFormat() = default;
 
 
-struct ListFormatPart {
-    StringView type;
-    String value;
-};
+    struct Partition {
+        StringView type;
+        String value;
+    };
+
+    virtual String format(ReadonlySpan<String> list) const = 0;
+    virtual Vector<Partition> format_to_parts(ReadonlySpan<String> list) const = 0;
 
 
-String format_list(StringView locale, ListFormatType, Style, ReadonlySpan<String> list);
-Vector<ListFormatPart> format_list_to_parts(StringView locale, ListFormatType, Style, ReadonlySpan<String> list);
+protected:
+    ListFormat() = default;
+};
 
 
 }
 }