From 74e078b918b579e073b9f5eeeae6904201dfe813 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Mon, 21 Oct 2019 16:43:45 +0300 Subject: [PATCH] Improved functions for converting value to number. Added functions: njs_value_to_number(), njs_value_to_integer(), njs_value_to_length() njs_value_to_int32(), njs_value_to_uint32(), njs_value_to_uint16(). Removed functions: njs_primitive_value_to_number(), njs_primitive_value_to_integer(), njs_primitive_value_to_int32(), njs_primitive_value_to_uint32(), njs_primitive_value_to_length(). Changed return type for njs_number_to_integer() function from int32_t to int64_t. --- src/njs_array.c | 97 +++++++++++++++++++------------------- src/njs_builtin.c | 9 +++- src/njs_number.c | 34 ++++++++++---- src/njs_number.h | 55 ++++++---------------- src/njs_object.c | 11 +---- src/njs_string.c | 35 ++++++++++---- src/njs_value.h | 115 +++++++++++++++++++++++++++++++++++++++++++--- src/njs_vmcode.c | 16 +++---- 8 files changed, 235 insertions(+), 137 deletions(-) diff --git a/src/njs_array.c b/src/njs_array.c index 1d5f38b0..6f019032 100644 --- a/src/njs_array.c +++ b/src/njs_array.c @@ -332,14 +332,13 @@ static njs_int_t njs_array_length(njs_vm_t *vm, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { - double num; - int64_t size; - uint32_t length; - njs_int_t ret; - njs_value_t *val; - njs_array_t *array; - njs_object_t *proto; - njs_value_t val_length; + double num; + int64_t size; + uint32_t length; + njs_int_t ret; + njs_value_t *val; + njs_array_t *array; + njs_object_t *proto; proto = njs_object(value); @@ -369,18 +368,16 @@ njs_array_length(njs_vm_t *vm, njs_value_t *value, njs_value_t *setval, } if (njs_slow_path(!njs_is_number(setval))) { - ret = njs_value_to_numeric(vm, &val_length, setval); - if (ret != NJS_OK) { + ret = njs_value_to_number(vm, setval, &num); + if (njs_slow_path(ret != NJS_OK)) { return ret; } - num = njs_number(&val_length); - } else { num = njs_number(setval); } - length = njs_number_to_uint32(num); + length = njs_number_to_length(num); if ((double) length != num) { njs_range_error(vm, "Invalid array length"); @@ -422,27 +419,21 @@ static njs_int_t njs_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - int64_t start, end, length; - njs_int_t ret; - njs_value_t prop_length; - - static const njs_value_t string_length = njs_string("length"); + int64_t start, end, length; + uint32_t object_length; + njs_int_t ret; - ret = njs_value_property(vm, &args[0], njs_value_arg(&string_length), - &prop_length); + ret = njs_object_length(vm, njs_arg(args, nargs, 0), &object_length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } - if (njs_slow_path(!njs_is_primitive(&prop_length))) { - ret = njs_value_to_numeric(vm, &prop_length, &prop_length); - if (ret != NJS_OK) { - return ret; - } - } + length = object_length; - start = njs_primitive_value_to_integer(njs_arg(args, nargs, 1)); - length = njs_primitive_value_to_length(&prop_length); + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } if (start < 0) { start += length; @@ -458,7 +449,10 @@ njs_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } else { if (njs_is_defined(njs_arg(args, nargs, 2))) { - end = njs_primitive_value_to_integer(njs_argument(args, 2)); + ret = njs_value_to_integer(vm, njs_argument(args, 2), &end); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } } else { end = length; @@ -1724,7 +1718,10 @@ njs_array_prototype_index_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return ret; } - from = njs_primitive_value_to_integer(njs_arg(args, nargs, 2)); + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } if (length == 0 || from >= (int64_t) length) { goto not_found; @@ -1781,7 +1778,10 @@ njs_array_prototype_last_index_of(njs_vm_t *vm, njs_value_t *args, } if (nargs > 2) { - from = njs_primitive_value_to_integer(njs_arg(args, nargs, 2)); + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } } else { from = length - 1; @@ -1868,7 +1868,10 @@ njs_array_prototype_includes(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, goto not_found; } - from = njs_primitive_value_to_integer(njs_arg(args, nargs, 2)); + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } if (from < 0) { from += length; @@ -1906,14 +1909,13 @@ static njs_int_t njs_array_prototype_fill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - njs_int_t ret; - njs_int_t i, start, end, length; + int64_t start, end; + uint32_t length; + njs_int_t i, ret; njs_array_t *array; - njs_value_t name, prop_length, *this, *value; + njs_value_t name, *this, *value; njs_object_t *object; - static const njs_value_t string_length = njs_string("length"); - this = njs_arg(args, nargs, 0); if (njs_is_primitive(this)) { @@ -1940,30 +1942,27 @@ njs_array_prototype_fill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, length = array->length; } else { - ret = njs_value_property(vm, this, njs_value_arg(&string_length), - &prop_length); + ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } + } - if (njs_slow_path(!njs_is_primitive(&prop_length))) { - ret = njs_value_to_numeric(vm, &prop_length, &prop_length); - if (ret != NJS_OK) { - return ret; - } - } - - length = njs_primitive_value_to_length(&prop_length); + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &start); + if (njs_slow_path(ret != NJS_OK)) { + return ret; } - start = njs_primitive_value_to_integer(njs_arg(args, nargs, 2)); start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length); if (njs_is_undefined(njs_arg(args, nargs, 3))) { end = length; } else { - end = njs_primitive_value_to_integer(njs_arg(args, nargs, 3)); + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 3), &end); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } } end = (end < 0) ? njs_max(length + end, 0) : njs_min(end, length); diff --git a/src/njs_builtin.c b/src/njs_builtin.c index 58269e6b..8835b03d 100644 --- a/src/njs_builtin.c +++ b/src/njs_builtin.c @@ -992,14 +992,19 @@ static njs_int_t njs_dump_value(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { + uint32_t n; + njs_int_t ret; njs_str_t str; - njs_uint_t n; njs_value_t *value, *indent; value = njs_arg(args, nargs, 1); indent = njs_arg(args, nargs, 2); - n = njs_primitive_value_to_integer(indent); + ret = njs_value_to_uint32(vm, indent, &n); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + n = njs_min(n, 5); if (njs_vm_value_dump(vm, &str, value, 1, n) != NJS_OK) { diff --git a/src/njs_number.c b/src/njs_number.c index 95865811..c74d2a72 100644 --- a/src/njs_number.c +++ b/src/njs_number.c @@ -547,10 +547,10 @@ njs_number_prototype_to_fixed(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { u_char *p; - int32_t frac; + int64_t frac; double number; size_t length, size; - njs_int_t point, prefix, postfix; + njs_int_t ret, point, prefix, postfix; njs_value_t *value; u_char buf[128], buf2[128]; @@ -569,7 +569,11 @@ njs_number_prototype_to_fixed(njs_vm_t *vm, njs_value_t *args, } } - frac = njs_primitive_value_to_integer(njs_arg(args, nargs, 1)); + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &frac); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + if (njs_slow_path(frac < 0 || frac > 100)) { njs_range_error(vm, "digits argument must be between 0 and 100"); return NJS_ERROR; @@ -582,7 +586,7 @@ njs_number_prototype_to_fixed(njs_vm_t *vm, njs_value_t *args, } point = 0; - length = njs_fixed_dtoa(number, frac, (char *) buf, &point); + length = njs_fixed_dtoa(number, (njs_int_t) frac, (char *) buf, &point); prefix = 0; postfix = 0; @@ -643,7 +647,8 @@ njs_number_prototype_to_precision(njs_vm_t *vm, njs_value_t *args, { double number; size_t size; - int32_t precision; + int64_t precision; + njs_int_t ret; njs_value_t *value; u_char buf[128]; @@ -672,13 +677,17 @@ njs_number_prototype_to_precision(njs_vm_t *vm, njs_value_t *args, return njs_number_to_string(vm, &vm->retval, value); } - precision = njs_primitive_value_to_integer(njs_argument(args, 1)); + ret = njs_value_to_integer(vm, njs_argument(args, 1), &precision); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + if (njs_slow_path(precision < 1 || precision > 100)) { njs_range_error(vm, "precision argument must be between 1 and 100"); return NJS_ERROR; } - size = njs_dtoa_precision(number, (char *) buf, precision); + size = njs_dtoa_precision(number, (char *) buf, (size_t) precision); return njs_string_new(vm, &vm->retval, buf, size, size); } @@ -690,7 +699,8 @@ njs_number_prototype_to_exponential(njs_vm_t *vm, njs_value_t *args, { double number; size_t size; - int32_t frac; + int64_t frac; + njs_int_t ret; njs_value_t *value; u_char buf[128]; @@ -714,7 +724,11 @@ njs_number_prototype_to_exponential(njs_vm_t *vm, njs_value_t *args, } if (njs_is_defined(njs_arg(args, nargs, 1))) { - frac = njs_primitive_value_to_integer(njs_argument(args, 1)); + ret = njs_value_to_integer(vm, njs_argument(args, 1), &frac); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + if (njs_slow_path(frac < 0 || frac > 100)) { njs_range_error(vm, "digits argument must be between 0 and 100"); return NJS_ERROR; @@ -724,7 +738,7 @@ njs_number_prototype_to_exponential(njs_vm_t *vm, njs_value_t *args, frac = -1; } - size = njs_dtoa_exponential(number, (char *) buf, frac); + size = njs_dtoa_exponential(number, (char *) buf, (njs_int_t) frac); return njs_string_new(vm, &vm->retval, buf, size, size); } diff --git a/src/njs_number.h b/src/njs_number.h index 4046aa37..5924e6da 100644 --- a/src/njs_number.h +++ b/src/njs_number.h @@ -61,10 +61,21 @@ njs_number_to_int64(double num) } -njs_inline int32_t +njs_inline int64_t njs_number_to_integer(double num) { - return (int32_t) njs_number_to_int64(num); + if (njs_slow_path(isinf(num))) { + if (num < 0) { + return INT64_MIN; + } + + return INT64_MAX; + + } else if (njs_slow_path(isnan(num))) { + return 0; + } + + return njs_number_to_int64(num); } @@ -131,46 +142,6 @@ njs_char_to_hex(u_char c) return c; } - -njs_inline double -njs_primitive_value_to_number(const njs_value_t *value) -{ - if (njs_fast_path(njs_is_numeric(value))) { - return njs_number(value); - } - - return njs_string_to_number(value, 0); -} - - -njs_inline int32_t -njs_primitive_value_to_integer(const njs_value_t *value) -{ - return njs_number_to_integer(njs_primitive_value_to_number(value)); -} - - -njs_inline int32_t -njs_primitive_value_to_int32(const njs_value_t *value) -{ - return njs_number_to_int32(njs_primitive_value_to_number(value)); -} - - -njs_inline uint32_t -njs_primitive_value_to_uint32(const njs_value_t *value) -{ - return njs_number_to_uint32(njs_primitive_value_to_number(value)); -} - - -njs_inline uint32_t -njs_primitive_value_to_length(const njs_value_t *value) -{ - return njs_number_to_length(njs_primitive_value_to_number(value)); -} - - njs_inline void njs_uint32_to_string(njs_value_t *value, uint32_t u32) { diff --git a/src/njs_object.c b/src/njs_object.c index 3633b4a4..d29ce635 100644 --- a/src/njs_object.c +++ b/src/njs_object.c @@ -2316,14 +2316,5 @@ njs_object_length(njs_vm_t *vm, njs_value_t *value, uint32_t *length) return ret; } - if (!njs_is_primitive(&value_length)) { - ret = njs_value_to_numeric(vm, &value_length, &value_length); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - } - - *length = njs_primitive_value_to_length(&value_length); - - return NJS_OK; + return njs_value_to_length(vm, &value_length, length); } diff --git a/src/njs_string.c b/src/njs_string.c index 7e59a971..b9abfac4 100644 --- a/src/njs_string.c +++ b/src/njs_string.c @@ -1235,16 +1235,22 @@ static njs_int_t njs_string_prototype_char_at(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - ssize_t start, length; + size_t length; + int64_t start; + njs_int_t ret; njs_slice_prop_t slice; njs_string_prop_t string; slice.string_length = njs_string_prop(&string, &args[0]); - start = njs_primitive_value_to_integer(njs_arg(args, nargs, 1)); + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + length = 1; - if (start < 0 || start >= (ssize_t) slice.string_length) { + if (start < 0 || start >= (int64_t) slice.string_length) { start = 0; length = 0; } @@ -1395,21 +1401,26 @@ njs_string_prototype_char_code_at(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { double num; - ssize_t index, length; + size_t length; + int64_t index; uint32_t code; + njs_int_t ret; const u_char *start, *end; njs_string_prop_t string; length = njs_string_prop(&string, &args[0]); - index = njs_primitive_value_to_integer(njs_arg(args, nargs, 1)); + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &index); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - if (njs_slow_path(index < 0 || index >= length)) { + if (njs_slow_path(index < 0 || index >= (int64_t) length)) { num = NAN; goto done; } - if ((uint32_t) length == string.size) { + if (length == string.size) { /* Byte or ASCII string. */ code = string.start[index]; @@ -2501,11 +2512,15 @@ njs_string_prototype_repeat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { u_char *p, *start; - int32_t n, max; - uint32_t size, length; + int64_t n, max; + uint64_t size, length; + njs_int_t ret; njs_string_prop_t string; - n = njs_primitive_value_to_integer(njs_arg(args, nargs, 1)); + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &n); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } (void) njs_string_prop(&string, &args[0]); diff --git a/src/njs_value.h b/src/njs_value.h index 032ee4ab..effeebdc 100644 --- a/src/njs_value.h +++ b/src/njs_value.h @@ -845,10 +845,12 @@ njs_int_t njs_value_property_delete(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *removed); +#include "njs_number.h" + + njs_inline njs_int_t -njs_value_to_numeric(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value) +njs_value_to_number(njs_vm_t *vm, njs_value_t *value, double *dst) { - double num; njs_int_t ret; njs_value_t primitive; @@ -862,14 +864,30 @@ njs_value_to_numeric(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value) } if (njs_slow_path(!njs_is_numeric(value))) { - num = NAN; + *dst = NAN; if (njs_is_string(value)) { - num = njs_string_to_number(value, 0); + *dst = njs_string_to_number(value, 0); } - } else { - num = njs_number(value); + return NJS_OK; + } + + *dst = njs_number(value); + + return NJS_OK; +} + + +njs_inline njs_int_t +njs_value_to_numeric(njs_vm_t *vm, njs_value_t *value, njs_value_t *dst) +{ + double num; + njs_int_t ret; + + ret = njs_value_to_number(vm, value, &num); + if (njs_slow_path(ret != NJS_OK)) { + return ret; } njs_set_number(dst, num); @@ -878,6 +896,91 @@ njs_value_to_numeric(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value) } +njs_inline njs_int_t +njs_value_to_integer(njs_vm_t *vm, njs_value_t *value, int64_t *dst) +{ + double num; + njs_int_t ret; + + ret = njs_value_to_number(vm, value, &num); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + *dst = njs_number_to_integer(num); + + return NJS_OK; +} + + +njs_inline njs_int_t +njs_value_to_length(njs_vm_t *vm, njs_value_t *value, uint32_t *dst) +{ + double num; + njs_int_t ret; + + ret = njs_value_to_number(vm, value, &num); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + *dst = njs_number_to_length(num); + + return NJS_OK; +} + + +njs_inline njs_int_t +njs_value_to_int32(njs_vm_t *vm, njs_value_t *value, int32_t *dst) +{ + double num; + njs_int_t ret; + + ret = njs_value_to_number(vm, value, &num); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + *dst = njs_number_to_int32(num); + + return NJS_OK; +} + + +njs_inline njs_int_t +njs_value_to_uint32(njs_vm_t *vm, njs_value_t *value, uint32_t *dst) +{ + double num; + njs_int_t ret; + + ret = njs_value_to_number(vm, value, &num); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + *dst = njs_number_to_uint32(num); + + return NJS_OK; +} + + +njs_inline njs_int_t +njs_value_to_uint16(njs_vm_t *vm, njs_value_t *value, uint16_t *dst) +{ + double num; + njs_int_t ret; + + ret = njs_value_to_number(vm, value, &num); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + *dst = njs_number_to_uint16(num); + + return NJS_OK; +} + + njs_inline njs_int_t njs_value_to_string(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value) { diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c index 382a4477..77993ed3 100644 --- a/src/njs_vmcode.c +++ b/src/njs_vmcode.c @@ -183,7 +183,7 @@ next: case NJS_VMCODE_DECREMENT: case NJS_VMCODE_POST_DECREMENT: if (njs_slow_path(!njs_is_numeric(value2))) { - ret = njs_value_to_numeric(vm, &numeric1, value2); + ret = njs_value_to_numeric(vm, value2, &numeric1); if (njs_slow_path(ret != NJS_OK)) { goto error; } @@ -350,8 +350,8 @@ next: case NJS_VMCODE_RIGHT_SHIFT: case NJS_VMCODE_UNSIGNED_RIGHT_SHIFT: if (njs_slow_path(!njs_is_numeric(value1))) { - ret = njs_value_to_numeric(vm, &numeric1, value1); - if (ret != NJS_OK) { + ret = njs_value_to_numeric(vm, value1, &numeric1); + if (njs_slow_path(ret != NJS_OK)) { goto error; } @@ -359,8 +359,8 @@ next: } if (njs_slow_path(!njs_is_numeric(value2))) { - ret = njs_value_to_numeric(vm, &numeric2, value2); - if (ret != NJS_OK) { + ret = njs_value_to_numeric(vm, value2, &numeric2); + if (njs_slow_path(ret != NJS_OK)) { goto error; } @@ -517,8 +517,8 @@ next: case NJS_VMCODE_UNARY_NEGATION: case NJS_VMCODE_BITWISE_NOT: if (njs_slow_path(!njs_is_numeric(value1))) { - ret = njs_value_to_numeric(vm, &numeric1, value1); - if (ret != NJS_OK) { + ret = njs_value_to_numeric(vm, value1, &numeric1); + if (njs_slow_path(ret != NJS_OK)) { goto error; } @@ -538,7 +538,7 @@ next: break; case NJS_VMCODE_BITWISE_NOT: - njs_set_int32(retval, ~njs_number_to_integer(num)); + njs_set_int32(retval, ~njs_number_to_uint32(num)); } pc += sizeof(njs_vmcode_2addr_t); -- 2.47.3