[ES6] Implement String.prototype.normalize
https://bugs.webkit.org/show_bug.cgi?id=150094

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

This patch implements String.prototype.normalize leveraging ICU.
It can provide the feature applying {NFC, NFD, NFKC, NFKD} normalization to a given string.

* runtime/StringPrototype.cpp:
(JSC::StringPrototype::finishCreation):
(JSC::normalize):
(JSC::stringProtoFuncNormalize):
* tests/es6.yaml:
* tests/stress/string-normalize.js: Added.
(unicode):
(shouldBe):
(shouldThrow):
(normalizeTest):

LayoutTests:

* js/Object-getOwnPropertyNames-expected.txt:
* js/script-tests/Object-getOwnPropertyNames.js:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@191235 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/tests/stress/string-normalize.js b/Source/JavaScriptCore/tests/stress/string-normalize.js
new file mode 100644
index 0000000..f9dae494
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/string-normalize.js
@@ -0,0 +1,120 @@
+function unicode(value) {
+    return value.split('').map((val) => "\\u" + ("0000" + val.charCodeAt(0).toString(16)).slice(-4)).join('');
+}
+
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${unicode(String(actual))}`);
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+shouldBe(String.prototype.hasOwnProperty('normalize'), true);
+shouldBe(String.prototype.hasOwnProperty.length, 1);
+shouldBe(Object.getOwnPropertyDescriptor(String.prototype, 'normalize').writable, true);
+shouldBe(Object.getOwnPropertyDescriptor(String.prototype, 'normalize').enumerable, false);
+shouldBe(Object.getOwnPropertyDescriptor(String.prototype, 'normalize').configurable, true);
+
+shouldThrow(() => {
+    "Test".normalize("Invalid");
+}, `RangeError: argument does not match any normalization form`);
+
+function normalizeTest(original, defaultValue, nfc, nfd, nfkc, nfkd) {
+    shouldBe(original.normalize(), defaultValue);
+    shouldBe(original.normalize("NFC"), nfc);
+    shouldBe(original.normalize("NFD"), nfd);
+    shouldBe(original.normalize("NFKC"), nfkc);
+    shouldBe(original.normalize("NFKD"), nfkd);
+}
+
+{
+    let text = "Cocoa";
+    normalizeTest(text, text, text, text, text, text);
+}
+
+{
+    // うさぎ
+    // \u3046\u3055\u304e
+    let text = "\u3046\u3055\u304e";
+    normalizeTest(text, text, text, "\u3046\u3055\u304d\u3099", text, "\u3046\u3055\u304d\u3099");
+}
+
+{
+    // é
+    let text = "\u00e9";
+    normalizeTest(text, text, text, "\u0065\u0301", text, "\u0065\u0301");
+}
+
+{
+    // http://unicode.org/faq/normalization.html#6
+    let text = "\u03d3";
+    normalizeTest(text, text, text, "\u03d2\u0301", "\u038e", "\u03a5\u0301");
+}
+{
+    // http://unicode.org/faq/normalization.html#6
+    let text = "\u03d4";
+    normalizeTest(text, text, text, "\u03d2\u0308", "\u03ab", "\u03a5\u0308");
+}
+{
+    // http://unicode.org/faq/normalization.html#6
+    let text = "\u1e9b";
+    normalizeTest(text, text, text, "\u017f\u0307", "\u1e61", "\u0073\u0307");
+}
+
+{
+    // http://unicode.org/faq/normalization.html#6
+    let text = "\u1e9b";
+    normalizeTest(text, text, text, "\u017f\u0307", "\u1e61", "\u0073\u0307");
+}
+
+{
+    // http://unicode.org/faq/normalization.html#12
+    normalizeTest("\ud834\udd60",
+            "\ud834\udd58\ud834\udd65\ud834\udd6e",
+            "\ud834\udd58\ud834\udd65\ud834\udd6e",
+            "\ud834\udd58\ud834\udd65\ud834\udd6e",
+            "\ud834\udd58\ud834\udd65\ud834\udd6e",
+            "\ud834\udd58\ud834\udd65\ud834\udd6e");
+    normalizeTest("\uFB2C",
+            "\u05e9\u05bc\u05c1",
+            "\u05e9\u05bc\u05c1",
+            "\u05e9\u05bc\u05c1",
+            "\u05e9\u05bc\u05c1",
+            "\u05e9\u05bc\u05c1",
+            "\u05e9\u05bc\u05c1"
+            );
+    normalizeTest("\u0390",
+            "\u0390",
+            "\u0390",
+            "\u03b9\u0308\u0301",
+            "\u0390",
+            "\u03b9\u0308\u0301"
+            );
+    normalizeTest("\u1F82",
+            "\u1f82",
+            "\u1f82",
+            "\u03b1\u0313\u0300\u0345",
+            "\u1f82",
+            "\u03b1\u0313\u0300\u0345"
+            );
+    normalizeTest("\uFDFA",
+            "\ufdfa",
+            "\ufdfa",
+            "\ufdfa",
+            "\u0635\u0644\u0649\u0020\u0627\u0644\u0644\u0647\u0020\u0639\u0644\u064a\u0647\u0020\u0648\u0633\u0644\u0645",
+            "\u0635\u0644\u0649\u0020\u0627\u0644\u0644\u0647\u0020\u0639\u0644\u064a\u0647\u0020\u0648\u0633\u0644\u0645"
+            );
+}