diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt index eb5b96d633..be329b274a 100644 --- a/Userland/Libraries/LibJS/CMakeLists.txt +++ b/Userland/Libraries/LibJS/CMakeLists.txt @@ -72,6 +72,9 @@ set(SOURCES Runtime/GlobalEnvironment.cpp Runtime/GlobalObject.cpp Runtime/IndexedProperties.cpp + Runtime/Intl/DisplayNames.cpp + Runtime/Intl/DisplayNamesConstructor.cpp + Runtime/Intl/DisplayNamesPrototype.cpp Runtime/Intl/Intl.cpp Runtime/IteratorOperations.cpp Runtime/IteratorPrototype.cpp diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h index 5c88c3c236..0d02c35536 100644 --- a/Userland/Libraries/LibJS/Forward.h +++ b/Userland/Libraries/LibJS/Forward.h @@ -76,7 +76,8 @@ __JS_ENUMERATE(Float32Array, float32_array, Float32ArrayPrototype, Float32ArrayConstructor, float) \ __JS_ENUMERATE(Float64Array, float64_array, Float64ArrayPrototype, Float64ArrayConstructor, double) -#define JS_ENUMERATE_INTL_OBJECTS +#define JS_ENUMERATE_INTL_OBJECTS \ + __JS_ENUMERATE(DisplayNames, display_names, DisplayNamesPrototype, DisplayNamesConstructor) #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 47d4de80af..83e8a53582 100644 --- a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp new file mode 100644 index 0000000000..2f0d15c3f2 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace JS::Intl { + +// 12 DisplayNames Objects, https://tc39.es/ecma402/#intl-displaynames-objects +DisplayNames::DisplayNames(Object& prototype) + : Object(prototype) +{ +} + +void DisplayNames::set_style(StringView style) +{ + if (style == "narrow"sv) { + m_style = Style::Narrow; + } else if (style == "short"sv) { + m_style = Style::Short; + } else if (style == "long"sv) { + m_style = Style::Long; + } else { + VERIFY_NOT_REACHED(); + } +} + +StringView DisplayNames::style_string() const +{ + switch (m_style) { + case Style::Narrow: + return "narrow"sv; + case Style::Short: + return "short"sv; + case Style::Long: + return "long"sv; + default: + VERIFY_NOT_REACHED(); + } +} + +void DisplayNames::set_type(StringView type) +{ + if (type == "language"sv) { + m_type = Type::Language; + } else if (type == "region"sv) { + m_type = Type::Region; + } else if (type == "script"sv) { + m_type = Type::Script; + } else if (type == "currency"sv) { + m_type = Type::Currency; + } else { + VERIFY_NOT_REACHED(); + } +} + +StringView DisplayNames::type_string() const +{ + switch (m_type) { + case Type::Language: + return "language"sv; + case Type::Region: + return "region"sv; + case Type::Script: + return "script"sv; + case Type::Currency: + return "currency"sv; + default: + VERIFY_NOT_REACHED(); + } +} + +void DisplayNames::set_fallback(StringView fallback) +{ + if (fallback == "none"sv) { + m_fallback = Fallback::None; + } else if (fallback == "code"sv) { + m_fallback = Fallback::Code; + } else { + VERIFY_NOT_REACHED(); + } +} + +StringView DisplayNames::fallback_string() const +{ + switch (m_fallback) { + case Fallback::None: + return "none"sv; + case Fallback::Code: + return "code"sv; + default: + VERIFY_NOT_REACHED(); + } +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DisplayNames.h b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNames.h new file mode 100644 index 0000000000..874c135450 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNames.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace JS::Intl { + +class DisplayNames final : public Object { + JS_OBJECT(DisplayNames, Object); + + enum class Style { + Invalid, + Narrow, + Short, + Long, + }; + + enum class Type { + Invalid, + Language, + Region, + Script, + Currency, + }; + + enum class Fallback { + Invalid, + None, + Code, + }; + +public: + DisplayNames(Object& prototype); + virtual ~DisplayNames() override = default; + + String const& locale() const { return m_locale; } + void set_locale(String locale) { m_locale = move(locale); } + + Style style() const { return m_style; } + void set_style(StringView style); + StringView style_string() const; + + Type type() const { return m_type; } + void set_type(StringView type); + StringView type_string() const; + + Fallback fallback() const { return m_fallback; } + void set_fallback(StringView fallback); + StringView fallback_string() const; + +private: + String m_locale; // [[Locale]] + Style m_style { Style::Invalid }; // [[Style]] + Type m_type { Type::Invalid }; // [[Type]] + Fallback m_fallback { Fallback::Invalid }; // [[Fallback]] +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp new file mode 100644 index 0000000000..44600b1861 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace JS::Intl { + +// 12.2 The Intl.DisplayNames Constructor, https://tc39.es/ecma402/#sec-intl-displaynames-constructor +DisplayNamesConstructor::DisplayNamesConstructor(GlobalObject& global_object) + : NativeFunction(vm().names.DisplayNames.as_string(), *global_object.function_prototype()) +{ +} + +void DisplayNamesConstructor::initialize(GlobalObject& global_object) +{ + NativeFunction::initialize(global_object); + + auto& vm = this->vm(); + + // 12.3.1 Intl.DisplayNames.prototype, https://tc39.es/ecma402/#sec-Intl.DisplayNames.prototype + define_direct_property(vm.names.prototype, global_object.intl_display_names_prototype(), 0); + define_direct_property(vm.names.length, Value(2), Attribute::Configurable); +} + +// 12.2.1 Intl.DisplayNames ( locales, options ), https://tc39.es/ecma402/#sec-Intl.DisplayNames +Value DisplayNamesConstructor::call() +{ + // 1. If NewTarget is undefined, throw a TypeError exception. + vm().throw_exception(global_object(), ErrorType::ConstructorWithoutNew, "Intl.DisplayNames"); + return {}; +} + +// 12.2.1 Intl.DisplayNames ( locales, options ), https://tc39.es/ecma402/#sec-Intl.DisplayNames +Value DisplayNamesConstructor::construct(FunctionObject& new_target) +{ + auto& vm = this->vm(); + auto& global_object = this->global_object(); + + // 2. Let displayNames be ? OrdinaryCreateFromConstructor(NewTarget, "%DisplayNames.prototype%", « [[InitializedDisplayNames]], [[Locale]], [[Style]], [[Type]], [[Fallback]], [[Fields]] »). + auto* display_names = ordinary_create_from_constructor(global_object, new_target, &GlobalObject::intl_display_names_prototype); + if (vm.exception()) + return {}; + + // 28. Return displayNames. + return display_names; +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.h b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.h new file mode 100644 index 0000000000..5ea50241c6 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace JS::Intl { + +class DisplayNamesConstructor final : public NativeFunction { + JS_OBJECT(DisplayNamesConstructor, NativeFunction); + +public: + explicit DisplayNamesConstructor(GlobalObject&); + virtual void initialize(GlobalObject&) override; + virtual ~DisplayNamesConstructor() override = default; + + virtual Value call() override; + virtual Value construct(FunctionObject& new_target) override; + +private: + virtual bool has_constructor() const override { return true; } +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp new file mode 100644 index 0000000000..d002c6c1e2 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace JS::Intl { + +// 12.4 Properties of the Intl.DisplayNames Prototype Object, https://tc39.es/ecma402/#sec-properties-of-intl-displaynames-prototype-object +DisplayNamesPrototype::DisplayNamesPrototype(GlobalObject& global_object) + : Object(*global_object.object_prototype()) +{ +} + +void DisplayNamesPrototype::initialize(GlobalObject& global_object) +{ + Object::initialize(global_object); + + auto& vm = this->vm(); + + // 12.4.2 Intl.DisplayNames.prototype[ @@toStringTag ], https://tc39.es/ecma402/#sec-Intl.DisplayNames.prototype-@@tostringtag + define_direct_property(*vm.well_known_symbol_to_string_tag(), js_string(vm, "Intl.DisplayNames"), Attribute::Configurable); +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.h b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.h new file mode 100644 index 0000000000..dc829cda90 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2021, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace JS::Intl { + +class DisplayNamesPrototype final : public Object { + JS_OBJECT(DisplayNamesPrototype, Object); + +public: + explicit DisplayNamesPrototype(GlobalObject&); + virtual void initialize(GlobalObject&) override; + virtual ~DisplayNamesPrototype() override = default; +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/Intl/Intl.cpp b/Userland/Libraries/LibJS/Runtime/Intl/Intl.cpp index 8165cb8cd7..8829143e68 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/Intl.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/Intl.cpp @@ -5,6 +5,7 @@ */ #include +#include #include namespace JS::Intl { @@ -23,6 +24,9 @@ void Intl::initialize(GlobalObject& global_object) // 8.1.1 Intl[ @@toStringTag ], https://tc39.es/ecma402/#sec-Intl-toStringTag define_direct_property(*vm.well_known_symbol_to_string_tag(), js_string(vm, "Intl"), Attribute::Configurable); + + u8 attr = Attribute::Writable | Attribute::Configurable; + define_direct_property(vm.names.DisplayNames, global_object.intl_display_names_constructor(), attr); } } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/DisplayNames/DisplayNames.@@toStringTag.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/DisplayNames/DisplayNames.@@toStringTag.js new file mode 100644 index 0000000000..2f393731c3 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/DisplayNames/DisplayNames.@@toStringTag.js @@ -0,0 +1,3 @@ +test("basic functionality", () => { + expect(Intl.DisplayNames.prototype[Symbol.toStringTag]).toBe("Intl.DisplayNames"); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/DisplayNames/DisplayNames.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/DisplayNames/DisplayNames.js new file mode 100644 index 0000000000..8f1607575c --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/DisplayNames/DisplayNames.js @@ -0,0 +1,13 @@ +describe("errors", () => { + test("called without new", () => { + expect(() => { + Intl.DisplayNames(); + }).toThrowWithMessage(TypeError, "Intl.DisplayNames constructor must be called with 'new'"); + }); +}); + +describe("normal behavior", () => { + test("length is 2", () => { + expect(Intl.DisplayNames).toHaveLength(2); + }); +});