From 8e73c2ce5776592d2f9261072a7ee9fd073ab0ae Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Wed, 29 Mar 2023 20:28:33 -0700 Subject: [PATCH] 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. --- src/njs_lexer.c | 4 +-- src/njs_number.c | 57 +++++++++++++++++++++++++++++++++----- src/njs_number.h | 6 ++-- src/njs_string.c | 52 +++++++++++++++++++--------------- 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, 127 insertions(+), 40 deletions(-) diff --git a/src/njs_lexer.c b/src/njs_lexer.c index 53ae46e2..310e9674 100644 --- a/src/njs_lexer.c +++ b/src/njs_lexer.c @@ -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; diff --git a/src/njs_number.c b/src/njs_number.c index 7a892bc1..4292efd7 100644 --- a/src/njs_number.c +++ b/src/njs_number.c @@ -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; } diff --git a/src/njs_number.h b/src/njs_number.h index 1844dc4a..8a4df150 100644 --- a/src/njs_number.h +++ b/src/njs_number.h @@ -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 --git a/src/njs_string.c b/src/njs_string.c index d2eb55ed..fb273142 100644 --- a/src/njs_string.c +++ b/src/njs_string.c @@ -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; diff --git a/src/njs_value.h b/src/njs_value.h index b1dfcd16..f0766f46 100644 --- a/src/njs_value.h +++ b/src/njs_value.h @@ -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); diff --git a/src/njs_value_conversion.h b/src/njs_value_conversion.h index 79163b7d..925a392a 100644 --- a/src/njs_value_conversion.h +++ b/src/njs_value_conversion.h @@ -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; diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c index 31a261c8..68de64e4 100644 --- a/src/njs_vmcode.c +++ b/src/njs_vmcode.c @@ -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 { diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 7b268083..65d5e993 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -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") }, -- 2.47.3