瀏覽代碼

LibJS: Implement a nearly empty Intl.Collator object

This adds plumbing for the Intl.Collator object, constructor, and
prototype.
Timothy Flynn 3 年之前
父節點
當前提交
4a3e142d55

+ 3 - 0
Userland/Libraries/LibJS/CMakeLists.txt

@@ -88,6 +88,9 @@ set(SOURCES
     Runtime/GlobalObject.cpp
     Runtime/IndexedProperties.cpp
     Runtime/Intl/AbstractOperations.cpp
+    Runtime/Intl/Collator.cpp
+    Runtime/Intl/CollatorConstructor.cpp
+    Runtime/Intl/CollatorPrototype.cpp
     Runtime/Intl/DateTimeFormat.cpp
     Runtime/Intl/DateTimeFormatConstructor.cpp
     Runtime/Intl/DateTimeFormatFunction.cpp

+ 1 - 0
Userland/Libraries/LibJS/Forward.h

@@ -68,6 +68,7 @@
     __JS_ENUMERATE(Float64Array, float64_array, Float64ArrayPrototype, Float64ArrayConstructor, double)
 
 #define JS_ENUMERATE_INTL_OBJECTS                                                                        \
+    __JS_ENUMERATE(Collator, collator, CollatorPrototype, CollatorConstructor)                           \
     __JS_ENUMERATE(DateTimeFormat, date_time_format, DateTimeFormatPrototype, DateTimeFormatConstructor) \
     __JS_ENUMERATE(DisplayNames, display_names, DisplayNamesPrototype, DisplayNamesConstructor)          \
     __JS_ENUMERATE(ListFormat, list_format, ListFormatPrototype, ListFormatConstructor)                  \

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

@@ -50,6 +50,8 @@
 #include <LibJS/Runtime/GeneratorPrototype.h>
 #include <LibJS/Runtime/GlobalEnvironment.h>
 #include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Intl/CollatorConstructor.h>
+#include <LibJS/Runtime/Intl/CollatorPrototype.h>
 #include <LibJS/Runtime/Intl/DateTimeFormatConstructor.h>
 #include <LibJS/Runtime/Intl/DateTimeFormatPrototype.h>
 #include <LibJS/Runtime/Intl/DisplayNamesConstructor.h>

+ 95 - 0
Userland/Libraries/LibJS/Runtime/Intl/Collator.cpp

@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2022, Tim Flynn <trflynn89@pm.me>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Runtime/Intl/Collator.h>
+
+namespace JS::Intl {
+
+// 10 Collator Objects, https://tc39.es/ecma402/#collator-objects
+Collator::Collator(Object& prototype)
+    : Object(prototype)
+{
+}
+
+void Collator::set_usage(StringView type)
+{
+    if (type == "sort"sv)
+        m_usage = Usage::Sort;
+    else if (type == "search"sv)
+        m_usage = Usage::Search;
+    else
+        VERIFY_NOT_REACHED();
+}
+
+StringView Collator::usage_string() const
+{
+    switch (m_usage) {
+    case Usage::Sort:
+        return "sort"sv;
+    case Usage::Search:
+        return "search"sv;
+    default:
+        VERIFY_NOT_REACHED();
+    }
+}
+
+void Collator::set_sensitivity(StringView type)
+{
+    if (type == "base"sv)
+        m_sensitivity = Sensitivity::Base;
+    else if (type == "accent"sv)
+        m_sensitivity = Sensitivity::Accent;
+    else if (type == "case"sv)
+        m_sensitivity = Sensitivity::Case;
+    else if (type == "variant"sv)
+        m_sensitivity = Sensitivity::Variant;
+    else
+        VERIFY_NOT_REACHED();
+}
+
+StringView Collator::sensitivity_string() const
+{
+    switch (m_sensitivity) {
+    case Sensitivity::Base:
+        return "base"sv;
+    case Sensitivity::Accent:
+        return "accent"sv;
+    case Sensitivity::Case:
+        return "case"sv;
+    case Sensitivity::Variant:
+        return "variant"sv;
+    default:
+        VERIFY_NOT_REACHED();
+    }
+}
+
+void Collator::set_case_first(StringView case_first)
+{
+    if (case_first == "upper"sv)
+        m_case_first = CaseFirst::Upper;
+    else if (case_first == "lower"sv)
+        m_case_first = CaseFirst::Lower;
+    else if (case_first == "false"sv)
+        m_case_first = CaseFirst::False;
+    else
+        VERIFY_NOT_REACHED();
+}
+
+StringView Collator::case_first_string() const
+{
+    switch (m_case_first) {
+    case CaseFirst::Upper:
+        return "upper"sv;
+    case CaseFirst::Lower:
+        return "lower"sv;
+    case CaseFirst::False:
+        return "false"sv;
+    default:
+        VERIFY_NOT_REACHED();
+    }
+}
+
+}

+ 82 - 0
Userland/Libraries/LibJS/Runtime/Intl/Collator.h

@@ -0,0 +1,82 @@
+/*
+ * 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>
+
+namespace JS::Intl {
+
+class Collator final : public Object {
+    JS_OBJECT(Collator, Object);
+
+public:
+    enum class Usage {
+        Sort,
+        Search,
+    };
+
+    enum class Sensitivity {
+        Base,
+        Accent,
+        Case,
+        Variant,
+    };
+
+    enum class CaseFirst {
+        Upper,
+        Lower,
+        False,
+    };
+
+    static constexpr auto relevant_extension_keys()
+    {
+        // 10.2.3 Internal Slots, https://tc39.es/ecma402/#sec-intl-collator-internal-slots
+        // The value of the [[RelevantExtensionKeys]] internal slot is a List that must include the element "co", may include any or all of the elements "kf" and "kn", and must not include any other elements.
+        return AK::Array { "co"sv, "kf"sv, "kn"sv };
+    }
+
+    explicit Collator(Object& prototype);
+    virtual ~Collator() override = default;
+
+    String const& locale() const { return m_locale; }
+    void set_locale(String locale) { m_locale = move(locale); }
+
+    Usage usage() const { return m_usage; }
+    void set_usage(StringView usage);
+    StringView usage_string() const;
+
+    Sensitivity sensitivity() const { return m_sensitivity; }
+    void set_sensitivity(StringView sensitivity);
+    StringView sensitivity_string() const;
+
+    CaseFirst case_first() const { return m_case_first; }
+    void set_case_first(StringView case_first);
+    StringView case_first_string() const;
+
+    String const& collation() const { return m_collation; }
+    void set_collation(String collation) { m_collation = move(collation); }
+
+    bool ignore_punctuation() const { return m_ignore_punctuation; }
+    void set_ignore_punctuation(bool ignore_punctuation) { m_ignore_punctuation = ignore_punctuation; }
+
+    bool numeric() const { return m_numeric; }
+    void set_numeric(bool numeric) { m_numeric = numeric; }
+
+private:
+    String m_locale;                                    // [[Locale]]
+    Usage m_usage { Usage::Sort };                      // [[Usage]]
+    Sensitivity m_sensitivity { Sensitivity::Variant }; // [[Sensitivity]]
+    CaseFirst m_case_first { CaseFirst::False };        // [[CaseFirst]]
+    String m_collation;                                 // [[Collation]]
+    bool m_ignore_punctuation { false };                // [[IgnorePunctuation]]
+    bool m_numeric { false };                           // [[Numeric]]
+};
+
+}

+ 56 - 0
Userland/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp

@@ -0,0 +1,56 @@
+/*
+ * 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/Collator.h>
+#include <LibJS/Runtime/Intl/CollatorConstructor.h>
+
+namespace JS::Intl {
+
+// 10.1 The Intl.Collator Constructor, https://tc39.es/ecma402/#sec-the-intl-collator-constructor
+CollatorConstructor::CollatorConstructor(GlobalObject& global_object)
+    : NativeFunction(vm().names.Collator.as_string(), *global_object.function_prototype())
+{
+}
+
+void CollatorConstructor::initialize(GlobalObject& global_object)
+{
+    NativeFunction::initialize(global_object);
+
+    auto& vm = this->vm();
+
+    // 10.2.1 Intl.Collator.prototype, https://tc39.es/ecma402/#sec-intl.collator.prototype
+    define_direct_property(vm.names.prototype, global_object.intl_collator_prototype(), 0);
+    define_direct_property(vm.names.length, Value(0), Attribute::Configurable);
+}
+
+// 10.1.2 Intl.Collator ( [ locales [ , options ] ] ), https://tc39.es/ecma402/#sec-intl.collator
+ThrowCompletionOr<Value> CollatorConstructor::call()
+{
+    // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget
+    return TRY(construct(*this));
+}
+
+// 10.1.2 Intl.Collator ( [ locales [ , options ] ] ), https://tc39.es/ecma402/#sec-intl.collator
+ThrowCompletionOr<Object*> CollatorConstructor::construct(FunctionObject& new_target)
+{
+    auto& global_object = this->global_object();
+
+    // 2. Let internalSlotsList be « [[InitializedCollator]], [[Locale]], [[Usage]], [[Sensitivity]], [[IgnorePunctuation]], [[Collation]], [[BoundCompare]] ».
+    // 3. If %Collator%.[[RelevantExtensionKeys]] contains "kn", then
+    //     a. Append [[Numeric]] as the last element of internalSlotsList.
+    // 4. If %Collator%.[[RelevantExtensionKeys]] contains "kf", then
+    //     a. Append [[CaseFirst]] as the last element of internalSlotsList.
+
+    // 5. Let collator be ? OrdinaryCreateFromConstructor(newTarget, "%Collator.prototype%", internalSlotsList).
+    auto* collator = TRY(ordinary_create_from_constructor<Collator>(global_object, new_target, &GlobalObject::intl_collator_prototype));
+
+    // 6. Return ? InitializeCollator(collator, locales, options).
+    return collator;
+}
+
+}

+ 28 - 0
Userland/Libraries/LibJS/Runtime/Intl/CollatorConstructor.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 CollatorConstructor final : public NativeFunction {
+    JS_OBJECT(CollatorConstructor, NativeFunction);
+
+public:
+    explicit CollatorConstructor(GlobalObject&);
+    virtual void initialize(GlobalObject&) override;
+    virtual ~CollatorConstructor() override = default;
+
+    virtual ThrowCompletionOr<Value> call() override;
+    virtual ThrowCompletionOr<Object*> construct(FunctionObject& new_target) override;
+
+private:
+    virtual bool has_constructor() const override { return true; }
+};
+
+}

+ 28 - 0
Userland/Libraries/LibJS/Runtime/Intl/CollatorPrototype.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/CollatorPrototype.h>
+
+namespace JS::Intl {
+
+// 10.3 Properties of the Intl.Collator Prototype Object, https://tc39.es/ecma402/#sec-properties-of-the-intl-collator-prototype-object
+CollatorPrototype::CollatorPrototype(GlobalObject& global_object)
+    : PrototypeObject(*global_object.object_prototype())
+{
+}
+
+void CollatorPrototype::initialize(GlobalObject& global_object)
+{
+    Object::initialize(global_object);
+
+    auto& vm = this->vm();
+
+    // 10.3.2 Intl.Collator.prototype [ @@toStringTag ], https://tc39.es/ecma402/#sec-intl.collator.prototype-@@tostringtag
+    define_direct_property(*vm.well_known_symbol_to_string_tag(), js_string(vm, "Intl.Collator"), Attribute::Configurable);
+}
+
+}

+ 23 - 0
Userland/Libraries/LibJS/Runtime/Intl/CollatorPrototype.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/Collator.h>
+#include <LibJS/Runtime/PrototypeObject.h>
+
+namespace JS::Intl {
+
+class CollatorPrototype final : public PrototypeObject<CollatorPrototype, Collator> {
+    JS_PROTOTYPE_OBJECT(CollatorPrototype, Collator, Collator);
+
+public:
+    explicit CollatorPrototype(GlobalObject&);
+    virtual void initialize(GlobalObject&) override;
+    virtual ~CollatorPrototype() override = default;
+};
+
+}

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

@@ -7,6 +7,7 @@
 #include <LibJS/Runtime/Array.h>
 #include <LibJS/Runtime/GlobalObject.h>
 #include <LibJS/Runtime/Intl/AbstractOperations.h>
+#include <LibJS/Runtime/Intl/CollatorConstructor.h>
 #include <LibJS/Runtime/Intl/DateTimeFormatConstructor.h>
 #include <LibJS/Runtime/Intl/DisplayNamesConstructor.h>
 #include <LibJS/Runtime/Intl/Intl.h>
@@ -34,6 +35,7 @@ void Intl::initialize(GlobalObject& global_object)
     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.Collator, global_object.intl_collator_constructor(), attr);
     define_direct_property(vm.names.DateTimeFormat, global_object.intl_date_time_format_constructor(), attr);
     define_direct_property(vm.names.DisplayNames, global_object.intl_display_names_constructor(), attr);
     define_direct_property(vm.names.ListFormat, global_object.intl_list_format_constructor(), attr);

+ 3 - 0
Userland/Libraries/LibJS/Tests/builtins/Intl/Collator/Collator.@@toStringTag.js

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

+ 5 - 0
Userland/Libraries/LibJS/Tests/builtins/Intl/Collator/Collator.js

@@ -0,0 +1,5 @@
+describe("normal behavior", () => {
+    test("length is 0", () => {
+        expect(Intl.Collator).toHaveLength(0);
+    });
+});