1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 16:07:47 +00:00

LibJS: Implement a nearly empty Intl.RelativeTimeFormat object

This adds plumbing for the Intl.RelativeTimeFormat object, constructor,
and prototype.
This commit is contained in:
Timothy Flynn 2022-01-25 10:41:57 -05:00 committed by Linus Groh
parent 0865f71d37
commit 79fdec85de
12 changed files with 271 additions and 1 deletions

View file

@ -106,6 +106,9 @@ set(SOURCES
Runtime/Intl/NumberFormatConstructor.cpp Runtime/Intl/NumberFormatConstructor.cpp
Runtime/Intl/NumberFormatFunction.cpp Runtime/Intl/NumberFormatFunction.cpp
Runtime/Intl/NumberFormatPrototype.cpp Runtime/Intl/NumberFormatPrototype.cpp
Runtime/Intl/RelativeTimeFormat.cpp
Runtime/Intl/RelativeTimeFormatConstructor.cpp
Runtime/Intl/RelativeTimeFormatPrototype.cpp
Runtime/IteratorOperations.cpp Runtime/IteratorOperations.cpp
Runtime/IteratorPrototype.cpp Runtime/IteratorPrototype.cpp
Runtime/JSONObject.cpp Runtime/JSONObject.cpp

View file

@ -72,7 +72,8 @@
__JS_ENUMERATE(DisplayNames, display_names, DisplayNamesPrototype, DisplayNamesConstructor) \ __JS_ENUMERATE(DisplayNames, display_names, DisplayNamesPrototype, DisplayNamesConstructor) \
__JS_ENUMERATE(ListFormat, list_format, ListFormatPrototype, ListFormatConstructor) \ __JS_ENUMERATE(ListFormat, list_format, ListFormatPrototype, ListFormatConstructor) \
__JS_ENUMERATE(Locale, locale, LocalePrototype, LocaleConstructor) \ __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 \ #define JS_ENUMERATE_TEMPORAL_OBJECTS \
__JS_ENUMERATE(Calendar, calendar, CalendarPrototype, CalendarConstructor) \ __JS_ENUMERATE(Calendar, calendar, CalendarPrototype, CalendarConstructor) \

View file

@ -61,6 +61,8 @@
#include <LibJS/Runtime/Intl/LocalePrototype.h> #include <LibJS/Runtime/Intl/LocalePrototype.h>
#include <LibJS/Runtime/Intl/NumberFormatConstructor.h> #include <LibJS/Runtime/Intl/NumberFormatConstructor.h>
#include <LibJS/Runtime/Intl/NumberFormatPrototype.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/IteratorPrototype.h>
#include <LibJS/Runtime/JSONObject.h> #include <LibJS/Runtime/JSONObject.h>
#include <LibJS/Runtime/MapConstructor.h> #include <LibJS/Runtime/MapConstructor.h>

View file

@ -13,6 +13,7 @@
#include <LibJS/Runtime/Intl/ListFormatConstructor.h> #include <LibJS/Runtime/Intl/ListFormatConstructor.h>
#include <LibJS/Runtime/Intl/LocaleConstructor.h> #include <LibJS/Runtime/Intl/LocaleConstructor.h>
#include <LibJS/Runtime/Intl/NumberFormatConstructor.h> #include <LibJS/Runtime/Intl/NumberFormatConstructor.h>
#include <LibJS/Runtime/Intl/RelativeTimeFormatConstructor.h>
namespace JS::Intl { 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.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.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.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); define_native_function(vm.names.getCanonicalLocales, get_canonical_locales, 1, attr);
} }

View file

@ -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();
}
}
}

View file

@ -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]]
};
}

View file

@ -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;
}
}

View file

@ -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; }
};
}

View file

@ -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);
}
}

View file

@ -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;
};
}

View file

@ -0,0 +1,3 @@
test("basic functionality", () => {
expect(Intl.RelativeTimeFormat.prototype[Symbol.toStringTag]).toBe("Intl.RelativeTimeFormat");
});

View file

@ -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);
});
});