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.
This commit is contained in:
Timothy Flynn 2025-04-07 15:56:31 -04:00
parent ba56fbde62
commit 5898248b6b
29 changed files with 284 additions and 116 deletions

View file

@ -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 {

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
*/
@ -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<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
*/
#pragma once
#include <AK/Array.h>
#include <AK/String.h>
#include <AK/StringView.h>
#include <LibJS/Runtime/Intl/CollatorCompareFunction.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibUnicode/Collator.h>
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<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const override;
String const& locale() const { return m_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
*/
@ -113,7 +113,7 @@ ThrowCompletionOr<GC::Ref<Object>> 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));

View file

@ -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<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)
{
if (formatter)

View file

@ -13,28 +13,24 @@
#include <AK/Vector.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Intl/DateTimeFormatConstructor.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibJS/Runtime/VM.h>
#include <LibUnicode/DateTimeFormat.h>
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<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> 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;

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
*/
@ -145,7 +145,7 @@ ThrowCompletionOr<GC::Ref<DateTimeFormat>> 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));

View file

@ -4,9 +4,9 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/AbstractOperations.h>
#include <LibJS/Runtime/Intl/DisplayNames.h>
#include <LibJS/Runtime/VM.h>
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<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)
{
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
*/
@ -9,14 +9,14 @@
#include <AK/Optional.h>
#include <AK/String.h>
#include <AK/StringView.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibUnicode/DisplayNames.h>
#include <LibUnicode/Locale.h>
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<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> 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]]

View file

@ -75,7 +75,7 @@ ThrowCompletionOr<GC::Ref<Object>> 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));

View file

@ -8,15 +8,12 @@
#include <AK/GenericShorthands.h>
#include <AK/StringBuilder.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/DurationFormat.h>
#include <LibJS/Runtime/Intl/ListFormat.h>
#include <LibJS/Runtime/Intl/ListFormatConstructor.h>
#include <LibJS/Runtime/Intl/MathematicalValue.h>
#include <LibJS/Runtime/Intl/NumberFormatConstructor.h>
#include <LibJS/Runtime/Intl/PluralRules.h>
#include <LibJS/Runtime/Intl/PluralRulesConstructor.h>
#include <LibJS/Runtime/Intl/RelativeTimeFormat.h>
#include <LibJS/Runtime/VM.h>
#include <LibJS/Runtime/ValueInlines.h>
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<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)
{
if (style == "long"sv)

View file

@ -10,15 +10,14 @@
#include <AK/Array.h>
#include <AK/String.h>
#include <LibCrypto/BigFraction/BigFraction.h>
#include <LibJS/Runtime/Intl/AbstractOperations.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibUnicode/Locale.h>
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<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const override;
void set_locale(String locale) { m_locale = move(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);
// 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));

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
*/
#include <AK/StringBuilder.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/ListFormat.h>
#include <LibJS/Runtime/Iterator.h>
#include <LibJS/Runtime/VM.h>
#include <LibUnicode/ListFormat.h>
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<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
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
*/
@ -9,15 +9,14 @@
#include <AK/String.h>
#include <AK/StringView.h>
#include <AK/Vector.h>
#include <LibJS/Runtime/Intl/AbstractOperations.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibUnicode/ListFormat.h>
#include <LibUnicode/Locale.h>
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<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const override;
String const& locale() const { return m_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;
// 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));

View file

@ -4,22 +4,15 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Checked.h>
#include <AK/StringBuilder.h>
#include <AK/Utf8View.h>
#include <LibCrypto/BigInt/SignedBigInteger.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/BigInt.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/NumberFormat.h>
#include <LibJS/Runtime/Intl/NumberFormatFunction.h>
#include <LibJS/Runtime/Intl/PluralRules.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/VM.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <LibUnicode/CurrencyCode.h>
#include <LibUnicode/DisplayNames.h>
#include <math.h>
#include <stdlib.h>
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<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
{
switch (m_computed_rounding_priority) {

View file

@ -6,19 +6,18 @@
#pragma once
#include <AK/Array.h>
#include <AK/Optional.h>
#include <AK/String.h>
#include <LibJS/Runtime/Intl/AbstractOperations.h>
#include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibJS/Runtime/Intl/MathematicalValue.h>
#include <LibJS/Runtime/Object.h>
#include <LibUnicode/Locale.h>
#include <LibUnicode/NumberFormat.h>
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<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> 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); }

View file

@ -83,7 +83,7 @@ ThrowCompletionOr<GC::Ref<Object>> 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));

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
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
*/
@ -21,6 +21,9 @@ class PluralRules final : public NumberFormatBase {
public:
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; }
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); }

View file

@ -71,7 +71,7 @@ ThrowCompletionOr<GC::Ref<Object>> 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));

View file

@ -5,14 +5,9 @@
*/
#include <AK/Enumerate.h>
#include <AK/StringBuilder.h>
#include <LibJS/Runtime/AbstractOperations.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/VM.h>
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<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
ThrowCompletionOr<Unicode::TimeUnit> singular_relative_time_unit(VM& vm, StringView unit)
{

View file

@ -6,32 +6,25 @@
#pragma once
#include <AK/Array.h>
#include <AK/String.h>
#include <AK/StringView.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Intl/AbstractOperations.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibUnicode/Locale.h>
#include <LibUnicode/NumberFormat.h>
#include <LibUnicode/RelativeTimeFormat.h>
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<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const override;
String const& locale() const { return m_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);
// 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);

View file

@ -6,8 +6,8 @@
*/
#include <AK/Utf16View.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/Segmenter.h>
#include <LibJS/Runtime/VM.h>
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<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
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) 2023, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2023-2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -8,18 +8,21 @@
#pragma once
#include <AK/String.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Intl/IntlObject.h>
#include <LibUnicode/Segmenter.h>
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<StringView> relevant_extension_keys() const override;
virtual ReadonlySpan<ResolutionOptionDescriptor> resolution_option_descriptors(VM&) const override;
String const& locale() const { return m_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;
// 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));