From 5898248b6b4c37f69c5ab42b6b52ffabcdee3fd9 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Mon, 7 Apr 2025 15:56:31 -0400 Subject: [PATCH] LibJS: Define constructor slots for describing how to read options This is an editorial change in the ECMA-402 spec. See: https://github.com/tc39/ecma402/commit/a2beb66 We implement this change by introducing a virtual interface that all Intl "service" objects must implement. A future patch will make use of the virtualized RelevantExtensionKeys and ResolutionOptionDescriptors accessors, and we will need to be able to use those slots from a generic instance type. --- Libraries/LibJS/Forward.h | 3 ++ Libraries/LibJS/Runtime/Intl/Collator.cpp | 27 ++++++++++++-- Libraries/LibJS/Runtime/Intl/Collator.h | 19 ++++------ .../Runtime/Intl/CollatorConstructor.cpp | 4 +-- .../LibJS/Runtime/Intl/DateTimeFormat.cpp | 26 +++++++++++++- Libraries/LibJS/Runtime/Intl/DateTimeFormat.h | 18 ++++------ .../Intl/DateTimeFormatConstructor.cpp | 4 +-- Libraries/LibJS/Runtime/Intl/DisplayNames.cpp | 18 ++++++++-- Libraries/LibJS/Runtime/Intl/DisplayNames.h | 13 ++++--- .../Runtime/Intl/DisplayNamesConstructor.cpp | 2 +- .../LibJS/Runtime/Intl/DurationFormat.cpp | 26 +++++++++++--- Libraries/LibJS/Runtime/Intl/DurationFormat.h | 17 ++++----- .../Intl/DurationFormatConstructor.cpp | 2 +- Libraries/LibJS/Runtime/Intl/IntlObject.h | 36 +++++++++++++++++++ Libraries/LibJS/Runtime/Intl/ListFormat.cpp | 19 ++++++++-- Libraries/LibJS/Runtime/Intl/ListFormat.h | 12 ++++--- .../Runtime/Intl/ListFormatConstructor.cpp | 2 +- Libraries/LibJS/Runtime/Intl/NumberFormat.cpp | 32 +++++++++++------ Libraries/LibJS/Runtime/Intl/NumberFormat.h | 17 ++++----- .../Runtime/Intl/NumberFormatConstructor.cpp | 2 +- Libraries/LibJS/Runtime/Intl/PluralRules.cpp | 14 ++++++++ Libraries/LibJS/Runtime/Intl/PluralRules.h | 5 ++- .../Runtime/Intl/PluralRulesConstructor.cpp | 2 +- .../LibJS/Runtime/Intl/RelativeTimeFormat.cpp | 28 +++++++++++---- .../LibJS/Runtime/Intl/RelativeTimeFormat.h | 19 ++++------ .../Intl/RelativeTimeFormatConstructor.cpp | 2 +- Libraries/LibJS/Runtime/Intl/Segmenter.cpp | 18 ++++++++-- Libraries/LibJS/Runtime/Intl/Segmenter.h | 11 +++--- .../Runtime/Intl/SegmenterConstructor.cpp | 2 +- 29 files changed, 284 insertions(+), 116 deletions(-) create mode 100644 Libraries/LibJS/Runtime/Intl/IntlObject.h diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index 8caf3538382..c1bb0fa7f6b 100644 --- a/Libraries/LibJS/Forward.h +++ b/Libraries/LibJS/Forward.h @@ -274,11 +274,14 @@ JS_ENUMERATE_INTL_OBJECTS #undef __JS_ENUMERATE class Intl; +class IntlObject; class MathematicalValue; // Not included in JS_ENUMERATE_INTL_OBJECTS due to missing distinct constructor class Segments; class SegmentsPrototype; + +struct ResolutionOptionDescriptor; }; namespace Temporal { diff --git a/Libraries/LibJS/Runtime/Intl/Collator.cpp b/Libraries/LibJS/Runtime/Intl/Collator.cpp index db48b3982c6..53655ebc7fd 100644 --- a/Libraries/LibJS/Runtime/Intl/Collator.cpp +++ b/Libraries/LibJS/Runtime/Intl/Collator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024, Tim Flynn + * Copyright (c) 2022-2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ @@ -12,7 +12,7 @@ GC_DEFINE_ALLOCATOR(Collator); // 10 Collator Objects, https://tc39.es/ecma402/#collator-objects Collator::Collator(Object& prototype) - : Object(ConstructWithPrototypeTag::Tag, prototype) + : IntlObject(ConstructWithPrototypeTag::Tag, prototype) { } @@ -22,4 +22,27 @@ void Collator::visit_edges(Visitor& visitor) visitor.visit(m_bound_compare); } +// 10.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl-collator-internal-slots +ReadonlySpan Collator::relevant_extension_keys() const +{ + // The value of the [[RelevantExtensionKeys]] internal slot is a List that must include the element "co", may include any or all of the elements "kf" and "kn", and must not include any other elements. + static constexpr AK::Array keys { "co"sv, "kf"sv, "kn"sv }; + return keys; +} + +// 10.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl-collator-internal-slots +ReadonlySpan Collator::resolution_option_descriptors(VM& vm) const +{ + // The value of the [[ResolutionOptionDescriptors]] internal slot is « { [[Key]]: "co", [[Property]]: "collation" }, { [[Key]]: "kn", [[Property]]: "numeric", [[Type]]: boolean }, { [[Key]]: "kf", [[Property]]: "caseFirst", [[Values]]: « "upper", "lower", "false" » } ». + static constexpr AK::Array case_first_values { "upper"sv, "lower"sv, "false"sv }; + + static auto descriptors = to_array({ + { .key = "co"sv, .property = vm.names.collation }, + { .key = "kn"sv, .property = vm.names.numeric, .type = OptionType::Boolean }, + { .key = "kf"sv, .property = vm.names.caseFirst, .values = case_first_values }, + }); + + return descriptors; +} + } diff --git a/Libraries/LibJS/Runtime/Intl/Collator.h b/Libraries/LibJS/Runtime/Intl/Collator.h index e17e9bcdcd0..d7b86f8ef61 100644 --- a/Libraries/LibJS/Runtime/Intl/Collator.h +++ b/Libraries/LibJS/Runtime/Intl/Collator.h @@ -1,34 +1,29 @@ /* - * Copyright (c) 2022-2024, Tim Flynn + * Copyright (c) 2022-2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once -#include #include #include #include -#include +#include #include namespace JS::Intl { -class Collator final : public Object { - JS_OBJECT(Collator, Object); +class Collator final : public IntlObject { + JS_OBJECT(Collator, IntlObject); GC_DECLARE_ALLOCATOR(Collator); public: - static constexpr auto relevant_extension_keys() - { - // 10.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl-collator-internal-slots - // The value of the [[RelevantExtensionKeys]] internal slot is a List that must include the element "co", may include any or all of the elements "kf" and "kn", and must not include any other elements. - return AK::Array { "co"sv, "kf"sv, "kn"sv }; - } - virtual ~Collator() override = default; + virtual ReadonlySpan relevant_extension_keys() const override; + virtual ReadonlySpan resolution_option_descriptors(VM&) const override; + String const& locale() const { return m_locale; } void set_locale(String locale) { m_locale = move(locale); } diff --git a/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp b/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp index 3358dd54cf1..17e05331706 100644 --- a/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024, Tim Flynn + * Copyright (c) 2022-2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ @@ -113,7 +113,7 @@ ThrowCompletionOr> CollatorConstructor::construct(FunctionObject opt.kf = locale_key_from_value(case_first); // 21. Let r be ResolveLocale(%Intl.Collator%.[[AvailableLocales]], requestedLocales, opt, %Intl.Collator%.[[RelevantExtensionKeys]], localeData). - auto result = resolve_locale(requested_locales, opt, Collator::relevant_extension_keys()); + auto result = resolve_locale(requested_locales, opt, collator->relevant_extension_keys()); // 22. Set collator.[[Locale]] to r.[[Locale]]. collator->set_locale(move(result.locale)); diff --git a/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp b/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp index 2b0306b7036..5e2f638302a 100644 --- a/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp +++ b/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp @@ -24,7 +24,7 @@ GC_DEFINE_ALLOCATOR(DateTimeFormat); // 11 DateTimeFormat Objects, https://tc39.es/ecma402/#datetimeformat-objects DateTimeFormat::DateTimeFormat(Object& prototype) - : Object(ConstructWithPrototypeTag::Tag, prototype) + : IntlObject(ConstructWithPrototypeTag::Tag, prototype) { } @@ -34,6 +34,30 @@ void DateTimeFormat::visit_edges(Cell::Visitor& visitor) visitor.visit(m_bound_format); } +// 11.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.datetimeformat-internal-slots +ReadonlySpan DateTimeFormat::relevant_extension_keys() const +{ + // The value of the [[RelevantExtensionKeys]] internal slot is « "ca", "hc", "nu" ». + static constexpr AK::Array keys { "ca"sv, "hc"sv, "nu"sv }; + return keys; +} + +// 11.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.datetimeformat-internal-slots +ReadonlySpan DateTimeFormat::resolution_option_descriptors(VM& vm) const +{ + // The value of the [[ResolutionOptionDescriptors]] internal slot is « { [[Key]]: "ca", [[Property]]: "calendar" }, { [[Key]]: "nu", [[Property]]: "numberingSystem" }, { [[Key]]: "hour12", [[Property]]: "hour12", [[Type]]: boolean }, { [[Key]]: "hc", [[Property]]: "hourCycle", [[Values]]: « "h11", "h12", "h23", "h24" » } ». + static constexpr AK::Array hour_cycle_values { "h11"sv, "h12"sv, "h23"sv, "h24"sv }; + + static auto descriptors = to_array({ + { .key = "ca"sv, .property = vm.names.calendar }, + { .key = "nu"sv, .property = vm.names.numberingSystem }, + { .key = "hour12"sv, .property = vm.names.hour12, .type = OptionType::Boolean }, + { .key = "hc"sv, .property = vm.names.hourCycle, .values = hour_cycle_values }, + }); + + return descriptors; +} + static Optional get_or_create_formatter(StringView locale, StringView time_zone, OwnPtr& formatter, Optional const& format) { if (formatter) diff --git a/Libraries/LibJS/Runtime/Intl/DateTimeFormat.h b/Libraries/LibJS/Runtime/Intl/DateTimeFormat.h index 2179f004cb1..1968a37f197 100644 --- a/Libraries/LibJS/Runtime/Intl/DateTimeFormat.h +++ b/Libraries/LibJS/Runtime/Intl/DateTimeFormat.h @@ -13,28 +13,24 @@ #include #include #include -#include +#include #include #include namespace JS::Intl { -class DateTimeFormat final : public Object { - JS_OBJECT(DateTimeFormat, Object); +class DateTimeFormat final : public IntlObject { + JS_OBJECT(DateTimeFormat, IntlObject); GC_DECLARE_ALLOCATOR(DateTimeFormat); using Patterns = Unicode::CalendarPattern; public: - static constexpr auto relevant_extension_keys() - { - // 11.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.datetimeformat-internal-slots - // The value of the [[RelevantExtensionKeys]] internal slot is « "ca", "hc", "nu" ». - return AK::Array { "ca"sv, "hc"sv, "nu"sv }; - } - virtual ~DateTimeFormat() override = default; + virtual ReadonlySpan relevant_extension_keys() const override; + virtual ReadonlySpan resolution_option_descriptors(VM&) const override; + String const& locale() const { return m_locale; } void set_locale(String locale) { m_locale = move(locale); } @@ -90,7 +86,7 @@ public: void set_temporal_time_zone(String temporal_time_zone) { m_temporal_time_zone = move(temporal_time_zone); } private: - DateTimeFormat(Object& prototype); + explicit DateTimeFormat(Object& prototype); virtual void visit_edges(Visitor&) override; diff --git a/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp b/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp index c68d1cdc887..1dbc8e79e92 100644 --- a/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024, Tim Flynn + * Copyright (c) 2021-2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ @@ -145,7 +145,7 @@ ThrowCompletionOr> create_date_time_format(VM& vm, Funct opt.hc = locale_key_from_value(hour_cycle); // 17. Let r be ResolveLocale(%Intl.DateTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %Intl.DateTimeFormat%.[[RelevantExtensionKeys]], %Intl.DateTimeFormat%.[[LocaleData]]). - auto result = resolve_locale(requested_locales, opt, DateTimeFormat::relevant_extension_keys()); + auto result = resolve_locale(requested_locales, opt, date_time_format->relevant_extension_keys()); // 18. Set dateTimeFormat.[[Locale]] to r.[[Locale]]. date_time_format->set_locale(move(result.locale)); diff --git a/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp b/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp index b82ed1ff515..0a43824c5d5 100644 --- a/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp +++ b/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp @@ -4,9 +4,9 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include #include +#include namespace JS::Intl { @@ -14,10 +14,24 @@ GC_DEFINE_ALLOCATOR(DisplayNames); // 12 DisplayNames Objects, https://tc39.es/ecma402/#intl-displaynames-objects DisplayNames::DisplayNames(Object& prototype) - : Object(ConstructWithPrototypeTag::Tag, prototype) + : IntlObject(ConstructWithPrototypeTag::Tag, prototype) { } +// 12.2.3 Internal slots, https://tc39.es/ecma402/#sec-Intl.DisplayNames-internal-slots +ReadonlySpan DisplayNames::relevant_extension_keys() const +{ + // The value of the [[RelevantExtensionKeys]] internal slot is « ». + return {}; +} + +// 12.2.3 Internal slots, https://tc39.es/ecma402/#sec-Intl.DisplayNames-internal-slots +ReadonlySpan DisplayNames::resolution_option_descriptors(VM&) const +{ + // The value of the [[ResolutionOptionDescriptors]] internal slot is « ». + return {}; +} + void DisplayNames::set_type(StringView type) { if (type == "language"sv) diff --git a/Libraries/LibJS/Runtime/Intl/DisplayNames.h b/Libraries/LibJS/Runtime/Intl/DisplayNames.h index 611ce86db0f..371c5a85b1f 100644 --- a/Libraries/LibJS/Runtime/Intl/DisplayNames.h +++ b/Libraries/LibJS/Runtime/Intl/DisplayNames.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024, Tim Flynn + * Copyright (c) 2021-2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ @@ -9,14 +9,14 @@ #include #include #include -#include +#include #include #include namespace JS::Intl { -class DisplayNames final : public Object { - JS_OBJECT(DisplayNames, Object); +class DisplayNames final : public IntlObject { + JS_OBJECT(DisplayNames, IntlObject); GC_DECLARE_ALLOCATOR(DisplayNames); enum class Type { @@ -38,6 +38,9 @@ class DisplayNames final : public Object { public: virtual ~DisplayNames() override = default; + virtual ReadonlySpan relevant_extension_keys() const override; + virtual ReadonlySpan resolution_option_descriptors(VM&) const override; + String const& locale() const { return m_locale; } void set_locale(String locale) { m_locale = move(locale); } @@ -59,7 +62,7 @@ public: StringView language_display_string() const { return Unicode::language_display_to_string(*m_language_display); } private: - DisplayNames(Object& prototype); + explicit DisplayNames(Object& prototype); String m_locale; // [[Locale]] Unicode::Style m_style { Unicode::Style::Long }; // [[Style]] diff --git a/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp b/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp index 3985d42fc6c..ca4c2e9ac5c 100644 --- a/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp @@ -75,7 +75,7 @@ ThrowCompletionOr> DisplayNamesConstructor::construct(FunctionOb opt.locale_matcher = matcher; // 9. Let r be ResolveLocale(%Intl.DisplayNames%.[[AvailableLocales]], requestedLocales, opt, %Intl.DisplayNames%.[[RelevantExtensionKeys]], %Intl.DisplayNames%.[[LocaleData]]). - auto result = resolve_locale(requested_locales, opt, {}); + auto result = resolve_locale(requested_locales, opt, display_names->relevant_extension_keys()); // 10. Let style be ? GetOption(options, "style", string, « "narrow", "short", "long" », "long"). auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "narrow"sv, "short"sv, "long"sv }, "long"sv)); diff --git a/Libraries/LibJS/Runtime/Intl/DurationFormat.cpp b/Libraries/LibJS/Runtime/Intl/DurationFormat.cpp index 7543bedc8a3..cc96d44127f 100644 --- a/Libraries/LibJS/Runtime/Intl/DurationFormat.cpp +++ b/Libraries/LibJS/Runtime/Intl/DurationFormat.cpp @@ -8,15 +8,12 @@ #include #include #include -#include #include #include #include #include #include -#include -#include -#include +#include #include namespace JS::Intl { @@ -25,10 +22,29 @@ GC_DEFINE_ALLOCATOR(DurationFormat); // 13 DurationFormat Objects, https://tc39.es/ecma402/#durationformat-objects DurationFormat::DurationFormat(Object& prototype) - : Object(ConstructWithPrototypeTag::Tag, prototype) + : IntlObject(ConstructWithPrototypeTag::Tag, prototype) { } +// 13.2.3 Internal slots, https://tc39.es/ecma402/#sec-Intl.DurationFormat-internal-slots +ReadonlySpan DurationFormat::relevant_extension_keys() const +{ + // The value of the [[RelevantExtensionKeys]] internal slot is « "nu" ». + static constexpr AK::Array keys { "nu"sv }; + return keys; +} + +// 13.2.3 Internal slots, https://tc39.es/ecma402/#sec-Intl.DurationFormat-internal-slots +ReadonlySpan DurationFormat::resolution_option_descriptors(VM& vm) const +{ + // The value of the [[ResolutionOptionDescriptors]] internal slot is « { [[Key]]: "nu", [[Property]]: "numberingSystem" } ». + static auto descriptors = to_array({ + { .key = "nu"sv, .property = vm.names.numberingSystem }, + }); + + return descriptors; +} + DurationFormat::Style DurationFormat::style_from_string(StringView style) { if (style == "long"sv) diff --git a/Libraries/LibJS/Runtime/Intl/DurationFormat.h b/Libraries/LibJS/Runtime/Intl/DurationFormat.h index 8a1a2c88698..d11fc96e1af 100644 --- a/Libraries/LibJS/Runtime/Intl/DurationFormat.h +++ b/Libraries/LibJS/Runtime/Intl/DurationFormat.h @@ -10,15 +10,14 @@ #include #include #include -#include -#include +#include #include #include namespace JS::Intl { -class DurationFormat final : public Object { - JS_OBJECT(DurationFormat, Object); +class DurationFormat final : public IntlObject { + JS_OBJECT(DurationFormat, IntlObject); GC_DECLARE_ALLOCATOR(DurationFormat); public: @@ -72,15 +71,11 @@ public: Display display { Display::Auto }; }; - static constexpr auto relevant_extension_keys() - { - // 13.2.3 Internal slots, https://tc39.es/ecma402/#sec-Intl.DurationFormat-internal-slots - // The value of the [[RelevantExtensionKeys]] internal slot is « "nu" ». - return AK::Array { "nu"sv }; - } - virtual ~DurationFormat() override = default; + virtual ReadonlySpan relevant_extension_keys() const override; + virtual ReadonlySpan resolution_option_descriptors(VM&) const override; + void set_locale(String locale) { m_locale = move(locale); } String const& locale() const { return m_locale; } diff --git a/Libraries/LibJS/Runtime/Intl/DurationFormatConstructor.cpp b/Libraries/LibJS/Runtime/Intl/DurationFormatConstructor.cpp index c74beee2753..f0227ea07d1 100644 --- a/Libraries/LibJS/Runtime/Intl/DurationFormatConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/DurationFormatConstructor.cpp @@ -81,7 +81,7 @@ ThrowCompletionOr> DurationFormatConstructor::construct(Function opt.nu = locale_key_from_value(numbering_system); // 9. Let r be ResolveLocale(%Intl.DurationFormat%.[[AvailableLocales]], requestedLocales, opt, %Intl.DurationFormat%.[[RelevantExtensionKeys]], %Intl.DurationFormat%.[[LocaleData]]). - auto result = resolve_locale(requested_locales, opt, DurationFormat::relevant_extension_keys()); + auto result = resolve_locale(requested_locales, opt, duration_format->relevant_extension_keys()); // 10. Set durationFormat.[[Locale]] to r.[[Locale]]. duration_format->set_locale(move(result.locale)); diff --git a/Libraries/LibJS/Runtime/Intl/IntlObject.h b/Libraries/LibJS/Runtime/Intl/IntlObject.h new file mode 100644 index 00000000000..759113bee17 --- /dev/null +++ b/Libraries/LibJS/Runtime/Intl/IntlObject.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace JS::Intl { + +// https://tc39.es/ecma402/#resolution-option-descriptor +struct ResolutionOptionDescriptor { + StringView key; + PropertyKey property; + OptionType type { OptionType::String }; + ReadonlySpan values {}; +}; + +class IntlObject : public Object { + JS_OBJECT(IntlObject, Object); + +public: + virtual ReadonlySpan relevant_extension_keys() const = 0; + virtual ReadonlySpan resolution_option_descriptors(VM&) const = 0; + +protected: + using Object::Object; +}; + +} diff --git a/Libraries/LibJS/Runtime/Intl/ListFormat.cpp b/Libraries/LibJS/Runtime/Intl/ListFormat.cpp index e978263bbb8..6dee312a67f 100644 --- a/Libraries/LibJS/Runtime/Intl/ListFormat.cpp +++ b/Libraries/LibJS/Runtime/Intl/ListFormat.cpp @@ -4,11 +4,10 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include -#include #include #include +#include #include namespace JS::Intl { @@ -17,10 +16,24 @@ GC_DEFINE_ALLOCATOR(ListFormat); // 14 ListFormat Objects, https://tc39.es/ecma402/#listformat-objects ListFormat::ListFormat(Object& prototype) - : Object(ConstructWithPrototypeTag::Tag, prototype) + : IntlObject(ConstructWithPrototypeTag::Tag, prototype) { } +// 14.2.3 Internal slots, https://tc39.es/ecma402/#sec-Intl.ListFormat-internal-slots +ReadonlySpan ListFormat::relevant_extension_keys() const +{ + // The value of the [[RelevantExtensionKeys]] internal slot is « ». + return {}; +} + +// 14.2.3 Internal slots, https://tc39.es/ecma402/#sec-Intl.ListFormat-internal-slots +ReadonlySpan ListFormat::resolution_option_descriptors(VM&) const +{ + // The value of the [[ResolutionOptionDescriptors]] internal slot is « ». + return {}; +} + // 14.5.2 CreatePartsFromList ( listFormat, list ), https://tc39.es/ecma402/#sec-createpartsfromlist Vector create_parts_from_list(ListFormat const& list_format, ReadonlySpan list) { diff --git a/Libraries/LibJS/Runtime/Intl/ListFormat.h b/Libraries/LibJS/Runtime/Intl/ListFormat.h index 5be25d83389..e1a392348b9 100644 --- a/Libraries/LibJS/Runtime/Intl/ListFormat.h +++ b/Libraries/LibJS/Runtime/Intl/ListFormat.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024, Tim Flynn + * Copyright (c) 2021-2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ @@ -9,15 +9,14 @@ #include #include #include -#include -#include +#include #include #include namespace JS::Intl { -class ListFormat final : public Object { - JS_OBJECT(ListFormat, Object); +class ListFormat final : public IntlObject { + JS_OBJECT(ListFormat, IntlObject); GC_DECLARE_ALLOCATOR(ListFormat); public: @@ -30,6 +29,9 @@ public: virtual ~ListFormat() override = default; + virtual ReadonlySpan relevant_extension_keys() const override; + virtual ReadonlySpan resolution_option_descriptors(VM&) const override; + String const& locale() const { return m_locale; } void set_locale(String locale) { m_locale = move(locale); } diff --git a/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp b/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp index c33bb978be2..049c8c1dd9b 100644 --- a/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp @@ -70,7 +70,7 @@ ThrowCompletionOr> ListFormatConstructor::construct(FunctionObje opt.locale_matcher = matcher; // 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, list_format->relevant_extension_keys()); // 9. Set listFormat.[[Locale]] to r.[[Locale]]. list_format->set_locale(move(result.locale)); diff --git a/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp b/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp index 0e73e91fe8b..12a2c10dc04 100644 --- a/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp +++ b/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp @@ -4,22 +4,15 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include -#include #include -#include #include #include -#include #include -#include -#include +#include +#include #include #include -#include #include -#include namespace JS::Intl { @@ -27,7 +20,7 @@ GC_DEFINE_ALLOCATOR(NumberFormatBase); GC_DEFINE_ALLOCATOR(NumberFormat); NumberFormatBase::NumberFormatBase(Object& prototype) - : Object(ConstructWithPrototypeTag::Tag, prototype) + : IntlObject(ConstructWithPrototypeTag::Tag, prototype) { } @@ -44,6 +37,25 @@ void NumberFormat::visit_edges(Cell::Visitor& visitor) visitor.visit(m_bound_format); } +// 16.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.numberformat-internal-slots +ReadonlySpan NumberFormat::relevant_extension_keys() const +{ + // The value of the [[RelevantExtensionKeys]] internal slot is « "nu" ». + static constexpr AK::Array keys { "nu"sv }; + return keys; +} + +// 16.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.numberformat-internal-slots +ReadonlySpan NumberFormat::resolution_option_descriptors(VM& vm) const +{ + // The value of the [[ResolutionOptionDescriptors]] internal slot is « { [[Key]]: "nu", [[Property]]: "numberingSystem" } ». + static auto descriptors = to_array({ + { .key = "nu"sv, .property = vm.names.numberingSystem }, + }); + + return descriptors; +} + StringView NumberFormatBase::computed_rounding_priority_string() const { switch (m_computed_rounding_priority) { diff --git a/Libraries/LibJS/Runtime/Intl/NumberFormat.h b/Libraries/LibJS/Runtime/Intl/NumberFormat.h index d9811e6ae16..8e1fb845205 100644 --- a/Libraries/LibJS/Runtime/Intl/NumberFormat.h +++ b/Libraries/LibJS/Runtime/Intl/NumberFormat.h @@ -6,19 +6,18 @@ #pragma once -#include #include #include #include +#include #include -#include #include #include namespace JS::Intl { -class NumberFormatBase : public Object { - JS_OBJECT(NumberFormatBase, Object); +class NumberFormatBase : public IntlObject { + JS_OBJECT(NumberFormatBase, IntlObject); GC_DECLARE_ALLOCATOR(NumberFormatBase); public: @@ -102,15 +101,11 @@ class NumberFormat final : public NumberFormatBase { GC_DECLARE_ALLOCATOR(NumberFormat); public: - static constexpr auto relevant_extension_keys() - { - // 16.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.numberformat-internal-slots - // The value of the [[RelevantExtensionKeys]] internal slot is « "nu" ». - return AK::Array { "nu"sv }; - } - virtual ~NumberFormat() override = default; + virtual ReadonlySpan relevant_extension_keys() const override; + virtual ReadonlySpan resolution_option_descriptors(VM&) const override; + String const& numbering_system() const { return m_numbering_system; } void set_numbering_system(String numbering_system) { m_numbering_system = move(numbering_system); } diff --git a/Libraries/LibJS/Runtime/Intl/NumberFormatConstructor.cpp b/Libraries/LibJS/Runtime/Intl/NumberFormatConstructor.cpp index 15967bb8e6a..cf79afa0566 100644 --- a/Libraries/LibJS/Runtime/Intl/NumberFormatConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/NumberFormatConstructor.cpp @@ -83,7 +83,7 @@ ThrowCompletionOr> NumberFormatConstructor::construct(FunctionOb opt.nu = locale_key_from_value(numbering_system); // 11. Let r be ResolveLocale(%Intl.NumberFormat%.[[AvailableLocales]], requestedLocales, opt, %Intl.NumberFormat%.[[RelevantExtensionKeys]], %Intl.NumberFormat%.[[LocaleData]]). - auto result = resolve_locale(requested_locales, opt, NumberFormat::relevant_extension_keys()); + auto result = resolve_locale(requested_locales, opt, number_format->relevant_extension_keys()); // 12. Set numberFormat.[[Locale]] to r.[[Locale]]. number_format->set_locale(move(result.locale)); diff --git a/Libraries/LibJS/Runtime/Intl/PluralRules.cpp b/Libraries/LibJS/Runtime/Intl/PluralRules.cpp index 935a6ee9667..0276522b50a 100644 --- a/Libraries/LibJS/Runtime/Intl/PluralRules.cpp +++ b/Libraries/LibJS/Runtime/Intl/PluralRules.cpp @@ -17,6 +17,20 @@ PluralRules::PluralRules(Object& prototype) { } +// 17.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.pluralrules-internal-slots +ReadonlySpan PluralRules::relevant_extension_keys() const +{ + // The value of the [[RelevantExtensionKeys]] internal slot is « ». + return {}; +} + +// 17.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.pluralrules-internal-slots +ReadonlySpan PluralRules::resolution_option_descriptors(VM&) const +{ + // The value of the [[ResolutionOptionDescriptors]] internal slot is « ». + return {}; +} + // 17.5.2 ResolvePlural ( pluralRules, n ), https://tc39.es/ecma402/#sec-resolveplural Unicode::PluralCategory resolve_plural(PluralRules const& plural_rules, Value number) { diff --git a/Libraries/LibJS/Runtime/Intl/PluralRules.h b/Libraries/LibJS/Runtime/Intl/PluralRules.h index 9880257356e..4d6b1bfd6dd 100644 --- a/Libraries/LibJS/Runtime/Intl/PluralRules.h +++ b/Libraries/LibJS/Runtime/Intl/PluralRules.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024, Tim Flynn + * Copyright (c) 2022-2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ @@ -21,6 +21,9 @@ class PluralRules final : public NumberFormatBase { public: virtual ~PluralRules() override = default; + virtual ReadonlySpan relevant_extension_keys() const override; + virtual ReadonlySpan resolution_option_descriptors(VM&) const override; + Unicode::PluralForm type() const { return m_type; } StringView type_string() const { return Unicode::plural_form_to_string(m_type); } void set_type(StringView type) { m_type = Unicode::plural_form_from_string(type); } diff --git a/Libraries/LibJS/Runtime/Intl/PluralRulesConstructor.cpp b/Libraries/LibJS/Runtime/Intl/PluralRulesConstructor.cpp index f2ca85d3f2a..7a19426e06c 100644 --- a/Libraries/LibJS/Runtime/Intl/PluralRulesConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/PluralRulesConstructor.cpp @@ -71,7 +71,7 @@ ThrowCompletionOr> PluralRulesConstructor::construct(FunctionObj opt.locale_matcher = matcher; // 8. Let r be ResolveLocale(%Intl.PluralRules%.[[AvailableLocales]], requestedLocales, opt, %Intl.PluralRules%.[[RelevantExtensionKeys]], %Intl.PluralRules%.[[LocaleData]]). - auto result = resolve_locale(requested_locales, opt, {}); + auto result = resolve_locale(requested_locales, opt, plural_rules->relevant_extension_keys()); // 9. Set pluralRules.[[Locale]] to r.[[locale]]. plural_rules->set_locale(move(result.locale)); diff --git a/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.cpp b/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.cpp index 4c2a846c730..4910dc91047 100644 --- a/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.cpp +++ b/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.cpp @@ -5,14 +5,9 @@ */ #include -#include -#include #include -#include -#include -#include -#include #include +#include namespace JS::Intl { @@ -20,10 +15,29 @@ GC_DEFINE_ALLOCATOR(RelativeTimeFormat); // 18 RelativeTimeFormat Objects, https://tc39.es/ecma402/#relativetimeformat-objects RelativeTimeFormat::RelativeTimeFormat(Object& prototype) - : Object(ConstructWithPrototypeTag::Tag, prototype) + : IntlObject(ConstructWithPrototypeTag::Tag, prototype) { } +// 18.2.3 Internal slots, https://tc39.es/ecma402/#sec-Intl.RelativeTimeFormat-internal-slots +ReadonlySpan RelativeTimeFormat::relevant_extension_keys() const +{ + // The value of the [[RelevantExtensionKeys]] internal slot is « "nu" ». + static constexpr AK::Array keys { "nu"sv }; + return keys; +} + +// 18.2.3 Internal slots, https://tc39.es/ecma402/#sec-Intl.RelativeTimeFormat-internal-slots +ReadonlySpan RelativeTimeFormat::resolution_option_descriptors(VM& vm) const +{ + // The value of the [[ResolutionOptionDescriptors]] internal slot is « { [[Key]]: "nu", [[Property]]: "numberingSystem" } ». + static auto descriptors = to_array({ + { .key = "nu"sv, .property = vm.names.numberingSystem }, + }); + + return descriptors; +} + // 18.5.1 SingularRelativeTimeUnit ( unit ), https://tc39.es/ecma402/#sec-singularrelativetimeunit ThrowCompletionOr singular_relative_time_unit(VM& vm, StringView unit) { diff --git a/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.h b/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.h index cd2c73d6d71..97086dc311d 100644 --- a/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.h +++ b/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.h @@ -6,32 +6,25 @@ #pragma once -#include #include #include #include -#include -#include +#include #include -#include #include namespace JS::Intl { -class RelativeTimeFormat final : public Object { - JS_OBJECT(RelativeTimeFormat, Object); +class RelativeTimeFormat final : public IntlObject { + JS_OBJECT(RelativeTimeFormat, IntlObject); GC_DECLARE_ALLOCATOR(RelativeTimeFormat); public: - static constexpr auto relevant_extension_keys() - { - // 18.2.3 Internal slots, https://tc39.es/ecma402/#sec-Intl.RelativeTimeFormat-internal-slots - // The value of the [[RelevantExtensionKeys]] internal slot is « "nu" ». - return AK::Array { "nu"sv }; - } - virtual ~RelativeTimeFormat() override = default; + virtual ReadonlySpan relevant_extension_keys() const override; + virtual ReadonlySpan resolution_option_descriptors(VM&) const override; + String const& locale() const { return m_locale; } void set_locale(String locale) { m_locale = move(locale); } diff --git a/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp b/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp index 75da8e74b98..80db4f30e68 100644 --- a/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp @@ -83,7 +83,7 @@ ThrowCompletionOr> RelativeTimeFormatConstructor::construct(Func opt.nu = locale_key_from_value(numbering_system); // 11. Let r be ResolveLocale(%Intl.RelativeTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %Intl.RelativeTimeFormat%.[[RelevantExtensionKeys]], %Intl.RelativeTimeFormat%.[[LocaleData]]). - auto result = resolve_locale(requested_locales, opt, RelativeTimeFormat::relevant_extension_keys()); + auto result = resolve_locale(requested_locales, opt, relative_time_format->relevant_extension_keys()); // 12. Let locale be r.[[Locale]]. auto locale = move(result.locale); diff --git a/Libraries/LibJS/Runtime/Intl/Segmenter.cpp b/Libraries/LibJS/Runtime/Intl/Segmenter.cpp index 8e9f9a37f15..bda7b5e0436 100644 --- a/Libraries/LibJS/Runtime/Intl/Segmenter.cpp +++ b/Libraries/LibJS/Runtime/Intl/Segmenter.cpp @@ -6,8 +6,8 @@ */ #include -#include #include +#include namespace JS::Intl { @@ -15,10 +15,24 @@ GC_DEFINE_ALLOCATOR(Segmenter); // 19 Segmenter Objects, https://tc39.es/ecma402/#segmenter-objects Segmenter::Segmenter(Object& prototype) - : Object(ConstructWithPrototypeTag::Tag, prototype) + : IntlObject(ConstructWithPrototypeTag::Tag, prototype) { } +// 19.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.segmenter-internal-slots +ReadonlySpan Segmenter::relevant_extension_keys() const +{ + // The value of the [[RelevantExtensionKeys]] internal slot is « ». + return {}; +} + +// 19.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.segmenter-internal-slots +ReadonlySpan Segmenter::resolution_option_descriptors(VM&) const +{ + // The value of the [[ResolutionOptionDescriptors]] internal slot is « ». + return {}; +} + // 19.7.1 CreateSegmentDataObject ( segmenter, string, startIndex, endIndex ), https://tc39.es/ecma402/#sec-createsegmentdataobject ThrowCompletionOr> create_segment_data_object(VM& vm, Unicode::Segmenter const& segmenter, Utf16View const& string, size_t start_index, size_t end_index) { diff --git a/Libraries/LibJS/Runtime/Intl/Segmenter.h b/Libraries/LibJS/Runtime/Intl/Segmenter.h index 3a1fd7c3076..90226cbee5a 100644 --- a/Libraries/LibJS/Runtime/Intl/Segmenter.h +++ b/Libraries/LibJS/Runtime/Intl/Segmenter.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, Idan Horowitz - * Copyright (c) 2023, Tim Flynn + * Copyright (c) 2023-2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ @@ -8,18 +8,21 @@ #pragma once #include -#include +#include #include namespace JS::Intl { -class Segmenter final : public Object { - JS_OBJECT(Segmenter, Object); +class Segmenter final : public IntlObject { + JS_OBJECT(Segmenter, IntlObject); GC_DECLARE_ALLOCATOR(Segmenter); public: virtual ~Segmenter() override = default; + virtual ReadonlySpan relevant_extension_keys() const override; + virtual ReadonlySpan resolution_option_descriptors(VM&) const override; + String const& locale() const { return m_locale; } void set_locale(String locale) { m_locale = move(locale); } diff --git a/Libraries/LibJS/Runtime/Intl/SegmenterConstructor.cpp b/Libraries/LibJS/Runtime/Intl/SegmenterConstructor.cpp index 8a68aaf973a..6319242208f 100644 --- a/Libraries/LibJS/Runtime/Intl/SegmenterConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/SegmenterConstructor.cpp @@ -71,7 +71,7 @@ ThrowCompletionOr> SegmenterConstructor::construct(FunctionObjec opt.locale_matcher = matcher; // 9. Let r be ResolveLocale(%Intl.Segmenter%.[[AvailableLocales]], requestedLocales, opt, %Intl.Segmenter%.[[RelevantExtensionKeys]], %Intl.Segmenter%.[[LocaleData]]). - auto result = resolve_locale(requested_locales, opt, {}); + auto result = resolve_locale(requested_locales, opt, segmenter->relevant_extension_keys()); // 10. Set segmenter.[[Locale]] to r.[[locale]]. segmenter->set_locale(move(result.locale));