LibJS: Define constructor slots for describing how to read options

This is an editorial change in the ECMA-402 spec. See:
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.
This commit is contained in:
Timothy Flynn 2025-04-07 15:56:31 -04:00 committed by Tim Flynn
commit 62793b1bd8
Notes: github-actions[bot] 2025-04-08 10:53:24 +00:00
29 changed files with 284 additions and 116 deletions

View file

@ -274,11 +274,14 @@ JS_ENUMERATE_INTL_OBJECTS
#undef __JS_ENUMERATE #undef __JS_ENUMERATE
class Intl; class Intl;
class IntlObject;
class MathematicalValue; class MathematicalValue;
// Not included in JS_ENUMERATE_INTL_OBJECTS due to missing distinct constructor // Not included in JS_ENUMERATE_INTL_OBJECTS due to missing distinct constructor
class Segments; class Segments;
class SegmentsPrototype; class SegmentsPrototype;
struct ResolutionOptionDescriptor;
}; };
namespace Temporal { namespace Temporal {

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@serenityos.org> * Copyright (c) 2022-2025, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -12,7 +12,7 @@ GC_DEFINE_ALLOCATOR(Collator);
// 10 Collator Objects, https://tc39.es/ecma402/#collator-objects // 10 Collator Objects, https://tc39.es/ecma402/#collator-objects
Collator::Collator(Object& prototype) 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); visitor.visit(m_bound_compare);
} }
// 10.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl-collator-internal-slots
ReadonlySpan<StringView> 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<ResolutionOptionDescriptor> 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<ResolutionOptionDescriptor>({
{ .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;
}
} }

View file

@ -1,34 +1,29 @@
/* /*
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@serenityos.org> * Copyright (c) 2022-2025, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#pragma once #pragma once
#include <AK/Array.h>
#include <AK/String.h> #include <AK/String.h>
#include <AK/StringView.h> #include <AK/StringView.h>
#include <LibJS/Runtime/Intl/CollatorCompareFunction.h> #include <LibJS/Runtime/Intl/CollatorCompareFunction.h>
#include <LibJS/Runtime/Object.h> #include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibUnicode/Collator.h> #include <LibUnicode/Collator.h>
namespace JS::Intl { namespace JS::Intl {
class Collator final : public Object { class Collator final : public IntlObject {
JS_OBJECT(Collator, Object); JS_OBJECT(Collator, IntlObject);
GC_DECLARE_ALLOCATOR(Collator); GC_DECLARE_ALLOCATOR(Collator);
public: 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 ~Collator() override = default;
virtual ReadonlySpan<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const override;
String const& locale() const { return m_locale; } String const& locale() const { return m_locale; }
void set_locale(String locale) { m_locale = move(locale); } void set_locale(String locale) { m_locale = move(locale); }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@serenityos.org> * Copyright (c) 2022-2025, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -113,7 +113,7 @@ ThrowCompletionOr<GC::Ref<Object>> CollatorConstructor::construct(FunctionObject
opt.kf = locale_key_from_value(case_first); opt.kf = locale_key_from_value(case_first);
// 21. Let r be ResolveLocale(%Intl.Collator%.[[AvailableLocales]], requestedLocales, opt, %Intl.Collator%.[[RelevantExtensionKeys]], localeData). // 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]]. // 22. Set collator.[[Locale]] to r.[[Locale]].
collator->set_locale(move(result.locale)); collator->set_locale(move(result.locale));

View file

@ -24,7 +24,7 @@ GC_DEFINE_ALLOCATOR(DateTimeFormat);
// 11 DateTimeFormat Objects, https://tc39.es/ecma402/#datetimeformat-objects // 11 DateTimeFormat Objects, https://tc39.es/ecma402/#datetimeformat-objects
DateTimeFormat::DateTimeFormat(Object& prototype) 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); visitor.visit(m_bound_format);
} }
// 11.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.datetimeformat-internal-slots
ReadonlySpan<StringView> 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<ResolutionOptionDescriptor> 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<ResolutionOptionDescriptor>({
{ .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<Unicode::DateTimeFormat const&> get_or_create_formatter(StringView locale, StringView time_zone, OwnPtr<Unicode::DateTimeFormat>& formatter, Optional<Unicode::CalendarPattern> const& format) static Optional<Unicode::DateTimeFormat const&> get_or_create_formatter(StringView locale, StringView time_zone, OwnPtr<Unicode::DateTimeFormat>& formatter, Optional<Unicode::CalendarPattern> const& format)
{ {
if (formatter) if (formatter)

View file

@ -13,28 +13,24 @@
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibJS/Runtime/Completion.h> #include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Intl/DateTimeFormatConstructor.h> #include <LibJS/Runtime/Intl/DateTimeFormatConstructor.h>
#include <LibJS/Runtime/Object.h> #include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibJS/Runtime/VM.h> #include <LibJS/Runtime/VM.h>
#include <LibUnicode/DateTimeFormat.h> #include <LibUnicode/DateTimeFormat.h>
namespace JS::Intl { namespace JS::Intl {
class DateTimeFormat final : public Object { class DateTimeFormat final : public IntlObject {
JS_OBJECT(DateTimeFormat, Object); JS_OBJECT(DateTimeFormat, IntlObject);
GC_DECLARE_ALLOCATOR(DateTimeFormat); GC_DECLARE_ALLOCATOR(DateTimeFormat);
using Patterns = Unicode::CalendarPattern; using Patterns = Unicode::CalendarPattern;
public: 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 ~DateTimeFormat() override = default;
virtual ReadonlySpan<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const override;
String const& locale() const { return m_locale; } String const& locale() const { return m_locale; }
void set_locale(String locale) { m_locale = move(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); } void set_temporal_time_zone(String temporal_time_zone) { m_temporal_time_zone = move(temporal_time_zone); }
private: private:
DateTimeFormat(Object& prototype); explicit DateTimeFormat(Object& prototype);
virtual void visit_edges(Visitor&) override; virtual void visit_edges(Visitor&) override;

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021-2024, Tim Flynn <trflynn89@ladybird.org> * Copyright (c) 2021-2025, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -145,7 +145,7 @@ ThrowCompletionOr<GC::Ref<DateTimeFormat>> create_date_time_format(VM& vm, Funct
opt.hc = locale_key_from_value(hour_cycle); opt.hc = locale_key_from_value(hour_cycle);
// 17. Let r be ResolveLocale(%Intl.DateTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %Intl.DateTimeFormat%.[[RelevantExtensionKeys]], %Intl.DateTimeFormat%.[[LocaleData]]). // 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]]. // 18. Set dateTimeFormat.[[Locale]] to r.[[Locale]].
date_time_format->set_locale(move(result.locale)); date_time_format->set_locale(move(result.locale));

View file

@ -4,9 +4,9 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/AbstractOperations.h> #include <LibJS/Runtime/Intl/AbstractOperations.h>
#include <LibJS/Runtime/Intl/DisplayNames.h> #include <LibJS/Runtime/Intl/DisplayNames.h>
#include <LibJS/Runtime/VM.h>
namespace JS::Intl { namespace JS::Intl {
@ -14,10 +14,24 @@ GC_DEFINE_ALLOCATOR(DisplayNames);
// 12 DisplayNames Objects, https://tc39.es/ecma402/#intl-displaynames-objects // 12 DisplayNames Objects, https://tc39.es/ecma402/#intl-displaynames-objects
DisplayNames::DisplayNames(Object& prototype) 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<StringView> 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<ResolutionOptionDescriptor> DisplayNames::resolution_option_descriptors(VM&) const
{
// The value of the [[ResolutionOptionDescriptors]] internal slot is « ».
return {};
}
void DisplayNames::set_type(StringView type) void DisplayNames::set_type(StringView type)
{ {
if (type == "language"sv) if (type == "language"sv)

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021-2024, Tim Flynn <trflynn89@serenityos.org> * Copyright (c) 2021-2025, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -9,14 +9,14 @@
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/String.h> #include <AK/String.h>
#include <AK/StringView.h> #include <AK/StringView.h>
#include <LibJS/Runtime/Object.h> #include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibUnicode/DisplayNames.h> #include <LibUnicode/DisplayNames.h>
#include <LibUnicode/Locale.h> #include <LibUnicode/Locale.h>
namespace JS::Intl { namespace JS::Intl {
class DisplayNames final : public Object { class DisplayNames final : public IntlObject {
JS_OBJECT(DisplayNames, Object); JS_OBJECT(DisplayNames, IntlObject);
GC_DECLARE_ALLOCATOR(DisplayNames); GC_DECLARE_ALLOCATOR(DisplayNames);
enum class Type { enum class Type {
@ -38,6 +38,9 @@ class DisplayNames final : public Object {
public: public:
virtual ~DisplayNames() override = default; virtual ~DisplayNames() override = default;
virtual ReadonlySpan<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const override;
String const& locale() const { return m_locale; } String const& locale() const { return m_locale; }
void set_locale(String locale) { m_locale = move(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); } StringView language_display_string() const { return Unicode::language_display_to_string(*m_language_display); }
private: private:
DisplayNames(Object& prototype); explicit DisplayNames(Object& prototype);
String m_locale; // [[Locale]] String m_locale; // [[Locale]]
Unicode::Style m_style { Unicode::Style::Long }; // [[Style]] Unicode::Style m_style { Unicode::Style::Long }; // [[Style]]

View file

@ -75,7 +75,7 @@ ThrowCompletionOr<GC::Ref<Object>> DisplayNamesConstructor::construct(FunctionOb
opt.locale_matcher = matcher; opt.locale_matcher = matcher;
// 9. Let r be ResolveLocale(%Intl.DisplayNames%.[[AvailableLocales]], requestedLocales, opt, %Intl.DisplayNames%.[[RelevantExtensionKeys]], %Intl.DisplayNames%.[[LocaleData]]). // 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"). // 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)); auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "narrow"sv, "short"sv, "long"sv }, "long"sv));

View file

@ -8,15 +8,12 @@
#include <AK/GenericShorthands.h> #include <AK/GenericShorthands.h>
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/DurationFormat.h> #include <LibJS/Runtime/Intl/DurationFormat.h>
#include <LibJS/Runtime/Intl/ListFormat.h> #include <LibJS/Runtime/Intl/ListFormat.h>
#include <LibJS/Runtime/Intl/ListFormatConstructor.h> #include <LibJS/Runtime/Intl/ListFormatConstructor.h>
#include <LibJS/Runtime/Intl/MathematicalValue.h> #include <LibJS/Runtime/Intl/MathematicalValue.h>
#include <LibJS/Runtime/Intl/NumberFormatConstructor.h> #include <LibJS/Runtime/Intl/NumberFormatConstructor.h>
#include <LibJS/Runtime/Intl/PluralRules.h> #include <LibJS/Runtime/VM.h>
#include <LibJS/Runtime/Intl/PluralRulesConstructor.h>
#include <LibJS/Runtime/Intl/RelativeTimeFormat.h>
#include <LibJS/Runtime/ValueInlines.h> #include <LibJS/Runtime/ValueInlines.h>
namespace JS::Intl { namespace JS::Intl {
@ -25,10 +22,29 @@ GC_DEFINE_ALLOCATOR(DurationFormat);
// 13 DurationFormat Objects, https://tc39.es/ecma402/#durationformat-objects // 13 DurationFormat Objects, https://tc39.es/ecma402/#durationformat-objects
DurationFormat::DurationFormat(Object& prototype) 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<StringView> 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<ResolutionOptionDescriptor> DurationFormat::resolution_option_descriptors(VM& vm) const
{
// The value of the [[ResolutionOptionDescriptors]] internal slot is « { [[Key]]: "nu", [[Property]]: "numberingSystem" } ».
static auto descriptors = to_array<ResolutionOptionDescriptor>({
{ .key = "nu"sv, .property = vm.names.numberingSystem },
});
return descriptors;
}
DurationFormat::Style DurationFormat::style_from_string(StringView style) DurationFormat::Style DurationFormat::style_from_string(StringView style)
{ {
if (style == "long"sv) if (style == "long"sv)

View file

@ -10,15 +10,14 @@
#include <AK/Array.h> #include <AK/Array.h>
#include <AK/String.h> #include <AK/String.h>
#include <LibCrypto/BigFraction/BigFraction.h> #include <LibCrypto/BigFraction/BigFraction.h>
#include <LibJS/Runtime/Intl/AbstractOperations.h> #include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/Duration.h> #include <LibJS/Runtime/Temporal/Duration.h>
#include <LibUnicode/Locale.h> #include <LibUnicode/Locale.h>
namespace JS::Intl { namespace JS::Intl {
class DurationFormat final : public Object { class DurationFormat final : public IntlObject {
JS_OBJECT(DurationFormat, Object); JS_OBJECT(DurationFormat, IntlObject);
GC_DECLARE_ALLOCATOR(DurationFormat); GC_DECLARE_ALLOCATOR(DurationFormat);
public: public:
@ -72,15 +71,11 @@ public:
Display display { Display::Auto }; 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 ~DurationFormat() override = default;
virtual ReadonlySpan<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const override;
void set_locale(String locale) { m_locale = move(locale); } void set_locale(String locale) { m_locale = move(locale); }
String const& locale() const { return m_locale; } String const& locale() const { return m_locale; }

View file

@ -81,7 +81,7 @@ ThrowCompletionOr<GC::Ref<Object>> DurationFormatConstructor::construct(Function
opt.nu = locale_key_from_value(numbering_system); opt.nu = locale_key_from_value(numbering_system);
// 9. Let r be ResolveLocale(%Intl.DurationFormat%.[[AvailableLocales]], requestedLocales, opt, %Intl.DurationFormat%.[[RelevantExtensionKeys]], %Intl.DurationFormat%.[[LocaleData]]). // 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]]. // 10. Set durationFormat.[[Locale]] to r.[[Locale]].
duration_format->set_locale(move(result.locale)); duration_format->set_locale(move(result.locale));

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Span.h>
#include <AK/StringView.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/PropertyKey.h>
namespace JS::Intl {
// https://tc39.es/ecma402/#resolution-option-descriptor
struct ResolutionOptionDescriptor {
StringView key;
PropertyKey property;
OptionType type { OptionType::String };
ReadonlySpan<StringView> values {};
};
class IntlObject : public Object {
JS_OBJECT(IntlObject, Object);
public:
virtual ReadonlySpan<StringView> relevant_extension_keys() const = 0;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const = 0;
protected:
using Object::Object;
};
}

View file

@ -4,11 +4,10 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/StringBuilder.h>
#include <LibJS/Runtime/Array.h> #include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/ListFormat.h> #include <LibJS/Runtime/Intl/ListFormat.h>
#include <LibJS/Runtime/Iterator.h> #include <LibJS/Runtime/Iterator.h>
#include <LibJS/Runtime/VM.h>
#include <LibUnicode/ListFormat.h> #include <LibUnicode/ListFormat.h>
namespace JS::Intl { namespace JS::Intl {
@ -17,10 +16,24 @@ GC_DEFINE_ALLOCATOR(ListFormat);
// 14 ListFormat Objects, https://tc39.es/ecma402/#listformat-objects // 14 ListFormat Objects, https://tc39.es/ecma402/#listformat-objects
ListFormat::ListFormat(Object& prototype) 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<StringView> 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<ResolutionOptionDescriptor> 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 // 14.5.2 CreatePartsFromList ( listFormat, list ), https://tc39.es/ecma402/#sec-createpartsfromlist
Vector<Unicode::ListFormat::Partition> create_parts_from_list(ListFormat const& list_format, ReadonlySpan<String> list) Vector<Unicode::ListFormat::Partition> create_parts_from_list(ListFormat const& list_format, ReadonlySpan<String> list)
{ {

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021-2024, Tim Flynn <trflynn89@serenityos.org> * Copyright (c) 2021-2025, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -9,15 +9,14 @@
#include <AK/String.h> #include <AK/String.h>
#include <AK/StringView.h> #include <AK/StringView.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibJS/Runtime/Intl/AbstractOperations.h> #include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibJS/Runtime/Object.h>
#include <LibUnicode/ListFormat.h> #include <LibUnicode/ListFormat.h>
#include <LibUnicode/Locale.h> #include <LibUnicode/Locale.h>
namespace JS::Intl { namespace JS::Intl {
class ListFormat final : public Object { class ListFormat final : public IntlObject {
JS_OBJECT(ListFormat, Object); JS_OBJECT(ListFormat, IntlObject);
GC_DECLARE_ALLOCATOR(ListFormat); GC_DECLARE_ALLOCATOR(ListFormat);
public: public:
@ -30,6 +29,9 @@ public:
virtual ~ListFormat() override = default; virtual ~ListFormat() override = default;
virtual ReadonlySpan<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const override;
String const& locale() const { return m_locale; } String const& locale() const { return m_locale; }
void set_locale(String locale) { m_locale = move(locale); } void set_locale(String locale) { m_locale = move(locale); }

View file

@ -70,7 +70,7 @@ ThrowCompletionOr<GC::Ref<Object>> ListFormatConstructor::construct(FunctionObje
opt.locale_matcher = matcher; opt.locale_matcher = matcher;
// 8. Let r be ResolveLocale(%Intl.ListFormat%.[[AvailableLocales]], requestedLocales, opt, %Intl.ListFormat%.[[RelevantExtensionKeys]], %Intl.ListFormat%.[[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, list_format->relevant_extension_keys());
// 9. 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));

View file

@ -4,22 +4,15 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Checked.h>
#include <AK/StringBuilder.h>
#include <AK/Utf8View.h>
#include <LibCrypto/BigInt/SignedBigInteger.h> #include <LibCrypto/BigInt/SignedBigInteger.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Array.h> #include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/BigInt.h> #include <LibJS/Runtime/BigInt.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/NumberFormat.h> #include <LibJS/Runtime/Intl/NumberFormat.h>
#include <LibJS/Runtime/Intl/NumberFormatFunction.h> #include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/Intl/PluralRules.h> #include <LibJS/Runtime/VM.h>
#include <LibJS/Runtime/ValueInlines.h> #include <LibJS/Runtime/ValueInlines.h>
#include <LibUnicode/CurrencyCode.h> #include <LibUnicode/CurrencyCode.h>
#include <LibUnicode/DisplayNames.h>
#include <math.h> #include <math.h>
#include <stdlib.h>
namespace JS::Intl { namespace JS::Intl {
@ -27,7 +20,7 @@ GC_DEFINE_ALLOCATOR(NumberFormatBase);
GC_DEFINE_ALLOCATOR(NumberFormat); GC_DEFINE_ALLOCATOR(NumberFormat);
NumberFormatBase::NumberFormatBase(Object& prototype) 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); visitor.visit(m_bound_format);
} }
// 16.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.numberformat-internal-slots
ReadonlySpan<StringView> 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<ResolutionOptionDescriptor> NumberFormat::resolution_option_descriptors(VM& vm) const
{
// The value of the [[ResolutionOptionDescriptors]] internal slot is « { [[Key]]: "nu", [[Property]]: "numberingSystem" } ».
static auto descriptors = to_array<ResolutionOptionDescriptor>({
{ .key = "nu"sv, .property = vm.names.numberingSystem },
});
return descriptors;
}
StringView NumberFormatBase::computed_rounding_priority_string() const StringView NumberFormatBase::computed_rounding_priority_string() const
{ {
switch (m_computed_rounding_priority) { switch (m_computed_rounding_priority) {

View file

@ -6,19 +6,18 @@
#pragma once #pragma once
#include <AK/Array.h>
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/String.h> #include <AK/String.h>
#include <LibJS/Runtime/Intl/AbstractOperations.h> #include <LibJS/Runtime/Intl/AbstractOperations.h>
#include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibJS/Runtime/Intl/MathematicalValue.h> #include <LibJS/Runtime/Intl/MathematicalValue.h>
#include <LibJS/Runtime/Object.h>
#include <LibUnicode/Locale.h> #include <LibUnicode/Locale.h>
#include <LibUnicode/NumberFormat.h> #include <LibUnicode/NumberFormat.h>
namespace JS::Intl { namespace JS::Intl {
class NumberFormatBase : public Object { class NumberFormatBase : public IntlObject {
JS_OBJECT(NumberFormatBase, Object); JS_OBJECT(NumberFormatBase, IntlObject);
GC_DECLARE_ALLOCATOR(NumberFormatBase); GC_DECLARE_ALLOCATOR(NumberFormatBase);
public: public:
@ -102,15 +101,11 @@ class NumberFormat final : public NumberFormatBase {
GC_DECLARE_ALLOCATOR(NumberFormat); GC_DECLARE_ALLOCATOR(NumberFormat);
public: 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 ~NumberFormat() override = default;
virtual ReadonlySpan<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const override;
String const& numbering_system() const { return m_numbering_system; } String const& numbering_system() const { return m_numbering_system; }
void set_numbering_system(String numbering_system) { m_numbering_system = move(numbering_system); } void set_numbering_system(String numbering_system) { m_numbering_system = move(numbering_system); }

View file

@ -83,7 +83,7 @@ ThrowCompletionOr<GC::Ref<Object>> NumberFormatConstructor::construct(FunctionOb
opt.nu = locale_key_from_value(numbering_system); opt.nu = locale_key_from_value(numbering_system);
// 11. Let r be ResolveLocale(%Intl.NumberFormat%.[[AvailableLocales]], requestedLocales, opt, %Intl.NumberFormat%.[[RelevantExtensionKeys]], %Intl.NumberFormat%.[[LocaleData]]). // 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]]. // 12. Set numberFormat.[[Locale]] to r.[[Locale]].
number_format->set_locale(move(result.locale)); number_format->set_locale(move(result.locale));

View file

@ -17,6 +17,20 @@ PluralRules::PluralRules(Object& prototype)
{ {
} }
// 17.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.pluralrules-internal-slots
ReadonlySpan<StringView> 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<ResolutionOptionDescriptor> 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 // 17.5.2 ResolvePlural ( pluralRules, n ), https://tc39.es/ecma402/#sec-resolveplural
Unicode::PluralCategory resolve_plural(PluralRules const& plural_rules, Value number) Unicode::PluralCategory resolve_plural(PluralRules const& plural_rules, Value number)
{ {

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@serenityos.org> * Copyright (c) 2022-2025, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -21,6 +21,9 @@ class PluralRules final : public NumberFormatBase {
public: public:
virtual ~PluralRules() override = default; virtual ~PluralRules() override = default;
virtual ReadonlySpan<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const override;
Unicode::PluralForm type() const { return m_type; } Unicode::PluralForm type() const { return m_type; }
StringView type_string() const { return Unicode::plural_form_to_string(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); } void set_type(StringView type) { m_type = Unicode::plural_form_from_string(type); }

View file

@ -71,7 +71,7 @@ ThrowCompletionOr<GC::Ref<Object>> PluralRulesConstructor::construct(FunctionObj
opt.locale_matcher = matcher; opt.locale_matcher = matcher;
// 8. Let r be ResolveLocale(%Intl.PluralRules%.[[AvailableLocales]], requestedLocales, opt, %Intl.PluralRules%.[[RelevantExtensionKeys]], %Intl.PluralRules%.[[LocaleData]]). // 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]]. // 9. Set pluralRules.[[Locale]] to r.[[locale]].
plural_rules->set_locale(move(result.locale)); plural_rules->set_locale(move(result.locale));

View file

@ -5,14 +5,9 @@
*/ */
#include <AK/Enumerate.h> #include <AK/Enumerate.h>
#include <AK/StringBuilder.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Array.h> #include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/NumberFormat.h>
#include <LibJS/Runtime/Intl/NumberFormatConstructor.h>
#include <LibJS/Runtime/Intl/PluralRules.h>
#include <LibJS/Runtime/Intl/RelativeTimeFormat.h> #include <LibJS/Runtime/Intl/RelativeTimeFormat.h>
#include <LibJS/Runtime/VM.h>
namespace JS::Intl { namespace JS::Intl {
@ -20,10 +15,29 @@ GC_DEFINE_ALLOCATOR(RelativeTimeFormat);
// 18 RelativeTimeFormat Objects, https://tc39.es/ecma402/#relativetimeformat-objects // 18 RelativeTimeFormat Objects, https://tc39.es/ecma402/#relativetimeformat-objects
RelativeTimeFormat::RelativeTimeFormat(Object& prototype) 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<StringView> 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<ResolutionOptionDescriptor> RelativeTimeFormat::resolution_option_descriptors(VM& vm) const
{
// The value of the [[ResolutionOptionDescriptors]] internal slot is « { [[Key]]: "nu", [[Property]]: "numberingSystem" } ».
static auto descriptors = to_array<ResolutionOptionDescriptor>({
{ .key = "nu"sv, .property = vm.names.numberingSystem },
});
return descriptors;
}
// 18.5.1 SingularRelativeTimeUnit ( unit ), https://tc39.es/ecma402/#sec-singularrelativetimeunit // 18.5.1 SingularRelativeTimeUnit ( unit ), https://tc39.es/ecma402/#sec-singularrelativetimeunit
ThrowCompletionOr<Unicode::TimeUnit> singular_relative_time_unit(VM& vm, StringView unit) ThrowCompletionOr<Unicode::TimeUnit> singular_relative_time_unit(VM& vm, StringView unit)
{ {

View file

@ -6,32 +6,25 @@
#pragma once #pragma once
#include <AK/Array.h>
#include <AK/String.h> #include <AK/String.h>
#include <AK/StringView.h> #include <AK/StringView.h>
#include <LibJS/Runtime/Completion.h> #include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Intl/AbstractOperations.h> #include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibJS/Runtime/Object.h>
#include <LibUnicode/Locale.h> #include <LibUnicode/Locale.h>
#include <LibUnicode/NumberFormat.h>
#include <LibUnicode/RelativeTimeFormat.h> #include <LibUnicode/RelativeTimeFormat.h>
namespace JS::Intl { namespace JS::Intl {
class RelativeTimeFormat final : public Object { class RelativeTimeFormat final : public IntlObject {
JS_OBJECT(RelativeTimeFormat, Object); JS_OBJECT(RelativeTimeFormat, IntlObject);
GC_DECLARE_ALLOCATOR(RelativeTimeFormat); GC_DECLARE_ALLOCATOR(RelativeTimeFormat);
public: 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 ~RelativeTimeFormat() override = default;
virtual ReadonlySpan<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const override;
String const& locale() const { return m_locale; } String const& locale() const { return m_locale; }
void set_locale(String locale) { m_locale = move(locale); } void set_locale(String locale) { m_locale = move(locale); }

View file

@ -83,7 +83,7 @@ ThrowCompletionOr<GC::Ref<Object>> RelativeTimeFormatConstructor::construct(Func
opt.nu = locale_key_from_value(numbering_system); opt.nu = locale_key_from_value(numbering_system);
// 11. Let r be ResolveLocale(%Intl.RelativeTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %Intl.RelativeTimeFormat%.[[RelevantExtensionKeys]], %Intl.RelativeTimeFormat%.[[LocaleData]]). // 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]]. // 12. Let locale be r.[[Locale]].
auto locale = move(result.locale); auto locale = move(result.locale);

View file

@ -6,8 +6,8 @@
*/ */
#include <AK/Utf16View.h> #include <AK/Utf16View.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/Segmenter.h> #include <LibJS/Runtime/Intl/Segmenter.h>
#include <LibJS/Runtime/VM.h>
namespace JS::Intl { namespace JS::Intl {
@ -15,10 +15,24 @@ GC_DEFINE_ALLOCATOR(Segmenter);
// 19 Segmenter Objects, https://tc39.es/ecma402/#segmenter-objects // 19 Segmenter Objects, https://tc39.es/ecma402/#segmenter-objects
Segmenter::Segmenter(Object& prototype) 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<StringView> 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<ResolutionOptionDescriptor> 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 // 19.7.1 CreateSegmentDataObject ( segmenter, string, startIndex, endIndex ), https://tc39.es/ecma402/#sec-createsegmentdataobject
ThrowCompletionOr<GC::Ref<Object>> create_segment_data_object(VM& vm, Unicode::Segmenter const& segmenter, Utf16View const& string, size_t start_index, size_t end_index) ThrowCompletionOr<GC::Ref<Object>> create_segment_data_object(VM& vm, Unicode::Segmenter const& segmenter, Utf16View const& string, size_t start_index, size_t end_index)
{ {

View file

@ -1,6 +1,6 @@
/* /*
* Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org> * Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org> * Copyright (c) 2023-2025, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -8,18 +8,21 @@
#pragma once #pragma once
#include <AK/String.h> #include <AK/String.h>
#include <LibJS/Runtime/Object.h> #include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibUnicode/Segmenter.h> #include <LibUnicode/Segmenter.h>
namespace JS::Intl { namespace JS::Intl {
class Segmenter final : public Object { class Segmenter final : public IntlObject {
JS_OBJECT(Segmenter, Object); JS_OBJECT(Segmenter, IntlObject);
GC_DECLARE_ALLOCATOR(Segmenter); GC_DECLARE_ALLOCATOR(Segmenter);
public: public:
virtual ~Segmenter() override = default; virtual ~Segmenter() override = default;
virtual ReadonlySpan<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const override;
String const& locale() const { return m_locale; } String const& locale() const { return m_locale; }
void set_locale(String locale) { m_locale = move(locale); } void set_locale(String locale) { m_locale = move(locale); }

View file

@ -71,7 +71,7 @@ ThrowCompletionOr<GC::Ref<Object>> SegmenterConstructor::construct(FunctionObjec
opt.locale_matcher = matcher; opt.locale_matcher = matcher;
// 9. Let r be ResolveLocale(%Intl.Segmenter%.[[AvailableLocales]], requestedLocales, opt, %Intl.Segmenter%.[[RelevantExtensionKeys]], %Intl.Segmenter%.[[LocaleData]]). // 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]]. // 10. Set segmenter.[[Locale]] to r.[[locale]].
segmenter->set_locale(move(result.locale)); segmenter->set_locale(move(result.locale));