[njs] Aligned Number constructor to the spec.

Dmitry Volyntsev xeioex at nginx.com
Thu Mar 30 03:48:04 UTC 2023


details:   https://hg.nginx.org/njs/rev/8dcea0ba0bf8
branches:  
changeset: 2078:8dcea0ba0bf8
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Wed Mar 29 20:28:33 2023 -0700
description:
Aligned Number constructor to the spec.

Previously, negative hexadecimal numbers were accepted as valid,
whereas they are invalid input for the constructor.
Also previously, the constructor did not accepted positive binary or
octadecimal numbers as valid.

This closes #630 issue on Github.

diffstat:

 src/njs_lexer.c            |   4 +-
 src/njs_number.c           |  57 ++++++++++++++++++++++++++++++++++++++++-----
 src/njs_number.h           |   6 +++-
 src/njs_string.c           |  54 ++++++++++++++++++++++++-------------------
 src/njs_value.h            |   2 +-
 src/njs_value_conversion.h |   2 +-
 src/njs_vmcode.c           |   6 ++--
 src/test/njs_unit_test.c   |  38 +++++++++++++++++++++++++++++-
 8 files changed, 128 insertions(+), 41 deletions(-)

diffs (337 lines):

diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/njs_lexer.c
--- a/src/njs_lexer.c	Mon Mar 27 22:41:27 2023 -0700
+++ b/src/njs_lexer.c	Wed Mar 29 20:28:33 2023 -0700
@@ -933,7 +933,7 @@ njs_lexer_number(njs_lexer_t *lexer, njs
                 goto illegal_token;
             }
 
-            token->number = njs_number_oct_parse(&p, lexer->end);
+            token->number = njs_number_oct_parse(&p, lexer->end, 1);
 
             if (p < lexer->end && (*p == '8' || *p == '9')) {
                 goto illegal_trailer;
@@ -951,7 +951,7 @@ njs_lexer_number(njs_lexer_t *lexer, njs
                 goto illegal_token;
             }
 
-            token->number = njs_number_bin_parse(&p, lexer->end);
+            token->number = njs_number_bin_parse(&p, lexer->end, 1);
 
             if (p < lexer->end && (*p >= '2' && *p <= '9')) {
                 goto illegal_trailer;
diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/njs_number.c
--- a/src/njs_number.c	Mon Mar 27 22:41:27 2023 -0700
+++ b/src/njs_number.c	Wed Mar 29 20:28:33 2023 -0700
@@ -62,7 +62,8 @@ njs_number_dec_parse(const u_char **star
 
 
 double
-njs_number_oct_parse(const u_char **start, const u_char *end)
+njs_number_oct_parse(const u_char **start, const u_char *end,
+    njs_bool_t literal)
 {
     u_char        c;
     double        num;
@@ -78,7 +79,7 @@ njs_number_oct_parse(const u_char **star
         c = *p - '0';
 
         if (njs_slow_path(c > 7)) {
-            if (*p == '_' && (p - _) > 1) {
+            if (literal && *p == '_' && (p - _) > 1) {
                 _ = p;
                 continue;
             }
@@ -96,7 +97,8 @@ njs_number_oct_parse(const u_char **star
 
 
 double
-njs_number_bin_parse(const u_char **start, const u_char *end)
+njs_number_bin_parse(const u_char **start, const u_char *end,
+    njs_bool_t literal)
 {
     u_char        c;
     double        num;
@@ -112,7 +114,7 @@ njs_number_bin_parse(const u_char **star
         c = *p - '0';
 
         if (njs_slow_path(c > 1)) {
-            if (*p == '_' && (p - _) > 1) {
+            if (literal && *p == '_' && (p - _) > 1) {
                 _ = p;
                 continue;
             }
@@ -1030,8 +1032,12 @@ njs_int_t
 njs_number_parse_float(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
-    njs_int_t    ret;
-    njs_value_t  *value, lvalue;
+    double             num;
+    njs_int_t          ret;
+    njs_value_t        *value, lvalue;
+    njs_bool_t         minus;
+    const u_char       *p, *start, *end;
+    njs_string_prop_t  string;
 
     value = njs_lvalue_arg(&lvalue, args, nargs, 1);
 
@@ -1040,7 +1046,44 @@ njs_number_parse_float(njs_vm_t *vm, njs
         return ret;
     }
 
-    njs_set_number(&vm->retval, njs_string_to_number(value, 1));
+    (void) njs_string_trim(value, &string, NJS_TRIM_START);
+
+    p = string.start;
+    end = p + string.size;
+
+    minus = 0;
+
+    if (p == end) {
+        num = NAN;
+        goto done;
+    }
+
+    if (*p == '+') {
+        p++;
+
+    } else if (*p == '-') {
+        p++;
+        minus = 1;
+    }
+
+    start = p;
+    num = njs_number_dec_parse(&p, end, 0);
+
+    if (p == start) {
+        if (p + njs_length("Infinity") > end
+            || memcmp(p, "Infinity", njs_length("Infinity")) != 0)
+        {
+            num = NAN;
+            goto done;
+        }
+
+        num = INFINITY;
+        p += njs_length("Infinity");
+    }
+
+done:
+
+    njs_set_number(&vm->retval, minus ? -num : num);
 
     return NJS_OK;
 }
diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/njs_number.h
--- a/src/njs_number.h	Mon Mar 27 22:41:27 2023 -0700
+++ b/src/njs_number.h	Wed Mar 29 20:28:33 2023 -0700
@@ -16,8 +16,10 @@
 double njs_key_to_index(const njs_value_t *value);
 double njs_number_dec_parse(const u_char **start, const u_char *end,
     njs_bool_t literal);
-double njs_number_oct_parse(const u_char **start, const u_char *end);
-double njs_number_bin_parse(const u_char **start, const u_char *end);
+double njs_number_oct_parse(const u_char **start, const u_char *end,
+    njs_bool_t literal);
+double njs_number_bin_parse(const u_char **start, const u_char *end,
+    njs_bool_t literal);
 double njs_number_hex_parse(const u_char **start, const u_char *end,
     njs_bool_t literal);
 njs_int_t njs_number_to_string(njs_vm_t *vm, njs_value_t *string,
diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/njs_string.c
--- a/src/njs_string.c	Mon Mar 27 22:41:27 2023 -0700
+++ b/src/njs_string.c	Wed Mar 29 20:28:33 2023 -0700
@@ -3874,7 +3874,7 @@ njs_string_prototype_iterator_obj(njs_vm
 
 
 double
-njs_string_to_number(const njs_value_t *value, njs_bool_t parse_float)
+njs_string_to_number(const njs_value_t *value)
 {
     double             num;
     njs_bool_t         minus;
@@ -3889,30 +3889,38 @@ njs_string_to_number(const njs_value_t *
     end = p + string.size;
 
     if (p == end) {
-        return parse_float ? NAN : 0.0;
+        return 0.0;
     }
 
     minus = 0;
 
-    if (*p == '+') {
-        p++;
-
-    } else if (*p == '-') {
-        p++;
-        minus = 1;
-    }
-
-    if (p == end) {
-        return NAN;
-    }
-
-    if (!parse_float
-        && p + 2 < end && p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
+    if (p + 2 < end && p[0] == '0'
+        && (p[1] == 'x' || p[1] == 'X'
+            || p[1] == 'b' || p[1] == 'B'
+            || p[1] == 'o' || p[1] == 'O'))
     {
         p += 2;
-        num = njs_number_hex_parse(&p, end, 0);
+
+        if (p[-1] == 'x' || p[-1] == 'X') {
+            num = njs_number_hex_parse(&p, end, 0);
+
+        } else if (p[-1] == 'b' || p[-1] == 'B') {
+            num = njs_number_bin_parse(&p, end, 0);
+
+        } else {
+            num = njs_number_oct_parse(&p, end, 0);
+        }
 
     } else {
+
+        if (*p == '+') {
+            p++;
+
+        } else if (*p == '-') {
+            p++;
+            minus = 1;
+        }
+
         start = p;
         num = njs_number_dec_parse(&p, end, 0);
 
@@ -3926,14 +3934,12 @@ njs_string_to_number(const njs_value_t *
         }
     }
 
-    if (!parse_float) {
-        while (p < end) {
-            if (!njs_is_whitespace(*p)) {
-                return NAN;
-            }
-
-            p++;
+    while (p < end) {
+        if (!njs_is_whitespace(*p)) {
+            return NAN;
         }
+
+        p++;
     }
 
     return minus ? -num : num;
diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/njs_value.h
--- a/src/njs_value.h	Mon Mar 27 22:41:27 2023 -0700
+++ b/src/njs_value.h	Wed Mar 29 20:28:33 2023 -0700
@@ -1073,7 +1073,7 @@ njs_int_t njs_primitive_value_to_string(
     const njs_value_t *src);
 njs_int_t njs_primitive_value_to_chain(njs_vm_t *vm, njs_chb_t *chain,
     const njs_value_t *src);
-double njs_string_to_number(const njs_value_t *value, njs_bool_t parse_float);
+double njs_string_to_number(const njs_value_t *value);
 njs_int_t njs_int64_to_string(njs_vm_t *vm, njs_value_t *value, int64_t i64);
 
 njs_bool_t njs_string_eq(const njs_value_t *v1, const njs_value_t *v2);
diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/njs_value_conversion.h
--- a/src/njs_value_conversion.h	Mon Mar 27 22:41:27 2023 -0700
+++ b/src/njs_value_conversion.h	Wed Mar 29 20:28:33 2023 -0700
@@ -33,7 +33,7 @@ njs_value_to_number(njs_vm_t *vm, njs_va
         *dst = NAN;
 
         if (njs_is_string(value)) {
-            *dst = njs_string_to_number(value, 0);
+            *dst = njs_string_to_number(value);
         }
 
         return NJS_OK;
diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/njs_vmcode.c
--- a/src/njs_vmcode.c	Mon Mar 27 22:41:27 2023 -0700
+++ b/src/njs_vmcode.c	Wed Mar 29 20:28:33 2023 -0700
@@ -2512,7 +2512,7 @@ again:
     /* If "hv" is a string then "lv" can be a numeric or symbol. */
     if (njs_is_string(hv)) {
         return !njs_is_symbol(lv)
-            && (njs_number(lv) == njs_string_to_number(hv, 0));
+            && (njs_number(lv) == njs_string_to_number(hv));
     }
 
     /* "hv" is an object and "lv" is either a string or a symbol or a numeric. */
@@ -2549,11 +2549,11 @@ njs_primitive_values_compare(njs_vm_t *v
             num2 = njs_number(val2);
 
         } else {
-            num2 = njs_string_to_number(val2, 0);
+            num2 = njs_string_to_number(val2);
         }
 
     } else if (njs_is_numeric(val2)) {
-        num1 = njs_string_to_number(val1, 0);
+        num1 = njs_string_to_number(val1);
         num2 = njs_number(val2);
 
     } else {
diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Mon Mar 27 22:41:27 2023 -0700
+++ b/src/test/njs_unit_test.c	Wed Mar 29 20:28:33 2023 -0700
@@ -994,7 +994,7 @@ static njs_unit_test_t  njs_test[] =
       njs_str("3") },
 
     { njs_str("5 - '-0x2'"),
-      njs_str("7") },
+      njs_str("NaN") },
 
     { njs_str("5 - '\t 0x2 \t'"),
       njs_str("3") },
@@ -13344,12 +13344,48 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("Number(false)"),
       njs_str("0") },
 
+    { njs_str("Number('0b111')"),
+      njs_str("7") },
+
+    { njs_str("Number('0B111')"),
+      njs_str("7") },
+
+    { njs_str("Number('0b1_11')"),
+      njs_str("NaN") },
+
+    { njs_str("Number('-0b111')"),
+      njs_str("NaN") },
+
     { njs_str("Number(123)"),
       njs_str("123") },
 
     { njs_str("Number('123')"),
       njs_str("123") },
 
+    { njs_str("Number('0o123')"),
+      njs_str("83") },
+
+    { njs_str("Number('0O123')"),
+      njs_str("83") },
+
+    { njs_str("Number('0o1_23')"),
+      njs_str("NaN") },
+
+    { njs_str("Number('-0o123')"),
+      njs_str("NaN") },
+
+    { njs_str("Number('0x123')"),
+      njs_str("291") },
+
+    { njs_str("Number('0X123')"),
+      njs_str("291") },
+
+    { njs_str("Number('0x1_23')"),
+      njs_str("NaN") },
+
+    { njs_str("Number('-0x123')"),
+      njs_str("NaN") },
+
     { njs_str("['1', ' 1 ', '1\\t', '1\\n', '1\\r\\n'].reduce((a, x) => a + Number(x), 0)"),
       njs_str("5") },
 


More information about the nginx-devel mailing list