]> git.kaiwu.me - njs.git/commitdiff
Aligned Number constructor to the spec.
authorDmitry Volyntsev <xeioex@nginx.com>
Thu, 30 Mar 2023 03:28:33 +0000 (20:28 -0700)
committerDmitry Volyntsev <xeioex@nginx.com>
Thu, 30 Mar 2023 03:28:33 +0000 (20:28 -0700)
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.

src/njs_lexer.c
src/njs_number.c
src/njs_number.h
src/njs_string.c
src/njs_value.h
src/njs_value_conversion.h
src/njs_vmcode.c
src/test/njs_unit_test.c

index 53ae46e2a695ae792ff66014bd4b3f6353eea361..310e9674f61aee622df3303ae65f17f1237d1b54 100644 (file)
@@ -933,7 +933,7 @@ njs_lexer_number(njs_lexer_t *lexer, njs_lexer_token_t *token)
                 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_lexer_token_t *token)
                 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;
index 7a892bc1b659fe957e4cfe34827ff41725478759..4292efd7e33fc975dcdb050397214e1867d85557 100644 (file)
@@ -62,7 +62,8 @@ njs_number_dec_parse(const u_char **start, const u_char *end,
 
 
 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 **start, const u_char *end)
         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 **start, const u_char *end)
 
 
 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 **start, const u_char *end)
         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_value_t *args, njs_uint_t nargs,
         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;
 }
index 1844dc4a296122fd47a6d967acc44490397acd52..8a4df15066ca05f3b457ad7206f9f95bd7ccb9c4 100644 (file)
 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,
index d2eb55ed53acc5d9b5888374bbd871de70c301d9..fb27314296b79610db5a9192859fb7a502045e0d 100644 (file)
@@ -3874,7 +3874,7 @@ njs_string_prototype_iterator_obj(njs_vm_t *vm, njs_value_t *args,
 
 
 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 *value, njs_bool_t parse_float)
     end = p + string.size;
 
     if (p == end) {
-        return parse_float ? NAN : 0.0;
+        return 0.0;
     }
 
     minus = 0;
 
-    if (*p == '+') {
-        p++;
+    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;
 
-    } else if (*p == '-') {
-        p++;
-        minus = 1;
-    }
+        if (p[-1] == 'x' || p[-1] == 'X') {
+            num = njs_number_hex_parse(&p, end, 0);
 
-    if (p == end) {
-        return NAN;
-    }
+        } else if (p[-1] == 'b' || p[-1] == 'B') {
+            num = njs_number_bin_parse(&p, end, 0);
 
-    if (!parse_float
-        && p + 2 < end && p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
-    {
-        p += 2;
-        num = njs_number_hex_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 *value, njs_bool_t parse_float)
         }
     }
 
-    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;
index b1dfcd1614d75f83b43d3efb268fe1d28ab941ed..f0766f46bc4bdc5e052f2e46d48bd3a442ad06c9 100644 (file)
@@ -1073,7 +1073,7 @@ njs_int_t njs_primitive_value_to_string(njs_vm_t *vm, njs_value_t *dst,
     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);
index 79163b7ddf6b3716625d9b83973184915e6fc038..925a392a83728f3069be8a3491ab4551d5de2b84 100644 (file)
@@ -33,7 +33,7 @@ njs_value_to_number(njs_vm_t *vm, njs_value_t *value, double *dst)
         *dst = NAN;
 
         if (njs_is_string(value)) {
-            *dst = njs_string_to_number(value, 0);
+            *dst = njs_string_to_number(value);
         }
 
         return NJS_OK;
index 31a261c8786f3ddc551483012a72244c5109a519..68de64e470cb1b7bb34061d0dc6a9fe47d0177b1 100644 (file)
@@ -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 *vm, njs_value_t *val1, njs_value_t *val2)
             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 {
index 7b268083961bd03eb63b653ed12fce8fdac00eba..65d5e993a0e84f2f83c1ef684bddc8b20ca99fdc 100644 (file)
@@ -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") },