浏览代码

LibJS: Implement get Intl.Collator.prototype.compare

Idan Horowitz 3 年之前
父节点
当前提交
6558f4ae6b

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

@@ -91,5 +91,10 @@ StringView Collator::case_first_string() const
         VERIFY_NOT_REACHED();
     }
 }
+void Collator::visit_edges(Visitor& visitor)
+{
+    Base::visit_edges(visitor);
+    visitor.visit(m_bound_compare);
+}
 
 }

+ 14 - 7
Userland/Libraries/LibJS/Runtime/Intl/Collator.h

@@ -9,6 +9,7 @@
 #include <AK/Array.h>
 #include <AK/String.h>
 #include <AK/StringView.h>
+#include <LibJS/Runtime/Intl/CollatorCompareFunction.h>
 #include <LibJS/Runtime/Object.h>
 
 namespace JS::Intl {
@@ -69,14 +70,20 @@ public:
     bool numeric() const { return m_numeric; }
     void set_numeric(bool numeric) { m_numeric = numeric; }
 
+    CollatorCompareFunction* bound_compare() const { return m_bound_compare; }
+    void set_bound_compare(CollatorCompareFunction* bound_compare) { m_bound_compare = bound_compare; }
+
 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]]
+    virtual void visit_edges(Visitor&) override;
+
+    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]]
+    CollatorCompareFunction* m_bound_compare { nullptr }; // [[BoundCompare]]
 };
 
 }

+ 23 - 0
Userland/Libraries/LibJS/Runtime/Intl/CollatorPrototype.cpp

@@ -6,6 +6,7 @@
 
 #include <LibJS/Runtime/GlobalObject.h>
 #include <LibJS/Runtime/Intl/Collator.h>
+#include <LibJS/Runtime/Intl/CollatorCompareFunction.h>
 #include <LibJS/Runtime/Intl/CollatorPrototype.h>
 
 namespace JS::Intl {
@@ -26,9 +27,31 @@ void CollatorPrototype::initialize(GlobalObject& global_object)
     define_direct_property(*vm.well_known_symbol_to_string_tag(), js_string(vm, "Intl.Collator"), Attribute::Configurable);
 
     u8 attr = Attribute::Writable | Attribute::Configurable;
+    define_native_accessor(vm.names.compare, compare_getter, {}, attr);
     define_native_function(vm.names.resolvedOptions, resolved_options, 0, attr);
 }
 
+// 10.3.3 get Intl.Collator.prototype.compare, https://tc39.es/ecma402/#sec-intl.collator.prototype.compare
+JS_DEFINE_NATIVE_FUNCTION(CollatorPrototype::compare_getter)
+{
+    // 1. Let collator be the this value.
+    // 2. Perform ? RequireInternalSlot(collator, [[InitializedCollator]]).
+    auto* collator = TRY(typed_this_object(global_object));
+
+    // 3. If collator.[[BoundCompare]] is undefined, then
+    if (!collator->bound_compare()) {
+        // a. Let F be a new built-in function object as defined in 10.3.3.1.
+        // b. Set F.[[Collator]] to collator.
+        auto* function = CollatorCompareFunction::create(global_object, *collator);
+
+        // c. Set collator.[[BoundCompare]] to F.
+        collator->set_bound_compare(function);
+    }
+
+    // 4. Return collator.[[BoundCompare]].
+    return collator->bound_compare();
+}
+
 // 10.3.4 Intl.Collator.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.collator.prototype.resolvedoptions
 JS_DEFINE_NATIVE_FUNCTION(CollatorPrototype::resolved_options)
 {

+ 1 - 0
Userland/Libraries/LibJS/Runtime/Intl/CollatorPrototype.h

@@ -20,6 +20,7 @@ public:
     virtual ~CollatorPrototype() override = default;
 
 private:
+    JS_DECLARE_NATIVE_FUNCTION(compare_getter);
     JS_DECLARE_NATIVE_FUNCTION(resolved_options);
 };
 

+ 41 - 0
Userland/Libraries/LibJS/Tests/builtins/Intl/Collator/Collator.prototype.compare.js

@@ -0,0 +1,41 @@
+describe("correct behavior", () => {
+    test("length is 2", () => {
+        expect(new Intl.Collator().compare).toHaveLength(2);
+    });
+
+    test("basic functionality", () => {
+        const collator = new Intl.Collator();
+        expect(collator.compare("", "")).toBe(0);
+        expect(collator.compare("a", "a")).toBe(0);
+        expect(collator.compare("6", "6")).toBe(0);
+
+        function compareBoth(a, b) {
+            const aTob = collator.compare(a, b);
+            const bToa = collator.compare(b, a);
+
+            expect(aTob > 0).toBeTrue();
+            expect(aTob).toBe(-bToa);
+        }
+
+        compareBoth("a", "");
+        compareBoth("1", "");
+        compareBoth("a", "A");
+        compareBoth("7", "3");
+        compareBoth("0000", "0");
+
+        expect(collator.compare("undefined")).toBe(0);
+        expect(collator.compare("undefined", undefined)).toBe(0);
+
+        expect(collator.compare("null", null)).toBe(0);
+        expect(collator.compare("null", undefined)).not.toBe(0);
+        expect(collator.compare("null") < 0).toBeTrue();
+    });
+
+    test("UTF-16", () => {
+        const collator = new Intl.Collator();
+        const string = "😀😀";
+        expect(collator.compare(string, "😀😀")).toBe(0);
+        expect(collator.compare(string, "\ud83d") > 0);
+        expect(collator.compare(string, "😀😀s") < 0);
+    });
+});