[njs] Fixed String.fromCodePoint(), broken after 0b82f1c9268c.

Valentin Bartenev vbart at nginx.com
Sat Jul 27 18:13:50 UTC 2019


details:   https://hg.nginx.org/njs/rev/575c018f2206
branches:  
changeset: 1077:575c018f2206
user:      Valentin Bartenev <vbart at nginx.com>
date:      Sat Jul 27 21:12:32 2019 +0300
description:
Fixed String.fromCodePoint(), broken after 0b82f1c9268c.

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().

diffstat:

 njs/njs_string.c         |  60 +++++++++++++++++++++++++++++++++++++++++++++++-
 njs/test/njs_unit_test.c |  44 ++++++++++++++++++++++++++++-------
 2 files changed, 94 insertions(+), 10 deletions(-)

diffs (174 lines):

diff -r cba3bd29f263 -r 575c018f2206 njs/njs_string.c
--- a/njs/njs_string.c	Sat Jul 27 17:03:02 2019 +0300
+++ b/njs/njs_string.c	Sat Jul 27 21:12:32 2019 +0300
@@ -58,6 +58,8 @@ static void njs_string_slice_args(njs_sl
     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_stri
     {
         .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,
     },
@@ -1730,6 +1732,62 @@ njs_string_from_char_code(njs_vm_t *vm, 
 
 
 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)
 {
diff -r cba3bd29f263 -r 575c018f2206 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Sat Jul 27 17:03:02 2019 +0300
+++ b/njs/test/njs_unit_test.c	Sat Jul 27 21:12:32 2019 +0300
@@ -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.fromCharCode(945, 946, 947)"),
-      nxt_string("αβγ") },
+    { nxt_string("String.fromCodePoint(1114111 + 1)"),
+      nxt_string("RangeError") },
+
+    { 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") },


More information about the nginx-devel mailing list