diff options
author | Timothy Flynn <trflynn89@pm.me> | 2022-01-25 10:41:57 -0500 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-01-25 19:02:59 +0000 |
commit | 79fdec85de20c35eba3ef34cdea12ab678c31586 (patch) | |
tree | a6bfb02ca57ae63e6e260048c22e6685f728e9eb /Userland | |
parent | 0865f71d37943848316791575ac3a2fc3324c938 (diff) | |
download | serenity-79fdec85de20c35eba3ef34cdea12ab678c31586.zip |
LibJS: Implement a nearly empty Intl.RelativeTimeFormat object
This adds plumbing for the Intl.RelativeTimeFormat object, constructor,
and prototype.
Diffstat (limited to 'Userland')
12 files changed, 271 insertions, 1 deletions
diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt index 9fe951bab9..d1c7e07993 100644 --- a/Userland/Libraries/LibJS/CMakeLists.txt +++ b/Userland/Libraries/LibJS/CMakeLists.txt @@ -106,6 +106,9 @@ set(SOURCES Runtime/Intl/NumberFormatConstructor.cpp Runtime/Intl/NumberFormatFunction.cpp Runtime/Intl/NumberFormatPrototype.cpp + Runtime/Intl/RelativeTimeFormat.cpp + Runtime/Intl/RelativeTimeFormatConstructor.cpp + Runtime/Intl/RelativeTimeFormatPrototype.cpp Runtime/IteratorOperations.cpp Runtime/IteratorPrototype.cpp Runtime/JSONObject.cpp diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h index 13802349cd..87d743cdf9 100644 --- a/Userland/Libraries/LibJS/Forward.h +++ b/Userland/Libraries/LibJS/Forward.h @@ -72,7 +72,8 @@ __JS_ENUMERATE(DisplayNames, display_names, DisplayNamesPrototype, DisplayNamesConstructor) \ __JS_ENUMERATE(ListFormat, list_format, ListFormatPrototype, ListFormatConstructor) \ __JS_ENUMERATE(Locale, locale, LocalePrototype, LocaleConstructor) \ - __JS_ENUMERATE(NumberFormat, number_format, NumberFormatPrototype, NumberFormatConstructor) + __JS_ENUMERATE(NumberFormat, number_format, NumberFormatPrototype, NumberFormatConstructor) \ + __JS_ENUMERATE(RelativeTimeFormat, relative_time_format, RelativeTimeFormatPrototype, RelativeTimeFormatConstructor) #define JS_ENUMERATE_TEMPORAL_OBJECTS \ __JS_ENUMERATE(Calendar, calendar, CalendarPrototype, CalendarConstructor) \ diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp index 7a18d2b37e..2d015c9b85 100644 --- a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -61,6 +61,8 @@ #include <LibJS/Runtime/Intl/LocalePrototype.h> #include <LibJS/Runtime/Intl/NumberFormatConstructor.h> #include <LibJS/Runtime/Intl/NumberFormatPrototype.h> +#include <LibJS/Runtime/Intl/RelativeTimeFormatConstructor.h> +#include <LibJS/Runtime/Intl/RelativeTimeFormatPrototype.h> #include <LibJS/Runtime/IteratorPrototype.h> #include <LibJS/Runtime/JSONObject.h> #include <LibJS/Runtime/MapConstructor.h> diff --git a/Userland/Libraries/LibJS/Runtime/Intl/Intl.cpp b/Userland/Libraries/LibJS/Runtime/Intl/Intl.cpp index 4c9cf5e20f..38a2dbc8cd 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/Intl.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/Intl.cpp @@ -13,6 +13,7 @@ #include <LibJS/Runtime/Intl/ListFormatConstructor.h> #include <LibJS/Runtime/Intl/LocaleConstructor.h> #include <LibJS/Runtime/Intl/NumberFormatConstructor.h> +#include <LibJS/Runtime/Intl/RelativeTimeFormatConstructor.h> namespace JS::Intl { @@ -37,6 +38,7 @@ void Intl::initialize(GlobalObject& global_object) define_direct_property(vm.names.ListFormat, global_object.intl_list_format_constructor(), attr); define_direct_property(vm.names.Locale, global_object.intl_locale_constructor(), attr); define_direct_property(vm.names.NumberFormat, global_object.intl_number_format_constructor(), attr); + define_direct_property(vm.names.RelativeTimeFormat, global_object.intl_relative_time_format_constructor(), attr); define_native_function(vm.names.getCanonicalLocales, get_canonical_locales, 1, attr); } diff --git a/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.cpp b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.cpp new file mode 100644 index 0000000000..eed3a02a24 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022, Tim Flynn <trflynn89@pm.me> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibJS/Runtime/Intl/RelativeTimeFormat.h> + +namespace JS::Intl { + +// 17 RelativeTimeFormat Objects, https://tc39.es/ecma402/#relativetimeformat-objects +RelativeTimeFormat::RelativeTimeFormat(Object& prototype) + : Object(prototype) +{ +} + +void RelativeTimeFormat::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + if (m_number_format) + visitor.visit(m_number_format); +} + +void RelativeTimeFormat::set_numeric(StringView numeric) +{ + if (numeric == "always"sv) { + m_numeric = Numeric::Always; + } else if (numeric == "auto"sv) { + m_numeric = Numeric::Auto; + } else { + VERIFY_NOT_REACHED(); + } +} + +StringView RelativeTimeFormat::numeric_string() const +{ + switch (m_numeric) { + case Numeric::Always: + return "always"sv; + case Numeric::Auto: + return "auto"sv; + default: + VERIFY_NOT_REACHED(); + } +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.h b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.h new file mode 100644 index 0000000000..1622320b83 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormat.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022, Tim Flynn <trflynn89@pm.me> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/Array.h> +#include <AK/String.h> +#include <AK/StringView.h> +#include <LibJS/Runtime/Object.h> +#include <LibUnicode/Locale.h> + +namespace JS::Intl { + +class RelativeTimeFormat final : public Object { + JS_OBJECT(RelativeTimeFormat, Object); + +public: + enum class Numeric { + Always, + Auto, + }; + + static constexpr auto relevant_extension_keys() + { + // 17.3.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 }; + } + + RelativeTimeFormat(Object& prototype); + virtual ~RelativeTimeFormat() override = default; + + String const& locale() const { return m_locale; } + void set_locale(String locale) { m_locale = move(locale); } + + String const& data_locale() const { return m_data_locale; } + void set_data_locale(String data_locale) { m_data_locale = move(data_locale); } + + String const& numbering_system() const { return m_numbering_system; } + void set_numbering_system(String numbering_system) { m_numbering_system = move(numbering_system); } + + Unicode::Style style() const { return m_style; } + void set_style(StringView style) { m_style = Unicode::style_from_string(style); } + StringView style_string() const { return Unicode::style_to_string(m_style); } + + Numeric numeric() const { return m_numeric; } + void set_numeric(StringView numeric); + StringView numeric_string() const; + + NumberFormat* number_format() const { return m_number_format; } + void set_number_format(NumberFormat* number_format) { m_number_format = number_format; } + +private: + virtual void visit_edges(Cell::Visitor&) override; + + String m_locale; // [[Locale]] + String m_data_locale; // [[DataLocale]] + String m_numbering_system; // [[NumberingSystem]] + Unicode::Style m_style { Unicode::Style::Long }; // [[Style]] + Numeric m_numeric { Numeric::Always }; // [[Numeric]] + NumberFormat* m_number_format { nullptr }; // [[NumberFormat]] +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp new file mode 100644 index 0000000000..76b481da29 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022, Tim Flynn <trflynn89@pm.me> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibJS/Runtime/AbstractOperations.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/Intl/RelativeTimeFormat.h> +#include <LibJS/Runtime/Intl/RelativeTimeFormatConstructor.h> + +namespace JS::Intl { + +// 17.2 The Intl.RelativeTimeFormat Constructor, https://tc39.es/ecma402/#sec-intl-relativetimeformat-constructor +RelativeTimeFormatConstructor::RelativeTimeFormatConstructor(GlobalObject& global_object) + : NativeFunction(vm().names.RelativeTimeFormat.as_string(), *global_object.function_prototype()) +{ +} + +void RelativeTimeFormatConstructor::initialize(GlobalObject& global_object) +{ + NativeFunction::initialize(global_object); + + auto& vm = this->vm(); + + // 17.3.1 Intl.RelativeTimeFormat.prototype, https://tc39.es/ecma402/#sec-Intl.RelativeTimeFormat.prototype + define_direct_property(vm.names.prototype, global_object.intl_relative_time_format_prototype(), 0); + define_direct_property(vm.names.length, Value(0), Attribute::Configurable); +} + +// 17.2.1 Intl.RelativeTimeFormat ( [ locales [ , options ] ] ), https://tc39.es/ecma402/#sec-Intl.RelativeTimeFormat +ThrowCompletionOr<Value> RelativeTimeFormatConstructor::call() +{ + // 1. If NewTarget is undefined, throw a TypeError exception. + return vm().throw_completion<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, "Intl.RelativeTimeFormat"); +} + +// 17.2.1 Intl.RelativeTimeFormat ( [ locales [ , options ] ] ), https://tc39.es/ecma402/#sec-Intl.RelativeTimeFormat +ThrowCompletionOr<Object*> RelativeTimeFormatConstructor::construct(FunctionObject& new_target) +{ + auto& global_object = this->global_object(); + + // 2. Let relativeTimeFormat be ? OrdinaryCreateFromConstructor(NewTarget, "%RelativeTimeFormat.prototype%", « [[InitializedRelativeTimeFormat]], [[Locale]], [[DataLocale]], [[Style]], [[Numeric]], [[NumberFormat]], [[NumberingSystem]], [[PluralRules]] »). + auto* relative_time_format = TRY(ordinary_create_from_constructor<RelativeTimeFormat>(global_object, new_target, &GlobalObject::intl_relative_time_format_prototype)); + + // 3. Return ? InitializeRelativeTimeFormat(relativeTimeFormat, locales, options). + return relative_time_format; +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.h b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.h new file mode 100644 index 0000000000..2fc1b0fdc0 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022, Tim Flynn <trflynn89@pm.me> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibJS/Runtime/NativeFunction.h> + +namespace JS::Intl { + +class RelativeTimeFormatConstructor final : public NativeFunction { + JS_OBJECT(RelativeTimeFormatConstructor, NativeFunction); + +public: + explicit RelativeTimeFormatConstructor(GlobalObject&); + virtual void initialize(GlobalObject&) override; + virtual ~RelativeTimeFormatConstructor() override = default; + + virtual ThrowCompletionOr<Value> call() override; + virtual ThrowCompletionOr<Object*> construct(FunctionObject& new_target) override; + +private: + virtual bool has_constructor() const override { return true; } +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatPrototype.cpp new file mode 100644 index 0000000000..bccb8dbf4e --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatPrototype.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022, Tim Flynn <trflynn89@pm.me> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/Intl/RelativeTimeFormatPrototype.h> + +namespace JS::Intl { + +// 17.4 Properties of the Intl.RelativeTimeFormat Prototype Object, https://tc39.es/ecma402/#sec-properties-of-intl-relativetimeformat-prototype-object +RelativeTimeFormatPrototype::RelativeTimeFormatPrototype(GlobalObject& global_object) + : PrototypeObject(*global_object.object_prototype()) +{ +} + +void RelativeTimeFormatPrototype::initialize(GlobalObject& global_object) +{ + Object::initialize(global_object); + + auto& vm = this->vm(); + + // 17.4.2 Intl.RelativeTimeFormat.prototype[ @@toStringTag ], https://tc39.es/ecma402/#sec-Intl.RelativeTimeFormat.prototype-toStringTag + define_direct_property(*vm.well_known_symbol_to_string_tag(), js_string(vm, "Intl.RelativeTimeFormat"sv), Attribute::Configurable); +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatPrototype.h b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatPrototype.h new file mode 100644 index 0000000000..8d40bc1595 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatPrototype.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022, Tim Flynn <trflynn89@pm.me> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibJS/Runtime/Intl/RelativeTimeFormat.h> +#include <LibJS/Runtime/PrototypeObject.h> + +namespace JS::Intl { + +class RelativeTimeFormatPrototype final : public PrototypeObject<RelativeTimeFormatPrototype, RelativeTimeFormat> { + JS_PROTOTYPE_OBJECT(RelativeTimeFormatPrototype, RelativeTimeFormat, Intl.RelativeTimeFormat); + +public: + explicit RelativeTimeFormatPrototype(GlobalObject&); + virtual void initialize(GlobalObject&) override; + virtual ~RelativeTimeFormatPrototype() override = default; +}; + +} diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/RelativeTimeFormat/RelativeTimeFormat.@@toStringTag.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/RelativeTimeFormat/RelativeTimeFormat.@@toStringTag.js new file mode 100644 index 0000000000..ab8844fd58 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/RelativeTimeFormat/RelativeTimeFormat.@@toStringTag.js @@ -0,0 +1,3 @@ +test("basic functionality", () => { + expect(Intl.RelativeTimeFormat.prototype[Symbol.toStringTag]).toBe("Intl.RelativeTimeFormat"); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/RelativeTimeFormat/RelativeTimeFormat.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/RelativeTimeFormat/RelativeTimeFormat.js new file mode 100644 index 0000000000..e755270127 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/RelativeTimeFormat/RelativeTimeFormat.js @@ -0,0 +1,16 @@ +describe("errors", () => { + test("called without new", () => { + expect(() => { + Intl.RelativeTimeFormat(); + }).toThrowWithMessage( + TypeError, + "Intl.RelativeTimeFormat constructor must be called with 'new'" + ); + }); +}); + +describe("normal behavior", () => { + test("length is 0", () => { + expect(Intl.RelativeTimeFormat).toHaveLength(0); + }); +}); |