From: Alexander Borisov Date: Wed, 28 Aug 2019 11:59:28 +0000 (+0300) Subject: Fixed Array prototype functions according to the specification. X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=852411570ee73053265bfd60049f7cd6c60e1ba8;p=njs.git Fixed Array prototype functions according to the specification. The following fuctions were fixed: every, filter, find, findIndex, forEach, map, reduce, some. --- diff --git a/src/njs_array.c b/src/njs_array.c index 3da31f03..949cc28a 100644 --- a/src/njs_array.c +++ b/src/njs_array.c @@ -8,6 +8,19 @@ #include +typedef struct { + njs_function_t *function; + njs_value_t *this_arg; + njs_value_t *value; + + njs_array_t *array; +} njs_array_interator_args_t; + + +typedef njs_int_t (*njs_array_iterator_handler_t)(njs_vm_t *vm, + njs_array_interator_args_t *args, njs_value_t *entry, uint32_t n); + + static njs_int_t njs_array_prototype_slice_copy(njs_vm_t *vm, njs_value_t *this, int64_t start, int64_t length); static njs_value_t *njs_array_copy(njs_value_t *dst, njs_value_t *src); @@ -1349,54 +1362,194 @@ njs_array_prototype_fill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_inline njs_int_t -njs_array_iterator_call(njs_vm_t *vm, njs_function_t *function, - const njs_value_t *this_arg, const njs_value_t *value, uint32_t n, - const njs_value_t *array) +njs_array_iterator_call(njs_vm_t *vm, njs_array_interator_args_t *args, + const njs_value_t *entry, uint32_t n) { njs_value_t arguments[3]; /* GC: array elt, array */ - arguments[0] = *value; + arguments[0] = *entry; njs_set_number(&arguments[1], n); - arguments[2] = *array; + arguments[2] = *args->value; - return njs_function_call(vm, function, this_arg, arguments, 3, + return njs_function_call(vm, args->function, args->this_arg, arguments, 3, &vm->retval); } -static njs_int_t -njs_array_prototype_for_each(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) +njs_inline njs_int_t +njs_array_iterator(njs_vm_t *vm, njs_array_interator_args_t *args, + njs_array_iterator_handler_t handler, uint32_t length) { - uint32_t i, length; + uint32_t i; njs_int_t ret; - njs_value_t *array, *value, *this_arg; - njs_function_t *function; + njs_value_t *entry, *value, character, index, string_obj, prop; + njs_object_t *object; + const u_char *p, *end, *pos; + njs_string_prop_t string_prop; - if (nargs < 2 || !njs_is_array(&args[0]) || !njs_is_function(&args[1])) { - njs_type_error(vm, "unexpected iterator arguments"); - return NJS_ERROR; + value = args->value; + + if (njs_is_array(value)) { + if (njs_slow_path(!njs_object_hash_is_empty(value))) { + goto process_object; + } + + length = njs_array_len(value); + + for (i = 0; i < length; i++) { + entry = &njs_array_start(value)[i]; + + ret = handler(vm, args, entry, i); + if (njs_slow_path(ret != NJS_OK)) { + if (ret > 0) { + return NJS_DECLINED; + } + + return NJS_ERROR; + } + + length = njs_min(length, njs_array_len(value)); + } + + return NJS_OK; } - array = &args[0]; - length = njs_array_len(array); - function = njs_function(&args[1]); - this_arg = njs_arg(args, nargs, 2); + if (njs_is_string(value) || njs_is_object_string(value)) { + + if (njs_is_string(value)) { + object = njs_object_value_alloc(vm, value, NJS_STRING); + if (njs_slow_path(object == NULL)) { + return NJS_ERROR; + } + + njs_set_type_object(&string_obj, object, NJS_OBJECT_STRING); + + args->value = &string_obj; + } + else { + value = njs_object_value(value); + } + + length = (uint32_t) njs_string_prop(&string_prop, value); + + p = string_prop.start; + end = p + string_prop.size; + + if (length == string_prop.size) { + /* Byte or ASCII string. */ + + for (i = 0; i < length; i++) { + /* This cannot fail. */ + (void) njs_string_new(vm, &character, p++, 1, 1); + + ret = handler(vm, args, &character, i); + if (njs_slow_path(ret != NJS_OK)) { + if (ret > 0) { + return NJS_DECLINED; + } + + return NJS_ERROR; + } + } + + } else { + /* UTF-8 string. */ + + for (i = 0; i < length; i++) { + pos = njs_utf8_next(p, end); + + /* This cannot fail. */ + (void) njs_string_new(vm, &character, p, pos - p, 1); + + ret = handler(vm, args, &character, i); + if (njs_slow_path(ret != NJS_OK)) { + if (ret > 0) { + return NJS_DECLINED; + } + + return NJS_ERROR; + } + + p = pos; + } + } + + return NJS_OK; + } + + if (!njs_is_object(value)) { + return NJS_OK; + } + +process_object: + + if (length > NJS_ARRAY_MAX_LENGTH) { + ret = njs_object_length(vm, value, &length); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } for (i = 0; i < length; i++) { - value = &njs_array_start(array)[i]; + njs_uint32_to_string(&index, i); + + ret = njs_value_property(vm, value, &index, &prop); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } - if (njs_is_valid(value)) { - ret = njs_array_iterator_call(vm, function, this_arg, value, i, - array); + if (ret != NJS_DECLINED) { + ret = handler(vm, args, &prop, i); if (njs_slow_path(ret != NJS_OK)) { - return ret; + if (ret > 0) { + return NJS_DECLINED; + } + + return NJS_ERROR; } } + } - length = njs_min(length, njs_array_len(array)); + return NJS_OK; +} + + +static njs_int_t +njs_array_handler_for_each(njs_vm_t *vm, njs_array_interator_args_t *args, + njs_value_t *entry, uint32_t n) +{ + if (njs_is_valid(entry)) { + return njs_array_iterator_call(vm, args, entry, n); + } + + return NJS_OK; +} + + +static njs_int_t +njs_array_prototype_for_each(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + njs_int_t ret; + njs_array_interator_args_t iargs; + + if (njs_is_null_or_undefined(njs_arg(args, nargs, 0)) + || !njs_is_function(njs_arg(args, nargs, 1))) + { + njs_type_error(vm, "unexpected iterator arguments"); + return NJS_ERROR; + } + + iargs.value = njs_argument(args, 0); + iargs.function = njs_function(&args[1]); + iargs.this_arg = njs_arg(args, nargs, 2); + + ret = njs_array_iterator(vm, &iargs, njs_array_handler_for_each, + NJS_ARRAY_MAX_LENGTH + 1); + if (njs_slow_path(ret != NJS_OK)) { + return ret; } njs_set_undefined(&vm->retval); @@ -1405,48 +1558,79 @@ njs_array_prototype_for_each(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } +static njs_int_t +njs_array_handler_some(njs_vm_t *vm, njs_array_interator_args_t *args, + njs_value_t *entry, uint32_t n) +{ + njs_int_t ret; + + if (njs_is_valid(entry)) { + ret = njs_array_iterator_call(vm, args, entry, n); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (njs_is_true(&vm->retval)) { + vm->retval = njs_value_true; + + return 1; + } + } + + return NJS_OK; +} + + static njs_int_t njs_array_prototype_some(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - uint32_t i, length; - njs_int_t ret; - njs_value_t *array, *value, *this_arg; - njs_function_t *function; - const njs_value_t *retval; + njs_int_t ret; + njs_array_interator_args_t iargs; - if (nargs < 2 || !njs_is_array(&args[0]) || !njs_is_function(&args[1])) { + if (njs_is_null_or_undefined(njs_arg(args, nargs, 0)) + || !njs_is_function(njs_arg(args, nargs, 1))) + { njs_type_error(vm, "unexpected iterator arguments"); return NJS_ERROR; } - array = &args[0]; - length = njs_array_len(array); - function = njs_function(&args[1]); - this_arg = njs_arg(args, nargs, 2); + iargs.value = njs_argument(args, 0); + iargs.function = njs_function(&args[1]); + iargs.this_arg = njs_arg(args, nargs, 2); - retval = &njs_value_false; + ret = njs_array_iterator(vm, &iargs, njs_array_handler_some, + NJS_ARRAY_MAX_LENGTH + 1); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } - for (i = 0; i < length; i++) { - value = &njs_array_start(array)[i]; + if (ret != NJS_DECLINED) { + vm->retval = njs_value_false; + } - if (njs_is_valid(value)) { - ret = njs_array_iterator_call(vm, function, this_arg, value, i, - array); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + return NJS_OK; +} - if (njs_is_true(&vm->retval)) { - retval = &njs_value_true; - break; - } + +static njs_int_t +njs_array_handler_every(njs_vm_t *vm, njs_array_interator_args_t *args, + njs_value_t *entry, uint32_t n) +{ + njs_int_t ret; + + if (njs_is_valid(entry)) { + ret = njs_array_iterator_call(vm, args, entry, n); + if (njs_slow_path(ret != NJS_OK)) { + return ret; } - length = njs_min(length, njs_array_len(array)); - } + if (!njs_is_true(&vm->retval)) { + vm->retval = njs_value_false; - vm->retval = *retval; + return 1; + } + } return NJS_OK; } @@ -1456,45 +1640,58 @@ static njs_int_t njs_array_prototype_every(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - uint32_t i, length; - njs_int_t ret; - njs_value_t *array, *value, *this_arg; - njs_function_t *function; - const njs_value_t *retval; + njs_int_t ret; + njs_array_interator_args_t iargs; - if (nargs < 2 || !njs_is_array(&args[0]) || !njs_is_function(&args[1])) { + if (njs_is_null_or_undefined(njs_arg(args, nargs, 0)) + || !njs_is_function(njs_arg(args, nargs, 1))) + { njs_type_error(vm, "unexpected iterator arguments"); return NJS_ERROR; } - array = &args[0]; - length = njs_array_len(array); - function = njs_function(&args[1]); - this_arg = njs_arg(args, nargs, 2); + iargs.value = njs_argument(args, 0); + iargs.function = njs_function(&args[1]); + iargs.this_arg = njs_arg(args, nargs, 2); - retval = &njs_value_true; + ret = njs_array_iterator(vm, &iargs, njs_array_handler_every, + NJS_ARRAY_MAX_LENGTH + 1); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + if (ret != NJS_DECLINED) { + vm->retval = njs_value_true; + } + + return NJS_OK; +} - for (i = 0; i < length; i++) { - value = &njs_array_start(array)[i]; - if (njs_is_valid(value)) { - ret = njs_array_iterator_call(vm, function, this_arg, value, i, - array); +static njs_int_t +njs_array_handler_filter(njs_vm_t *vm, njs_array_interator_args_t *args, + njs_value_t *entry, uint32_t n) +{ + njs_int_t ret; + njs_value_t copy; + + if (njs_is_valid(entry)) { + copy = *entry; + + ret = njs_array_iterator_call(vm, args, ©, n); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (njs_is_true(&vm->retval)) { + + ret = njs_array_add(vm, args->array, ©); if (njs_slow_path(ret != NJS_OK)) { return ret; } - - if (!njs_is_true(&vm->retval)) { - retval = &njs_value_false; - break; - } } - - length = njs_min(length, njs_array_len(array)); } - vm->retval = *retval; - return NJS_OK; } @@ -1503,49 +1700,61 @@ static njs_int_t njs_array_prototype_filter(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - uint32_t i, length; - njs_int_t ret; - njs_array_t *retval; - njs_value_t *array, value, *this_arg; - njs_function_t *function; + njs_int_t ret; + njs_array_interator_args_t iargs; - if (nargs < 2 || !njs_is_array(&args[0]) || !njs_is_function(&args[1])) { + if (njs_is_null_or_undefined(njs_arg(args, nargs, 0)) + || !njs_is_function(njs_arg(args, nargs, 1))) + { njs_type_error(vm, "unexpected iterator arguments"); return NJS_ERROR; } - array = &args[0]; - length = njs_array_len(array); - function = njs_function(&args[1]); - this_arg = njs_arg(args, nargs, 2); + iargs.value = njs_argument(args, 0); + iargs.function = njs_function(&args[1]); + iargs.this_arg = njs_arg(args, nargs, 2); - retval = njs_array_alloc(vm, 0, NJS_ARRAY_SPARE); - if (njs_slow_path(retval == NULL)) { + iargs.array = njs_array_alloc(vm, 0, NJS_ARRAY_SPARE); + if (njs_slow_path(iargs.array == NULL)) { return NJS_ERROR; } - for (i = 0; i < length; i++) { - value = njs_array_start(array)[i]; + ret = njs_array_iterator(vm, &iargs, njs_array_handler_filter, + NJS_ARRAY_MAX_LENGTH + 1); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - if (njs_is_valid(&value)) { - ret = njs_array_iterator_call(vm, function, this_arg, &value, i, - array); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + njs_set_array(&vm->retval, iargs.array); - if (njs_is_true(&vm->retval)) { - ret = njs_array_add(vm, retval, &value); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - } - } + return NJS_OK; +} + + +static njs_int_t +njs_array_handler_find(njs_vm_t *vm, njs_array_interator_args_t *args, + njs_value_t *entry, uint32_t n) +{ + njs_int_t ret; + njs_value_t copy; + + if (njs_is_valid(entry)) { + copy = *entry; - length = njs_min(length, njs_array_len(array)); + } else { + njs_set_undefined(©); } - njs_set_array(&vm->retval, retval); + ret = njs_array_iterator_call(vm, args, ©, n); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (njs_is_true(&vm->retval)) { + vm->retval = copy; + + return 1; + } return NJS_OK; } @@ -1555,45 +1764,58 @@ static njs_int_t njs_array_prototype_find(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - uint32_t i, length; - njs_int_t ret; - njs_value_t *array, value, *this_arg; - njs_function_t *function; - const njs_value_t *retval; + njs_int_t ret; + njs_array_interator_args_t iargs; - if (nargs < 2 || !njs_is_array(&args[0]) || !njs_is_function(&args[1])) { + if (njs_is_null_or_undefined(njs_arg(args, nargs, 0)) + || !njs_is_function(njs_arg(args, nargs, 1))) + { njs_type_error(vm, "unexpected iterator arguments"); return NJS_ERROR; } - array = &args[0]; - length = njs_array_len(array); - function = njs_function(&args[1]); - this_arg = njs_arg(args, nargs, 2); + iargs.value = njs_argument(args, 0); + iargs.function = njs_function(&args[1]); + iargs.this_arg = njs_arg(args, nargs, 2); - retval = &njs_value_undefined; + ret = njs_array_iterator(vm, &iargs, njs_array_handler_find, + NJS_ARRAY_MAX_LENGTH + 1); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } - for (i = 0; i < length; i++) { - value = njs_array_start(array)[i]; + if (ret != NJS_DECLINED) { + vm->retval = njs_value_undefined; + } - if (!njs_is_valid(&value)) { - njs_set_undefined(&value); - } + return NJS_OK; +} - ret = njs_array_iterator_call(vm, function, this_arg, &value, i, array); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - if (njs_is_true(&vm->retval)) { - retval = &value; - break; - } +static njs_int_t +njs_array_handler_find_index(njs_vm_t *vm, njs_array_interator_args_t *args, + njs_value_t *entry, uint32_t n) +{ + njs_int_t ret; + njs_value_t copy; - length = njs_min(length, njs_array_len(array)); + if (njs_is_valid(entry)) { + copy = *entry; + + } else { + njs_set_undefined(©); } - vm->retval = *retval; + ret = njs_array_iterator_call(vm, args, ©, n); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (njs_is_true(&vm->retval)) { + njs_set_number(&vm->retval, n); + + return 1; + } return NJS_OK; } @@ -1603,46 +1825,56 @@ static njs_int_t njs_array_prototype_find_index(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - double index; - uint32_t i, length; - njs_int_t ret; - njs_value_t *array, value, *this_arg; - njs_function_t *function; + njs_int_t ret; + njs_array_interator_args_t iargs; - if (nargs < 2 || !njs_is_array(&args[0]) || !njs_is_function(&args[1])) { + if (njs_is_null_or_undefined(njs_arg(args, nargs, 0)) + || !njs_is_function(njs_arg(args, nargs, 1))) + { njs_type_error(vm, "unexpected iterator arguments"); return NJS_ERROR; } - array = &args[0]; - length = njs_array_len(array); - function = njs_function(&args[1]); - this_arg = njs_arg(args, nargs, 2); + iargs.value = njs_argument(args, 0); + iargs.function = njs_function(&args[1]); + iargs.this_arg = njs_arg(args, nargs, 2); - index = -1; + ret = njs_array_iterator(vm, &iargs, njs_array_handler_find_index, + NJS_ARRAY_MAX_LENGTH + 1); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } - for (i = 0; i < length; i++) { - value = njs_array_start(array)[i]; + if (ret != NJS_DECLINED) { + njs_set_number(&vm->retval, -1); + } - if (!njs_is_valid(&value)) { - njs_set_undefined(&value); - } + return NJS_OK; +} - ret = njs_array_iterator_call(vm, function, this_arg, &value, i, array); + +static njs_int_t +njs_array_handler_map(njs_vm_t *vm, njs_array_interator_args_t *args, + njs_value_t *entry, uint32_t n) +{ + njs_int_t ret; + njs_array_t *retval; + + retval = args->array; + + njs_set_invalid(&retval->start[n]); + + if (njs_is_valid(entry)) { + ret = njs_array_iterator_call(vm, args, entry, n); if (njs_slow_path(ret != NJS_OK)) { return ret; } - if (njs_is_true(&vm->retval)) { - index = i; - break; + if (njs_is_valid(&vm->retval)) { + retval->start[n] = vm->retval; } - - length = njs_min(length, njs_array_len(array)); } - njs_set_number(&vm->retval, index); - return NJS_OK; } @@ -1651,75 +1883,93 @@ static njs_int_t njs_array_prototype_map(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - uint32_t i, length, size; - njs_int_t ret; - njs_array_t *retval; - njs_value_t *array, *value, *this_arg; - njs_function_t *function; + uint32_t length, i; + njs_int_t ret; + njs_array_t *array; + njs_array_interator_args_t iargs; - if (nargs < 2 || !njs_is_array(&args[0]) || !njs_is_function(&args[1])) { + if (njs_is_null_or_undefined(njs_arg(args, nargs, 0)) + || !njs_is_function(njs_arg(args, nargs, 1))) + { njs_type_error(vm, "unexpected iterator arguments"); return NJS_ERROR; } - array = &args[0]; - length = njs_array_len(array); - function = njs_function(&args[1]); - this_arg = njs_arg(args, nargs, 2); + iargs.value = njs_argument(args, 0); + iargs.function = njs_function(&args[1]); + iargs.this_arg = njs_arg(args, nargs, 2); - size = length; + ret = njs_value_length(vm, iargs.value, &length); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - retval = njs_array_alloc(vm, length, 0); - if (njs_slow_path(retval == NULL)) { + iargs.array = njs_array_alloc(vm, length, 0); + if (njs_slow_path(iargs.array == NULL)) { return NJS_ERROR; } - for (i = 0; i < length; i++) { - njs_set_invalid(&retval->start[i]); - - value = &njs_array_start(array)[i]; + if (length > 0) { + ret = njs_array_iterator(vm, &iargs, njs_array_handler_map, length); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - if (njs_is_valid(value)) { - ret = njs_array_iterator_call(vm, function, this_arg, value, i, - array); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + if (njs_is_array(iargs.value) + && njs_object_hash_is_empty(iargs.value)) + { + array = iargs.array; - if (njs_is_valid(&vm->retval)) { - retval->start[i] = vm->retval; + for (i = njs_array_len(iargs.value); i < length; i++) { + njs_set_invalid(&array->start[i]); } } - - length = njs_min(length, njs_array_len(array)); } - for ( ; i < size; i++) { - njs_set_invalid(&retval->start[i]); - } - - njs_set_array(&vm->retval, retval); + njs_set_array(&vm->retval, iargs.array); return NJS_OK; } njs_inline njs_int_t -njs_array_iterator_reduce(njs_vm_t *vm, njs_function_t *function, - njs_value_t *accumulator, njs_value_t *value, uint32_t n, - njs_value_t *array) +njs_array_iterator_reduce(njs_vm_t *vm, njs_array_interator_args_t *args, + njs_value_t *entry, uint32_t n) { njs_value_t arguments[5]; /* GC: array elt, array */ njs_set_undefined(&arguments[0]); - arguments[1] = *accumulator; - arguments[2] = *value; + arguments[1] = *args->this_arg; + arguments[2] = *entry; njs_set_number(&arguments[3], n); - arguments[4] = *array; + arguments[4] = *args->value; + + return njs_function_apply(vm, args->function, arguments, 5, args->this_arg); +} + + +static njs_int_t +njs_array_handler_reduce(njs_vm_t *vm, njs_array_interator_args_t *args, + njs_value_t *entry, uint32_t n) +{ + njs_int_t ret; + + if (njs_is_valid(entry)) { - return njs_function_apply(vm, function, arguments, 5, accumulator); + if (!njs_is_valid(args->this_arg)) { + *(args->this_arg) = *entry; + return NJS_OK; + } + + ret = njs_array_iterator_reduce(vm, args, entry, n); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + + return NJS_OK; } @@ -1727,44 +1977,31 @@ static njs_int_t njs_array_prototype_reduce(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - uint32_t i, length; - njs_int_t ret; - njs_value_t accumulator, *array, *value; - njs_function_t *function; + njs_int_t ret; + njs_value_t accumulator; + njs_array_interator_args_t iargs; - if (nargs < 2 || !njs_is_array(&args[0]) || !njs_is_function(&args[1])) { + if (njs_is_null_or_undefined(njs_arg(args, nargs, 0)) + || !njs_is_function(njs_arg(args, nargs, 1))) + { njs_type_error(vm, "unexpected iterator arguments"); return NJS_ERROR; } - array = &args[0]; - length = njs_array_len(array); - function = njs_function(&args[1]); - njs_set_invalid(&accumulator); if (nargs > 2) { - accumulator = args[2]; + accumulator = *njs_argument(args, 2); } - for (i = 0; i < length; i++) { - value = &njs_array_start(array)[i]; - - if (njs_is_valid(value)) { - - if (!njs_is_valid(&accumulator)) { - accumulator = njs_array_start(array)[i]; - continue; - } + iargs.value = njs_argument(args, 0); + iargs.function = njs_function(&args[1]); + iargs.this_arg = &accumulator; - ret = njs_array_iterator_reduce(vm, function, &accumulator, value, - i, array); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - } - - length = njs_min(length, njs_array_len(array)); + ret = njs_array_iterator(vm, &iargs, njs_array_handler_reduce, + NJS_ARRAY_MAX_LENGTH + 1); + if (njs_slow_path(ret != NJS_OK)) { + return ret; } if (!njs_is_valid(&accumulator)) { @@ -1782,20 +2019,19 @@ static njs_int_t njs_array_prototype_reduce_right(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - int32_t i; - uint32_t length; - njs_int_t ret; - njs_value_t accumulator, *array, *value; - njs_function_t *function; + int32_t i; + uint32_t length; + njs_int_t ret; + njs_value_t accumulator, *value, *entry; + njs_array_interator_args_t iter; if (nargs < 2 || !njs_is_array(&args[0]) || !njs_is_function(&args[1])) { njs_type_error(vm, "unexpected iterator arguments"); return NJS_ERROR; } - array = &args[0]; - length = njs_array_len(array); - function = njs_function(&args[1]); + value = &args[0]; + length = njs_array_len(value); njs_set_invalid(&accumulator); @@ -1803,24 +2039,27 @@ njs_array_prototype_reduce_right(njs_vm_t *vm, njs_value_t *args, accumulator = args[2]; } + iter.value = value; + iter.function = njs_function(&args[1]); + iter.this_arg = &accumulator; + for (i = length - 1; i >= 0; i--) { - value = &njs_array_start(array)[i]; + entry = &njs_array_start(value)[i]; - if (njs_is_valid(value)) { + if (njs_is_valid(entry)) { if (!njs_is_valid(&accumulator)) { - accumulator = njs_array_start(array)[i]; + accumulator = njs_array_start(value)[i]; continue; } - ret = njs_array_iterator_reduce(vm, function, &accumulator, value, - i, array); + ret = njs_array_iterator_reduce(vm, &iter, entry, i); if (njs_slow_path(ret != NJS_OK)) { return ret; } } - length = njs_min(length, njs_array_len(array)); + length = njs_min(length, njs_array_len(value)); } if (!njs_is_valid(&accumulator)) { diff --git a/src/njs_object.c b/src/njs_object.c index 9c734115..e09d1e13 100644 --- a/src/njs_object.c +++ b/src/njs_object.c @@ -2297,3 +2297,30 @@ const njs_object_init_t njs_object_prototype_init = { njs_object_prototype_properties, njs_nitems(njs_object_prototype_properties), }; + + +njs_int_t +njs_object_length(njs_vm_t *vm, njs_value_t *value, uint32_t *length) +{ + njs_int_t ret; + njs_value_t value_length; + + const njs_value_t string_length = njs_string("length"); + + ret = njs_value_property(vm, value, njs_value_arg(&string_length), + &value_length); + if (njs_slow_path(ret == NJS_ERROR)) { + 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; +} diff --git a/src/njs_object.h b/src/njs_object.h index d6bc79b0..564cced1 100644 --- a/src/njs_object.h +++ b/src/njs_object.h @@ -69,6 +69,7 @@ njs_value_t *njs_property_constructor_create(njs_vm_t *vm, njs_lvlhsh_t *hash, njs_value_t *constructor); njs_int_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused); +njs_int_t njs_object_length(njs_vm_t *vm, njs_value_t *value, uint32_t *dest); njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name, const njs_value_t *value, uint8_t attributes); diff --git a/src/njs_value.c b/src/njs_value.c index 9a034d19..667a648a 100644 --- a/src/njs_value.c +++ b/src/njs_value.c @@ -230,6 +230,28 @@ njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value, } +njs_int_t +njs_value_length(njs_vm_t *vm, njs_value_t *value, uint32_t *length) +{ + njs_string_prop_t string_prop; + + if (njs_is_string(value)) { + *length = (uint32_t) njs_string_prop(&string_prop, value); + + } else if (njs_is_primitive(value)) { + *length = 0; + + } else if (njs_is_array(value)) { + *length = njs_array_len(value); + + } else { + return njs_object_length(vm, value, length); + } + + return NJS_OK; +} + + const char * njs_type_string(njs_value_type_t type) { diff --git a/src/njs_value.h b/src/njs_value.h index 79bc90d6..0c4c425b 100644 --- a/src/njs_value.h +++ b/src/njs_value.h @@ -526,6 +526,10 @@ typedef struct { ((value)->type == NJS_OBJECT_VALUE) +#define njs_is_object_string(value) \ + ((value)->type == NJS_OBJECT_STRING) + + #define njs_object_value_type(type) \ (type + NJS_OBJECT) @@ -586,6 +590,10 @@ typedef struct { (&(value)->data.u.object->hash) +#define njs_object_hash_is_empty(value) \ + (njs_lvlhsh_is_empty(njs_object_hash(value))) + + #define njs_array(value) \ ((value)->data.u.array) @@ -814,6 +822,7 @@ njs_array_t *njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value, njs_object_enum_t kind, njs_bool_t all); njs_array_t *njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value, njs_object_enum_t kind, njs_bool_t all); +njs_int_t njs_value_length(njs_vm_t *vm, njs_value_t *value, uint32_t *dest); const char *njs_type_string(njs_value_type_t type); const char *njs_arg_type_string(uint8_t arg); diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index cc8566c6..eafc7a86 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -4047,6 +4047,16 @@ static njs_unit_test_t njs_test[] = "a.forEach(function(v, i, a) { c++ }); c"), njs_str("0") }, + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" + "Object.defineProperty(o, 'length', {get: () => 4});" + "Object.defineProperty(o, '3', {get: () => 'd'});" + "var r = ''; Array.prototype.forEach.call(o, function(v, i, a) { r += v }); r"), + njs_str("abcd") }, + + { njs_str("var s = 't'; var t = '';" + "Array.prototype.forEach.call(s, function (a, b, c) {t = typeof c;}); [t, typeof s];"), + njs_str("object,string") }, + { njs_str("var a = [];" "a.some(function(v, i, a) { return v > 1 })"), njs_str("false") }, @@ -4063,6 +4073,20 @@ static njs_unit_test_t njs_test[] = "a.some(function(v, i, a) { return v > 3 })"), njs_str("false") }, + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': { valueOf: function() { return 3 }}};" + "var r = Array.prototype.some.call(o, function(el, i, arr) {return el == 'c'}); r"), + njs_str("true") }, + + { njs_str("var o = {0: 'a', 1: 'b', 2: 'd', 'length': { valueOf: function() { return 3 }}};" + "var r = Array.prototype.some.call(o, function(el, i, arr) {return el == 'c'}); r"), + njs_str("false") }, + + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" + "Object.defineProperty(o, 'length', {get: () => 4});" + "Object.defineProperty(o, '3', {get: () => 'd'});" + "var r = Array.prototype.some.call(o, function(v, i, a) { return v === 'd' }); r"), + njs_str("true") }, + { njs_str("var a = [];" "a.every(function(v, i, a) { return v > 1 })"), njs_str("true") }, @@ -4079,6 +4103,26 @@ static njs_unit_test_t njs_test[] = "a.every(function(v, i, a) { return v > 0 })"), njs_str("true") }, + { njs_str("var o = {0: 'c', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" + "var r = Array.prototype.every.call(o, function(el, i, arr) {return el == 'c'}); r"), + njs_str("false") }, + + { njs_str("var o = {0: 'c', 1: 'c', 2: 'c', 'length': { valueOf() { return 3 }}};" + "var r = Array.prototype.every.call(o, function(el, i, arr) {return el == 'c'}); r"), + njs_str("true") }, + + { njs_str("var o = {0: 'x', 1: 'y', 2: 'z'};" + "Object.defineProperty(o, 'length', {get: () => 4});" + "Object.defineProperty(o, '3', {get: () => 'a'});" + "var r = Array.prototype.some.call(o, function(v, i, a) { return v === 'a' }); r"), + njs_str("true") }, + + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" + "Object.defineProperty(o, 'length', {get: () => 4});" + "Object.defineProperty(o, '3', {get: () => 'b'});" + "var r = Array.prototype.some.call(o, function(v, i, a) { return v === 'y' }); r"), + njs_str("false") }, + { njs_str("[].fill(1);"), njs_str("") }, @@ -4212,6 +4256,16 @@ static njs_unit_test_t njs_test[] = "a.filter(function(v, i, a) { a[i+1] = v+10; return true })"), njs_str("1,11,21,31,41,51,61") }, + { njs_str("var o = {0: 'c', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" + "var r = Array.prototype.filter.call(o, function(el, i, arr) {return el == 'c'}); r"), + njs_str("c,c") }, + + { njs_str("var o = {0: 'c', 1: 'a', 2: 'b'};" + "Object.defineProperty(o, 'length', {get: () => 4});" + "Object.defineProperty(o, '3', {get: () => 'c'});" + "var r = Array.prototype.filter.call(o, function(el, i, arr) { return el == 'c' }); r"), + njs_str("c,c") }, + { njs_str("var a = [];" "a.find(function(v, i, a) { return v > 1 })"), njs_str("undefined") }, @@ -4252,9 +4306,23 @@ static njs_unit_test_t njs_test[] = njs_str("3") }, { njs_str("var a = [1,2,3,4,5,6];" - "a.find(function(v, i, a) { a.shift(); return v == 4 })"), + "a.find(function(v, i, a) { a.shift(); return v == 4 })"), njs_str("undefined") }, + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" + "var r = Array.prototype.find.call(o, function(el, i, arr) {return el == 'b'}); r"), + njs_str("b") }, + + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" + "var r = Array.prototype.find.call(o, function(el, i, arr) {delete o['1']; return el == 'c'}); r"), + njs_str("c") }, + + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" + "Object.defineProperty(o, 'length', {get: () => 4});" + "Object.defineProperty(o, '3', {get: () => 'd'});" + "var r = Array.prototype.find.call(o, function(el, i, arr) { return el == 'd' }); r"), + njs_str("d") }, + { njs_str("var a = [];" "a.findIndex(function(v, i, a) { return v > 1 })"), njs_str("-1") }, @@ -4299,6 +4367,16 @@ static njs_unit_test_t njs_test[] = "a.findIndex(function(v, i, a) { a.shift(); return v == 4 })"), njs_str("-1") }, + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" + "var r = Array.prototype.findIndex.call(o, function(el, i, arr) {return el == 'b'}); r"), + njs_str("1") }, + + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" + "Object.defineProperty(o, 'length', {get: () => 4});" + "Object.defineProperty(o, '3', {get: () => 'd'});" + "var r = Array.prototype.findIndex.call(o, function(el, i, arr) { return el == 'd' }); r"), + njs_str("3") }, + { njs_str("var a = [];" "a.map(function(v, i, a) { return v + 1 })"), njs_str("") }, @@ -4323,6 +4401,21 @@ static njs_unit_test_t njs_test[] = "a.map(function(v, i, a) { a.shift(); return v + 1 })"), njs_str("2,4,6,,,") }, + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" + "var r = Array.prototype.map.call(o, num => num + '1'); r"), + njs_str("a1,b1,c1") }, + + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" + "Object.defineProperty(o, 'length', {get: () => 4});" + "Object.defineProperty(o, '3', {get: () => 'd'});" + "var r = Array.prototype.map.call(o, function(el, i, arr) { return el + '1' }); r"), + njs_str("a1,b1,c1,d1") }, + + { njs_str("Array.prototype.map.call(new String('abc')," + " (val, idx, obj) => {return obj instanceof String})" + ".every(x => x === true)"), + njs_str("true") }, + { njs_str("var a = [];" "a.reduce(function(p, v, i, a) { return p + v })"), njs_str("TypeError: invalid index") }, @@ -4359,6 +4452,23 @@ static njs_unit_test_t njs_test[] = " { return a.concat(b) }, [])"), njs_str("0,1,2,3,4,5") }, + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" + "var reducer = (a, b) => a + b;" + "var a = Array.prototype.reduce.call(o, reducer); a"), + njs_str("abc") }, + + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" + "Object.defineProperty(o, 'length', {get: () => 4});" + "Object.defineProperty(o, '3', {get: () => 'd'});" + "var r = Array.prototype.reduce.call(o, (a, b) => a + b); r"), + njs_str("abcd") }, + + { njs_str("var o = {1: 'b', 2: 'c', 3: 'd'};" + "Object.defineProperty(o, 'length', {get: () => 4});" + "Object.defineProperty(o, '0', {get: () => 'a'});" + "var r = Array.prototype.reduce.call(o, (a, b) => a + b); r"), + njs_str("abcd") }, + { njs_str("var a = [];" "a.reduceRight(function(p, v, i, a) { return p + v })"), njs_str("TypeError: invalid index") },