/* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include #define njs_array_func(type) \ ((type << 1) | NJS_ARRAY_FUNC) #define njs_array_arg(type) \ ((type << 1) | NJS_ARRAY_ARG) #define njs_array_type(magic) (magic >> 1) #define njs_array_arg1(magic) (magic & 0x1) typedef enum { NJS_ARRAY_EVERY = 0, NJS_ARRAY_SOME, NJS_ARRAY_INCLUDES, NJS_ARRAY_INDEX_OF, NJS_ARRAY_FOR_EACH, NJS_ARRAY_FIND, NJS_ARRAY_FIND_INDEX, NJS_ARRAY_REDUCE, NJS_ARRAY_FILTER, NJS_ARRAY_MAP, } njs_array_iterator_fun_t; typedef enum { NJS_ARRAY_LAST_INDEX_OF = 0, NJS_ARRAY_REDUCE_RIGHT, } njs_array_reverse_iterator_fun_t; typedef enum { NJS_ARRAY_FUNC = 0, NJS_ARRAY_ARG } njs_array_iterator_arg_t; static njs_int_t njs_array_prototype_slice_copy(njs_vm_t *vm, njs_value_t *this, int64_t start, int64_t length, njs_value_t *retval); njs_array_t * njs_array_alloc(njs_vm_t *vm, njs_bool_t flat, uint64_t length, uint32_t spare) { uint64_t size; njs_int_t ret; njs_array_t *array; njs_value_t value; if (njs_slow_path(length > UINT32_MAX)) { goto overflow; } array = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_t)); if (njs_slow_path(array == NULL)) { goto memory_error; } size = length + spare; if (flat || size <= NJS_ARRAY_LARGE_OBJECT_LENGTH) { array->data = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), size * sizeof(njs_value_t)); if (njs_slow_path(array->data == NULL)) { goto memory_error; } } else { array->data = NULL; } array->start = array->data; njs_lvlhsh_init(&array->object.hash); array->object.shared_hash = vm->shared->array_instance_hash; array->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_ARRAY); array->object.slots = NULL; array->object.type = NJS_ARRAY; array->object.shared = 0; array->object.extensible = 1; array->object.error_data = 0; array->object.fast_array = (array->data != NULL); if (njs_fast_path(array->object.fast_array)) { array->size = size; array->length = length; } else { array->size = 0; array->length = 0; njs_set_array(&value, array); ret = njs_array_length_redefine(vm, &value, length, 1); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } return array; memory_error: njs_memory_error(vm); return NULL; overflow: njs_range_error(vm, "Invalid array length"); return NULL; } void njs_array_destroy(njs_vm_t *vm, njs_array_t *array) { if (array->data != NULL) { njs_mp_free(vm->mem_pool, array->data); } /* TODO: destroy keys. */ njs_mp_free(vm->mem_pool, array); } njs_int_t njs_array_convert_to_slow_array(njs_vm_t *vm, njs_array_t *array) { uint32_t i, length; njs_value_t value; njs_object_prop_t *prop; if (njs_slow_path(!array->object.fast_array)) { return NJS_OK; } njs_set_array(&value, array); array->object.fast_array = 0; length = array->length; for (i = 0; i < length; i++) { if (njs_is_valid(&array->start[i])) { prop = njs_object_property_add(vm, &value, njs_number_atom(i), 0); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } njs_value_assign(njs_prop_value(prop), &array->start[i]); } } njs_mp_free(vm->mem_pool, array->data); array->start = NULL; return NJS_OK; } njs_int_t njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value, uint32_t length, int writable) { njs_object_prop_t *prop; if (njs_slow_path(!njs_is_array(value))) { njs_internal_error(vm, "njs_array_length_redefine() " "applied to non-array"); return NJS_ERROR; } prop = njs_object_property_add(vm, value, NJS_ATOM_STRING_length, 1); if (njs_slow_path(prop == NULL)) { njs_internal_error(vm, "njs_array_length_redefine() " "cannot redefine \"length\""); return NJS_ERROR; } prop->writable = writable; prop->enumerable = 0; prop->configurable = 0; njs_value_number_set(njs_prop_value(prop), length); return NJS_OK; } njs_int_t njs_array_length_set(njs_vm_t *vm, njs_value_t *value, njs_object_prop_t *prev, njs_value_t *setval) { double num, idx; int64_t prev_length; uint32_t i, length; njs_int_t ret; njs_array_t *keys; ret = njs_value_to_number(vm, setval, &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } length = (uint32_t) njs_number_to_length(num); if ((double) length != num) { njs_range_error(vm, "Invalid array length"); return NJS_ERROR; } ret = njs_value_to_length(vm, njs_prop_value(prev), &prev_length); if (njs_slow_path(ret != NJS_OK)) { return ret; } keys = NULL; if (length < prev_length) { keys = njs_array_indices(vm, value); if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } if (keys->length != 0) { i = keys->length - 1; do { idx = njs_string_to_index(&keys->start[i]); if (idx >= length) { ret = njs_value_property_delete(vm, value, keys->start[i].atom_id, NULL, 1); if (njs_slow_path(ret == NJS_ERROR)) { goto done; } } } while (i-- != 0); } } ret = njs_array_length_redefine(vm, value, length, prev->writable); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = NJS_OK; done: if (keys != NULL) { njs_array_destroy(vm, keys); } return ret; } static njs_int_t njs_array_copy_within(njs_vm_t *vm, njs_value_t *array, int64_t to_pos, int64_t from_pos, int64_t count, njs_bool_t forward) { int64_t i, from, to, len; njs_int_t ret; njs_array_t *arr; njs_value_t value; njs_assert(to_pos >= 0); njs_assert(from_pos >= 0); if (njs_fast_path(njs_is_fast_array(array) && count > 0)) { arr = njs_array(array); len = arr->length; if (to_pos + count < len && from_pos + count < len) { memmove(&arr->start[to_pos], &arr->start[from_pos], count * sizeof(njs_value_t)); return NJS_OK; } } if (!forward) { from_pos += count - 1; to_pos += count - 1; } for (i = 0; i < count; i++) { if (forward) { from = from_pos + i; to = to_pos + i; } else { from = from_pos - i; to = to_pos - i; } ret = njs_value_property_i64(vm, array, from, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (ret == NJS_OK) { ret = njs_value_property_i64_set(vm, array, to, &value); } else { ret = njs_value_property_i64_delete(vm, array, to, NULL); } if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } } return NJS_OK; } njs_int_t njs_array_add(njs_vm_t *vm, njs_array_t *array, njs_value_t *value) { njs_int_t ret; ret = njs_array_expand(vm, array, 0, 1); if (njs_fast_path(ret == NJS_OK)) { array->start[array->length++] = *value; } return ret; } njs_int_t njs_array_string_add(njs_vm_t *vm, njs_array_t *array, const u_char *start, size_t size, size_t length) { njs_int_t ret; ret = njs_array_expand(vm, array, 0, 1); if (njs_fast_path(ret == NJS_OK)) { return njs_string_new(vm, &array->start[array->length++], start, size, length); } return ret; } njs_int_t njs_array_expand(njs_vm_t *vm, njs_array_t *array, uint32_t prepend, uint32_t append) { uint32_t free_before, free_after; uint64_t size; njs_value_t *start, *old; njs_assert(array->object.fast_array); free_before = array->start - array->data; free_after = array->size - array->length - free_before; if (njs_fast_path(free_before >= prepend && free_after >= append)) { return NJS_OK; } size = (uint64_t) prepend + array->length + append; if (size < 16) { size *= 2; } else { size += size / 2; } if (njs_slow_path(size > (UINT32_MAX / sizeof(njs_value_t)))) { goto memory_error; } start = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), size * sizeof(njs_value_t)); if (njs_slow_path(start == NULL)) { goto memory_error; } array->size = size; old = array->data; array->data = start; start += prepend; if (array->length != 0) { memcpy(start, array->start, array->length * sizeof(njs_value_t)); } array->start = start; if (old != NULL) { njs_mp_free(vm->mem_pool, old); } return NJS_OK; memory_error: njs_memory_error(vm); return NJS_ERROR; } static njs_int_t njs_array_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; uint32_t size; njs_value_t *value; njs_array_t *array; args = &args[1]; size = nargs - 1; if (size == 1 && njs_is_number(&args[0])) { num = njs_number(&args[0]); size = (uint32_t) njs_number_to_length(num); if ((double) size != num) { njs_range_error(vm, "Invalid array length"); return NJS_ERROR; } args = NULL; } array = njs_array_alloc(vm, size <= NJS_ARRAY_FLAT_MAX_LENGTH, size, NJS_ARRAY_SPARE); if (njs_fast_path(array != NULL)) { if (array->object.fast_array) { value = array->start; if (args == NULL) { while (size != 0) { njs_set_invalid(value); value++; size--; } } else { while (size != 0) { *value++ = *args++; size--; } } } njs_set_array(retval, array); return NJS_OK; } return NJS_ERROR; } static njs_int_t njs_array_from(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length, i; njs_int_t ret; njs_array_t *array; njs_value_t *this, *items, *mapfn; njs_value_t arguments[3], value, result; njs_function_t *function; mapfn = njs_arg(args, nargs, 2); if (njs_slow_path(!njs_is_function_or_undefined(mapfn))) { njs_type_error(vm, "\"mapfn\" argument is not callable"); return NJS_ERROR; } function = NULL; if (njs_is_function(mapfn)) { function = njs_function(mapfn); } items = njs_arg(args, nargs, 1); ret = njs_value_to_object(vm, items); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, items, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } this = njs_argument(args, 0); if (njs_is_constructor(this)) { njs_set_number(&arguments[0], length); ret = njs_value_construct(vm, this, arguments, 1, &value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { array = njs_array_alloc(vm, 1, length, 0); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } njs_set_array(&value, array); } arguments[0] = *njs_arg(args, nargs, 3); for (i = 0; i < length; i++) { ret = njs_value_property_i64(vm, items, i, &result); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (function != NULL) { njs_value_assign(&arguments[1], &result); njs_set_number(&arguments[2], i); ret = njs_function_apply(vm, function, arguments, 3, &result); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } ret = njs_value_create_data_prop_i64(vm, &value, i, &result, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } } ret = njs_object_length_set(vm, &value, length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_value_assign(retval, &value); return NJS_OK; } static njs_int_t njs_array_is_array(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_set_boolean(retval, nargs > 1 && njs_is_array(&args[1])); return NJS_OK; } static njs_int_t njs_array_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint32_t length, i; njs_array_t *array; length = nargs > 1 ? nargs - 1 : 0; array = njs_array_alloc(vm, 0, length, NJS_ARRAY_SPARE); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } if (array->object.fast_array) { for (i = 0; i < length; i++) { array->start[i] = args[i + 1]; } } njs_set_array(retval, array); return NJS_OK; } static const njs_object_prop_init_t njs_array_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("Array"), NJS_DECLARE_PROP_HANDLER(STRING_prototype, njs_object_prototype_create, 0, 0), NJS_DECLARE_PROP_NATIVE(STRING_from, njs_array_from, 1, 0), NJS_DECLARE_PROP_NATIVE(STRING_isArray, njs_array_is_array, 1, 0), NJS_DECLARE_PROP_NATIVE(STRING_of, njs_array_of, 0, 0), }; const njs_object_init_t njs_array_constructor_init = { njs_array_constructor_properties, njs_nitems(njs_array_constructor_properties), }; static njs_int_t njs_array_length(njs_vm_t *vm,njs_object_prop_t *prop, uint32_t unused, 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; proto = njs_object(value); if (njs_fast_path(setval == NULL)) { array = njs_object_proto_lookup(proto, NJS_ARRAY, njs_array_t); if (njs_slow_path(array == NULL)) { njs_set_undefined(retval); return NJS_DECLINED; } njs_set_number(retval, array->length); return NJS_OK; } if (proto->type != NJS_ARRAY) { njs_set_undefined(retval); return NJS_DECLINED; } if (njs_slow_path(!njs_is_valid(setval))) { return NJS_DECLINED; } ret = njs_value_to_number(vm, setval, &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } length = (uint32_t) njs_number_to_length(num); if ((double) length != num) { njs_range_error(vm, "Invalid array length"); return NJS_ERROR; } array = (njs_array_t *) proto; if (njs_fast_path(array->object.fast_array)) { if (njs_fast_path(length <= NJS_ARRAY_LARGE_OBJECT_LENGTH)) { size = (int64_t) length - array->length; if (size > 0) { ret = njs_array_expand(vm, array, 0, size); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } val = &array->start[array->length]; do { njs_set_invalid(val); val++; size--; } while (size != 0); } array->length = length; njs_value_assign(retval, setval); return NJS_OK; } ret = njs_array_convert_to_slow_array(vm, array); if (njs_slow_path(ret != NJS_OK)) { return ret; } } prop->type = NJS_PROPERTY; njs_set_number(njs_prop_value(prop), length); njs_value_assign(retval, setval); return NJS_OK; } static njs_int_t njs_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t start, end, length, object_length; njs_int_t ret; njs_value_t *this; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, this, &object_length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } length = object_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; if (start < 0) { start = 0; } } if (start >= length) { start = 0; length = 0; } else { if (njs_is_defined(njs_arg(args, nargs, 2))) { ret = njs_value_to_integer(vm, njs_argument(args, 2), &end); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { end = length; } if (end < 0) { end += length; } if (length >= end) { length = end - start; if (length < 0) { start = 0; length = 0; } } else { length -= start; } } return njs_array_prototype_slice_copy(vm, this, start, length, retval); } static njs_int_t njs_array_prototype_slice_copy(njs_vm_t *vm, njs_value_t *this, int64_t start, int64_t length, njs_value_t *retval) { u_char *c, buf[4]; size_t size; uint32_t n; njs_int_t ret; njs_array_t *array, *keys; njs_value_t *value, *last, val, self; const u_char *src, *end; njs_slice_prop_t string_slice; njs_string_prop_t string; keys = NULL; array = njs_array_alloc(vm, 0, length, NJS_ARRAY_SPARE); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } if (njs_slow_path(length == 0)) { ret = NJS_OK; goto done; } n = 0; if (njs_fast_path(array->object.fast_array)) { if (njs_is_string(this) || njs_is_object_string(this)) { if (njs_is_object_string(this)) { this = njs_object_value(this); } string_slice.start = start; string_slice.length = length; string_slice.string_length = njs_string_prop(vm, &string, this); njs_string_slice_string_prop(&string, &string, &string_slice); src = string.start; end = src + string.size; do { value = &array->start[n++]; c = buf; c = njs_utf8_copy(c, &src, end); size = c - buf; ret = njs_string_new(vm, value, buf, size, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } length--; } while (length != 0); } else if (njs_is_object(this)) { last = &array->start[length]; for (value = array->start; value < last; value++, start++) { ret = njs_value_property_i64(vm, this, start, value); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_ERROR) { return NJS_ERROR; } njs_set_invalid(value); } } } else { /* Primitive types. */ value = array->start; do { njs_set_invalid(value++); length--; } while (length != 0); } ret = NJS_OK; goto done; } njs_set_array(&self, array); if (njs_fast_object(length)) { do { ret = njs_value_property_i64(vm, this, start++, &val); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (ret == NJS_OK) { ret = njs_value_property_i64_set(vm, &self, start, &val); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } length--; } while (length != 0); ret = NJS_OK; goto done; } keys = njs_array_indices(vm, this); if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } for (n = 0; n < keys->length; n++) { ret = njs_value_property(vm, this, keys->start[n].atom_id, &val); if (njs_slow_path(ret == NJS_ERROR)) { goto done; } ret = njs_value_property_set(vm, &self, keys->start[n].atom_id, &val); if (njs_slow_path(ret == NJS_ERROR)) { goto done; } } ret = NJS_OK; done: if (keys != NULL) { njs_array_destroy(vm, keys); } njs_set_array(retval, array); return ret; } static njs_int_t njs_array_prototype_push(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length; njs_int_t ret; njs_uint_t i; njs_array_t *array; njs_value_t *this; length = 0; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_fast_array(this)) { array = njs_array(this); if (nargs != 0) { ret = njs_array_expand(vm, array, 0, nargs); if (njs_slow_path(ret != NJS_OK)) { return ret; } for (i = 1; i < nargs; i++) { array->start[array->length++] = args[i]; } } njs_set_number(retval, array->length); return NJS_OK; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_slow_path((length + nargs - 1) > NJS_MAX_LENGTH)) { njs_type_error(vm, "Invalid length"); return NJS_ERROR; } for (i = 1; i < nargs; i++) { ret = njs_value_property_i64_set(vm, this, length++, &args[i]); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } ret = njs_object_length_set(vm, this, length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_set_number(retval, length); return NJS_OK; } static njs_int_t njs_array_prototype_pop(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length; njs_int_t ret; njs_value_t *this; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (length == 0) { ret = njs_object_length_set(vm, this, length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_set_undefined(retval); return NJS_OK; } ret = njs_value_property_i64(vm, this, --length, retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_is_fast_array(this)) { njs_array(this)->length--; } else { ret = njs_value_property_i64_delete(vm, this, length, NULL); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } ret = njs_object_length_set(vm, this, length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } return NJS_OK; } static njs_int_t njs_array_prototype_unshift(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double idx; int64_t from, to, length; njs_int_t ret; njs_uint_t n; njs_array_t *array, *keys; njs_value_t *this, entry; this = njs_argument(args, 0); length = 0; n = nargs - 1; ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_fast_path(njs_is_fast_array(this))) { array = njs_array(this); if (n != 0) { ret = njs_array_expand(vm, array, n, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } array->length += n; n = nargs; do { n--; array->start--; array->start[0] = args[n]; } while (n > 1); } njs_set_number(retval, array->length); return NJS_OK; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (n == 0) { goto done; } if (njs_slow_path((length + n) > NJS_MAX_LENGTH)) { njs_type_error(vm, "Invalid length"); return NJS_ERROR; } if (!njs_fast_object(length)) { keys = njs_array_indices(vm, this); if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } from = keys->length; while (from > 0) { ret = njs_value_property_delete(vm, this, keys->start[--from].atom_id, &entry, 1); if (njs_slow_path(ret == NJS_ERROR)) { njs_array_destroy(vm, keys); return ret; } if (ret == NJS_OK) { idx = njs_string_to_index(&keys->start[from]) + n; ret = njs_value_property_i64_set(vm, this, idx, &entry); if (njs_slow_path(ret == NJS_ERROR)) { njs_array_destroy(vm, keys); return ret; } } } njs_array_destroy(vm, keys); length += n; goto copy; } from = length; length += n; to = length; while (from > 0) { ret = njs_value_property_i64_delete(vm, this, --from, &entry); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } to--; if (ret == NJS_OK) { ret = njs_value_property_i64_set(vm, this, to, &entry); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } } copy: for (n = 1; n < nargs; n++) { ret = njs_value_property_i64_set(vm, this, n - 1, &args[n]); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } done: ret = njs_object_length_set(vm, this, length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_set_number(retval, length); return NJS_OK; } static njs_int_t njs_array_prototype_shift(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t i, length; njs_int_t ret; njs_array_t *array; njs_value_t *this, entry; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (length == 0) { ret = njs_object_length_set(vm, this, length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_set_undefined(retval); return NJS_OK; } ret = njs_value_property_i64(vm, this, 0, retval); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (njs_is_fast_array(this)) { array = njs_array(this); array->start++; array->length--; } else { ret = njs_value_property_i64_delete(vm, this, 0, retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } for (i = 1; i < length; i++) { ret = njs_value_property_i64_delete(vm, this, i, &entry); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (ret == NJS_OK) { ret = njs_value_property_i64_set(vm, this, i - 1, &entry); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } } ret = njs_object_length_set(vm, this, length - 1); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } return NJS_OK; } static njs_int_t njs_array_prototype_splice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t i, n, start, length, items, delta, delete; njs_int_t ret; njs_value_t *this, value, del_object; njs_array_t *array, *deleted; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start); if (njs_slow_path(ret != NJS_OK)) { return ret; } start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length); items = 0; delete = 0; if (nargs == 2) { delete = length - start; } else if (nargs > 2) { items = nargs - 3; ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &delete); if (njs_slow_path(ret != NJS_OK)) { return ret; } delete = njs_min(njs_max(delete, 0), length - start); } delta = items - delete; if (njs_slow_path((length + delta) > NJS_MAX_LENGTH)) { njs_type_error(vm, "Invalid length"); return NJS_ERROR; } /* TODO: ArraySpeciesCreate(). */ deleted = njs_array_alloc(vm, 0, delete, 0); if (njs_slow_path(deleted == NULL)) { return NJS_ERROR; } njs_set_array(&del_object, deleted); if (njs_fast_path(njs_is_fast_array(this) && deleted->object.fast_array && delete <= deleted->length && start + delete <= njs_array_len(this))) { array = njs_array(this); for (i = 0, n = start; i < delete; i++, n++) { njs_value_assign(&deleted->start[i], &array->start[n]); } } else { for (i = 0, n = start; i < delete; i++, n++) { ret = njs_value_property_i64(vm, this, n, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (ret == NJS_OK) { ret = njs_value_create_data_prop_i64(vm, &del_object, i, &value, 0); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } else { if (deleted->object.fast_array) { njs_set_invalid(&deleted->start[i]); } } } } ret = njs_object_length_set(vm, &del_object, delete); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (delta != 0) { ret = njs_array_copy_within(vm, this, start + items, start + delete, length - (start + delete), delta < 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } for (i = length - 1; i >= length + delta; i--) { ret = njs_value_property_i64_delete(vm, this, i, NULL); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } } } /* Copy new items. */ for (i = 3, n = start; items-- > 0; i++, n++) { ret = njs_value_property_i64_set(vm, this, n, &args[i]); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } } ret = njs_object_length_set(vm, this, length + delta); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_set_array(retval, deleted); return NJS_OK; } static njs_int_t njs_array_prototype_to_spliced(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t i, n, r, start, length, to_insert, to_skip, new_length; njs_int_t ret; njs_value_t *this, a, value; njs_array_t *array; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start); if (njs_slow_path(ret != NJS_OK)) { return ret; } start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length); to_insert = 0; to_skip = 0; if (nargs == 2) { to_skip = length - start; } else if (nargs > 2) { to_insert = nargs - 3; ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &to_skip); if (njs_slow_path(ret != NJS_OK)) { return ret; } to_skip = njs_min(njs_max(to_skip, 0), length - start); } new_length = length + to_insert - to_skip; if (njs_slow_path(new_length > NJS_MAX_LENGTH)) { njs_type_error(vm, "Invalid length"); return NJS_ERROR; } array = njs_array_alloc(vm, 0, new_length, 0); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } njs_set_array(&a, array); for (i = 0; i < start; i++) { ret = njs_value_property_i64(vm, this, i, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_create_data_prop_i64(vm, &a, i, &value, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } } for (n = 3; to_insert-- > 0; i++, n++) { ret = njs_value_create_data_prop_i64(vm, &a, i, &args[n], 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } } r = start + to_skip; while (i < new_length) { ret = njs_value_property_i64(vm, this, r, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_create_data_prop_i64(vm, &a, i, &value, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } r++; i++; } njs_set_array(retval, array); return NJS_OK; } static njs_int_t njs_array_prototype_reverse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length, l, h; njs_int_t ret, lret, hret; njs_value_t value, lvalue, hvalue, *this; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_slow_path(length < 2)) { njs_value_assign(retval, this); return NJS_OK; } for (l = 0, h = length - 1; l < h; l++, h--) { lret = njs_value_property_i64(vm, this, l, &lvalue); if (njs_slow_path(lret == NJS_ERROR)) { return NJS_ERROR; } hret = njs_value_property_i64(vm, this, h, &hvalue); if (njs_slow_path(hret == NJS_ERROR)) { return NJS_ERROR; } if (lret == NJS_OK) { ret = njs_value_property_i64_set(vm, this, h, &lvalue); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (hret == NJS_OK) { ret = njs_value_property_i64_set(vm, this, l, &hvalue); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } } else { ret = njs_value_property_i64_delete(vm, this, l, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } } } else if (hret == NJS_OK) { ret = njs_value_property_i64_set(vm, this, l, &hvalue); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_property_i64_delete(vm, this, h, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } } } njs_value_assign(retval, this); return NJS_OK; } static njs_int_t njs_array_prototype_to_reversed(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length, i; njs_int_t ret; njs_array_t *array; njs_value_t *this, a, value; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_length(vm, this, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } array = njs_array_alloc(vm, 0, length, 0); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } njs_set_array(&a, array); for (i = 0; i < length; i++) { ret = njs_value_property_i64(vm, this, length - i - 1, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_create_data_prop_i64(vm, &a, i, &value, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } } njs_set_array(retval, array); return NJS_OK; } njs_int_t njs_array_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t value; njs_flathsh_query_t lhq; if (njs_is_object(njs_argument(args, 0))) { lhq.proto = &njs_object_hash_proto; lhq.key_hash = NJS_ATOM_STRING_join; ret = njs_object_property(vm, njs_object(njs_argument(args, 0)), &lhq, &value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_is_function(&value)) { return njs_function_apply(vm, njs_function(&value), args, nargs, retval); } } return njs_object_prototype_to_string(vm, args, nargs, unused, retval); } static njs_int_t njs_array_prototype_join(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *p; int64_t i, size, len, length; njs_int_t ret; njs_chb_t chain; njs_value_t *value, *this, entry; njs_string_prop_t separator, string; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } value = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_is_string(value))) { if (njs_is_undefined(value)) { value = NULL; } else { ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } if (value != NULL) { (void) njs_string_prop(vm, &separator, value); } else { separator.start = (u_char *) ","; separator.length = 1; separator.size = 1; } if (njs_slow_path(!njs_is_object(this))) { njs_set_empty_string(vm, retval); return NJS_OK; } length = 0; ret = njs_object_length(vm, this, &len); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_slow_path(len == 0)) { njs_set_empty_string(vm, retval); return NJS_OK; } value = &entry; NJS_CHB_MP_INIT(&chain, njs_vm_memory_pool(vm)); for (i = 0; i < len; i++) { ret = njs_value_property_i64(vm, this, i, value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (!njs_is_null_or_undefined(value)) { if (!njs_is_string(value)) { ret = njs_value_to_chain(vm, &chain, value); if (njs_slow_path(ret < NJS_OK)) { return ret; } length += ret; } else { (void) njs_string_prop(vm, &string, value); length += string.length; njs_chb_append(&chain, string.start, string.size); } } length += separator.length; njs_chb_append(&chain, separator.start, separator.size); if (njs_slow_path(length > NJS_STRING_MAX_LENGTH)) { njs_range_error(vm, "invalid string length"); return NJS_ERROR; } } njs_chb_drop(&chain, separator.size); size = njs_chb_size(&chain); if (njs_slow_path(size < 0)) { njs_memory_error(vm); return NJS_ERROR; } length -= separator.length; p = njs_string_alloc(vm, retval, size, length); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } njs_chb_join_to(&chain, p); njs_chb_destroy(&chain); return NJS_OK; } static int njs_array_indices_handler(const void *first, const void *second, void *ctx) { double num1, num2; int64_t diff, cmp_res; njs_str_t str1, str2; const njs_value_t *val1, *val2; val1 = first; val2 = second; num1 = njs_string_to_index(val1); num2 = njs_string_to_index(val2); if (!isnan(num1) || !isnan(num2)) { if (isnan(num1)) { return 1; } if (isnan(num2)) { return -1; } diff = (int64_t) (num1 - num2); if (diff < 0) { return -1; } return diff != 0; } /* properties from njs_value_own_enumerate() are always intialized. */ njs_string_get_unsafe(val1, &str1); njs_string_get_unsafe(val2, &str2); cmp_res = strncmp((const char *) str1.start, (const char *) str2.start, njs_min(str1.length, str2.length)); if (cmp_res == 0) { if (str1.length < str2.length) { return -1; } else if (str1.length > str2.length) { return 1; } else { return 0; } } return cmp_res; } int njs_array_indices_handler_nums(const void *first, const void *second, void *ctx) { double num1, num2; int64_t diff; const njs_value_t *val1, *val2; val1 = first; val2 = second; num1 = njs_string_to_index(val1); num2 = njs_string_to_index(val2); if (!isnan(num1) || !isnan(num2)) { if (isnan(num1)) { if (!isnan(num2)) { return 1; } else { return 0; } } if (isnan(num2)) { return -1; } diff = (int64_t) (num1 - num2); if (diff < 0) { return -1; } return diff != 0; } return 0; } njs_array_t * njs_array_keys(njs_vm_t *vm, njs_value_t *object, njs_bool_t all) { njs_array_t *keys; keys = njs_value_own_enumerate(vm, object, NJS_ENUM_KEYS | NJS_ENUM_STRING | (!all ? NJS_ENUM_ENUMERABLE_ONLY : 0)); if (njs_slow_path(keys == NULL)) { return NULL; } njs_qsort(keys->start, keys->length, sizeof(njs_value_t), njs_array_indices_handler, NULL); return keys; } njs_array_t * njs_array_indices(njs_vm_t *vm, njs_value_t *object) { double idx; uint32_t i; njs_array_t *keys; keys = njs_array_keys(vm, object, 1); if (njs_slow_path(keys == NULL)) { return NULL; } for (i = 0; i < keys->length; i++) { idx = njs_string_to_index(&keys->start[i]); if (isnan(idx)) { keys->length = i; break; } } return keys; } njs_inline njs_int_t njs_is_concat_spreadable(njs_vm_t *vm, njs_value_t *value) { njs_int_t ret; njs_value_t retval; if (njs_slow_path(!njs_is_object(value))) { return NJS_DECLINED; } ret = njs_value_property(vm, value, NJS_ATOM_SYMBOL_isConcatSpreadable, &retval); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (njs_is_defined(&retval)) { return njs_bool(&retval) ? NJS_OK : NJS_DECLINED; } return njs_is_array(value) ? NJS_OK : NJS_DECLINED; } static njs_int_t njs_array_prototype_concat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double idx; int64_t k, len, length; njs_int_t ret; njs_uint_t i; njs_value_t this, value, *e; njs_array_t *array, *keys; ret = njs_value_to_object(vm, &args[0]); if (njs_slow_path(ret != NJS_OK)) { return ret; } /* TODO: ArraySpeciesCreate(). */ array = njs_array_alloc(vm, 0, 0, NJS_ARRAY_SPARE); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } njs_set_array(&this, array); len = 0; length = 0; for (i = 0; i < nargs; i++) { e = njs_argument(args, i); ret = njs_is_concat_spreadable(vm, e); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (ret == NJS_OK) { ret = njs_object_length(vm, e, &len); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_slow_path((length + len) > NJS_MAX_LENGTH)) { njs_type_error(vm, "Invalid length"); return NJS_ERROR; } if (njs_is_fast_array(e) || njs_fast_object(len)) { for (k = 0; k < len; k++, length++) { ret = njs_value_property_i64(vm, e, k, &value); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_ERROR) { return NJS_ERROR; } njs_set_invalid(&value); } ret = njs_value_property_i64_set(vm, &this, length, &value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } continue; } keys = njs_array_indices(vm, e); if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } for (k = 0; k < keys->length; k++) { ret = njs_value_property_val(vm, e, &keys->start[k], &value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (ret == NJS_OK) { idx = njs_string_to_index(&keys->start[k]) + length; ret = njs_value_property_i64_set(vm, &this, idx, &value); if (njs_slow_path(ret == NJS_ERROR)) { njs_array_destroy(vm, keys); return ret; } } } njs_array_destroy(vm, keys); length += len; continue; } if (njs_slow_path((length + len) >= NJS_MAX_LENGTH)) { njs_type_error(vm, "Invalid length"); return NJS_ERROR; } ret = njs_value_property_i64_set(vm, &this, length, e); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } length++; } ret = njs_object_length_set(vm, &this, length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_value_assign(retval, &this); return NJS_OK; } 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_value_t *retval) { int64_t i, length, start, end; njs_int_t ret; njs_array_t *array; njs_value_t *this, *value; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &start); if (njs_slow_path(ret != NJS_OK)) { return ret; } start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length); if (njs_is_undefined(njs_arg(args, nargs, 3))) { end = length; } else { 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); value = njs_arg(args, nargs, 1); if (njs_is_fast_array(this)) { array = njs_array(this); end = njs_min(end, array->length); for (i = start; i < end; i++) { njs_value_assign(&array->start[i], value); } njs_value_assign(retval, this); return NJS_OK; } while (start < end) { ret = njs_value_property_i64_set(vm, this, start++, value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } njs_value_assign(retval, this); return NJS_OK; } njs_inline njs_int_t njs_array_iterator_call(njs_vm_t *vm, njs_iterator_args_t *args, const njs_value_t *entry, uint32_t n, njs_value_t *retval) { njs_value_t arguments[3]; arguments[0] = *entry; njs_set_number(&arguments[1], n); njs_value_assign(&arguments[2], &args->value); return njs_function_call(vm, args->function, njs_value_arg(&args->argument), arguments, 3, retval); } static njs_int_t njs_array_handler_every(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { njs_int_t ret; if (njs_is_valid(entry)) { ret = njs_array_iterator_call(vm, args, entry, n, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!njs_is_true(retval)) { njs_value_assign(retval, &njs_value_false); return NJS_DONE; } } return NJS_OK; } static njs_int_t njs_array_handler_some(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { njs_int_t ret; if (njs_is_valid(entry)) { ret = njs_array_iterator_call(vm, args, entry, n, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_true(retval)) { njs_value_assign(retval, &njs_value_true); return NJS_DONE; } } return NJS_OK; } static njs_int_t njs_array_handler_includes(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { if (!njs_is_valid(entry)) { entry = njs_value_arg(&njs_value_undefined); } if (njs_values_same_zero(vm, njs_value_arg(&args->argument), entry)) { njs_set_true(retval); return NJS_DONE; } return NJS_OK; } static njs_int_t njs_array_handler_index_of(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { if (njs_values_strict_equal(vm, njs_value_arg(&args->argument), entry)) { njs_set_number(retval, n); return NJS_DONE; } return NJS_OK; } static njs_int_t njs_array_handler_for_each(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { if (njs_is_valid(entry)) { return njs_array_iterator_call(vm, args, entry, n, retval); } return NJS_OK; } static njs_int_t njs_array_handler_find(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { njs_int_t ret; njs_value_t copy; if (njs_is_valid(entry)) { copy = *entry; } else { njs_set_undefined(©); } ret = njs_array_iterator_call(vm, args, ©, n, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_true(retval)) { njs_value_assign(retval, ©); return NJS_DONE; } return NJS_OK; } static njs_int_t njs_array_handler_find_index(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { njs_int_t ret; njs_value_t copy; if (njs_is_valid(entry)) { copy = *entry; } else { njs_set_undefined(©); } ret = njs_array_iterator_call(vm, args, ©, n, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_true(retval)) { njs_set_number(retval, n); return NJS_DONE; } return NJS_OK; } static njs_int_t njs_array_handler_reduce(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { njs_int_t ret; njs_value_t arguments[5]; if (njs_is_valid(entry)) { if (!njs_value_is_valid(njs_value_arg(&args->argument))) { njs_value_assign(&args->argument, entry); return NJS_OK; } njs_set_undefined(&arguments[0]); njs_value_assign(&arguments[1], &args->argument); arguments[2] = *entry; njs_set_number(&arguments[3], n); njs_value_assign(&arguments[4], &args->value); ret = njs_function_apply(vm, args->function, arguments, 5, njs_value_arg(&args->argument)); if (njs_slow_path(ret != NJS_OK)) { return ret; } } return NJS_OK; } static njs_int_t njs_array_handler_filter(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { njs_int_t ret; njs_value_t copy; if (njs_is_valid(entry)) { copy = *entry; ret = njs_array_iterator_call(vm, args, ©, n, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_true(retval)) { ret = njs_array_add(vm, args->data, ©); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } return NJS_OK; } static njs_int_t njs_array_handler_map(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { njs_int_t ret; njs_array_t *array; njs_value_t this; array = args->data; if (array->object.fast_array) { njs_set_invalid(&array->start[n]); } if (njs_is_valid(entry)) { ret = njs_array_iterator_call(vm, args, entry, n, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_valid(retval)) { if (array->object.fast_array) { njs_value_assign(&array->start[n], retval); } else { njs_set_array(&this, array); ret = njs_value_property_i64_set(vm, &this, n, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } } return NJS_OK; } static njs_int_t njs_array_prototype_iterator(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { int64_t i, length; njs_int_t ret; njs_array_t *array; njs_iterator_args_t iargs; njs_iterator_handler_t handler; njs_value_assign(&iargs.value, njs_argument(args, 0)); ret = njs_value_to_object(vm, njs_value_arg(&iargs.value)); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_length(vm, njs_value_arg(&iargs.value), &iargs.to); if (njs_slow_path(ret != NJS_OK)) { return ret; } iargs.from = 0; if (njs_array_arg1(magic) == NJS_ARRAY_FUNC) { if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) { njs_type_error(vm, "callback argument is not callable"); return NJS_ERROR; } iargs.function = njs_function(njs_argument(args, 1)); njs_value_assign(&iargs.argument, njs_arg(args, nargs, 2)); } else { njs_value_assign(&iargs.argument, njs_arg(args, nargs, 1)); } switch (njs_array_type(magic)) { case NJS_ARRAY_EVERY: handler = njs_array_handler_every; break; case NJS_ARRAY_SOME: handler = njs_array_handler_some; break; case NJS_ARRAY_INCLUDES: case NJS_ARRAY_INDEX_OF: switch (njs_array_type(magic)) { case NJS_ARRAY_INCLUDES: handler = njs_array_handler_includes; if (iargs.to == 0) { goto done; } break; default: handler = njs_array_handler_index_of; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &iargs.from); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (iargs.from < 0) { iargs.from += iargs.to; if (iargs.from < 0) { iargs.from = 0; } } break; case NJS_ARRAY_FOR_EACH: handler = njs_array_handler_for_each; break; case NJS_ARRAY_FIND: handler = njs_array_handler_find; break; case NJS_ARRAY_FIND_INDEX: handler = njs_array_handler_find_index; break; case NJS_ARRAY_REDUCE: handler = njs_array_handler_reduce; if (nargs <= 2) { njs_value_invalid_set(njs_value_arg(&iargs.argument)); } break; case NJS_ARRAY_FILTER: case NJS_ARRAY_MAP: default: if (njs_array_type(magic) == NJS_ARRAY_FILTER) { length = 0; handler = njs_array_handler_filter; } else { length = iargs.to; handler = njs_array_handler_map; } array = njs_array_alloc(vm, 0, length, NJS_ARRAY_SPARE); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } if (array->object.fast_array) { for (i = 0; i < length; i++) { njs_set_invalid(&array->start[i]); } } iargs.data = array; break; } ret = njs_object_iterate(vm, &iargs, handler, retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (ret == NJS_DONE) { return NJS_OK; } done: /* Default values. */ switch (njs_array_type(magic)) { case NJS_ARRAY_EVERY: njs_set_boolean(retval, 1); break; case NJS_ARRAY_SOME: case NJS_ARRAY_INCLUDES: njs_set_boolean(retval, 0); break; case NJS_ARRAY_INDEX_OF: case NJS_ARRAY_FIND_INDEX: njs_set_number(retval, -1); break; case NJS_ARRAY_FOR_EACH: case NJS_ARRAY_FIND: njs_set_undefined(retval); break; case NJS_ARRAY_REDUCE: if (!njs_value_is_valid(njs_value_arg(&iargs.argument))) { njs_type_error(vm, "Reduce of empty object with no initial value"); return NJS_ERROR; } njs_value_assign(retval, njs_value_arg(&iargs.argument)); break; case NJS_ARRAY_FILTER: case NJS_ARRAY_MAP: default: njs_set_array(retval, iargs.data); } return NJS_OK; } static njs_int_t njs_array_prototype_reverse_iterator(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t type, njs_value_t *retval) { int64_t from, length; njs_int_t ret; njs_iterator_args_t iargs; njs_iterator_handler_t handler; njs_value_assign(&iargs.value, njs_argument(args, 0)); ret = njs_value_to_object(vm, njs_value_arg(&iargs.value)); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_value_assign(&iargs.argument, njs_arg(args, nargs, 1)); ret = njs_value_length(vm, njs_value_arg(&iargs.value), &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } switch (type) { case NJS_ARRAY_LAST_INDEX_OF: handler = njs_array_handler_index_of; if (length == 0) { goto done; } if (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; } if (from >= 0) { from = njs_min(from, length - 1); } else if (from < 0) { from += length; } break; case NJS_ARRAY_REDUCE_RIGHT: default: handler = njs_array_handler_reduce; if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) { njs_type_error(vm, "callback argument is not callable"); return NJS_ERROR; } iargs.function = njs_function(njs_argument(args, 1)); njs_value_invalid_set(njs_value_arg(&iargs.argument)); if (nargs > 2) { njs_value_assign(&iargs.argument, njs_argument(args, 2)); } else if (length == 0) { goto done; } from = length - 1; break; } iargs.from = from; iargs.to = 0; ret = njs_object_iterate_reverse(vm, &iargs, handler, retval); if (njs_fast_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (ret == NJS_DONE) { return NJS_OK; } done: switch (type) { case NJS_ARRAY_LAST_INDEX_OF: njs_set_number(retval, -1); break; case NJS_ARRAY_REDUCE_RIGHT: default: if (!njs_value_is_valid(njs_value_arg(&iargs.argument))) { njs_type_error(vm, "Reduce of empty object with no initial value"); return NJS_ERROR; } njs_value_assign(retval, njs_value_arg(&iargs.argument)); break; } return NJS_OK; } typedef struct { njs_value_t value; njs_value_t *str; int64_t pos; } njs_array_sort_slot_t; typedef struct { njs_vm_t *vm; njs_function_t *function; njs_bool_t exception; njs_arr_t strings; } njs_array_sort_ctx_t; static int njs_array_compare(const void *a, const void *b, void *c) { double num; njs_int_t ret; njs_value_t arguments[3], retval; njs_array_sort_ctx_t *ctx; njs_array_sort_slot_t *aslot, *bslot; ctx = c; if (ctx->exception) { return 0; } aslot = (njs_array_sort_slot_t *) a; bslot = (njs_array_sort_slot_t *) b; if (ctx->function != NULL) { njs_set_undefined(&arguments[0]); arguments[1] = aslot->value; arguments[2] = bslot->value; ret = njs_function_apply(ctx->vm, ctx->function, arguments, 3, &retval); if (njs_slow_path(ret != NJS_OK)) { goto exception; } ret = njs_value_to_number(ctx->vm, &retval, &num); if (njs_slow_path(ret != NJS_OK)) { goto exception; } if (njs_slow_path(isnan(num))) { return 0; } if (num != 0) { return (num > 0) - (num < 0); } goto compare_same; } if (aslot->str == NULL) { aslot->str = njs_arr_add(&ctx->strings); ret = njs_value_to_string(ctx->vm, aslot->str, &aslot->value); if (njs_slow_path(ret != NJS_OK)) { goto exception; } } if (bslot->str == NULL) { bslot->str = njs_arr_add(&ctx->strings); ret = njs_value_to_string(ctx->vm, bslot->str, &bslot->value); if (njs_slow_path(ret != NJS_OK)) { goto exception; } } ret = njs_string_cmp(ctx->vm, aslot->str, bslot->str); if (ret != 0) { return ret; } compare_same: /* Ensures stable sorting. */ return (aslot->pos > bslot->pos) - (aslot->pos < bslot->pos); exception: ctx->exception = 1; return 0; } static njs_array_sort_slot_t * njs_sort_indexed_properties(njs_vm_t *vm, njs_value_t *obj, int64_t length, njs_function_t *compare, njs_bool_t skip_holes, int64_t *nslots, int64_t *nunds) { int64_t i, ilength, nlen; njs_int_t ret; njs_array_t *array, *keys; njs_value_t *start, *strings, key; njs_array_sort_ctx_t ctx; njs_array_sort_slot_t *p, *end, *slots, *newslots; njs_assert(length != 0); slots = NULL; keys = NULL; ctx.vm = vm; ctx.function = compare; ctx.strings.separate = 0; ctx.strings.pointer = 0; ctx.exception = 0; if (njs_fast_path(njs_is_fast_array(obj))) { array = njs_array(obj); start = array->start; slots = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_sort_slot_t) * length); if (njs_slow_path(slots == NULL)) { njs_memory_error(vm); return NULL; } *nunds = 0; p = slots; for (i = 0; i < length; i++) { if (njs_fast_path(njs_is_valid(&start[i]))) { /* not an empty value at index i. */ njs_value_assign(&p->value, &start[i]); } else { ret = njs_uint32_to_string(vm, &key, i); if (njs_slow_path(ret != NJS_OK)) { goto exception; } ret = njs_value_property_val(vm, obj, &key, &p->value); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } if (ret == NJS_DECLINED && skip_holes) { continue; } } if (njs_slow_path(njs_is_undefined(&p->value))) { (*nunds)++; continue; } p->pos = i; p->str = NULL; p++; } *nslots = p - slots; } else { if (skip_holes) { keys = njs_array_indices(vm, obj); if (njs_slow_path(keys == NULL)) { return NULL; } slots = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_sort_slot_t) * keys->length); if (njs_slow_path(slots == NULL)) { njs_memory_error(vm); ret = NJS_ERROR; goto exception; } *nunds = 0; p = slots; ilength = njs_min(keys->length, length); for (i = 0; i < ilength; i++) { ret = njs_value_property_val(vm, obj, &keys->start[i], &p->value); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } if (ret == NJS_DECLINED) { continue; } if (njs_is_undefined(&p->value)) { (*nunds)++; continue; } p->pos = i; p->str = NULL; p++; } *nslots = p - slots; } else { /* !skip_holes */ nlen = njs_min(length, 8); slots = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_sort_slot_t) * nlen); if (njs_slow_path(slots == NULL)) { njs_memory_error(vm); ret = NJS_ERROR; goto exception; } p = slots; end = slots + nlen; for (i = 0; i < length; i++) { if (p >= end) { nlen = njs_min(njs_max((p - slots) * 2, 8), length); newslots = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_sort_slot_t) * nlen); if (njs_slow_path(newslots == NULL)) { njs_memory_error(vm); ret = NJS_ERROR; goto exception; } p = (void *) njs_cpymem(newslots, slots, sizeof(njs_array_sort_slot_t) * (p - slots)); njs_mp_free(vm->mem_pool, slots); slots = newslots; end = slots + nlen; } ret = njs_value_property_i64(vm, obj, i, &p->value); if (njs_slow_path(ret == NJS_ERROR)) { ret = NJS_ERROR; goto exception; } if (njs_is_undefined(&p->value)) { continue; } p->pos = i; p->str = NULL; p++; } *nslots = p - slots; *nunds = length - *nslots; } } strings = njs_arr_init(vm->mem_pool, &ctx.strings, NULL, *nslots + 1, sizeof(njs_value_t)); if (njs_slow_path(strings == NULL)) { njs_mp_free(vm->mem_pool, slots); return NULL; } njs_qsort(slots, *nslots, sizeof(njs_array_sort_slot_t), njs_array_compare, &ctx); ret = NJS_OK; njs_arr_destroy(&ctx.strings); exception: if (keys != NULL) { njs_array_destroy(vm, keys); } if ((ctx.exception || ret == NJS_ERROR) && slots != NULL) { njs_mp_free(vm->mem_pool, slots); return NULL; } return slots; } static njs_int_t njs_array_prototype_sort(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t i, nslots, nunds, length; njs_int_t ret; njs_value_t *this, *comparefn; njs_function_t *compare; njs_array_sort_slot_t *slots; comparefn = njs_arg(args, nargs, 1); if (njs_is_defined(comparefn)) { if (njs_slow_path(!njs_is_function(comparefn))) { njs_type_error(vm, "comparefn must be callable or undefined"); return NJS_ERROR; } compare = njs_function(comparefn); } else { compare = NULL; } this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_length(vm, this, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } slots = NULL; if (length == 0) { goto done; } /* Satisfy gcc -O3 */ nslots = 0; slots = njs_sort_indexed_properties(vm, this, length, compare, 1, &nslots, &nunds); if (njs_slow_path(slots == NULL)) { ret = NJS_ERROR; goto exception; } njs_assert(length >= (nslots + nunds)); for (i = 0; i < nslots; i++) { ret = njs_value_property_i64_set(vm, this, i, &slots[i].value); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } } for (i = nslots; nunds-- > 0; i++) { ret = njs_value_property_i64_set(vm, this, i, njs_value_arg(&njs_value_undefined)); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } } for (; i < length; i++) { ret = njs_value_property_i64_delete(vm, this, i, NULL); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } } done: njs_value_assign(retval, this); ret = NJS_OK; exception: if (slots != NULL) { njs_mp_free(vm->mem_pool, slots); } return ret; } static njs_int_t njs_array_prototype_to_sorted(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t i, nslots, nunds, length; njs_int_t ret; njs_array_t *array; njs_value_t *this, *comparefn, a; njs_function_t *compare; njs_array_sort_slot_t *slots; comparefn = njs_arg(args, nargs, 1); if (njs_is_defined(comparefn)) { if (njs_slow_path(!njs_is_function(comparefn))) { njs_type_error(vm, "comparefn must be callable or undefined"); return NJS_ERROR; } compare = njs_function(comparefn); } else { compare = NULL; } this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_length(vm, this, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } array = njs_array_alloc(vm, 0, length, 0); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } if (length != 0) { slots = njs_sort_indexed_properties(vm, this, length, compare, 0, &nslots, &nunds); if (njs_slow_path(slots == NULL)) { ret = NJS_ERROR; goto exception; } } else { slots = NULL; length = 0; nslots = 0; nunds = 0; } njs_assert(length == (nslots + nunds)); njs_set_array(&a, array); for (i = 0; i < nslots; i++) { ret = njs_value_create_data_prop_i64(vm, &a, i, &slots[i].value, 0); if (njs_slow_path(ret != NJS_OK)) { goto exception; } } for (; i < length; i++) { ret = njs_value_create_data_prop_i64(vm, &a, i, njs_value_arg(&njs_value_undefined), 0); if (njs_slow_path(ret != NJS_OK)) { goto exception; } } ret = NJS_OK; njs_set_array(retval, array); exception: if (slots != NULL) { njs_mp_free(vm->mem_pool, slots); } return ret; } static njs_int_t njs_array_prototype_copy_within(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length, count, to, from, end; njs_int_t ret; njs_value_t *this, *value; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_length(vm, this, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &to); if (njs_slow_path(ret != NJS_OK)) { return ret; } to = (to < 0) ? njs_max(length + to, 0) : njs_min(to, length); ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); if (njs_slow_path(ret != NJS_OK)) { return ret; } from = (from < 0) ? njs_max(length + from, 0) : njs_min(from, length); value = njs_arg(args, nargs, 3); if (njs_is_undefined(value)) { end = length; } else { ret = njs_value_to_integer(vm, value, &end); if (njs_slow_path(ret != NJS_OK)) { return ret; } } end = (end < 0) ? njs_max(length + end, 0) : njs_min(end, length); count = njs_min(end - from, length - to); njs_value_assign(retval, this); return njs_array_copy_within(vm, this, to, from, count, !(from < to && to < from + count)); } static njs_int_t njs_array_prototype_iterator_obj(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t kind, njs_value_t *retval) { njs_int_t ret; njs_value_t *this; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_array_iterator_create(vm, this, retval, kind); } static const njs_object_prop_init_t njs_array_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER(STRING_length, njs_array_length, 0, NJS_OBJECT_PROP_VALUE_W), NJS_DECLARE_PROP_HANDLER(STRING_constructor, njs_object_prototype_create_constructor, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_NATIVE(STRING_concat, njs_array_prototype_concat, 1, 0), NJS_DECLARE_PROP_NATIVE(STRING_copyWithin, njs_array_prototype_copy_within, 2, 0), NJS_DECLARE_PROP_NATIVE(STRING_entries, njs_array_prototype_iterator_obj, 0, NJS_ENUM_BOTH), NJS_DECLARE_PROP_NATIVE(STRING_every, njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_EVERY)), NJS_DECLARE_PROP_NATIVE(STRING_fill, njs_array_prototype_fill, 1, 0), NJS_DECLARE_PROP_NATIVE(STRING_filter, njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_FILTER)), NJS_DECLARE_PROP_NATIVE(STRING_find, njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_FIND)), NJS_DECLARE_PROP_NATIVE(STRING_findIndex, njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_FIND_INDEX)), NJS_DECLARE_PROP_NATIVE(STRING_forEach, njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_FOR_EACH)), NJS_DECLARE_PROP_NATIVE(STRING_includes, njs_array_prototype_iterator, 1, njs_array_arg(NJS_ARRAY_INCLUDES)), NJS_DECLARE_PROP_NATIVE(STRING_indexOf, njs_array_prototype_iterator, 1, njs_array_arg(NJS_ARRAY_INDEX_OF)), NJS_DECLARE_PROP_NATIVE(STRING_join, njs_array_prototype_join, 1, 0), NJS_DECLARE_PROP_NATIVE(STRING_keys, njs_array_prototype_iterator_obj, 0, NJS_ENUM_KEYS), NJS_DECLARE_PROP_NATIVE(STRING_lastIndexOf, njs_array_prototype_reverse_iterator, 1, NJS_ARRAY_LAST_INDEX_OF), NJS_DECLARE_PROP_NATIVE(STRING_map, njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_MAP)), NJS_DECLARE_PROP_NATIVE(STRING_pop, njs_array_prototype_pop, 0, 0), NJS_DECLARE_PROP_NATIVE(STRING_push, njs_array_prototype_push, 1, 0), NJS_DECLARE_PROP_NATIVE(STRING_reduce, njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_REDUCE)), NJS_DECLARE_PROP_NATIVE(STRING_reduceRight, njs_array_prototype_reverse_iterator, 1, njs_array_func(NJS_ARRAY_REDUCE_RIGHT)), NJS_DECLARE_PROP_NATIVE(STRING_reverse, njs_array_prototype_reverse, 0, 0), NJS_DECLARE_PROP_NATIVE(STRING_shift, njs_array_prototype_shift, 0, 0), NJS_DECLARE_PROP_NATIVE(STRING_slice, njs_array_prototype_slice, 2, 0), NJS_DECLARE_PROP_NATIVE(STRING_some, njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_SOME)), NJS_DECLARE_PROP_NATIVE(STRING_sort, njs_array_prototype_sort, 1, 0), NJS_DECLARE_PROP_NATIVE(STRING_splice, njs_array_prototype_splice, 2, 0), NJS_DECLARE_PROP_NATIVE(STRING_toReversed, njs_array_prototype_to_reversed, 0, 0), NJS_DECLARE_PROP_NATIVE(STRING_toSorted, njs_array_prototype_to_sorted, 1, 0), NJS_DECLARE_PROP_NATIVE(STRING_toSpliced, njs_array_prototype_to_spliced, 2, 0), NJS_DECLARE_PROP_NATIVE(STRING_toString, njs_array_prototype_to_string, 0, 0), NJS_DECLARE_PROP_NATIVE(STRING_unshift, njs_array_prototype_unshift, 1, 0), NJS_DECLARE_PROP_NATIVE(STRING_values, njs_array_prototype_iterator_obj, 0, NJS_ENUM_VALUES), NJS_DECLARE_PROP_NATIVE(SYMBOL_iterator, njs_array_prototype_iterator_obj, 0, NJS_ENUM_VALUES), }; const njs_object_init_t njs_array_prototype_init = { njs_array_prototype_properties, njs_nitems(njs_array_prototype_properties), }; const njs_object_prop_init_t njs_array_instance_properties[] = { NJS_DECLARE_PROP_HANDLER(STRING_length, njs_array_length, 0, NJS_OBJECT_PROP_VALUE_W), }; const njs_object_init_t njs_array_instance_init = { njs_array_instance_properties, njs_nitems(njs_array_instance_properties), }; const njs_object_type_init_t njs_array_type_init = { .constructor = njs_native_ctor(njs_array_constructor, 1, 0), .constructor_props = &njs_array_constructor_init, .prototype_props = &njs_array_prototype_init, .prototype_value = { .object = { .type = NJS_ARRAY, .fast_array = 1 } }, };