diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index 63f95930ff..7a7c4295af 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -253,6 +253,7 @@ namespace JS { P(length) \ P(link) \ P(load) \ + P(localeCompare) \ P(log) \ P(log1p) \ P(log2) \ diff --git a/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp b/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp index eb9f28da53..7ce3d70df7 100644 --- a/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp @@ -153,6 +153,7 @@ void StringPrototype::initialize(GlobalObject& global_object) define_native_function(vm.names.strike, strike, 0, attr); define_native_function(vm.names.sub, sub, 0, attr); define_native_function(vm.names.sup, sup, 0, attr); + define_native_function(vm.names.localeCompare, locale_compare, 1, attr); define_native_function(*vm.well_known_symbol_iterator(), symbol_iterator, 0, attr); } @@ -1202,4 +1203,25 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::sup) return create_html(global_object, vm.this_value(global_object), "sup", String::empty(), Value()); } +// 22.1.3.10 String.prototype.localeCompare ( that [ , reserved1 [ , reserved2 ] ] ), https://tc39.es/ecma262/#sec-string.prototype.localecompare +// NOTE: This is the minimum localeCompare implementation for engines without ECMA-402. +JS_DEFINE_NATIVE_FUNCTION(StringPrototype::locale_compare) +{ + auto string = ak_string_from(vm, global_object); + if (!string.has_value()) + return {}; + + auto that_string = vm.argument(0).to_string(global_object); + if (vm.exception()) + return {}; + + // FIXME: Actually compare the string not just according to their bits. + if (string == that_string) + return Value(0); + if (string.value() < that_string) + return Value(-1); + + return Value(1); +} + } diff --git a/Userland/Libraries/LibJS/Runtime/StringPrototype.h b/Userland/Libraries/LibJS/Runtime/StringPrototype.h index 92c6dc00a0..fdb2438238 100644 --- a/Userland/Libraries/LibJS/Runtime/StringPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/StringPrototype.h @@ -69,6 +69,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(strike); JS_DECLARE_NATIVE_FUNCTION(sub); JS_DECLARE_NATIVE_FUNCTION(sup); + JS_DECLARE_NATIVE_FUNCTION(locale_compare); JS_DECLARE_NATIVE_FUNCTION(symbol_iterator); }; diff --git a/Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.localeCompare.js b/Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.localeCompare.js new file mode 100644 index 0000000000..6762638b78 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.localeCompare.js @@ -0,0 +1,39 @@ +test("basic functionality", () => { + expect(String.prototype.localeCompare).toHaveLength(1); + + expect("".localeCompare("")).toBe(0); + expect("a".localeCompare("a")).toBe(0); + expect("6".localeCompare("6")).toBe(0); + + function compareBoth(a, b) { + const aTob = a.localeCompare(b); + const bToa = b.localeCompare(a); + + expect(aTob).toBe(1); + expect(aTob).toBe(-bToa); + } + + compareBoth("a", ""); + compareBoth("1", ""); + compareBoth("a", "A"); + compareBoth("7", "3"); + compareBoth("0000", "0"); + + expect("undefined".localeCompare()).toBe(0); + expect("undefined".localeCompare(undefined)).toBe(0); + + expect("null".localeCompare(null)).toBe(0); + expect("null".localeCompare(undefined)).not.toBe(0); + expect("null".localeCompare()).toBe(-1); + + expect(() => { + String.prototype.localeCompare.call(undefined, undefined); + }).toThrowWithMessage(TypeError, "undefined cannot be converted to an object"); +}); + +test("UTF-16", () => { + var s = "😀😀"; + expect(s.localeCompare("😀😀")).toBe(0); + expect(s.localeCompare("\ud83d")).toBe(1); + expect(s.localeCompare("😀😀s")).toBe(-1); +});