diff options
5 files changed, 84 insertions, 1 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index f9a45402e8..19a26c2631 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -342,6 +342,7 @@ namespace JS { P(nanoseconds) \ P(negated) \ P(next) \ + P(normalize) \ P(notation) \ P(now) \ P(numberingSystem) \ diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h index 9e0ca0ed0b..03a90af47d 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h @@ -293,7 +293,8 @@ M(BadArgCountAtLeastOne, "{}() needs at least one argument") \ M(BadArgCountMany, "{}() needs {} arguments") \ M(NotEnoughMemoryToAllocate, "Not enough memory to allocate {} bytes") \ - M(InvalidEnumerationValue, "Invalid value '{}' for enumeration type '{}'") + M(InvalidEnumerationValue, "Invalid value '{}' for enumeration type '{}'") \ + M(InvalidNormalizationForm, "The normalization form must be one of NFC, NFD, NFKC, NFKD. Got '{}'") namespace JS { diff --git a/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp b/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp index 396ca4897d..f8a55b8e3c 100644 --- a/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp @@ -143,6 +143,7 @@ void StringPrototype::initialize(GlobalObject& global_object) define_native_function(vm.names.at, at, 1, attr); define_native_function(vm.names.match, match, 1, attr); define_native_function(vm.names.matchAll, match_all, 1, attr); + define_native_function(vm.names.normalize, normalize, 0, attr); define_native_function(vm.names.replace, replace, 2, attr); define_native_function(vm.names.replaceAll, replace_all, 2, attr); define_native_function(vm.names.search, search, 1, attr); @@ -803,6 +804,31 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::match_all) return TRY(Value(rx).invoke(global_object, *vm.well_known_symbol_match_all(), js_string(vm, move(string)))); } +// 22.1.3.14 String.prototype.normalize ( [ form ] ), https://tc39.es/ecma262/#sec-string.prototype.normalize +JS_DEFINE_NATIVE_FUNCTION(StringPrototype::normalize) +{ + // 1. Let O be ? RequireObjectCoercible(this value). + // 2. Let S be ? ToString(O). + auto string = TRY(ak_string_from(vm, global_object)); + + // 3. If form is undefined, let f be "NFC". + // 4. Else, let f be ? ToString(form). + String form = "NFC"; + auto form_value = vm.argument(0); + if (!form_value.is_undefined()) + form = TRY(form_value.to_string(global_object)); + + // 5. If f is not one of "NFC", "NFD", "NFKC", or "NFKD", throw a RangeError exception. + if (!form.is_one_of("NFC"sv, "NFD"sv, "NFKC"sv, "NFKD"sv)) + return vm.throw_completion<RangeError>(global_object, ErrorType::InvalidNormalizationForm, form); + + // FIXME: 6. Let ns be the String value that is the result of normalizing S into the normalization form named by f as specified in https://unicode.org/reports/tr15/. + auto ns = string; + + // 7. return ns. + return js_string(vm, move(ns)); +} + // 22.1.3.18 String.prototype.replace ( searchValue, replaceValue ), https://tc39.es/ecma262/#sec-string.prototype.replace JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace) { diff --git a/Userland/Libraries/LibJS/Runtime/StringPrototype.h b/Userland/Libraries/LibJS/Runtime/StringPrototype.h index e33f8bb029..2493e47002 100644 --- a/Userland/Libraries/LibJS/Runtime/StringPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/StringPrototype.h @@ -57,6 +57,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(at); JS_DECLARE_NATIVE_FUNCTION(match); JS_DECLARE_NATIVE_FUNCTION(match_all); + JS_DECLARE_NATIVE_FUNCTION(normalize); JS_DECLARE_NATIVE_FUNCTION(replace); JS_DECLARE_NATIVE_FUNCTION(replace_all); JS_DECLARE_NATIVE_FUNCTION(search); diff --git a/Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.normalize.js b/Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.normalize.js new file mode 100644 index 0000000000..3081cf73af --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.normalize.js @@ -0,0 +1,54 @@ +test("Function length is 0", () => { + expect(String.prototype.normalize).toHaveLength(0); +}); + +test("Function name is normalize", () => { + expect(String.prototype.normalize.name).toBe("normalize"); +}); + +test("Type is function", () => { + expect(typeof String.prototype.normalize).toBe("function"); +}); + +test("Invalid form throws", () => { + expect(() => "foo".normalize("bar")).toThrowWithMessage( + RangeError, + "The normalization form must be one of NFC, NFD, NFKC, NFKD. Got 'bar'" + ); + expect(() => "foo".normalize("NFC1")).toThrowWithMessage( + RangeError, + "The normalization form must be one of NFC, NFD, NFKC, NFKD. Got 'NFC1'" + ); + expect(() => "foo".normalize(null)).toThrowWithMessage( + RangeError, + "The normalization form must be one of NFC, NFD, NFKC, NFKD. Got 'null'" + ); +}); + +test("Invalid object throws", () => { + expect(() => String.prototype.normalize.call(undefined)).toThrowWithMessage( + TypeError, + "undefined cannot be converted to an object" + ); + expect(() => String.prototype.normalize.call(null)).toThrowWithMessage( + TypeError, + "null cannot be converted to an object" + ); +}); + +// Tests below here are skipped due to the function being a stub at the moment +test.skip("Normalization works", () => { + var s = "\u1E9B\u0323"; + + expect(s.normalize("NFC")).toBe("\u1E9B\u0323"); + expect(s.normalize("NFD")).toBe("\u017F\u0323\u0307"); + expect(s.normalize("NFKC")).toBe("\u1E69"); + expect(s.normalize("NFKD")).toBe("\u0073\u0323\u0307"); +}); + +test.skip("Default parameter is NFC", () => { + var s = "\u1E9B\u0323"; + + expect(s.normalize("NFC")).toBe(s.normalize()); + expect(s.normalize("NFC")).toBe(s.normalize(undefined)); +}); |