]> git.kaiwu.me - njs.git/commitdiff
Fixed String.fromCodePoint(), broken after 0b82f1c9268c.
authorValentin Bartenev <vbart@nginx.com>
Sat, 27 Jul 2019 18:12:32 +0000 (21:12 +0300)
committerValentin Bartenev <vbart@nginx.com>
Sat, 27 Jul 2019 18:12:32 +0000 (21:12 +0300)
As it turned out, fromCodePoint() has shared the same handler with
fromCharCode(), but according to the specification these functions
have different semantics.  As a result, before 0b82f1c9268c both
functions had behaviour of fromCodePoint(), while after the change
they had behaviour of fromCharCode().

So, the fix is to revert back the code used before the change,
but only for fromCodePoint().

njs/njs_string.c
njs/test/njs_unit_test.c

index 50fc497bfedb0a96f86191f0f8f9c52634094d6c..19046f36fb110b59c7fe132016f8ba6febf2db6c 100644 (file)
@@ -58,6 +58,8 @@ static void njs_string_slice_args(njs_slice_prop_t *slice, njs_value_t *args,
     nxt_uint_t nargs);
 static njs_ret_t njs_string_from_char_code(njs_vm_t *vm,
     njs_value_t *args, nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_string_from_code_point(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
 static njs_ret_t njs_string_bytes_from(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused);
 static njs_ret_t njs_string_bytes_from_array(njs_vm_t *vm,
@@ -608,7 +610,7 @@ static const njs_object_prop_t  njs_string_constructor_properties[] =
     {
         .type = NJS_METHOD,
         .name = njs_string("fromCodePoint"),
-        .value = njs_native_function(njs_string_from_char_code, 0),
+        .value = njs_native_function(njs_string_from_code_point, 0),
         .writable = 1,
         .configurable = 1,
     },
@@ -1729,6 +1731,62 @@ njs_string_from_char_code(njs_vm_t *vm, njs_value_t *args,
 }
 
 
+static njs_ret_t
+njs_string_from_code_point(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    u_char      *p;
+    double      num;
+    size_t      size;
+    int32_t     code;
+    njs_ret_t   ret;
+    nxt_uint_t  i;
+
+    for (i = 1; i < nargs; i++) {
+        if (!njs_is_numeric(&args[i])) {
+            ret = njs_value_to_numeric(vm, &args[i], &args[i]);
+            if (ret != NXT_OK) {
+                return ret;
+            }
+        }
+    }
+
+    size = 0;
+
+    for (i = 1; i < nargs; i++) {
+        num = njs_number(&args[i]);
+        if (isnan(num)) {
+            goto range_error;
+        }
+
+        code = num;
+
+        if (code != num || code < 0 || code >= 0x110000) {
+            goto range_error;
+        }
+
+        size += nxt_utf8_size(code);
+    }
+
+    p = njs_string_alloc(vm, &vm->retval, size, nargs - 1);
+    if (nxt_slow_path(p == NULL)) {
+        return NXT_ERROR;
+    }
+
+    for (i = 1; i < nargs; i++) {
+        p = nxt_utf8_encode(p, njs_number(&args[i]));
+    }
+
+    return NXT_OK;
+
+range_error:
+
+    njs_range_error(vm, NULL);
+
+    return NXT_ERROR;
+}
+
+
 static njs_ret_t
 njs_string_prototype_index_of(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
index 6bab1984f6492dc755cd222967c0c2716b622ddc..42123720e5150e473439ac011a16cc1c45f991c2 100644 (file)
@@ -5348,20 +5348,32 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("String.fromCharCode('_').charCodeAt(0)"),
       nxt_string("0") },
 
+    { nxt_string("String.fromCodePoint('_')"),
+      nxt_string("RangeError") },
+
     { nxt_string("String.fromCharCode(65.14)"),
       nxt_string("A") },
 
+    { nxt_string("String.fromCodePoint(3.14)"),
+      nxt_string("RangeError") },
+
     { nxt_string("String.fromCharCode(65.14 + 65536)"),
       nxt_string("A") },
 
+    { nxt_string("String.fromCodePoint(65 + 65536)"),
+      nxt_string("𐁁") },
+
     { nxt_string("String.fromCharCode(2**53 + 10)"),
       nxt_string("\n") },
 
-    { nxt_string("String.fromCharCode(65, 90)"),
-      nxt_string("AZ") },
+    { nxt_string("String.fromCodePoint(1114111 + 1)"),
+      nxt_string("RangeError") },
 
-    { nxt_string("String.fromCharCode(945, 946, 947)"),
-      nxt_string("αβγ") },
+    { nxt_string("String.fromCharCode(65, 90) + String.fromCodePoint(65, 90)"),
+      nxt_string("AZAZ") },
+
+    { nxt_string("String.fromCharCode(945, 946, 947) + String.fromCodePoint(945, 946, 947)"),
+      nxt_string("αβγαβγ") },
 
     { nxt_string("(function() {"
                  "    var n;"
@@ -5373,6 +5385,18 @@ static njs_unit_test_t  njs_test[] =
                  "})()"),
       nxt_string("65536") },
 
+#if (!NXT_HAVE_MEMORY_SANITIZER) /* very long test under MSAN */
+    { nxt_string("(function() {"
+                 "    var n;"
+                 "    for (n = 0; n <= 1114111; n++) {"
+                 "        if (String.fromCodePoint(n).codePointAt(0) !== n)"
+                 "            return n;"
+                 "    }"
+                 "    return -1"
+                 "})()"),
+      nxt_string("-1") },
+#endif
+
     { nxt_string("var a = 'abcdef'; function f(a) {"
                  "return a.slice(a.indexOf('cd')) } f(a)"),
       nxt_string("cdef") },
@@ -5579,9 +5603,10 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("['ȿ', 'Ȿ', 'ȿ'.toUpperCase(), 'Ȿ'.toLowerCase()].map((v)=>v.toUTF8().length)"),
       nxt_string("2,3,3,2") },
 
+#if (!NXT_HAVE_MEMORY_SANITIZER) /* very long tests under MSAN */
     { nxt_string("var a = [], code;"
-                 "for (code = 0; code < 65536; code++) {"
-                 "    var s = String.fromCharCode(code);"
+                 "for (code = 0; code <= 1114111; code++) {"
+                 "    var s = String.fromCodePoint(code);"
                  "    var n = s.toUpperCase();"
                  "    if (s != n && s != n.toLowerCase())"
                  "        a.push(code);"
@@ -5589,13 +5614,14 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("181,305,383,453,456,459,498,837,962,976,977,981,982,1008,1009,1013,7296,7297,7298,7299,7300,7301,7302,7303,7304,7835,8126") },
 
     { nxt_string("var a = [], code;"
-                 "for (code = 0; code < 65536; code++) {"
-                 "    var s = String.fromCharCode(code);"
+                 "for (code = 0; code <= 1114111; code++) {"
+                 "    var s = String.fromCodePoint(code);"
                  "    var n = s.toLowerCase();"
                  "    if (s != n && s != n.toUpperCase())"
                  "        a.push(code);"
                  "} a"),
       nxt_string("304,453,456,459,498,1012,7838,8486,8490,8491") },
+#endif
 
     { nxt_string("'abc'.trim()"),
       nxt_string("abc") },