LibJS: Re-arrange and rename a few Intl properties

This is an editorial change in the ECMA-402 spec. See:
https://github.com/tc39/ecma402/commit/a46e37d
https://github.com/tc39/ecma402/commit/e102741
https://github.com/tc39/ecma402/commit/67a8417
https://github.com/tc39/ecma402/commit/ecb086c
This commit is contained in:
Timothy Flynn 2025-03-03 08:47:10 -05:00 committed by Tim Flynn
parent 8b0f6cb876
commit aa61307392
Notes: github-actions[bot] 2025-03-04 12:37:30 +00:00
23 changed files with 481 additions and 481 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2022-2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -25,38 +25,15 @@ void CollatorPrototype::initialize(Realm& realm)
auto& vm = this->vm();
// 10.3.2 Intl.Collator.prototype [ @@toStringTag ], https://tc39.es/ecma402/#sec-intl.collator.prototype-@@tostringtag
// 10.3.4 Intl.Collator.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma402/#sec-intl.collator.prototype-%symbol.tostringtag%
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.Collator"_string), Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_accessor(realm, vm.names.compare, compare_getter, {}, attr);
define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr);
define_native_accessor(realm, vm.names.compare, compare_getter, {}, attr);
}
// 10.3.3 get Intl.Collator.prototype.compare, https://tc39.es/ecma402/#sec-intl.collator.prototype.compare
JS_DEFINE_NATIVE_FUNCTION(CollatorPrototype::compare_getter)
{
auto& realm = *vm.current_realm();
// 1. Let collator be the this value.
// 2. Perform ? RequireInternalSlot(collator, [[InitializedCollator]]).
auto collator = TRY(typed_this_object(vm));
// 3. If collator.[[BoundCompare]] is undefined, then
if (!collator->bound_compare()) {
// a. Let F be a new built-in function object as defined in 10.3.3.1.
// b. Set F.[[Collator]] to collator.
auto function = CollatorCompareFunction::create(realm, collator);
// c. Set collator.[[BoundCompare]] to F.
collator->set_bound_compare(function);
}
// 4. Return collator.[[BoundCompare]].
return collator->bound_compare();
}
// 10.3.4 Intl.Collator.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.collator.prototype.resolvedoptions
// 10.3.2 Intl.Collator.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.collator.prototype.resolvedoptions
JS_DEFINE_NATIVE_FUNCTION(CollatorPrototype::resolved_options)
{
auto& realm = *vm.current_realm();
@ -89,4 +66,27 @@ JS_DEFINE_NATIVE_FUNCTION(CollatorPrototype::resolved_options)
return options;
}
// 10.3.3 get Intl.Collator.prototype.compare, https://tc39.es/ecma402/#sec-intl.collator.prototype.compare
JS_DEFINE_NATIVE_FUNCTION(CollatorPrototype::compare_getter)
{
auto& realm = *vm.current_realm();
// 1. Let collator be the this value.
// 2. Perform ? RequireInternalSlot(collator, [[InitializedCollator]]).
auto collator = TRY(typed_this_object(vm));
// 3. If collator.[[BoundCompare]] is undefined, then
if (!collator->bound_compare()) {
// a. Let F be a new built-in function object as defined in 10.3.3.1.
// b. Set F.[[Collator]] to collator.
auto function = CollatorCompareFunction::create(realm, collator);
// c. Set collator.[[BoundCompare]] to F.
collator->set_bound_compare(function);
}
// 4. Return collator.[[BoundCompare]].
return collator->bound_compare();
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2022-2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -22,8 +22,8 @@ public:
private:
explicit CollatorPrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(compare_getter);
JS_DECLARE_NATIVE_FUNCTION(resolved_options);
JS_DECLARE_NATIVE_FUNCTION(compare_getter);
};
}

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
*/
@ -28,128 +28,19 @@ void DateTimeFormatPrototype::initialize(Realm& realm)
auto& vm = this->vm();
// 11.3.2 Intl.DateTimeFormat.prototype [ @@toStringTag ], https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype-@@tostringtag
// 11.3.7 Intl.DateTimeFormat.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype-%symbol.tostringtag%
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.DateTimeFormat"_string), Attribute::Configurable);
define_native_accessor(realm, vm.names.format, format, nullptr, Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.formatToParts, format_to_parts, 1, attr);
define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr);
define_native_function(realm, vm.names.formatRange, format_range, 2, attr);
define_native_function(realm, vm.names.formatRangeToParts, format_range_to_parts, 2, attr);
define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr);
define_native_function(realm, vm.names.formatToParts, format_to_parts, 1, attr);
}
// 11.3.3 get Intl.DateTimeFormat.prototype.format, https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.format
JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::format)
{
auto& realm = *vm.current_realm();
// 1. Let dtf be the this value.
// 2. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then
// a. Set dtf to ? UnwrapDateTimeFormat(dtf).
// 3. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
auto date_time_format = TRY(typed_this_object(vm));
// 4. If dtf.[[BoundFormat]] is undefined, then
if (!date_time_format->bound_format()) {
// a. Let F be a new built-in function object as defined in DateTime Format Functions (11.1.6).
// b. Set F.[[DateTimeFormat]] to dtf.
auto bound_format = DateTimeFormatFunction::create(realm, date_time_format);
// c. Set dtf.[[BoundFormat]] to F.
date_time_format->set_bound_format(bound_format);
}
// 5. Return dtf.[[BoundFormat]].
return date_time_format->bound_format();
}
// 11.3.4 Intl.DateTimeFormat.prototype.formatToParts ( date ), https://tc39.es/ecma402/#sec-Intl.DateTimeFormat.prototype.formatToParts
// 15.10.1 Intl.DateTimeFormat.prototype.formatToParts ( date ), https://tc39.es/proposal-temporal/#sec-Intl.DateTimeFormat.prototype.formatToParts
JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::format_to_parts)
{
auto& realm = *vm.current_realm();
auto date_value = vm.argument(0);
// 1. Let dtf be the this value.
// 2. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
auto date_time_format = TRY(typed_this_object(vm));
FormattableDateTime date { 0 };
// 3. If date is undefined, then
if (date_value.is_undefined()) {
// a. Let x be ! Call(%Date.now%, undefined).
date = MUST(call(vm, *realm.intrinsics().date_constructor_now_function(), js_undefined())).as_double();
}
// 4. Else,
else {
// a. Let x be ? ToDateTimeFormattable(date).
date = TRY(to_date_time_formattable(vm, date_value));
}
// 5. Return ? FormatDateTimeToParts(dtf, x).
return TRY(format_date_time_to_parts(vm, date_time_format, date));
}
// 11.3.5 Intl.DateTimeFormat.prototype.formatRange ( startDate, endDate ), https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.formatRange
// 15.10.2 Intl.DateTimeFormat.prototype.formatRange ( startDate, endDate ), https://tc39.es/proposal-temporal/#sec-intl.datetimeformat.prototype.formatRange
JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::format_range)
{
auto start_date_value = vm.argument(0);
auto end_date_value = vm.argument(1);
// 1. Let dtf be this value.
// 2. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
auto date_time_format = TRY(typed_this_object(vm));
// 3. If startDate is undefined or endDate is undefined, throw a TypeError exception.
if (start_date_value.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "startDate"sv);
if (end_date_value.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "endDate"sv);
// 4. Let x be ? ToDateTimeFormattable(startDate).
auto start_date = TRY(to_date_time_formattable(vm, start_date_value));
// 5. Let y be ? ToDateTimeFormattable(endDate).
auto end_date = TRY(to_date_time_formattable(vm, end_date_value));
// 6. Return ? FormatDateTimeRange(dtf, x, y).
auto formatted = TRY(format_date_time_range(vm, date_time_format, start_date, end_date));
return PrimitiveString::create(vm, move(formatted));
}
// 11.3.6 Intl.DateTimeFormat.prototype.formatRangeToParts ( startDate, endDate ), https://tc39.es/ecma402/#sec-Intl.DateTimeFormat.prototype.formatRangeToParts
// 15.10.3 Intl.DateTimeFormat.prototype.formatRangeToParts ( startDate, endDate ), https://tc39.es/proposal-temporal/#sec-Intl.DateTimeFormat.prototype.formatRangeToParts
JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::format_range_to_parts)
{
auto start_date_value = vm.argument(0);
auto end_date_value = vm.argument(1);
// 1. Let dtf be this value.
// 2. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
auto date_time_format = TRY(typed_this_object(vm));
// 3. If startDate is undefined or endDate is undefined, throw a TypeError exception.
if (start_date_value.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "startDate"sv);
if (end_date_value.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "endDate"sv);
// 4. Let x be ? ToDateTimeFormattable(startDate).
auto start_date = TRY(to_date_time_formattable(vm, start_date_value));
// 5. Let y be ? ToDateTimeFormattable(endDate).
auto end_date = TRY(to_date_time_formattable(vm, end_date_value));
// 6. Return ? FormatDateTimeRangeToParts(dtf, x, y).
return TRY(format_date_time_range_to_parts(vm, date_time_format, start_date, end_date));
}
// 11.3.7 Intl.DateTimeFormat.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.resolvedoptions
// 11.3.2 Intl.DateTimeFormat.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.resolvedoptions
JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::resolved_options)
{
auto& realm = *vm.current_realm();
@ -229,4 +120,113 @@ JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::resolved_options)
return options;
}
// 11.3.3 get Intl.DateTimeFormat.prototype.format, https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.format
JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::format)
{
auto& realm = *vm.current_realm();
// 1. Let dtf be the this value.
// 2. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then
// a. Set dtf to ? UnwrapDateTimeFormat(dtf).
// 3. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
auto date_time_format = TRY(typed_this_object(vm));
// 4. If dtf.[[BoundFormat]] is undefined, then
if (!date_time_format->bound_format()) {
// a. Let F be a new built-in function object as defined in DateTime Format Functions (11.1.6).
// b. Set F.[[DateTimeFormat]] to dtf.
auto bound_format = DateTimeFormatFunction::create(realm, date_time_format);
// c. Set dtf.[[BoundFormat]] to F.
date_time_format->set_bound_format(bound_format);
}
// 5. Return dtf.[[BoundFormat]].
return date_time_format->bound_format();
}
// 11.3.4 Intl.DateTimeFormat.prototype.formatRange ( startDate, endDate ), https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.formatRange
// 15.10.2 Intl.DateTimeFormat.prototype.formatRange ( startDate, endDate ), https://tc39.es/proposal-temporal/#sec-intl.datetimeformat.prototype.formatRange
JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::format_range)
{
auto start_date_value = vm.argument(0);
auto end_date_value = vm.argument(1);
// 1. Let dtf be this value.
// 2. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
auto date_time_format = TRY(typed_this_object(vm));
// 3. If startDate is undefined or endDate is undefined, throw a TypeError exception.
if (start_date_value.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "startDate"sv);
if (end_date_value.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "endDate"sv);
// 4. Let x be ? ToDateTimeFormattable(startDate).
auto start_date = TRY(to_date_time_formattable(vm, start_date_value));
// 5. Let y be ? ToDateTimeFormattable(endDate).
auto end_date = TRY(to_date_time_formattable(vm, end_date_value));
// 6. Return ? FormatDateTimeRange(dtf, x, y).
auto formatted = TRY(format_date_time_range(vm, date_time_format, start_date, end_date));
return PrimitiveString::create(vm, move(formatted));
}
// 11.3.5 Intl.DateTimeFormat.prototype.formatRangeToParts ( startDate, endDate ), https://tc39.es/ecma402/#sec-Intl.DateTimeFormat.prototype.formatRangeToParts
// 15.10.3 Intl.DateTimeFormat.prototype.formatRangeToParts ( startDate, endDate ), https://tc39.es/proposal-temporal/#sec-Intl.DateTimeFormat.prototype.formatRangeToParts
JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::format_range_to_parts)
{
auto start_date_value = vm.argument(0);
auto end_date_value = vm.argument(1);
// 1. Let dtf be this value.
// 2. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
auto date_time_format = TRY(typed_this_object(vm));
// 3. If startDate is undefined or endDate is undefined, throw a TypeError exception.
if (start_date_value.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "startDate"sv);
if (end_date_value.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "endDate"sv);
// 4. Let x be ? ToDateTimeFormattable(startDate).
auto start_date = TRY(to_date_time_formattable(vm, start_date_value));
// 5. Let y be ? ToDateTimeFormattable(endDate).
auto end_date = TRY(to_date_time_formattable(vm, end_date_value));
// 6. Return ? FormatDateTimeRangeToParts(dtf, x, y).
return TRY(format_date_time_range_to_parts(vm, date_time_format, start_date, end_date));
}
// 11.3.6 Intl.DateTimeFormat.prototype.formatToParts ( date ), https://tc39.es/ecma402/#sec-Intl.DateTimeFormat.prototype.formatToParts
// 15.10.1 Intl.DateTimeFormat.prototype.formatToParts ( date ), https://tc39.es/proposal-temporal/#sec-Intl.DateTimeFormat.prototype.formatToParts
JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::format_to_parts)
{
auto& realm = *vm.current_realm();
auto date_value = vm.argument(0);
// 1. Let dtf be the this value.
// 2. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
auto date_time_format = TRY(typed_this_object(vm));
FormattableDateTime date { 0 };
// 3. If date is undefined, then
if (date_value.is_undefined()) {
// a. Let x be ! Call(%Date.now%, undefined).
date = MUST(call(vm, *realm.intrinsics().date_constructor_now_function(), js_undefined())).as_double();
}
// 4. Else,
else {
// a. Let x be ? ToDateTimeFormattable(date).
date = TRY(to_date_time_formattable(vm, date_value));
}
// 5. Return ? FormatDateTimeToParts(dtf, x).
return TRY(format_date_time_to_parts(vm, date_time_format, date));
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2021-2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -22,11 +22,11 @@ public:
private:
explicit DateTimeFormatPrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(resolved_options);
JS_DECLARE_NATIVE_FUNCTION(format);
JS_DECLARE_NATIVE_FUNCTION(format_to_parts);
JS_DECLARE_NATIVE_FUNCTION(format_range);
JS_DECLARE_NATIVE_FUNCTION(format_range_to_parts);
JS_DECLARE_NATIVE_FUNCTION(resolved_options);
JS_DECLARE_NATIVE_FUNCTION(format_to_parts);
};
}

View file

@ -27,12 +27,42 @@ void DisplayNamesPrototype::initialize(Realm& realm)
auto& vm = this->vm();
// 12.3.2 Intl.DisplayNames.prototype[ @@toStringTag ], https://tc39.es/ecma402/#sec-Intl.DisplayNames.prototype-@@tostringtag
// 12.3.4 Intl.DisplayNames.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma402/#sec-intl.displaynames.prototype-%symbol.tostringtag%
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.DisplayNames"_string), Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.of, of, 1, attr);
define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr);
define_native_function(realm, vm.names.of, of, 1, attr);
}
// 12.3.2 Intl.DisplayNames.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-Intl.DisplayNames.prototype.resolvedOptions
JS_DEFINE_NATIVE_FUNCTION(DisplayNamesPrototype::resolved_options)
{
auto& realm = *vm.current_realm();
// 1. Let displayNames be this value.
// 2. Perform ? RequireInternalSlot(displayNames, [[InitializedDisplayNames]]).
auto display_names = TRY(typed_this_object(vm));
// 3. Let options be OrdinaryObjectCreate(%Object.prototype%).
auto options = Object::create(realm, realm.intrinsics().object_prototype());
// 4. For each row of Table 18, except the header row, in table order, do
// a. Let p be the Property value of the current row.
// b. Let v be the value of displayNames's internal slot whose name is the Internal Slot value of the current row.
// c. Assert: v is not undefined.
// d. Perform ! CreateDataPropertyOrThrow(options, p, v).
MUST(options->create_data_property_or_throw(vm.names.locale, PrimitiveString::create(vm, display_names->locale())));
MUST(options->create_data_property_or_throw(vm.names.style, PrimitiveString::create(vm, display_names->style_string())));
MUST(options->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, display_names->type_string())));
MUST(options->create_data_property_or_throw(vm.names.fallback, PrimitiveString::create(vm, display_names->fallback_string())));
// NOTE: Step 4c indicates languageDisplay must not be undefined, but it is only set when the type option is language.
if (display_names->has_language_display())
MUST(options->create_data_property_or_throw(vm.names.languageDisplay, PrimitiveString::create(vm, display_names->language_display_string())));
// 5. Return options.
return options;
}
// 12.3.3 Intl.DisplayNames.prototype.of ( code ), https://tc39.es/ecma402/#sec-Intl.DisplayNames.prototype.of
@ -89,34 +119,4 @@ JS_DEFINE_NATIVE_FUNCTION(DisplayNamesPrototype::of)
return js_undefined();
}
// 12.3.4 Intl.DisplayNames.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-Intl.DisplayNames.prototype.resolvedOptions
JS_DEFINE_NATIVE_FUNCTION(DisplayNamesPrototype::resolved_options)
{
auto& realm = *vm.current_realm();
// 1. Let displayNames be this value.
// 2. Perform ? RequireInternalSlot(displayNames, [[InitializedDisplayNames]]).
auto display_names = TRY(typed_this_object(vm));
// 3. Let options be OrdinaryObjectCreate(%Object.prototype%).
auto options = Object::create(realm, realm.intrinsics().object_prototype());
// 4. For each row of Table 18, except the header row, in table order, do
// a. Let p be the Property value of the current row.
// b. Let v be the value of displayNames's internal slot whose name is the Internal Slot value of the current row.
// c. Assert: v is not undefined.
// d. Perform ! CreateDataPropertyOrThrow(options, p, v).
MUST(options->create_data_property_or_throw(vm.names.locale, PrimitiveString::create(vm, display_names->locale())));
MUST(options->create_data_property_or_throw(vm.names.style, PrimitiveString::create(vm, display_names->style_string())));
MUST(options->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, display_names->type_string())));
MUST(options->create_data_property_or_throw(vm.names.fallback, PrimitiveString::create(vm, display_names->fallback_string())));
// NOTE: Step 4c indicates languageDisplay must not be undefined, but it is only set when the type option is language.
if (display_names->has_language_display())
MUST(options->create_data_property_or_throw(vm.names.languageDisplay, PrimitiveString::create(vm, display_names->language_display_string())));
// 5. Return options.
return options;
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2021-2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -22,8 +22,8 @@ public:
private:
explicit DisplayNamesPrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(of);
JS_DECLARE_NATIVE_FUNCTION(resolved_options);
JS_DECLARE_NATIVE_FUNCTION(of);
};
}

View file

@ -26,13 +26,38 @@ void ListFormatPrototype::initialize(Realm& realm)
auto& vm = this->vm();
// 14.3.2 Intl.ListFormat.prototype [ @@toStringTag ], https://tc39.es/ecma402/#sec-Intl.ListFormat.prototype-toStringTag
// 14.3.5 Intl.ListFormat.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma402/#sec-Intl.ListFormat.prototype-toStringTag
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.ListFormat"_string), Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr);
define_native_function(realm, vm.names.format, format, 1, attr);
define_native_function(realm, vm.names.formatToParts, format_to_parts, 1, attr);
define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr);
}
// 14.3.2 Intl.ListFormat.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-Intl.ListFormat.prototype.resolvedoptions
JS_DEFINE_NATIVE_FUNCTION(ListFormatPrototype::resolved_options)
{
auto& realm = *vm.current_realm();
// 1. Let lf be the this value.
// 2. Perform ? RequireInternalSlot(lf, [[InitializedListFormat]]).
auto list_format = TRY(typed_this_object(vm));
// 3. Let options be OrdinaryObjectCreate(%Object.prototype%).
auto options = Object::create(realm, realm.intrinsics().object_prototype());
// 4. For each row of Table 24, except the header row, in table order, do
// a. Let p be the Property value of the current row.
// b. Let v be the value of lf's internal slot whose name is the Internal Slot value of the current row.
// c. Assert: v is not undefined.
// d. Perform ! CreateDataPropertyOrThrow(options, p, v).
MUST(options->create_data_property_or_throw(vm.names.locale, PrimitiveString::create(vm, list_format->locale())));
MUST(options->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, list_format->type_string())));
MUST(options->create_data_property_or_throw(vm.names.style, PrimitiveString::create(vm, list_format->style_string())));
// 5. Return options.
return options;
}
// 14.3.3 Intl.ListFormat.prototype.format ( list ), https://tc39.es/ecma402/#sec-Intl.ListFormat.prototype.format
@ -68,29 +93,4 @@ JS_DEFINE_NATIVE_FUNCTION(ListFormatPrototype::format_to_parts)
return format_list_to_parts(vm, list_format, string_list);
}
// 14.3.5 Intl.ListFormat.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-Intl.ListFormat.prototype.resolvedoptions
JS_DEFINE_NATIVE_FUNCTION(ListFormatPrototype::resolved_options)
{
auto& realm = *vm.current_realm();
// 1. Let lf be the this value.
// 2. Perform ? RequireInternalSlot(lf, [[InitializedListFormat]]).
auto list_format = TRY(typed_this_object(vm));
// 3. Let options be OrdinaryObjectCreate(%Object.prototype%).
auto options = Object::create(realm, realm.intrinsics().object_prototype());
// 4. For each row of Table 24, except the header row, in table order, do
// a. Let p be the Property value of the current row.
// b. Let v be the value of lf's internal slot whose name is the Internal Slot value of the current row.
// c. Assert: v is not undefined.
// d. Perform ! CreateDataPropertyOrThrow(options, p, v).
MUST(options->create_data_property_or_throw(vm.names.locale, PrimitiveString::create(vm, list_format->locale())));
MUST(options->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, list_format->type_string())));
MUST(options->create_data_property_or_throw(vm.names.style, PrimitiveString::create(vm, list_format->style_string())));
// 5. Return options.
return options;
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2021-2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -22,9 +22,9 @@ public:
private:
explicit ListFormatPrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(resolved_options);
JS_DECLARE_NATIVE_FUNCTION(format);
JS_DECLARE_NATIVE_FUNCTION(format_to_parts);
JS_DECLARE_NATIVE_FUNCTION(resolved_options);
};
}

View file

@ -39,7 +39,7 @@ void LocalePrototype::initialize(Realm& realm)
define_native_function(realm, vm.names.getTextInfo, get_text_info, 0, attr);
define_native_function(realm, vm.names.getWeekInfo, get_week_info, 0, attr);
// 15.3.2 Intl.Locale.prototype[ @@toStringTag ], https://tc39.es/ecma402/#sec-Intl.Locale.prototype-@@tostringtag
// 15.3.15 Intl.Locale.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma402/#sec-intl.locale.prototype-%symbol.tostringtag%
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.Locale"_string), Attribute::Configurable);
define_native_accessor(realm, vm.names.baseName, base_name, {}, Attribute::Configurable);
@ -48,57 +48,14 @@ void LocalePrototype::initialize(Realm& realm)
define_native_accessor(realm, vm.names.collation, collation, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.firstDayOfWeek, first_day_of_week, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.hourCycle, hour_cycle, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.language, language, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.numberingSystem, numbering_system, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.numeric, numeric, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.language, language, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.script, script, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.region, region, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.script, script, {}, Attribute::Configurable);
}
// 15.3.3 Intl.Locale.prototype.maximize ( ), https://tc39.es/ecma402/#sec-Intl.Locale.prototype.maximize
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::maximize)
{
auto& realm = *vm.current_realm();
// 1. Let loc be the this value.
// 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).
auto locale_object = TRY(typed_this_object(vm));
// 3. Let maximal be the result of the Add Likely Subtags algorithm applied to loc.[[Locale]]. If an error is signaled, set maximal to loc.[[Locale]].
auto maximal = Unicode::add_likely_subtags(locale_object->locale()).value_or(locale_object->locale());
// 4. Return ! Construct(%Locale%, maximal).
return Locale::create(realm, locale_object, move(maximal));
}
// 15.3.4 Intl.Locale.prototype.minimize ( ), https://tc39.es/ecma402/#sec-Intl.Locale.prototype.minimize
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::minimize)
{
auto& realm = *vm.current_realm();
// 1. Let loc be the this value.
// 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).
auto locale_object = TRY(typed_this_object(vm));
// 3. Let minimal be the result of the Remove Likely Subtags algorithm applied to loc.[[Locale]]. If an error is signaled, set minimal to loc.[[Locale]].
auto minimal = Unicode::remove_likely_subtags(locale_object->locale()).value_or(locale_object->locale());
// 4. Return ! Construct(%Locale%, minimal).
return Locale::create(realm, locale_object, move(minimal));
}
// 15.3.5 Intl.Locale.prototype.toString ( ), https://tc39.es/ecma402/#sec-Intl.Locale.prototype.toString
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::to_string)
{
// 1. Let loc be the this value.
// 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).
auto locale_object = TRY(typed_this_object(vm));
// 3. Return loc.[[Locale]].
return PrimitiveString::create(vm, locale_object->locale());
}
// 15.3.6 get Intl.Locale.prototype.baseName, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.baseName
// 15.3.2 get Intl.Locale.prototype.baseName, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.baseName
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::base_name)
{
// 1. Let loc be the this value.
@ -121,12 +78,12 @@ JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::base_name)
__JS_ENUMERATE(hour_cycle) \
__JS_ENUMERATE(numbering_system)
// 15.3.7 get Intl.Locale.prototype.calendar, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.calendar
// 15.3.8 get Intl.Locale.prototype.caseFirst, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.caseFirst
// 15.3.9 get Intl.Locale.prototype.collation, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.collation
// 15.3.3 get Intl.Locale.prototype.calendar, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.calendar
// 15.3.4 get Intl.Locale.prototype.caseFirst, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.caseFirst
// 15.3.5 get Intl.Locale.prototype.collation, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.collation
// 1.4.10 get Intl.Locale.prototype.firstDayOfWeek, https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.firstDayOfWeek
// 15.3.10 get Intl.Locale.prototype.hourCycle, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.hourCycle
// 15.3.12 get Intl.Locale.prototype.numberingSystem, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.numberingSystem
// 15.3.6 get Intl.Locale.prototype.hourCycle, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.hourCycle
// 15.3.10 get Intl.Locale.prototype.numberingSystem, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.numberingSystem
#define __JS_ENUMERATE(keyword) \
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::keyword) \
{ \
@ -138,18 +95,7 @@ JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::base_name)
JS_ENUMERATE_LOCALE_KEYWORD_PROPERTIES
#undef __JS_ENUMERATE
// 15.3.11 get Intl.Locale.prototype.numeric, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.numeric
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::numeric)
{
// 1. Let loc be the this value.
// 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).
auto locale_object = TRY(typed_this_object(vm));
// 3. Return loc.[[Numeric]].
return Value(locale_object->numeric());
}
// 15.3.13 get Intl.Locale.prototype.language, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.language
// 15.3.7 get Intl.Locale.prototype.language, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.language
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::language)
{
// 1. Let loc be the this value.
@ -166,7 +112,71 @@ JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::language)
return PrimitiveString::create(vm, locale->language_id.language.release_value());
}
// 15.3.14 get Intl.Locale.prototype.script, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.script
// 15.3.8 Intl.Locale.prototype.maximize ( ), https://tc39.es/ecma402/#sec-Intl.Locale.prototype.maximize
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::maximize)
{
auto& realm = *vm.current_realm();
// 1. Let loc be the this value.
// 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).
auto locale_object = TRY(typed_this_object(vm));
// 3. Let maximal be the result of the Add Likely Subtags algorithm applied to loc.[[Locale]]. If an error is signaled, set maximal to loc.[[Locale]].
auto maximal = Unicode::add_likely_subtags(locale_object->locale()).value_or(locale_object->locale());
// 4. Return ! Construct(%Locale%, maximal).
return Locale::create(realm, locale_object, move(maximal));
}
// 15.3.9 Intl.Locale.prototype.minimize ( ), https://tc39.es/ecma402/#sec-Intl.Locale.prototype.minimize
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::minimize)
{
auto& realm = *vm.current_realm();
// 1. Let loc be the this value.
// 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).
auto locale_object = TRY(typed_this_object(vm));
// 3. Let minimal be the result of the Remove Likely Subtags algorithm applied to loc.[[Locale]]. If an error is signaled, set minimal to loc.[[Locale]].
auto minimal = Unicode::remove_likely_subtags(locale_object->locale()).value_or(locale_object->locale());
// 4. Return ! Construct(%Locale%, minimal).
return Locale::create(realm, locale_object, move(minimal));
}
// 15.3.11 get Intl.Locale.prototype.numeric, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.numeric
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::numeric)
{
// 1. Let loc be the this value.
// 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).
auto locale_object = TRY(typed_this_object(vm));
// 3. Return loc.[[Numeric]].
return Value(locale_object->numeric());
}
// 15.3.12 get Intl.Locale.prototype.region, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.region
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::region)
{
// 1. Let loc be the this value.
// 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).
auto locale_object = TRY(typed_this_object(vm));
// 3. Let locale be loc.[[Locale]].
auto locale = Unicode::parse_unicode_locale_id(locale_object->locale());
// 4. Assert: locale matches the unicode_locale_id production.
VERIFY(locale.has_value());
// 5. If the unicode_language_id production of locale does not contain the ["-" unicode_region_subtag] sequence, return undefined.
if (!locale->language_id.region.has_value())
return js_undefined();
// 6. Return the substring of locale corresponding to the unicode_region_subtag production of the unicode_language_id.
return PrimitiveString::create(vm, locale->language_id.region.release_value());
}
// 15.3.13 get Intl.Locale.prototype.script, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.script
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::script)
{
// 1. Let loc be the this value.
@ -187,25 +197,15 @@ JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::script)
return PrimitiveString::create(vm, locale->language_id.script.release_value());
}
// 15.3.15 get Intl.Locale.prototype.region, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.region
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::region)
// 15.3.14 Intl.Locale.prototype.toString ( ), https://tc39.es/ecma402/#sec-Intl.Locale.prototype.toString
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::to_string)
{
// 1. Let loc be the this value.
// 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).
auto locale_object = TRY(typed_this_object(vm));
// 3. Let locale be loc.[[Locale]].
auto locale = Unicode::parse_unicode_locale_id(locale_object->locale());
// 4. Assert: locale matches the unicode_locale_id production.
VERIFY(locale.has_value());
// 5. If the unicode_language_id production of locale does not contain the ["-" unicode_region_subtag] sequence, return undefined.
if (!locale->language_id.region.has_value())
return js_undefined();
// 6. Return the substring of locale corresponding to the unicode_region_subtag production of the unicode_language_id.
return PrimitiveString::create(vm, locale->language_id.region.release_value());
// 3. Return loc.[[Locale]].
return PrimitiveString::create(vm, locale_object->locale());
}
#define JS_ENUMERATE_LOCALE_INFO_PROPERTIES \

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2022, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2021-2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -32,11 +32,11 @@ private:
JS_DECLARE_NATIVE_FUNCTION(collation);
JS_DECLARE_NATIVE_FUNCTION(first_day_of_week);
JS_DECLARE_NATIVE_FUNCTION(hour_cycle);
JS_DECLARE_NATIVE_FUNCTION(language);
JS_DECLARE_NATIVE_FUNCTION(numbering_system);
JS_DECLARE_NATIVE_FUNCTION(numeric);
JS_DECLARE_NATIVE_FUNCTION(language);
JS_DECLARE_NATIVE_FUNCTION(script);
JS_DECLARE_NATIVE_FUNCTION(region);
JS_DECLARE_NATIVE_FUNCTION(script);
JS_DECLARE_NATIVE_FUNCTION(get_calendars);
JS_DECLARE_NATIVE_FUNCTION(get_collations);
JS_DECLARE_NATIVE_FUNCTION(get_hour_cycles);

View file

@ -27,113 +27,19 @@ void NumberFormatPrototype::initialize(Realm& realm)
auto& vm = this->vm();
// 16.3.2 Intl.NumberFormat.prototype [ @@toStringTag ], https://tc39.es/ecma402/#sec-intl.numberformat.prototype-@@tostringtag
// 16.3.7 Intl.NumberFormat.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma402/#sec-intl.numberformat.prototype-%symbol.tostringtag%
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.NumberFormat"_string), Attribute::Configurable);
define_native_accessor(realm, vm.names.format, format, nullptr, Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.formatToParts, format_to_parts, 1, attr);
define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr);
define_native_function(realm, vm.names.formatRange, format_range, 2, attr);
define_native_function(realm, vm.names.formatRangeToParts, format_range_to_parts, 2, attr);
define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr);
define_native_function(realm, vm.names.formatToParts, format_to_parts, 1, attr);
}
// 16.3.3 get Intl.NumberFormat.prototype.format, https://tc39.es/ecma402/#sec-intl.numberformat.prototype.format
JS_DEFINE_NATIVE_FUNCTION(NumberFormatPrototype::format)
{
auto& realm = *vm.current_realm();
// 1. Let nf be the this value.
// 2. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then
// a. Set nf to ? UnwrapNumberFormat(nf).
// 3. Perform ? RequireInternalSlot(nf, [[InitializedNumberFormat]]).
auto number_format = TRY(typed_this_object(vm));
// 4. If nf.[[BoundFormat]] is undefined, then
if (!number_format->bound_format()) {
// a. Let F be a new built-in function object as defined in Number Format Functions (16.1.4).
// b. Set F.[[NumberFormat]] to nf.
auto bound_format = NumberFormatFunction::create(realm, number_format);
// c. Set nf.[[BoundFormat]] to F.
number_format->set_bound_format(bound_format);
}
// 5. Return nf.[[BoundFormat]].
return number_format->bound_format();
}
// 16.3.4 Intl.NumberFormat.prototype.formatToParts ( value ), https://tc39.es/ecma402/#sec-intl.numberformat.prototype.formattoparts
JS_DEFINE_NATIVE_FUNCTION(NumberFormatPrototype::format_to_parts)
{
auto value = vm.argument(0);
// 1. Let nf be the this value.
// 2. Perform ? RequireInternalSlot(nf, [[InitializedNumberFormat]]).
auto number_format = TRY(typed_this_object(vm));
// 3. Let x be ? ToIntlMathematicalValue(value).
auto mathematical_value = TRY(to_intl_mathematical_value(vm, value));
// 4. Return ? FormatNumericToParts(nf, x).
return format_numeric_to_parts(vm, number_format, move(mathematical_value));
}
// 16.3.5 Intl.NumberFormat.prototype.formatRange ( start, end ), https://tc39.es/ecma402/#sec-intl.numberformat.prototype.formatrange
JS_DEFINE_NATIVE_FUNCTION(NumberFormatPrototype::format_range)
{
auto start = vm.argument(0);
auto end = vm.argument(1);
// 1. Let nf be the this value.
// 2. Perform ? RequireInternalSlot(nf, [[InitializedNumberFormat]]).
auto number_format = TRY(typed_this_object(vm));
// 3. If start is undefined or end is undefined, throw a TypeError exception.
if (start.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "start"sv);
if (end.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "end"sv);
// 4. Let x be ? ToIntlMathematicalValue(start).
auto x = TRY(to_intl_mathematical_value(vm, start));
// 5. Let y be ? ToIntlMathematicalValue(end).
auto y = TRY(to_intl_mathematical_value(vm, end));
// 6. Return ? FormatNumericRange(nf, x, y).
auto formatted = TRY(format_numeric_range(vm, number_format, move(x), move(y)));
return PrimitiveString::create(vm, move(formatted));
}
// 16.3.6 Intl.NumberFormat.prototype.formatRangeToParts ( start, end ), https://tc39.es/ecma402/#sec-intl.numberformat.prototype.formatrangetoparts
JS_DEFINE_NATIVE_FUNCTION(NumberFormatPrototype::format_range_to_parts)
{
auto start = vm.argument(0);
auto end = vm.argument(1);
// 1. Let nf be the this value.
// 2. Perform ? RequireInternalSlot(nf, [[InitializedNumberFormat]]).
auto number_format = TRY(typed_this_object(vm));
// 3. If start is undefined or end is undefined, throw a TypeError exception.
if (start.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "start"sv);
if (end.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "end"sv);
// 4. Let x be ? ToIntlMathematicalValue(start).
auto x = TRY(to_intl_mathematical_value(vm, start));
// 5. Let y be ? ToIntlMathematicalValue(end).
auto y = TRY(to_intl_mathematical_value(vm, end));
// 6. Return ? FormatNumericRangeToParts(nf, x, y).
return TRY(format_numeric_range_to_parts(vm, number_format, move(x), move(y)));
}
// 16.3.7 Intl.NumberFormat.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.numberformat.prototype.resolvedoptions
// 16.3.2 Intl.NumberFormat.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.numberformat.prototype.resolvedoptions
JS_DEFINE_NATIVE_FUNCTION(NumberFormatPrototype::resolved_options)
{
auto& realm = *vm.current_realm();
@ -188,4 +94,98 @@ JS_DEFINE_NATIVE_FUNCTION(NumberFormatPrototype::resolved_options)
return options;
}
// 16.3.3 get Intl.NumberFormat.prototype.format, https://tc39.es/ecma402/#sec-intl.numberformat.prototype.format
JS_DEFINE_NATIVE_FUNCTION(NumberFormatPrototype::format)
{
auto& realm = *vm.current_realm();
// 1. Let nf be the this value.
// 2. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then
// a. Set nf to ? UnwrapNumberFormat(nf).
// 3. Perform ? RequireInternalSlot(nf, [[InitializedNumberFormat]]).
auto number_format = TRY(typed_this_object(vm));
// 4. If nf.[[BoundFormat]] is undefined, then
if (!number_format->bound_format()) {
// a. Let F be a new built-in function object as defined in Number Format Functions (16.1.4).
// b. Set F.[[NumberFormat]] to nf.
auto bound_format = NumberFormatFunction::create(realm, number_format);
// c. Set nf.[[BoundFormat]] to F.
number_format->set_bound_format(bound_format);
}
// 5. Return nf.[[BoundFormat]].
return number_format->bound_format();
}
// 16.3.4 Intl.NumberFormat.prototype.formatRange ( start, end ), https://tc39.es/ecma402/#sec-intl.numberformat.prototype.formatrange
JS_DEFINE_NATIVE_FUNCTION(NumberFormatPrototype::format_range)
{
auto start = vm.argument(0);
auto end = vm.argument(1);
// 1. Let nf be the this value.
// 2. Perform ? RequireInternalSlot(nf, [[InitializedNumberFormat]]).
auto number_format = TRY(typed_this_object(vm));
// 3. If start is undefined or end is undefined, throw a TypeError exception.
if (start.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "start"sv);
if (end.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "end"sv);
// 4. Let x be ? ToIntlMathematicalValue(start).
auto x = TRY(to_intl_mathematical_value(vm, start));
// 5. Let y be ? ToIntlMathematicalValue(end).
auto y = TRY(to_intl_mathematical_value(vm, end));
// 6. Return ? FormatNumericRange(nf, x, y).
auto formatted = TRY(format_numeric_range(vm, number_format, move(x), move(y)));
return PrimitiveString::create(vm, move(formatted));
}
// 16.3.5 Intl.NumberFormat.prototype.formatRangeToParts ( start, end ), https://tc39.es/ecma402/#sec-intl.numberformat.prototype.formatrangetoparts
JS_DEFINE_NATIVE_FUNCTION(NumberFormatPrototype::format_range_to_parts)
{
auto start = vm.argument(0);
auto end = vm.argument(1);
// 1. Let nf be the this value.
// 2. Perform ? RequireInternalSlot(nf, [[InitializedNumberFormat]]).
auto number_format = TRY(typed_this_object(vm));
// 3. If start is undefined or end is undefined, throw a TypeError exception.
if (start.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "start"sv);
if (end.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "end"sv);
// 4. Let x be ? ToIntlMathematicalValue(start).
auto x = TRY(to_intl_mathematical_value(vm, start));
// 5. Let y be ? ToIntlMathematicalValue(end).
auto y = TRY(to_intl_mathematical_value(vm, end));
// 6. Return ? FormatNumericRangeToParts(nf, x, y).
return TRY(format_numeric_range_to_parts(vm, number_format, move(x), move(y)));
}
// 16.3.6 Intl.NumberFormat.prototype.formatToParts ( value ), https://tc39.es/ecma402/#sec-intl.numberformat.prototype.formattoparts
JS_DEFINE_NATIVE_FUNCTION(NumberFormatPrototype::format_to_parts)
{
auto value = vm.argument(0);
// 1. Let nf be the this value.
// 2. Perform ? RequireInternalSlot(nf, [[InitializedNumberFormat]]).
auto number_format = TRY(typed_this_object(vm));
// 3. Let x be ? ToIntlMathematicalValue(value).
auto mathematical_value = TRY(to_intl_mathematical_value(vm, value));
// 4. Return ? FormatNumericToParts(nf, x).
return format_numeric_to_parts(vm, number_format, move(mathematical_value));
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2021-2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -22,11 +22,11 @@ public:
private:
explicit NumberFormatPrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(resolved_options);
JS_DECLARE_NATIVE_FUNCTION(format);
JS_DECLARE_NATIVE_FUNCTION(format_to_parts);
JS_DECLARE_NATIVE_FUNCTION(format_range);
JS_DECLARE_NATIVE_FUNCTION(format_range_to_parts);
JS_DECLARE_NATIVE_FUNCTION(resolved_options);
JS_DECLARE_NATIVE_FUNCTION(format_to_parts);
};
}

View file

@ -27,58 +27,16 @@ void PluralRulesPrototype::initialize(Realm& realm)
auto& vm = this->vm();
// 17.3.2 Intl.PluralRules.prototype [ @@toStringTag ], https://tc39.es/ecma402/#sec-intl.pluralrules.prototype-tostringtag
// 17.3.5 Intl.PluralRules.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma402/#sec-intl.pluralrules.prototype-%symbol.tostringtag%
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.PluralRules"_string), Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr);
define_native_function(realm, vm.names.select, select, 1, attr);
define_native_function(realm, vm.names.selectRange, select_range, 2, attr);
define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr);
}
// 17.3.3 Intl.PluralRules.prototype.select ( value ), https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.select
JS_DEFINE_NATIVE_FUNCTION(PluralRulesPrototype::select)
{
// 1. Let pr be the this value.
// 2. Perform ? RequireInternalSlot(pr, [[InitializedPluralRules]]).
auto plural_rules = TRY(typed_this_object(vm));
// 3. Let n be ? ToNumber(value).
auto number = TRY(vm.argument(0).to_number(vm));
// 4. Return ! ResolvePlural(pr, n).[[PluralCategory]].
auto plurality = resolve_plural(plural_rules, number);
return PrimitiveString::create(vm, Unicode::plural_category_to_string(plurality));
}
// 17.3.4 Intl.PluralRules.prototype.selectRange ( start, end ), https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.selectrange
JS_DEFINE_NATIVE_FUNCTION(PluralRulesPrototype::select_range)
{
auto start = vm.argument(0);
auto end = vm.argument(1);
// 1. Let pr be the this value.
// 2. Perform ? RequireInternalSlot(pr, [[InitializedPluralRules]]).
auto plural_rules = TRY(typed_this_object(vm));
// 3. If start is undefined or end is undefined, throw a TypeError exception.
if (start.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "start"sv);
if (end.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "end"sv);
// 4. Let x be ? ToNumber(start).
auto x = TRY(start.to_number(vm));
// 5. Let y be ? ToNumber(end).
auto y = TRY(end.to_number(vm));
// 6. Return ? ResolvePluralRange(pr, x, y).
auto plurality = TRY(resolve_plural_range(vm, plural_rules, x, y));
return PrimitiveString::create(vm, Unicode::plural_category_to_string(plurality));
}
// 17.3.5 Intl.PluralRules.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.resolvedoptions
// 17.3.2 Intl.PluralRules.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.resolvedoptions
JS_DEFINE_NATIVE_FUNCTION(PluralRulesPrototype::resolved_options)
{
auto& realm = *vm.current_realm();
@ -127,4 +85,46 @@ JS_DEFINE_NATIVE_FUNCTION(PluralRulesPrototype::resolved_options)
return options;
}
// 17.3.3 Intl.PluralRules.prototype.select ( value ), https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.select
JS_DEFINE_NATIVE_FUNCTION(PluralRulesPrototype::select)
{
// 1. Let pr be the this value.
// 2. Perform ? RequireInternalSlot(pr, [[InitializedPluralRules]]).
auto plural_rules = TRY(typed_this_object(vm));
// 3. Let n be ? ToNumber(value).
auto number = TRY(vm.argument(0).to_number(vm));
// 4. Return ! ResolvePlural(pr, n).[[PluralCategory]].
auto plurality = resolve_plural(plural_rules, number);
return PrimitiveString::create(vm, Unicode::plural_category_to_string(plurality));
}
// 17.3.4 Intl.PluralRules.prototype.selectRange ( start, end ), https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.selectrange
JS_DEFINE_NATIVE_FUNCTION(PluralRulesPrototype::select_range)
{
auto start = vm.argument(0);
auto end = vm.argument(1);
// 1. Let pr be the this value.
// 2. Perform ? RequireInternalSlot(pr, [[InitializedPluralRules]]).
auto plural_rules = TRY(typed_this_object(vm));
// 3. If start is undefined or end is undefined, throw a TypeError exception.
if (start.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "start"sv);
if (end.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "end"sv);
// 4. Let x be ? ToNumber(start).
auto x = TRY(start.to_number(vm));
// 5. Let y be ? ToNumber(end).
auto y = TRY(end.to_number(vm));
// 6. Return ? ResolvePluralRange(pr, x, y).
auto plurality = TRY(resolve_plural_range(vm, plural_rules, x, y));
return PrimitiveString::create(vm, Unicode::plural_category_to_string(plurality));
}
}

View file

@ -22,9 +22,9 @@ public:
private:
explicit PluralRulesPrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(resolved_options);
JS_DECLARE_NATIVE_FUNCTION(select);
JS_DECLARE_NATIVE_FUNCTION(select_range);
JS_DECLARE_NATIVE_FUNCTION(resolved_options);
};
}

View file

@ -25,13 +25,39 @@ void RelativeTimeFormatPrototype::initialize(Realm& realm)
auto& vm = this->vm();
// 18.3.2 Intl.RelativeTimeFormat.prototype[ @@toStringTag ], https://tc39.es/ecma402/#sec-Intl.RelativeTimeFormat.prototype-toStringTag
// 18.3.5 Intl.RelativeTimeFormat.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma402/#sec-Intl.RelativeTimeFormat.prototype-toStringTag
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.RelativeTimeFormat"_string), Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr);
define_native_function(realm, vm.names.format, format, 2, attr);
define_native_function(realm, vm.names.formatToParts, format_to_parts, 2, attr);
define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr);
}
// 18.3.2 Intl.RelativeTimeFormat.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.relativetimeformat.prototype.resolvedoptions
JS_DEFINE_NATIVE_FUNCTION(RelativeTimeFormatPrototype::resolved_options)
{
auto& realm = *vm.current_realm();
// 1. Let relativeTimeFormat be the this value.
// 2. Perform ? RequireInternalSlot(relativeTimeFormat, [[InitializedRelativeTimeFormat]]).
auto relative_time_format = TRY(typed_this_object(vm));
// 3. Let options be OrdinaryObjectCreate(%Object.prototype%).
auto options = Object::create(realm, realm.intrinsics().object_prototype());
// 4. For each row of Table 30, except the header row, in table order, do
// a. Let p be the Property value of the current row.
// b. Let v be the value of relativeTimeFormat's internal slot whose name is the Internal Slot value of the current row.
// c. Assert: v is not undefined.
// d. Perform ! CreateDataPropertyOrThrow(options, p, v).
MUST(options->create_data_property_or_throw(vm.names.locale, PrimitiveString::create(vm, relative_time_format->locale())));
MUST(options->create_data_property_or_throw(vm.names.style, PrimitiveString::create(vm, relative_time_format->style_string())));
MUST(options->create_data_property_or_throw(vm.names.numeric, PrimitiveString::create(vm, relative_time_format->numeric_string())));
MUST(options->create_data_property_or_throw(vm.names.numberingSystem, PrimitiveString::create(vm, relative_time_format->numbering_system())));
// 5. Return options.
return options;
}
// 18.3.3 Intl.RelativeTimeFormat.prototype.format ( value, unit ), https://tc39.es/ecma402/#sec-Intl.RelativeTimeFormat.prototype.format
@ -69,30 +95,4 @@ JS_DEFINE_NATIVE_FUNCTION(RelativeTimeFormatPrototype::format_to_parts)
return TRY(format_relative_time_to_parts(vm, relative_time_format, value.as_double(), unit.bytes_as_string_view()));
}
// 18.3.5 Intl.RelativeTimeFormat.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.relativetimeformat.prototype.resolvedoptions
JS_DEFINE_NATIVE_FUNCTION(RelativeTimeFormatPrototype::resolved_options)
{
auto& realm = *vm.current_realm();
// 1. Let relativeTimeFormat be the this value.
// 2. Perform ? RequireInternalSlot(relativeTimeFormat, [[InitializedRelativeTimeFormat]]).
auto relative_time_format = TRY(typed_this_object(vm));
// 3. Let options be OrdinaryObjectCreate(%Object.prototype%).
auto options = Object::create(realm, realm.intrinsics().object_prototype());
// 4. For each row of Table 30, except the header row, in table order, do
// a. Let p be the Property value of the current row.
// b. Let v be the value of relativeTimeFormat's internal slot whose name is the Internal Slot value of the current row.
// c. Assert: v is not undefined.
// d. Perform ! CreateDataPropertyOrThrow(options, p, v).
MUST(options->create_data_property_or_throw(vm.names.locale, PrimitiveString::create(vm, relative_time_format->locale())));
MUST(options->create_data_property_or_throw(vm.names.style, PrimitiveString::create(vm, relative_time_format->style_string())));
MUST(options->create_data_property_or_throw(vm.names.numeric, PrimitiveString::create(vm, relative_time_format->numeric_string())));
MUST(options->create_data_property_or_throw(vm.names.numberingSystem, PrimitiveString::create(vm, relative_time_format->numbering_system())));
// 5. Return options.
return options;
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2022-2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -22,9 +22,9 @@ public:
private:
explicit RelativeTimeFormatPrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(resolved_options);
JS_DECLARE_NATIVE_FUNCTION(format);
JS_DECLARE_NATIVE_FUNCTION(format_to_parts);
JS_DECLARE_NATIVE_FUNCTION(resolved_options);
};
}

View file

@ -12,11 +12,11 @@ namespace JS::Intl {
GC_DEFINE_ALLOCATOR(SegmentIterator);
// 19.6.1 CreateSegmentIterator ( segmenter, string ), https://tc39.es/ecma402/#sec-createsegmentsobject
// 19.6.1 CreateSegmentIterator ( segmenter, string ), https://tc39.es/ecma402/#sec-createsegmentiterator
GC::Ref<SegmentIterator> SegmentIterator::create(Realm& realm, Unicode::Segmenter const& segmenter, Utf16View const& string, Segments const& segments)
{
// 1. Let internalSlotsList be « [[IteratingSegmenter]], [[IteratedString]], [[IteratedStringNextSegmentCodeUnitIndex]] ».
// 2. Let iterator be OrdinaryObjectCreate(%SegmentIteratorPrototype%, internalSlotsList).
// 2. Let iterator be OrdinaryObjectCreate(%IntlSegmentIteratorPrototype%, internalSlotsList).
// 3. Set iterator.[[IteratingSegmenter]] to segmenter.
// 4. Set iterator.[[IteratedString]] to string.
// 5. Set iterator.[[IteratedStringNextSegmentCodeUnitIndex]] to 0.

View file

@ -14,7 +14,7 @@ namespace JS::Intl {
GC_DEFINE_ALLOCATOR(SegmentIteratorPrototype);
// 19.6.2 The %SegmentIteratorPrototype% Object, https://tc39.es/ecma402/#sec-%segmentiteratorprototype%-object
// 19.6.2 The %IntlSegmentIteratorPrototype% Object, https://tc39.es/ecma402/#sec-%intlsegmentiteratorprototype%-object
SegmentIteratorPrototype::SegmentIteratorPrototype(Realm& realm)
: PrototypeObject(realm.intrinsics().iterator_prototype())
{
@ -26,14 +26,14 @@ void SegmentIteratorPrototype::initialize(Realm& realm)
auto& vm = this->vm();
// 19.6.2.2 %SegmentIteratorPrototype% [ @@toStringTag ], https://tc39.es/ecma402/#sec-%segmentiteratorprototype%.@@tostringtag
// 19.6.2.2 %IntlSegmentIteratorPrototype% [ %Symbol.toStringTag% ], https://tc39.es/ecma402/#sec-%intlsegmentiteratorprototype%.%symbol.tostringtag%
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Segmenter String Iterator"_string), Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.next, next, 0, attr);
}
// 19.6.2.1 %SegmentIteratorPrototype%.next ( ), https://tc39.es/ecma402/#sec-%segmentiteratorprototype%.next
// 19.6.2.1 %IntlSegmentIteratorPrototype%.next ( ), https://tc39.es/ecma402/#sec-%intlsegmentiteratorprototype%.next
JS_DEFINE_NATIVE_FUNCTION(SegmentIteratorPrototype::next)
{
// 1. Let iterator be the this value.

View file

@ -95,7 +95,7 @@ JS_DEFINE_NATIVE_FUNCTION(SegmenterConstructor::supported_locales_of)
auto locales = vm.argument(0);
auto options = vm.argument(1);
// 1. Let availableLocales be %Segmenter%.[[AvailableLocales]].
// 1. Let availableLocales be %Intl.Segmenter%.[[AvailableLocales]].
// 2. Let requestedLocales be ? CanonicalizeLocaleList(locales).
auto requested_locales = TRY(canonicalize_locale_list(vm, locales));

View file

@ -25,7 +25,7 @@ void SegmenterPrototype::initialize(Realm& realm)
auto& vm = this->vm();
// 19.3.2 Intl.Segmenter.prototype [ @@toStringTag ], https://tc39.es/ecma402/#sec-intl.segmenter.prototype-@@tostringtag
// 19.3.4 Intl.Segmenter.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma402/#sec-intl.segmenter.prototype-%symbol.tostringtag%
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.Segmenter"_string), Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
@ -33,7 +33,7 @@ void SegmenterPrototype::initialize(Realm& realm)
define_native_function(realm, vm.names.segment, segment, 1, attr);
}
// 19.3.4 Intl.Segmenter.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.segmenter.prototype.resolvedoptions
// 19.3.2 Intl.Segmenter.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.segmenter.prototype.resolvedoptions
JS_DEFINE_NATIVE_FUNCTION(SegmenterPrototype::resolved_options)
{
auto& realm = *vm.current_realm();

View file

@ -22,8 +22,8 @@ public:
private:
explicit SegmenterPrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(segment);
JS_DECLARE_NATIVE_FUNCTION(resolved_options);
JS_DECLARE_NATIVE_FUNCTION(segment);
};
}

View file

@ -16,7 +16,7 @@ GC_DEFINE_ALLOCATOR(Segments);
GC::Ref<Segments> Segments::create(Realm& realm, Unicode::Segmenter const& segmenter, Utf16String string)
{
// 1. Let internalSlotsList be « [[SegmentsSegmenter]], [[SegmentsString]] ».
// 2. Let segments be OrdinaryObjectCreate(%SegmentsPrototype%, internalSlotsList).
// 2. Let segments be OrdinaryObjectCreate(%IntlSegmentsPrototype%, internalSlotsList).
// 3. Set segments.[[SegmentsSegmenter]] to segmenter.
// 4. Set segments.[[SegmentsString]] to string.
// 5. Return segments.

View file

@ -13,7 +13,7 @@ namespace JS::Intl {
GC_DEFINE_ALLOCATOR(SegmentsPrototype);
// 19.5.2 The %SegmentsPrototype% Object, https://tc39.es/ecma402/#sec-%segmentsprototype%-object
// 19.5.2 The %IntlSegmentsPrototype% Object, https://tc39.es/ecma402/#sec-%intlsegmentsprototype%-object
SegmentsPrototype::SegmentsPrototype(Realm& realm)
: PrototypeObject(realm.intrinsics().object_prototype())
{
@ -30,7 +30,7 @@ void SegmentsPrototype::initialize(Realm& realm)
define_native_function(realm, vm.names.containing, containing, 1, attr);
}
// 19.5.2.1 %SegmentsPrototype%.containing ( index ), https://tc39.es/ecma402/#sec-%segmentsprototype%.containing
// 19.5.2.1 %IntlSegmentsPrototype%.containing ( index ), https://tc39.es/ecma402/#sec-%intlsegmentsprototype%.containing
JS_DEFINE_NATIVE_FUNCTION(SegmentsPrototype::containing)
{
// 1. Let segments be the this value.
@ -63,7 +63,7 @@ JS_DEFINE_NATIVE_FUNCTION(SegmentsPrototype::containing)
return TRY(create_segment_data_object(vm, segmenter, string, start_index, end_index));
}
// 19.5.2.2 %SegmentsPrototype% [ @@iterator ] ( ), https://tc39.es/ecma402/#sec-%segmentsprototype%-@@iterator
// 19.5.2.2 %IntlSegmentsPrototype% [ %Symbol.iterator% ] ( ), https://tc39.es/ecma402/#sec-%intlsegmentsprototype%-%symbol.iterator%
JS_DEFINE_NATIVE_FUNCTION(SegmentsPrototype::symbol_iterator)
{
auto& realm = *vm.current_realm();