Преглед изворни кода

LibJS: Implement a nearly empty Intl.DisplayNames object

This adds plumbing for the Intl.DisplayNames object, constructor, and
prototype.
Timothy Flynn пре 3 година
родитељ
комит
0fb4e8b749

+ 3 - 0
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

+ 2 - 1
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)                           \

+ 2 - 0
Userland/Libraries/LibJS/Runtime/GlobalObject.cpp

@@ -41,6 +41,8 @@
 #include <LibJS/Runtime/GeneratorObjectPrototype.h>
 #include <LibJS/Runtime/GlobalEnvironment.h>
 #include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Intl/DisplayNamesConstructor.h>
+#include <LibJS/Runtime/Intl/DisplayNamesPrototype.h>
 #include <LibJS/Runtime/Intl/Intl.h>
 #include <LibJS/Runtime/IteratorPrototype.h>
 #include <LibJS/Runtime/JSONObject.h>

+ 99 - 0
Userland/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp

@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2021, Tim Flynn <trflynn89@pm.me>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Intl/DisplayNames.h>
+
+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();
+    }
+}
+
+}

+ 65 - 0
Userland/Libraries/LibJS/Runtime/Intl/DisplayNames.h

@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2021, Tim Flynn <trflynn89@pm.me>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/String.h>
+#include <AK/StringView.h>
+#include <LibJS/Runtime/Object.h>
+
+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]]
+};
+
+}

+ 54 - 0
Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp

@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021, 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/DisplayNames.h>
+#include <LibJS/Runtime/Intl/DisplayNamesConstructor.h>
+
+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<TypeError>(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<DisplayNames>(global_object, new_target, &GlobalObject::intl_display_names_prototype);
+    if (vm.exception())
+        return {};
+
+    // 28. Return displayNames.
+    return display_names;
+}
+
+}

+ 28 - 0
Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.h

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021, Tim Flynn <trflynn89@pm.me>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/NativeFunction.h>
+
+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; }
+};
+
+}

+ 30 - 0
Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp

@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2021, Tim Flynn <trflynn89@pm.me>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/TypeCasts.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Intl/DisplayNames.h>
+#include <LibJS/Runtime/Intl/DisplayNamesPrototype.h>
+
+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);
+}
+
+}

+ 22 - 0
Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.h

@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2021, Tim Flynn <trflynn89@pm.me>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/Object.h>
+
+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;
+};
+
+}

+ 4 - 0
Userland/Libraries/LibJS/Runtime/Intl/Intl.cpp

@@ -5,6 +5,7 @@
  */
 
 #include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Intl/DisplayNamesConstructor.h>
 #include <LibJS/Runtime/Intl/Intl.h>
 
 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);
 }
 
 }

+ 3 - 0
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");
+});

+ 13 - 0
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);
+    });
+});