#include <njs_main.h>
-#define NJS_ARRAY_LARGE_OBJECT_LENGTH 4096
+#define njs_fast_object(_sz) ((_sz) <= NJS_ARRAY_FAST_OBJECT_LENGTH)
typedef struct {
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);
-static njs_array_t *njs_object_indexes(njs_vm_t *vm, njs_value_t *object);
njs_array_t *
njs_array_alloc(njs_vm_t *vm, 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;
}
- size = length + spare;
-
- if (njs_slow_path(size > NJS_ARRAY_MAX_LENGTH)) {
- goto memory_error;
- }
-
array = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_t));
if (njs_slow_path(array == NULL)) {
goto memory_error;
}
- 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;
+ size = length + spare;
+
+ if (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;
array->object.type = NJS_ARRAY;
array->object.shared = 0;
array->object.extensible = 1;
- array->object.fast_array = 1;
- array->size = size;
- array->length = length;
+ 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);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NULL;
+ }
+ }
return 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 index, value;
+ njs_object_prop_t *prop;
+
+ njs_set_array(&value, array);
+ array->object.fast_array = 0;
+
+ length = array->length;
+
+ for (i = 0; i < length; i++) {
+ njs_uint32_to_string(&index, i);
+
+ if (njs_is_valid(&array->start[i])) {
+ prop = njs_object_property_add(vm, &value, &index, 0);
+ if (njs_slow_path(prop == NULL)) {
+ return NJS_ERROR;
+ }
+
+ prop->value = array->start[i];
+ }
+ }
+
+ /* GC: release value. */
+
+ njs_mp_free(vm->mem_pool, array->start);
+ array->start = NULL;
+
+ return NJS_OK;
+}
+
+
+njs_int_t
+njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value, uint32_t length)
+{
+ njs_object_prop_t *prop;
+
+ static const njs_value_t string_length = njs_string("length");
+
+ 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_value_arg(&string_length), 1);
+ if (njs_slow_path(prop == NULL)) {
+ njs_internal_error(vm, "njs_array_length_redefine() "
+ "cannot redefine \"length\"");
+ return NJS_ERROR;
+ }
+
+ prop->enumerable = 0;
+ prop->configurable = 0;
+
+ njs_value_number_set(&prop->value, 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;
+ uint32_t i, length, prev_length;
+ njs_int_t ret;
+ njs_array_t *array, *keys;
+
+ array = njs_object_proto_lookup(njs_object(value), NJS_ARRAY, njs_array_t);
+ if (njs_slow_path(array == NULL)) {
+ return NJS_DECLINED;
+ }
+
+ ret = njs_value_to_number(vm, setval, &num);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ length = 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, &prev->value, &prev_length);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ 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],
+ NULL);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+ }
+ } while (i-- != 0);
+ }
+ }
+
+ ret = njs_array_length_redefine(vm, value, length);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ return NJS_OK;
+}
+
+
njs_int_t
njs_array_add(njs_vm_t *vm, njs_array_t *array, njs_value_t *value)
{
if (njs_fast_path(array != NULL)) {
- value = array->start;
+ if (array->object.fast_array) {
+ value = array->start;
- if (args == NULL) {
- while (size != 0) {
- njs_set_invalid(value);
- value++;
- size--;
- }
+ if (args == NULL) {
+ while (size != 0) {
+ njs_set_invalid(value);
+ value++;
+ size--;
+ }
- } else {
- while (size != 0) {
- njs_retain(args);
- *value++ = *args++;
- size--;
+ } else {
+ while (size != 0) {
+ njs_retain(args);
+ *value++ = *args++;
+ size--;
+ }
}
}
-
njs_set_array(&vm->retval, array);
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_array_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
{
- uint32_t length, i;
- njs_array_t *array;
+ uint32_t length, i;
+ njs_array_t *array;
- length = nargs > 1 ? nargs - 1 : 0;
+ length = nargs > 1 ? nargs - 1 : 0;
- array = njs_array_alloc(vm, length, NJS_ARRAY_SPARE);
- if (njs_slow_path(array == NULL)) {
- return NJS_ERROR;
- }
+ array = njs_array_alloc(vm, length, NJS_ARRAY_SPARE);
+ if (njs_slow_path(array == NULL)) {
+ return NJS_ERROR;
+ }
- njs_set_array(&vm->retval, array);
+ njs_set_array(&vm->retval, array);
+ if (array->object.fast_array) {
for (i = 0; i < length; i++) {
array->start[i] = args[i + 1];
}
+ }
- return NJS_OK;
+ return NJS_OK;
}
return NJS_DECLINED;
}
- if (njs_slow_path(!njs_is_number(setval))) {
- ret = njs_value_to_number(vm, setval, &num);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
- } else {
- num = njs_number(setval);
+ ret = njs_value_to_number(vm, setval, &num);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
}
length = njs_number_to_length(num);
-
if ((double) length != num) {
njs_range_error(vm, "Invalid array length");
return NJS_ERROR;
array = (njs_array_t *) proto;
- size = (int64_t) length - array->length;
+ 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;
- }
+ 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];
+ val = &array->start[array->length];
- do {
- njs_set_invalid(val);
- val++;
- size--;
- } while (size != 0);
+ do {
+ njs_set_invalid(val);
+ val++;
+ size--;
+ } while (size != 0);
+ }
+
+ array->length = length;
+
+ *retval = *setval;
+ return NJS_OK;
+ }
+
+ ret = njs_array_convert_to_slow_array(vm, array);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
}
- array->length = length;
+ prop->type = NJS_PROPERTY;
+ njs_set_number(&prop->value, length);
*retval = *setval;
+
return NJS_OK;
}
-/*
- * Array.slice(start[, end]).
- * JavaScript 1.2, ECMAScript 3.
- */
-
static njs_int_t
njs_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
int64_t start, end, length;
uint32_t object_length;
njs_int_t ret;
- njs_value_t *value;
+ njs_value_t *this;
- value = njs_arg(args, nargs, 0);
+ this = njs_argument(args, 0);
- ret = njs_value_to_object(vm, value);
+ ret = njs_value_to_object(vm, this);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
- ret = njs_object_length(vm, value, &object_length);
+ ret = njs_object_length(vm, this, &object_length);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
}
}
- return njs_array_prototype_slice_copy(vm, value, start, length);
+ return njs_array_prototype_slice_copy(vm, this, start, length);
}
u_char *dst;
uint32_t n;
njs_int_t ret;
- njs_array_t *array;
- njs_value_t *value, name;
+ njs_array_t *array, *keys;
+ njs_value_t *value, index, retval, array_value;
const u_char *src, *end;
njs_slice_prop_t string_slice;
njs_string_prop_t string;
return NJS_ERROR;
}
- njs_set_array(&vm->retval, array);
+ if (njs_slow_path(length == 0)) {
+ goto done;
+ }
- if (length != 0) {
- n = 0;
+ n = 0;
- if (njs_fast_path(njs_is_array(this))) {
+ if (njs_fast_path(array->object.fast_array)) {
+ if (njs_fast_path(njs_is_fast_array(this))) {
value = njs_array_start(this);
do {
- /* GC: retain long string and object in values[start]. */
- array->start[n++] = value[start++];
+ if (njs_fast_path(njs_is_valid(&value[start]))) {
+ array->start[n++] = value[start++];
+
+ } else {
+
+ /* src value may be in Array.prototype object. */
+
+ njs_uint32_to_string(&index, start++);
+
+ value = &array->start[n++];
+ ret = njs_value_property(vm, this, &index, value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return NJS_ERROR;
+ }
+
+ if (ret != NJS_OK) {
+ njs_set_invalid(value);
+ }
+ }
+
length--;
} while (length != 0);
} else if (njs_is_object(this)) {
do {
- njs_uint32_to_string(&name, start++);
+ njs_uint32_to_string(&index, start++);
value = &array->start[n++];
- ret = njs_value_property(vm, this, &name, value);
+ ret = njs_value_property(vm, this, &index, value);
if (ret != NJS_OK) {
njs_set_invalid(value);
length--;
} while (length != 0);
}
+
+ goto done;
+ }
+
+ njs_set_array(&array_value, array);
+
+ if (njs_fast_object(length)) {
+ do {
+ njs_uint32_to_string(&index, start++);
+
+ ret = njs_value_property(vm, this, &index, &retval);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return NJS_ERROR;
+ }
+
+ if (ret == NJS_OK) {
+ ret = njs_value_property_set(vm, &array_value, &index, &retval);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+ }
+
+ length--;
+ } while (length != 0);
+
+ 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], &retval);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+
+ ret = njs_value_property_set(vm, &array_value, &keys->start[n],
+ &retval);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
}
+done:
+
+ njs_set_array(&vm->retval, array);
+
return NJS_OK;
}
njs_int_t ret;
njs_uint_t i;
njs_array_t *array;
- njs_value_t *value, index;
+ njs_value_t *this, index;
- value = njs_arg(args, nargs, 0);
length = 0;
+ this = njs_argument(args, 0);
- ret = njs_value_to_object(vm, value);
+ ret = njs_value_to_object(vm, this);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
- if (njs_is_array(&args[0])) {
- array = njs_array(&args[0]);
+ if (njs_is_fast_array(this)) {
+ array = njs_array(this);
if (nargs != 0) {
ret = njs_array_expand(vm, array, 0, nargs);
return NJS_OK;
}
- ret = njs_object_length(vm, value, &length);
+ ret = njs_object_length(vm, this, &length);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
for (i = 1; i < nargs; i++) {
njs_uint32_to_string(&index, length++);
- ret = njs_value_property_set(vm, value, &index, &args[i]);
+ ret = njs_value_property_set(vm, this, &index, &args[i]);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
}
- ret = njs_object_length_set(vm, value, length);
+ ret = njs_object_length_set(vm, this, length);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
uint32_t length;
njs_int_t ret;
njs_array_t *array;
- njs_value_t *value, *entry, index;
+ njs_value_t *this, *entry, index;
- value = njs_arg(args, nargs, 0);
+ this = njs_argument(args, 0);
- ret = njs_value_to_object(vm, value);
+ ret = njs_value_to_object(vm, this);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
njs_set_undefined(&vm->retval);
- if (njs_is_array(&args[0])) {
- array = njs_array(&args[0]);
+ if (njs_is_fast_array(this)) {
+ array = njs_array(this);
if (array->length != 0) {
array->length--;
return NJS_OK;
}
- ret = njs_object_length(vm, value, &length);
+ ret = njs_object_length(vm, this, &length);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
if (length != 0) {
njs_uint32_to_string(&index, --length);
- ret = njs_value_property_delete(vm, value, &index, &vm->retval);
+ ret = njs_value_property_delete(vm, this, &index, &vm->retval);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
}
- ret = njs_object_length_set(vm, value, length);
+ ret = njs_object_length_set(vm, this, length);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
njs_int_t ret;
njs_uint_t n;
njs_array_t *array, *keys;
- njs_value_t *value, entry, index;
+ njs_value_t *this, entry, index;
- value = njs_arg(args, nargs, 0);
+ this = njs_argument(args, 0);
length = 0;
n = nargs - 1;
- ret = njs_value_to_object(vm, value);
+ ret = njs_value_to_object(vm, this);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
- if (njs_is_array(value)) {
- array = njs_array(value);
+ if (njs_is_fast_array(this)) {
+ array = njs_array(this);
if (array->length > (UINT32_MAX - n)) {
njs_type_error(vm, "Invalid length");
return NJS_OK;
}
- ret = njs_object_length(vm, value, &length);
+ ret = njs_object_length(vm, this, &length);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
return NJS_ERROR;
}
- if (length > NJS_ARRAY_LARGE_OBJECT_LENGTH) {
- keys = njs_object_indexes(vm, value);
+ 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, value, &keys->start[--from],
+ ret = njs_value_property_delete(vm, this, &keys->start[--from],
&entry);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
njs_uint32_to_string(&index, (uint32_t) idx + nargs - 1);
- ret = njs_value_property_set(vm, value, &index, &entry);
+ ret = njs_value_property_set(vm, this, &index, &entry);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
while (from > 0) {
njs_uint32_to_string(&index, --from);
- ret = njs_value_property_delete(vm, value, &index, &entry);
+ ret = njs_value_property_delete(vm, this, &index, &entry);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
if (ret == NJS_OK) {
njs_uint32_to_string(&index, to);
- ret = njs_value_property_set(vm, value, &index, &entry);
+ ret = njs_value_property_set(vm, this, &index, &entry);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
for (n = 1; n < nargs; n++) {
njs_uint32_to_string(&index, n - 1);
- ret = njs_value_property_set(vm, value, &index, &args[n]);
+ ret = njs_value_property_set(vm, this, &index, &args[n]);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
done:
- ret = njs_object_length_set(vm, value, length);
+ ret = njs_object_length_set(vm, this, length);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
uint32_t i, length;
njs_int_t ret;
njs_array_t *array;
- njs_value_t *value, *item, entry, index;
+ njs_value_t *this, *item, entry, index;
- value = njs_arg(args, nargs, 0);
+ this = njs_argument(args, 0);
length = 0;
- ret = njs_value_to_object(vm, value);
+ ret = njs_value_to_object(vm, this);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
njs_set_undefined(&vm->retval);
- if (njs_is_array(&args[0])) {
- array = njs_array(&args[0]);
+ if (njs_is_fast_array(this)) {
+ array = njs_array(this);
if (array->length != 0) {
array->length--;
return NJS_OK;
}
- ret = njs_object_length(vm, value, &length);
+ ret = njs_object_length(vm, this, &length);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
njs_uint32_to_string(&index, 0);
- ret = njs_value_property_delete(vm, value, &index, &vm->retval);
+ ret = njs_value_property_delete(vm, this, &index, &vm->retval);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
for (i = 1; i < length; i++) {
njs_uint32_to_string(&index, i);
- ret = njs_value_property_delete(vm, value, &index, &entry);
+ ret = njs_value_property_delete(vm, this, &index, &entry);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
if (ret == NJS_OK) {
njs_uint32_to_string(&index, i - 1);
- ret = njs_value_property_set(vm, value, &index, &entry);
+ ret = njs_value_property_set(vm, this, &index, &entry);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
done:
- ret = njs_object_length_set(vm, value, length);
+ ret = njs_object_length_set(vm, this, length);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
int64_t n, start, length, items, delta, delete;
njs_int_t ret;
njs_uint_t i;
- njs_value_t *value;
+ njs_value_t *this;
njs_array_t *array, *deleted;
- value = njs_arg(args, nargs, 0);
+ this = njs_argument(args, 0);
- ret = njs_value_to_object(vm, value);
+ ret = njs_value_to_object(vm, this);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
+ if (njs_slow_path(!njs_is_fast_array(this))) {
+ njs_internal_error(vm, "splice() is not implemented yet for objects");
+ return NJS_ERROR;
+ }
+
array = NULL;
start = 0;
delete = 0;
- if (njs_is_array(value)) {
- array = njs_array(value);
+ if (njs_is_fast_array(this)) {
+ array = njs_array(this);
length = array->length;
if (nargs > 1) {
- value = njs_argument(args, 1);
-
- if (njs_slow_path(!njs_is_number(value))) {
- ret = njs_value_to_integer(vm, value, &start);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
- } else {
- start = njs_number_to_integer(njs_number(value));
+ ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
}
if (start < 0) {
delete = length - start;
if (nargs > 2) {
- value = njs_argument(args, 2);
-
- if (njs_slow_path(!njs_is_number(value))) {
- ret = njs_value_to_integer(vm, value, &n);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
- } else {
- n = njs_number_to_integer(njs_number(value));
+ ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &n);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
}
if (n < 0) {
return NJS_ERROR;
}
+ if (njs_slow_path(!deleted->object.fast_array)) {
+ njs_internal_error(vm, "deleted is not a fast_array");
+ return NJS_ERROR;
+ }
+
if (array != NULL && (delete >= 0 || nargs > 3)) {
/* Move deleted items to a new array to return. */
njs_array_prototype_reverse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
+ uint32_t length;
njs_int_t ret;
- njs_uint_t i, n, length;
+ njs_uint_t i, n;
njs_value_t value, *this;
njs_array_t *array;
- this = njs_arg(args, nargs, 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_array(this)) {
+ ret = njs_object_length(vm, this, &length);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+
+ if (njs_slow_path(length == 0)) {
+ vm->retval = *this;
+ return NJS_OK;
+ }
+
+ if (njs_slow_path(!njs_is_fast_array(this))) {
+ njs_internal_error(vm, "reverse() is not implemented yet for objects");
+ return NJS_ERROR;
+ }
+
+ if (njs_is_fast_array(this)) {
array = njs_array(this);
length = array->length;
}
njs_set_array(&vm->retval, array);
-
- } else {
- /* STUB */
- vm->retval = *this;
}
return NJS_OK;
u_char *p, *last;
size_t size;
ssize_t length;
+ uint32_t len;
njs_int_t ret;
njs_chb_t chain;
njs_utf8_t utf8;
njs_uint_t i;
njs_array_t *array;
- njs_value_t *value;
+ njs_value_t *value, *this, index, entry;
njs_string_prop_t separator, string;
- ret = njs_value_to_object(vm, &args[0]);
+ this = njs_argument(args, 0);
+
+ ret = njs_value_to_object(vm, this);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
(void) njs_string_prop(&separator, value);
- if (!njs_is_array(&args[0]) || njs_array_len(&args[0]) == 0) {
+ if (njs_slow_path(!njs_is_object(this))) {
vm->retval = njs_string_empty;
return NJS_OK;
}
- array = njs_array(&args[0]);
+ length = 0;
+ array = NULL;
+ utf8 = njs_is_byte_string(&separator) ? NJS_STRING_BYTE : NJS_STRING_UTF8;
+
+ if (njs_is_fast_array(this)) {
+ array = njs_array(this);
+ len = array->length;
+
+ } else {
+ ret = njs_object_length(vm, this, &len);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+ }
+
+ if (njs_slow_path(len == 0)) {
+ vm->retval = njs_string_empty;
+ return NJS_OK;
+ }
njs_chb_init(&chain, vm->mem_pool);
- length = 0;
- utf8 = njs_is_byte_string(&separator) ? NJS_STRING_BYTE : NJS_STRING_UTF8;
+ for (i = 0; i < len; i++) {
+ if (njs_fast_path(array != NULL)) {
+ value = &array->start[i];
- for (i = 0; i < array->length; i++) {
- value = &array->start[i];
+ } else {
+ njs_uint32_to_string(&index, i);
+
+ ret = njs_value_property(vm, this, &index, &entry);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+
+ value = &entry;
+ }
if (njs_is_valid(value) && !njs_is_null_or_undefined(value)) {
if (!njs_is_string(value)) {
static int
-njs_object_indexes_handler(const void *first, const void *second)
+njs_array_indices_handler(const void *first, const void *second)
{
double num1, num2;
+ int64_t diff;
njs_str_t str1, str2;
const njs_value_t *val1, *val2;
return -1;
}
- return (int) (num1 - num2);
+ diff = (int64_t) (num1 - num2);
+
+ if (diff < 0) {
+ return -1;
+ }
+
+ return diff != 0;
}
njs_string_get(val1, &str1);
}
-static njs_array_t *
-njs_object_indexes(njs_vm_t *vm, njs_value_t *object)
+njs_array_t *
+njs_array_keys(njs_vm_t *vm, const njs_value_t *object, njs_bool_t all)
{
- double idx;
- uint32_t i;
njs_array_t *keys;
keys = njs_value_own_enumerate(vm, object, NJS_ENUM_KEYS, NJS_ENUM_STRING,
- 0);
+ all);
if (njs_slow_path(keys == NULL)) {
return NULL;
}
qsort(keys->start, keys->length, sizeof(njs_value_t),
- njs_object_indexes_handler);
+ njs_array_indices_handler);
+
+ return keys;
+}
+
+
+njs_array_t *
+njs_array_indices(njs_vm_t *vm, const 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]);
entry = (ret == NJS_OK) ? &prop : njs_value_arg(&njs_value_invalid);
ret = handler(vm, args, entry, i);
-
if (njs_slow_path(ret != NJS_OK)) {
if (ret > 0) {
return NJS_DECLINED;
to = args->to;
if (njs_is_array(value)) {
- if (njs_slow_path(!njs_object_hash_is_empty(value))) {
+ if (njs_slow_path(!njs_is_fast_array(value))) {
goto process_object;
}
process_object:
- if ((to - from) > NJS_ARRAY_LARGE_OBJECT_LENGTH) {
- keys = njs_object_indexes(vm, value);
+ if (!njs_fast_object(to - from)) {
+ keys = njs_array_indices(vm, value);
if (njs_slow_path(keys == NULL)) {
return NJS_ERROR;
}
for (i = 0; i < keys->length; i++) {
idx = njs_string_to_index(&keys->start[i]);
- if (idx < from || idx > to) {
+ if (idx < from || idx >= to) {
continue;
}
ret = njs_array_object_handler(vm, handler, args, &keys->start[i],
- i);
+ idx);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
to = args->to;
if (njs_is_array(value)) {
- if (njs_slow_path(!njs_object_hash_is_empty(value))) {
+ if (njs_slow_path(!njs_is_fast_array(value))) {
goto process_object;
}
process_object:
- if ((from - to) > NJS_ARRAY_LARGE_OBJECT_LENGTH) {
- keys = njs_object_indexes(vm, value);
+ if (!njs_fast_object(from - to)) {
+ keys = njs_array_indices(vm, value);
if (njs_slow_path(keys == NULL)) {
return NJS_ERROR;
}
}
+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;
+
+ static const njs_value_t key =
+ njs_wellknown_symbol(NJS_SYMBOL_IS_CONCAT_SPREADABLE);
+
+ if (njs_slow_path(!njs_is_object(value))) {
+ return NJS_DECLINED;
+ }
+
+ ret = njs_value_property(vm, value, njs_value_arg(&key), &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)
{
+ double idx;
+ uint32_t len;
uint64_t length;
njs_int_t ret;
- njs_uint_t i;
- njs_value_t *value;
- njs_array_t *array;
+ njs_uint_t i, k;
+ njs_value_t this, index, retval, *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, 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++) {
- if (njs_is_array(&args[i])) {
- length += njs_array_len(&args[i]);
+ e = njs_argument(args, i);
- } else {
- length++;
+ ret = njs_is_concat_spreadable(vm, e);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return NJS_ERROR;
}
- }
- array = njs_array_alloc(vm, length, NJS_ARRAY_SPARE);
- if (njs_slow_path(array == NULL)) {
- return NJS_ERROR;
- }
+ if (ret == NJS_OK) {
+ ret = njs_object_length(vm, e, &len);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
- njs_set_array(&vm->retval, array);
+ if (njs_slow_path((length + len) >= NJS_ARRAY_MAX_LENGTH53)) {
+ njs_type_error(vm, "Invalid length");
+ return NJS_ERROR;
+ }
- value = array->start;
+ if (njs_is_fast_array(&this) && njs_is_fast_array(e)
+ && (length + len) <= NJS_ARRAY_LARGE_OBJECT_LENGTH)
+ {
+ for (k = 0; k < len; k++, length++) {
+ value = &njs_array_start(e)[k];
- for (i = 0; i < nargs; i++) {
- value = njs_array_copy(value, &args[i]);
- }
+ if (njs_slow_path(!njs_is_valid(value))) {
+ njs_uint32_to_string(&index, k);
+ ret = njs_value_property(vm, e, &index,
+ &retval);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
- return NJS_OK;
-}
+ if (ret == NJS_DECLINED) {
+ njs_set_invalid(&retval);
+ }
+ value = &retval;
+ }
-static njs_value_t *
-njs_array_copy(njs_value_t *dst, njs_value_t *src)
-{
- njs_uint_t n;
+ ret = njs_array_add(vm, array, value);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+ }
+
+ continue;
+ }
+
+ if (njs_fast_object(len)) {
+ for (k = 0; k < len; k++, length++) {
+ njs_uint32_to_string(&index, k);
+
+ ret = njs_value_property(vm, e, &index, &retval);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+
+ if (ret != NJS_OK) {
+ continue;
+ }
+
+ njs_uint32_to_string(&index, length);
+
+ ret = njs_value_property_set(vm, &this, &index, &retval);
+ 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(vm, e, &keys->start[k], &retval);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+
+ idx = njs_string_to_index(&keys->start[k]);
+
+ if (ret == NJS_OK) {
+ njs_uint32_to_string(&index, length + idx);
+
+ ret = njs_value_property_set(vm, &this, &index, &retval);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+ }
+ }
- n = 1;
+ length += len;
- if (njs_is_array(src)) {
- n = njs_array_len(src);
- src = njs_array_start(src);
+ continue;
+ }
+
+ if (njs_slow_path((length + len) >= NJS_ARRAY_MAX_LENGTH53)) {
+ njs_type_error(vm, "Invalid length");
+ return NJS_ERROR;
+ }
+
+ if (njs_is_fast_array(&this)) {
+ ret = njs_array_add(vm, array, e);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ } else {
+ njs_uint32_to_string(&index, length);
+
+ ret = njs_value_property_set(vm, &this, &index, e);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+ }
+
+ length++;
}
- while (n != 0) {
- /* GC: njs_retain src */
- *dst++ = *src++;
- n--;
+ ret = njs_object_length_set(vm, &this, length);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
}
- return dst;
+ vm->retval = this;
+
+ return NJS_OK;
}
njs_int_t ret;
njs_array_iterator_args_t iargs;
- iargs.value = njs_arg(args, nargs, 0);
+ iargs.value = njs_argument(args, 0);
ret = njs_value_to_object(vm, iargs.value);
if (njs_slow_path(ret != NJS_OK)) {
njs_int_t ret;
njs_array_iterator_args_t iargs;
- iargs.value = njs_arg(args, nargs, 0);
+ iargs.value = njs_argument(args, 0);
ret = njs_value_to_object(vm, iargs.value);
if (njs_slow_path(ret != NJS_OK)) {
njs_int_t ret;
njs_array_iterator_args_t iargs;
- iargs.value = njs_arg(args, nargs, 0);
+ iargs.value = njs_argument(args, 0);
ret = njs_value_to_object(vm, iargs.value);
if (njs_slow_path(ret != NJS_OK)) {
njs_array_t *array;
njs_value_t name, *this, *value;
- this = njs_arg(args, nargs, 0);
+ this = njs_argument(args, 0);
ret = njs_value_to_object(vm, this);
if (njs_slow_path(ret != NJS_OK)) {
array = NULL;
- if (njs_is_array(this)) {
+ if (njs_is_fast_array(this)) {
array = njs_array(this);
length = array->length;
{
njs_int_t ret;
- iargs->value = njs_arg(args, nargs, 0);
+ iargs->value = njs_argument(args, 0);
ret = njs_value_to_object(vm, iargs->value);
if (njs_slow_path(ret != NJS_OK)) {
{
njs_int_t ret;
njs_array_t *retval;
+ njs_value_t this, key;
retval = args->array;
- njs_set_invalid(&retval->start[n]);
+ if (retval->object.fast_array) {
+ njs_set_invalid(&retval->start[n]);
+ }
if (njs_is_valid(entry)) {
ret = njs_array_iterator_call(vm, args, entry, n);
}
if (njs_is_valid(&vm->retval)) {
- retval->start[n] = vm->retval;
+ if (retval->object.fast_array) {
+ retval->start[n] = vm->retval;
+
+ } else {
+ njs_set_array(&this, retval);
+ njs_uint32_to_string(&key, n);
+
+ ret = njs_value_property_set(vm, &this, &key, &vm->retval);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+ }
}
}
uint32_t length, i;
njs_int_t ret;
njs_array_t *array;
+ njs_value_t *this;
njs_array_iterator_args_t iargs;
- iargs.value = njs_arg(args, nargs, 0);
+ this = njs_argument(args, 0);
- ret = njs_value_to_object(vm, iargs.value);
+ ret = njs_value_to_object(vm, this);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
- ret = njs_value_length(vm, iargs.value, &length);
+ ret = njs_value_length(vm, this, &length);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
return NJS_ERROR;
}
- if (length > NJS_ARRAY_LARGE_OBJECT_LENGTH) {
- for (i = 0; i < length; i++) {
- njs_set_invalid(&iargs.array->start[i]);
- }
+ if (njs_slow_path(length == 0)) {
+ goto done;
}
- if (length > 0) {
- iargs.from = 0;
- iargs.to = length;
+ iargs.from = 0;
+ iargs.to = length;
+ iargs.value = this;
+ iargs.function = njs_function(njs_argument(args, 1));
+ iargs.argument = njs_arg(args, nargs, 2);
- iargs.function = njs_function(njs_argument(args, 1));
- iargs.argument = njs_arg(args, nargs, 2);
+ if (iargs.array->object.fast_array) {
+ array = iargs.array;
- ret = njs_array_iterator(vm, &iargs, njs_array_handler_map);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ for (i = 0; i < length; i++) {
+ njs_set_invalid(&array->start[i]);
}
+ }
- if (njs_is_array(&args[0]) && njs_object_hash_is_empty(&args[0])) {
- array = iargs.array;
-
- for (i = njs_array_len(&args[0]); i < length; i++) {
- njs_set_invalid(&array->start[i]);
- }
- }
+ ret = njs_array_iterator(vm, &iargs, njs_array_handler_map);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
}
+done:
+
njs_set_array(&vm->retval, iargs.array);
return NJS_OK;
njs_int_t ret;
if (njs_is_valid(entry)) {
-
if (!njs_is_valid(args->argument)) {
*(args->argument) = *entry;
return NJS_OK;
njs_value_t accumulator;
njs_array_iterator_args_t iargs;
- iargs.value = njs_arg(args, nargs, 0);
+ iargs.value = njs_argument(args, 0);
ret = njs_value_to_object(vm, iargs.value);
if (njs_slow_path(ret != NJS_OK)) {
njs_array_prototype_sort(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused)
{
- uint32_t n, index, current;
+ uint32_t n, index, length, current;
njs_int_t ret;
njs_array_t *array;
- njs_value_t retval, value, *start, arguments[3];;
+ njs_value_t retval, value, *this, *start, arguments[3];
njs_function_t *function;
- if (!njs_is_array(&args[0]) || njs_array_len(&args[0]) == 0) {
- vm->retval = args[0];
+ 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;
+ }
+
+ if (njs_slow_path(length == 0)) {
+ vm->retval = *this;
return NJS_OK;
}
function = (njs_function_t *) &njs_array_string_sort_function;
}
+ if (njs_slow_path(!njs_is_fast_array(this))) {
+ njs_internal_error(vm, "sort() is not implemented yet for objects");
+ return NJS_ERROR;
+ }
+
index = 0;
current = 0;
retval = njs_value_zero;
njs_array_t *array;
njs_value_t *this, *value, from_key, to_key, prop;
- this = njs_arg(args, nargs, 0);
+ this = njs_argument(args, 0);
ret = njs_value_to_object(vm, this);
if (njs_slow_path(ret != NJS_OK)) {
njs_vm_retval_set(vm, this);
- if (njs_is_array(this)) {
- if (njs_slow_path(!njs_object_hash_is_empty(this))) {
- goto process_object;
- }
-
+ if (njs_fast_path(njs_is_fast_array(this))) {
array = njs_array(this);
while (count-- > 0) {
return NJS_OK;
}
-process_object:
-
while (count-- > 0) {
/* FIXME: largest index is 2**53-1. */
.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 } },
+ .prototype_value = { .object = { .type = NJS_ARRAY, .fast_array = 1 } },
};
#define _NJS_ARRAY_H_INCLUDED_
-#define NJS_ARRAY_MAX_INDEX 0xffffffff
-#define NJS_ARRAY_INVALID_INDEX NJS_ARRAY_MAX_INDEX
-
-#define NJS_ARRAY_SPARE 8
-#define NJS_ARRAY_MAX_LENGTH (UINT32_MAX/ sizeof(njs_value_t))
+#define NJS_ARRAY_MAX_INDEX 0xffffffff
+#define NJS_ARRAY_INVALID_INDEX NJS_ARRAY_MAX_INDEX
+#define NJS_ARRAY_SPARE 8
+#define NJS_ARRAY_MAX_LENGTH (UINT32_MAX/ sizeof(njs_value_t))
+#define NJS_ARRAY_MAX_LENGTH53 (0x1fffffffffffff)
+#define NJS_ARRAY_FAST_OBJECT_LENGTH (128)
+#define NJS_ARRAY_LARGE_OBJECT_LENGTH (32768)
njs_array_t *njs_array_alloc(njs_vm_t *vm, uint64_t length, uint32_t spare);
njs_int_t njs_array_add(njs_vm_t *vm, njs_array_t *array, njs_value_t *value);
+njs_int_t njs_array_convert_to_slow_array(njs_vm_t *vm, njs_array_t *array);
+njs_int_t njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value,
+ uint32_t length);
+njs_int_t njs_array_length_set(njs_vm_t *vm, njs_value_t *value,
+ njs_object_prop_t *prev, njs_value_t *setval);
+njs_array_t *njs_array_keys(njs_vm_t *vm, const njs_value_t *array,
+ njs_bool_t all);
+njs_array_t *njs_array_indices(njs_vm_t *vm, const njs_value_t *object);
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 njs_array_expand(njs_vm_t *vm, njs_array_t *array, uint32_t prepend,
array->object.type = NJS_ARRAY_BUFFER;
array->object.shared = 0;
array->object.extensible = 1;
+ array->object.error_data = 0;
+ array->object.fast_array = 0;
array->size = size;
return array;
#define njs_chb_append(chain, msg, len) \
- njs_chb_append0(chain, (const char *) msg, len)
+ njs_chb_append0(chain, (const char *) (msg), len)
#define njs_chb_append_literal(chain, literal) \
njs_chb_append0(chain, literal, njs_length(literal))
#endif
+#define njs_stringify(v) #v
+
+
#if (NJS_HAVE_MEMORY_SANITIZER)
#include <sanitizer/msan_interface.h>
ov->object.type = NJS_OBJECT_VALUE;
ov->object.shared = 0;
ov->object.extensible = 1;
+ ov->object.error_data = 0;
+ ov->object.fast_array = 0;
ov->object.__proto__ = &vm->prototypes[type].object;
return ov;
date->object.type = NJS_DATE;
date->object.shared = 0;
date->object.extensible = 1;
+ date->object.error_data = 0;
+ date->object.fast_array = 0;
date->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_DATE].object;
date->time = time;
error->type = NJS_OBJECT;
error->shared = 0;
error->extensible = 1;
+ error->fast_array = 0;
error->error_data = 1;
error->__proto__ = &vm->prototypes[type].object;
* it from ordinary internal errors.
*/
object->extensible = 0;
+ object->fast_array = 0;
object->error_data = 1;
njs_set_object(value, object);
njs_value_t value;
uint8_t written; /* 1 bit */
+ uint8_t array; /* 1 bit */
enum {
NJS_JSON_OBJECT,
} type:8;
uint32_t index;
+ uint32_t length;
njs_array_t *keys;
njs_object_prop_t *prop;
} njs_json_state_t;
reviver = njs_arg(args, nargs, 2);
- if (njs_is_function(reviver) && njs_is_object(&value)) {
+ if (njs_slow_path(njs_is_function(reviver) && njs_is_object(&value))) {
parse->function = njs_function(reviver);
parse->depth = 0;
state = &parse->states[parse->depth++];
state->value = *value;
state->index = 0;
-
- if (njs_is_array(value)) {
- state->type = NJS_JSON_ARRAY;
-
- } else {
- state->type = NJS_JSON_OBJECT;
- state->prop = NULL;
- state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS,
- NJS_ENUM_STRING, 0);
- if (state->keys == NULL) {
- return NULL;
- }
+ state->prop = NULL;
+ state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS,
+ NJS_ENUM_STRING, 0);
+ if (state->keys == NULL) {
+ return NULL;
}
return state;
}
-#define njs_json_is_non_empty(_value) \
- ((njs_is_object(_value) && !njs_object_hash_is_empty(_value)) \
- || (njs_is_array(_value) && njs_array_len(_value) != 0))
-
-
static njs_int_t
njs_json_parse_iterator(njs_vm_t *vm, njs_json_parse_t *parse,
njs_value_t *object)
{
- njs_int_t ret;
- njs_value_t *key, *value, wrapper;
- njs_object_t *obj;
- njs_json_state_t *state;
- njs_object_prop_t *prop;
- njs_lvlhsh_query_t lhq;
+ njs_int_t ret;
+ njs_value_t *key, wrapper;
+ njs_object_t *obj;
+ njs_json_state_t *state;
+ njs_object_prop_t *prop;
+ njs_property_query_t pq;
obj = njs_json_wrap_value(vm, &wrapper, object);
if (njs_slow_path(obj == NULL)) {
- goto memory_error;
+ return NJS_ERROR;
}
state = njs_json_push_parse_state(vm, parse, &wrapper);
if (njs_slow_path(state == NULL)) {
- goto memory_error;
+ return NJS_ERROR;
}
- lhq.proto = &njs_object_hash_proto;
-
for ( ;; ) {
+ if (state->index < state->keys->length) {
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 0);
- switch (state->type) {
- case NJS_JSON_OBJECT:
- if (state->index < state->keys->length) {
- key = &state->keys->start[state->index];
- njs_string_get(key, &lhq.key);
- lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
+ key = &state->keys->start[state->index];
- ret = njs_lvlhsh_find(njs_object_hash(&state->value), &lhq);
- if (njs_slow_path(ret == NJS_DECLINED)) {
+ ret = njs_property_query(vm, &pq, &state->value, key);
+ if (njs_slow_path(ret != NJS_OK)) {
+ if (ret == NJS_DECLINED) {
state->index++;
- break;
+ continue;
}
- prop = lhq.value;
-
- if (prop->type == NJS_WHITEOUT) {
- state->index++;
- break;
- }
+ return NJS_ERROR;
+ }
- state->prop = prop;
+ prop = pq.lhq.value;
- if (njs_json_is_non_empty(&prop->value)) {
- state = njs_json_push_parse_state(vm, parse, &prop->value);
- if (state == NULL) {
- goto memory_error;
- }
+ if (prop->type == NJS_WHITEOUT) {
+ state->index++;
+ continue;
+ }
- break;
- }
+ state->prop = prop;
- } else {
- state = njs_json_pop_parse_state(parse);
+ if (prop->type == NJS_PROPERTY && njs_is_object(&prop->value)) {
+ state = njs_json_push_parse_state(vm, parse, &prop->value);
if (state == NULL) {
- vm->retval = parse->retval;
- return NJS_OK;
+ return NJS_ERROR;
}
- }
- ret = njs_json_parse_iterator_call(vm, parse, state);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ continue;
}
- break;
-
- case NJS_JSON_ARRAY:
- if (state->index < njs_array_len(&state->value)) {
- value = &njs_array_start(&state->value)[state->index];
-
- if (njs_json_is_non_empty(value)) {
- state = njs_json_push_parse_state(vm, parse, value);
- if (state == NULL) {
- goto memory_error;
- }
-
- break;
+ if (prop->type == NJS_PROPERTY_REF
+ && njs_is_object(prop->value.data.u.value))
+ {
+ state = njs_json_push_parse_state(vm, parse,
+ prop->value.data.u.value);
+ if (state == NULL) {
+ return NJS_ERROR;
}
- } else {
- state = njs_json_pop_parse_state(parse);
+ continue;
}
- ret = njs_json_parse_iterator_call(vm, parse, state);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ } else {
+ state = njs_json_pop_parse_state(parse);
+ if (state == NULL) {
+ vm->retval = parse->retval;
+ return NJS_OK;
}
+ }
- break;
+ ret = njs_json_parse_iterator_call(vm, parse, state);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
}
}
-memory_error:
-
- njs_memory_error(vm);
+ njs_internal_error(vm, "njs_json_parse_iterator() unexpected exit");
return NJS_ERROR;
}
njs_json_parse_iterator_call(njs_vm_t *vm, njs_json_parse_t *parse,
njs_json_state_t *state)
{
- njs_int_t ret;
- njs_value_t arguments[3], *value;
+ njs_int_t ret;
+ njs_value_t arguments[3], *value;
+ njs_object_prop_t *prop;
+
+ prop = state->prop;
arguments[0] = state->value;
+ arguments[1] = state->keys->start[state->index++];
- switch (state->type) {
- case NJS_JSON_OBJECT:
- arguments[1] = state->keys->start[state->index++];
- arguments[2] = state->prop->value;
+ switch (prop->type) {
+ case NJS_PROPERTY:
+ arguments[2] = prop->value;
ret = njs_function_apply(vm, parse->function, arguments, 3,
&parse->retval);
}
if (njs_is_undefined(&parse->retval)) {
- state->prop->type = NJS_WHITEOUT;
+ prop->type = NJS_WHITEOUT;
} else {
- state->prop->value = parse->retval;
+ prop->value = parse->retval;
}
break;
- case NJS_JSON_ARRAY:
- njs_uint32_to_string(&arguments[1], state->index);
- value = &njs_array_start(&state->value)[state->index++];
+ case NJS_PROPERTY_REF:
+ value = prop->value.data.u.value;
arguments[2] = *value;
ret = njs_function_apply(vm, parse->function, arguments, 3,
return ret;
}
- *value = parse->retval;
+ if (njs_is_undefined(&parse->retval)) {
+ njs_set_invalid(value);
+
+ } else {
+ *value = parse->retval;
+ }
break;
+
+ default:
+ njs_internal_error(vm, "njs_json_parse_iterator_call() unexpected "
+ "property type:%s", njs_prop_type_string(prop->type));
}
return NJS_OK;
njs_json_push_stringify_state(njs_vm_t *vm, njs_json_stringify_t *stringify,
const njs_value_t *value)
{
+ njs_int_t ret;
njs_json_state_t *state;
if (njs_slow_path(stringify->depth >= NJS_JSON_MAX_DEPTH)) {
state->index = 0;
state->written = 0;
- if (njs_is_array(value)) {
+ if (njs_is_fast_array(value)) {
state->type = NJS_JSON_ARRAY;
+ state->array = 1;
} else {
state->type = NJS_JSON_OBJECT;
+ state->array = njs_is_array(value);
if (njs_is_array(&stringify->replacer)) {
state->keys = njs_array(&stringify->replacer);
+ } else if (njs_is_array(value)) {
+ state->keys = njs_array_keys(vm, value, 0);
+ if (njs_slow_path(state->keys == NULL)) {
+ return NULL;
+ }
+
+ ret = njs_object_length(vm, &state->value, &state->length);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return NULL;
+ }
+
} else {
state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS,
stringify->keys_type, 0);
}
-#define njs_json_stringify_indent(times) \
- if (stringify->space.length != 0) { \
- njs_chb_append(&chain,"\n", 1); \
- for (i = 0; i < (njs_int_t) (times) - 1; i++) { \
- njs_chb_append_str(&chain, &stringify->space); \
- } \
+njs_inline void
+njs_json_stringify_indent(njs_json_stringify_t *stringify, njs_chb_t *chain,
+ njs_int_t times)
+{
+ njs_int_t i;
+
+ if (stringify->space.length != 0) {
+ times += stringify->depth;
+ njs_chb_append(chain,"\n", 1);
+ for (i = 0; i < (times - 1); i++) {
+ njs_chb_append_str(chain, &stringify->space);
+ }
}
+}
static njs_int_t
njs_json_stringify_iterator(njs_vm_t *vm, njs_json_stringify_t *stringify,
{
u_char *p;
size_t size;
- njs_chb_t chain;
ssize_t length;
- njs_int_t i;
njs_int_t ret;
- njs_value_t *key, *value, wrapper;
+ njs_chb_t chain;
+ njs_value_t *key, *value, index, wrapper;
njs_object_t *obj;
njs_json_state_t *state;
switch (state->type) {
case NJS_JSON_OBJECT:
if (state->index == 0) {
- njs_chb_append_literal(&chain,"{");
- njs_json_stringify_indent(stringify->depth);
+ njs_chb_append(&chain, state->array ? "[" : "{", 1);
+ njs_json_stringify_indent(stringify, &chain, 0);
}
- if (state->index >= state->keys->length) {
- njs_json_stringify_indent(stringify->depth - 1);
- njs_chb_append_literal(&chain,"}");
+ if ((state->array && state->index >= state->length)
+ || (!state->array && state->index >= state->keys->length))
+ {
+ njs_json_stringify_indent(stringify, &chain, -1);
+ njs_chb_append(&chain, state->array ? "]" : "}", 1);
state = njs_json_pop_stringify_state(stringify);
if (state == NULL) {
}
value = &stringify->retval;
- key = &state->keys->start[state->index++];
+
+ if (state->array) {
+ njs_uint32_to_string(&index, state->index++);
+ key = &index;
+
+ } else {
+ key = &state->keys->start[state->index++];
+ }
+
ret = njs_value_property(vm, &state->value, key, value);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
+ if (state->array && ret == NJS_DECLINED) {
+ njs_set_null(value);
+ }
+
ret = njs_json_stringify_to_json(stringify, state, key, value);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
if (state->written) {
njs_chb_append_literal(&chain,",");
- njs_json_stringify_indent(stringify->depth);
+ njs_json_stringify_indent(stringify, &chain, 0);
}
state->written = 1;
- njs_json_append_string(&chain, key, '\"');
- njs_chb_append_literal(&chain,":");
- if (stringify->space.length != 0) {
- njs_chb_append_literal(&chain," ");
+
+ if (!state->array) {
+ njs_json_append_string(&chain, key, '\"');
+ njs_chb_append_literal(&chain,":");
+ if (stringify->space.length != 0) {
+ njs_chb_append_literal(&chain," ");
+ }
}
if (njs_json_is_object(value)) {
case NJS_JSON_ARRAY:
if (state->index == 0) {
njs_chb_append_literal(&chain,"[");
- njs_json_stringify_indent(stringify->depth);
+ njs_json_stringify_indent(stringify, &chain, 0);
}
if (state->index >= njs_array_len(&state->value)) {
- njs_json_stringify_indent(stringify->depth - 1);
+ njs_json_stringify_indent(stringify, &chain, -1);
njs_chb_append_literal(&chain,"]");
state = njs_json_pop_stringify_state(stringify);
if (state->written) {
njs_chb_append_literal(&chain,",");
- njs_json_stringify_indent(stringify->depth);
+ njs_json_stringify_indent(stringify, &chain, 0);
}
stringify->retval = njs_array_start(&state->value)[state->index++];
}
+njs_inline njs_bool_t
+njs_dump_empty(njs_json_stringify_t *stringify, njs_json_state_t *state,
+ njs_chb_t *chain, double key, double prev, njs_bool_t sep_position)
+{
+ int64_t diff;
+
+ if (!state->array || isnan(prev)) {
+ return 0;
+ }
+
+ if (isnan(key)) {
+ key = state->length;
+ }
+
+ diff = key - prev;
+
+ if (diff > 1) {
+ if (sep_position == 0 && state->keys->length) {
+ njs_chb_append_literal(chain, ",");
+ njs_json_stringify_indent(stringify, chain, 1);
+ }
+
+ njs_chb_sprintf(chain, 64, "<%L empty items>", diff - 1);
+ state->written = 1;
+
+ if (sep_position == 1 && state->keys->length) {
+ njs_chb_append_literal(chain, ",");
+ njs_json_stringify_indent(stringify, chain, 1);
+ }
+ }
+
+ return 1;
+}
+
+
static const njs_value_t string_get = njs_string("[Getter]");
static const njs_value_t string_set = njs_string("[Setter]");
static const njs_value_t string_get_set = njs_long_string("[Getter/Setter]");
njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, njs_value_t *value,
njs_uint_t console, njs_uint_t indent)
{
- njs_int_t i, ret;
+ njs_int_t ret;
njs_chb_t chain;
njs_str_t str;
njs_arr_t visited;
goto memory_error;
}
+ key = NULL;
(void) njs_dump_visit(&visited, value);
for ( ;; ) {
njs_chb_append_literal(&chain, " ");
}
- njs_chb_append_literal(&chain, "{");
- njs_json_stringify_indent(stringify->depth + 1);
+ njs_chb_append(&chain, state->array ? "[" : "{", 1);
+ njs_json_stringify_indent(stringify, &chain, 1);
+
}
if (state->index >= state->keys->length) {
- njs_json_stringify_indent(stringify->depth);
- njs_chb_append_literal(&chain, "}");
+ njs_dump_empty(stringify, state, &chain, state->length,
+ (state->index > 0) ? njs_key_to_index(key) : -1, 0);
+
+ njs_json_stringify_indent(stringify, &chain, 0);
+ njs_chb_append(&chain, state->array ? "]" : "}", 1);
state = njs_json_pop_stringify_state(stringify);
if (state == NULL) {
break;
}
+ if (state->written) {
+ njs_chb_append_literal(&chain, ",");
+ njs_json_stringify_indent(stringify, &chain, 1);
+ }
+
+ state->written = 1;
+
+ njs_dump_empty(stringify, state, &chain, njs_key_to_index(key),
+ (state->index > 1) ? njs_key_to_index(&key[-1]) : -1, 1);
+
+ if (!state->array || isnan(njs_key_to_index(key))) {
+ njs_key_string_get(vm, key, &pq.lhq.key);
+ njs_chb_append(&chain, pq.lhq.key.start, pq.lhq.key.length);
+ njs_chb_append_literal(&chain, ":");
+ if (stringify->space.length != 0) {
+ njs_chb_append_literal(&chain, " ");
+ }
+ }
+
val = &prop->value;
if (prop->type == NJS_PROPERTY_HANDLER) {
}
}
- if (state->written) {
- njs_chb_append_literal(&chain, ",");
- njs_json_stringify_indent(stringify->depth + 1);
- }
-
- state->written = 1;
- njs_key_string_get(vm, key, &pq.lhq.key);
- njs_chb_append(&chain, pq.lhq.key.start, pq.lhq.key.length);
- njs_chb_append_literal(&chain, ":");
- if (stringify->space.length != 0) {
- njs_chb_append_literal(&chain, " ");
- }
-
if (njs_dump_is_recursive(val)) {
if (njs_slow_path(njs_dump_visited(&visited, val))) {
njs_chb_append_literal(&chain, "[Circular]");
}
njs_chb_append_literal(&chain, "[");
- njs_json_stringify_indent(stringify->depth + 1);
+ njs_json_stringify_indent(stringify, &chain, 1);
}
if (state->index >= njs_array_len(&state->value)) {
- njs_json_stringify_indent(stringify->depth);
+ njs_json_stringify_indent(stringify, &chain, 0);
njs_chb_append_literal(&chain, "]");
state = njs_json_pop_stringify_state(stringify);
if (state->written) {
njs_chb_append_literal(&chain, ",");
- njs_json_stringify_indent(stringify->depth + 1);
+ njs_json_stringify_indent(stringify, &chain, 1);
}
val = &njs_array_start(&state->value)[state->index++];
*object = module->object;
object->__proto__ = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
object->shared = 0;
+ object->extensible = 0;
object->error_data = 0;
+ object->fast_array = 0;
njs_set_object(&vm->retval, object);
ov->object.type = njs_object_value_type(type);
ov->object.shared = 0;
ov->object.extensible = 1;
+ ov->object.error_data = 0;
ov->object.fast_array = 0;
index = njs_primitive_prototype_index(type);
return NULL;
}
+ if (njs_slow_path(!items->object.fast_array)) {
+ njs_internal_error(vm, "njs_object_enumerate() too many keys");
+ return NULL;
+ }
+
ret = njs_object_enumerate_value(vm, object, items, kind, type, all);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
return NULL;
}
+ if (njs_slow_path(!items->object.fast_array)) {
+ njs_internal_error(vm, "njs_object_own_enumerate() too many keys");
+ return NULL;
+ }
+
ret = njs_object_own_enumerate_value(vm, object, object, items, kind, type,
all);
if (njs_slow_path(ret != NJS_OK)) {
length = 0;
array = (njs_array_t *) object;
- for (i = 0; i < array->length; i++) {
- if (njs_is_valid(&array->start[i])) {
- length++;
+ if (object->fast_array) {
+ for (i = 0; i < array->length; i++) {
+ if (njs_is_valid(&array->start[i])) {
+ length++;
+ }
}
}
njs_value_t *item;
njs_array_t *entry;
+ if (!array->object.fast_array) {
+ return NJS_OK;
+ }
+
item = items->start;
switch (kind) {
const njs_value_t *value, uint8_t attributes);
njs_int_t njs_object_property(njs_vm_t *vm, const njs_value_t *value,
njs_lvlhsh_query_t *lhq, njs_value_t *retval);
+njs_object_prop_t *njs_object_property_add(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *key, njs_bool_t replace);
njs_int_t njs_object_prop_define(njs_vm_t *vm, njs_value_t *object,
njs_value_t *name, njs_value_t *value, njs_object_prop_define_t type);
njs_int_t njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest,
}
+njs_object_prop_t *
+njs_object_property_add(njs_vm_t *vm, njs_value_t *object, njs_value_t *key,
+ njs_bool_t replace)
+{
+ njs_int_t ret;
+ njs_object_prop_t *prop;
+ njs_lvlhsh_query_t lhq;
+
+ prop = njs_object_prop_alloc(vm, key, &njs_value_invalid, 1);
+ if (njs_slow_path(prop == NULL)) {
+ return NULL;
+ }
+
+ lhq.proto = &njs_object_hash_proto;
+ njs_key_string_get(vm, key, &lhq.key);
+ lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
+ lhq.value = prop;
+ lhq.replace = replace;
+ lhq.pool = vm->mem_pool;
+
+ ret = njs_lvlhsh_insert(njs_object_hash(object), &lhq);
+ if (njs_slow_path(ret != NJS_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
+ return NULL;
+ }
+
+ return prop;
+}
+
+
/*
* ES5.1, 8.12.9: [[DefineOwnProperty]]
- * Limited support of special descriptors like length and array index
- * (values can be set, but without property flags support).
*/
njs_int_t
njs_object_prop_define(njs_vm_t *vm, njs_value_t *object,
njs_value_t *name, njs_value_t *value, njs_object_prop_define_t type)
{
+ uint32_t length;
njs_int_t ret;
+ njs_array_t *array;
njs_object_prop_t *prop, *prev;
njs_property_query_t pq;
- njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 1);
+ static const njs_str_t length_key = njs_str("length");
if (njs_slow_path(!njs_is_key(name))) {
ret = njs_value_to_key(vm, name, name);
}
}
- ret = njs_property_query(vm, &pq, object, name);
+again:
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 1);
+
+ ret = njs_property_query(vm, &pq, object, name);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
break;
case NJS_PROPERTY_REF:
+ if (njs_is_accessor_descriptor(prop)
+ || prop->configurable == NJS_ATTRIBUTE_FALSE
+ || prop->enumerable == NJS_ATTRIBUTE_FALSE
+ || prop->writable == NJS_ATTRIBUTE_FALSE)
+ {
+ array = njs_array(object);
+ length = array->length;
+
+ ret = njs_array_convert_to_slow_array(vm, array);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_array_length_redefine(vm, object, length);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ goto again;
+ }
+
if (njs_is_valid(&prop->value)) {
*prev->value.data.u.value = prop->value;
} else {
{
goto exception;
}
-
- if (pq.shared) {
- /*
- * shared non-configurable NJS_PROPERTY_HANDLER are not copied
- * by njs_object_property_query().
- */
-
- ret = njs_prop_private_copy(vm, &pq);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
- prev = pq.lhq.value;
- }
}
if (njs_is_generic_descriptor(prop)) {
}
} else {
+ if (njs_slow_path(pq.lhq.key_hash == NJS_LENGTH_HASH)) {
+ njs_key_string_get(vm, &pq.key, &pq.lhq.key);
+
+ if (njs_strstr_eq(&pq.lhq.key, &length_key)) {
+ ret = njs_array_length_set(vm, object, prev, &prop->value);
+ if (ret != NJS_DECLINED) {
+ return ret;
+ }
+ }
+ }
+
prev->value = prop->value;
}
}
promise->object.type = NJS_PROMISE;
promise->object.shared = 0;
promise->object.extensible = 1;
+ promise->object.error_data = 0;
+ promise->object.fast_array = 0;
promise->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_PROMISE].object;
data = (njs_promise_data_t *) ((uint8_t *) promise + sizeof(njs_promise_t));
function->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
function->object.shared_hash = vm->shared->arrow_instance_hash;
function->object.type = NJS_FUNCTION;
- function->object.shared = 0;
function->object.extensible = 1;
function->args_offset = 1;
function->native = 1;
regexp->object.type = NJS_REGEXP;
regexp->object.shared = 0;
regexp->object.extensible = 1;
+ regexp->object.fast_array = 0;
+ regexp->object.error_data = 0;
njs_set_number(®exp->last_index, 0);
regexp->pattern = pattern;
njs_string_short_set(®exp->string, 0, 0);
njs_lvlhsh_init(&array->object.shared_hash);
array->object.__proto__ = &vm->prototypes[type].object;
array->object.type = NJS_TYPED_ARRAY;
- array->object.shared = 0;
array->object.extensible = 1;
array->object.fast_array = 1;
} else if (njs_is_primitive(value)) {
*length = 0;
- } else if (njs_is_array(value)) {
+ } else if (njs_is_fast_array(value)) {
*length = njs_array_len(value);
} else {
if (!njs_is_null_or_undefined_or_boolean(key)) {
switch (proto->type) {
case NJS_ARRAY:
+ array = (njs_array_t *) proto;
num = njs_key_to_index(key);
+
if (njs_fast_path(njs_key_is_integer_index(num, key))) {
- array = (njs_array_t *) proto;
- return njs_array_property_query(vm, pq, array, num);
+ ret = njs_array_property_query(vm, pq, array, num);
+ if (njs_fast_path(ret != NJS_DECLINED)) {
+ return (ret == NJS_DONE) ? NJS_DECLINED : ret;
+ }
}
break;
if (njs_fast_path(njs_key_is_integer_index(num, key))) {
ov = (njs_object_value_t *) proto;
ret = njs_string_property_query(vm, pq, &ov->value, num);
-
if (njs_fast_path(ret != NJS_DECLINED)) {
return ret;
}
if (ret == NJS_OK) {
prop = pq->lhq.value;
- if (!prop->configurable
- && prop->type == NJS_PROPERTY_HANDLER)
- {
- /* Postpone making a mutable NJS_PROPERTY_HANDLER copy. */
- pq->shared = 1;
- return ret;
- }
-
return njs_prop_private_copy(vm, pq);
}
}
njs_array_property_query(njs_vm_t *vm, njs_property_query_t *pq,
njs_array_t *array, uint32_t index)
{
- uint32_t size;
+ uint32_t size, length;
njs_int_t ret;
- njs_value_t *value;
+ njs_value_t *setval, value;
njs_object_prop_t *prop;
- if (index >= array->length) {
- if (pq->query != NJS_PROPERTY_QUERY_SET) {
+ if (pq->query == NJS_PROPERTY_QUERY_SET) {
+ if (!array->object.extensible) {
return NJS_DECLINED;
}
- if (!array->object.extensible) {
- return NJS_DECLINED;
+ if (njs_fast_path(array->object.fast_array)) {
+ if (njs_fast_path(index < NJS_ARRAY_LARGE_OBJECT_LENGTH)) {
+ if (index >= array->length) {
+ size = index - array->length + 1;
+
+ ret = njs_array_expand(vm, array, 0, size);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ setval = &array->start[array->length];
+
+ while (size != 0) {
+ njs_set_invalid(setval);
+ setval++;
+ size--;
+ }
+
+ array->length = index + 1;
+ }
+
+ goto prop;
+ }
+
+ ret = njs_array_convert_to_slow_array(vm, array);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
}
- size = index - array->length;
+ njs_set_array(&value, array);
- ret = njs_array_expand(vm, array, 0, size + 1);
+ ret = njs_object_length(vm, &value, &length);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
- value = &array->start[array->length];
+ if ((index + 1) > length) {
+ ret = njs_array_length_redefine(vm, &value, index + 1);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+ }
- while (size != 0) {
- njs_set_invalid(value);
- value++;
- size--;
+ ret = njs_lvlhsh_find(&array->object.hash, &pq->lhq);
+ if (ret == NJS_OK) {
+ prop = pq->lhq.value;
+
+ if (prop->type != NJS_WHITEOUT) {
+ return NJS_OK;
+ }
+
+ if (pq->own) {
+ pq->own_whiteout = prop;
+ }
+
+ return NJS_DECLINED;
}
- array->length = index + 1;
+ return NJS_DONE;
}
+ if (njs_slow_path(!array->object.fast_array)) {
+ return NJS_DECLINED;
+ }
+
+ if (index >= array->length) {
+ return NJS_DECLINED;
+ }
+
+prop:
+
prop = &pq->scratch;
if (pq->query == NJS_PROPERTY_QUERY_GET) {
goto slow_path;
}
- /* NJS_ARRAY */
+ /* njs_is_fast_array() */
array = njs_array(value);
- if (njs_slow_path(index >= array->length)) {
+ if (njs_slow_path(index >= array->length
+ || !njs_is_valid(&array->start[index])))
+ {
goto slow_path;
}
*retval = array->start[index];
- if (njs_slow_path(!njs_is_valid(retval))) {
- njs_set_undefined(retval);
- }
-
return NJS_OK;
}
njs_typed_array_t *tarray;
njs_property_query_t pq;
+ static const njs_str_t length_key = njs_str("length");
+
if (njs_fast_path(njs_is_number(key))) {
num = njs_number(key);
return NJS_ERROR;
}
-
njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 0);
ret = njs_property_query(vm, &pq, value, key);
if (pq.own) {
switch (prop->type) {
case NJS_PROPERTY:
+ if (njs_slow_path(pq.lhq.key_hash == NJS_LENGTH_HASH)) {
+ njs_key_string_get(vm, &pq.key, &pq.lhq.key);
+
+ if (njs_strstr_eq(&pq.lhq.key, &length_key)) {
+ ret = njs_array_length_set(vm, value, prop, setval);
+ if (ret != NJS_DECLINED) {
+ return ret;
+ }
+ }
+ }
+
goto found;
case NJS_PROPERTY_REF:
((value)->type == NJS_ARRAY)
+#define njs_is_fast_array(value) \
+ (njs_is_array(value) && njs_array(value)->object.fast_array)
+
+
#define njs_is_array_buffer(value) \
((value)->type == NJS_ARRAY_BUFFER)
*(value) = njs_value_undefined
+#define njs_set_null(value) \
+ *(value) = njs_value_null
+
+
#define njs_set_true(value) \
*(value) = njs_value_true
njs_str("3524578"),
1 },
- { "array 10M",
- njs_str("var arr = new Array(10000000);"
+ { "array 1M",
+ njs_str("var arr = new Array(1000000);"
"var count = 0, length = arr.length;"
"arr.fill(2);"
"for (var i = 0; i < length; i++) { count += arr[i]; }"
"count"),
- njs_str("20000000"),
+ njs_str("2000000"),
1 },
{ "typed array 10M",
-
/*
* Copyright (C) Igor Sysoev
* Copyright (C) NGINX, Inc.
#define NJS_HAVE_LARGE_STACK (!NJS_HAVE_ADDRESS_SANITIZER && !NJS_HAVE_MEMORY_SANITIZER)
+#define _NJS_ARRAY(sz) "Array(" njs_stringify(sz) ")"
+#define NJS_LARGE_ARRAY _NJS_ARRAY(NJS_ARRAY_LARGE_OBJECT_LENGTH + 1)
+#define NJS_LARGE_ARRAY_LEN "32769"
typedef struct {
"true:true,Infinity:Infinity,-Infinity:-Infinity,NaN:NaN,") },
{ njs_str("--[][3e9]"),
- njs_str("MemoryError") },
+ njs_str("NaN") },
{ njs_str("[].length"),
njs_str("0") },
njs_str("RangeError: Invalid array length") },
{ njs_str("[].length = 2**32 - 1"),
- njs_str("MemoryError") },
+ njs_str("4294967295") },
+
+ { njs_str("var a = []; a.length = 2**32 - 1; a.length"),
+ njs_str("4294967295") },
{ njs_str("[].length = 3e9"),
- njs_str("MemoryError") },
+ njs_str("3000000000") },
- { njs_str("Object.defineProperty([], 'length',{value: 2**32 - 1})"),
- njs_str("MemoryError") },
+ { njs_str("var a = []; Object.defineProperty(a, 'length',{value: 2**32 - 1}); a.length"),
+ njs_str("4294967295") },
{ njs_str("[].length = 2**32"),
njs_str("RangeError: Invalid array length") },
{ njs_str("var a = [1,2,3]; a.join(':')"),
njs_str("1:2:3") },
+ { njs_str("["
+ " [],"
+ " ['β', 'γ'],"
+ "]"
+ ".map(v=>{v.length = 2**14+1; var out = v.join('α'); return [out[0], out[out.length - 1],out.length]})"
+ ".map(v=>njs.dump(v))"),
+ njs_str("['α','α',16384],['β','α',16386]") },
+
{ njs_str("["
" 'α'.repeat(33),"
" String.bytesFrom(Array(16).fill(0x9d)),"
{ njs_str("var a = []; a[5] = 5; a"),
njs_str(",,,,,5") },
- { njs_str("var a = []; a.concat([])"),
- njs_str("") },
+ { njs_str("var a = []; a.concat([],[1],[])"),
+ njs_str("1") },
+
+ { njs_str("var a = []; a[Symbol.isConcatSpreadable] = undefined; [].concat(a).length"),
+ njs_str("0") },
+
+ { njs_str("var a = []; Object.defineProperty(a, Symbol.isConcatSpreadable, {get:()=>{throw 'Oops'}}); "
+ "[].concat(a)"),
+ njs_str("Oops") },
+
+ { njs_str("var a = [].concat([1,2,3], {length:3, 1:4, 2:5, [Symbol.isConcatSpreadable]:1});"
+ "njs.dump([a, a.length])"),
+ njs_str("[[1,2,3,<empty>,4,5],6]") },
+
+ { njs_str("njs.dump([].concat([1,2,3], {length:3, 1:4, 2:5}))"),
+ njs_str("[1,2,3,{length:3,1:4,2:5}]") },
+
+ { njs_str("Array.prototype[1] = 1; var x = [0]; x.length = 2; "
+ "x.concat().hasOwnProperty('1') === true"),
+ njs_str("true") },
+
+ { njs_str("var a = " NJS_LARGE_ARRAY ";"
+ "a[32] = 1; a = a.concat([1]);"
+ "njs.dump([a[0], a[32],a.length])"),
+ njs_str("[undefined,1,32770]") },
+
+ { njs_str("var a = " NJS_LARGE_ARRAY ";"
+ "a[32] = 1; a = [1].concat(a);"
+ "njs.dump([a[0], a[33],a.length])"),
+ njs_str("[1,1,32770]") },
+
+ { njs_str("var re = /abc/; re[Symbol.isConcatSpreadable] = true;"
+ "re[0] = 1, re[1] = 2, re[2] = 3, re.length = 3;"
+ "[].concat(re)"),
+ njs_str("1,2,3") },
+
+ { njs_str("var s = new String('yuck\\uD83D\\uDCA9'); s[Symbol.isConcatSpreadable] = true;"
+ "[].concat(s)"),
+ njs_str("y,u,c,k,💩") },
{ njs_str("var s = { toString: function() { return 'S' } };"
"var v = { toString: 8, valueOf: function() { return 'V' } };"
{ njs_str("Array.prototype.toString.call('abc')"),
njs_str("[object String]") },
+ { njs_str("var a = " NJS_LARGE_ARRAY "; var s = a.toString();"
+ "[s.length]"),
+ njs_str("32768") },
+
/* Empty array elements. */
{ njs_str("[,,]"),
{ njs_str("Array.prototype.slice.call({length:-1})"),
njs_str("") },
+ { njs_str("Array.prototype[1] = 1; var x = [0]; x.length = 2;"
+ "var a = x.slice(); a.hasOwnProperty('1')"),
+ njs_str("true") },
+
{ njs_str("Array.prototype.slice.call('αβZγ')"),
njs_str("α,β,Z,γ") },
{ njs_str("var o = { length: 3 }; Array.prototype.pop.call(o); o.length"),
njs_str("2") },
+ { njs_str("var a = " NJS_LARGE_ARRAY "; a[a.length - 1] = 'z'; a[a.length -2] = 'y';"
+ "Array.prototype.pop.call(a); [a.length, a[a.length - 1]]"),
+ njs_str("32768,y") },
+
{ njs_str("Array.prototype.shift()"),
njs_str("undefined") },
{ njs_str("var o = { length: 3 }; Array.prototype.shift.call(o); o.length"),
njs_str("2") },
+ { njs_str("var a = [1,2,3];"
+ "Object.defineProperty(a, '1', {enumerable:false});"
+ "a.shift(); a"),
+ njs_str("2,3") },
+
{ njs_str("var a = []; a.splice()"),
njs_str("") },
{ njs_str("var a = [1,2,3,4]; a.reverse()"),
njs_str("4,3,2,1") },
+ { njs_str("var o = {1:true, 2:'', length:-2}; Array.prototype.reverse.call(o) === o"),
+ njs_str("true") },
+
{ njs_str("var a = [1,2,3,4]; a.indexOf()"),
njs_str("-1") },
"Array.prototype.indexOf.call(o); i"),
njs_str("1") },
+#if (!NJS_HAVE_MEMORY_SANITIZER) /* False-positive in MSAN? */
+ { njs_str("var a = new Array(); a[100] =1; a[99999] = ''; a[10] = new Object(); "
+ "a[5555] = 5.5; a[123456] = 'str'; a[5] = 1E+309; "
+ "[1, '', 'str', 1E+309, 5.5, true, 5, 'str1', null, new Object()].map(v=>a.indexOf(v))"),
+ njs_str("100,99999,123456,5,5555,-1,-1,-1,-1,-1") },
+#endif
+
+ { njs_str("Array.prototype.indexOf.call({199:true, 200:'200.59', length:200}, '200.59')"),
+ njs_str("-1") },
+
+ { njs_str("Array.prototype.indexOf.call({199:true, 200:'200.59', length:201}, '200.59')"),
+ njs_str("200") },
+
+ { njs_str("Array.prototype.indexOf.call({1:true, 2:'200.59', length:2}, '200.59')"),
+ njs_str("-1") },
+
+ { njs_str("Array.prototype.indexOf.call({1:true, 2:'200.59', length:3}, '200.59')"),
+ njs_str("2") },
+
{ njs_str("[].lastIndexOf(1, -1)"),
njs_str("-1") },
"Array.prototype.lastIndexOf.call(o, 'd')"),
njs_str("3") },
+#if (!NJS_HAVE_MEMORY_SANITIZER) /* False-positive in MSAN? */
+ { njs_str("var a = new Array(); a[100] =1; a[99999] = ''; a[10] = new Object(); "
+ "a[5555] = 5.5; a[123456] = 'str'; a[5] = 1E+309; "
+ "[1,'', 'str', 1E+309, 5.5, true, 5, 'str1', null, new Object()].map(v=>a.lastIndexOf(v))"),
+ njs_str("100,99999,123456,5,5555,-1,-1,-1,-1,-1") },
+#endif
+
{ njs_str("var obj = {'10000000': 'x', '10000001': 'y', '10000002': 'z'}; var a = [];"
"obj.length = 90000000;"
"Array.prototype.lastIndexOf.call(obj, 'y');"),
"Array.prototype.lastIndexOf.call(o); i"),
njs_str("1") },
+ { njs_str("Array.prototype.lastIndexOf.call({199:true, 200:'200.59', length:200}, '200.59')"),
+ njs_str("-1") },
+
{ njs_str("[''].lastIndexOf.call('00000000000000000000000000000а00')"),
njs_str("-1") },
"catch (e) {i += '; ' + e} i"),
njs_str("1; TypeError: unexpected iterator arguments") },
+ { njs_str("var obj = new Date(); obj.length = 1; obj[0] = 1;"
+ "Array.prototype.every.call(obj, (val,idx,obj)=>!(obj instanceof Date))"),
+ njs_str("false") },
+
+ { njs_str("var vis = false; var a = []; "
+ "Object.defineProperty(a, '0', {get:()=>{vis = true; return 11;}, configurable:true});"
+ "Object.defineProperty(a, '1', {get:()=>{if (vis) {return 9;} else {return 11}}, configurable:true});"
+ "a.every(val=>val > 10)"),
+ njs_str("false") },
+
{ njs_str("var o = {0: 'x', 1: 'y', 2: 'z'};"
"Object.defineProperty(o, 'length', {get: () => 4});"
"Object.defineProperty(o, '3', {get: () => 'a'});"
"catch (e) {i += '; ' + e} i"),
njs_str("1; TypeError: unexpected iterator arguments") },
+ { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {enumerable:false});"
+ "a.map(v=>v)"),
+ njs_str("1,2,3") },
+
+ { njs_str("Array.prototype.map.call({0:9, length:2**16}, val=>val<10).length"),
+ njs_str("65536") },
+
{ njs_str("var a = [];"
"a.reduce(function(p, v, i, a) { return p + v })"),
njs_str("TypeError: Reduce of empty object with no initial value") },
"var a = [6,o,4,3,2,1]; a.sort()"),
njs_str("1,2,3,4,5,6") },
+ { njs_str("var a = {0:3,1:2,2:1}; Array.prototype.sort.call(a) === a"),
+ njs_str("true") },
+
+ { njs_str("var a = {0:3,1:2,2:1,length:0}; Array.prototype.sort.call(a) === a"),
+ njs_str("true") },
+
{ njs_str("var a = [1,2,3,4,5,6];"
"a.sort(function(x, y) { return x - y })"),
njs_str("1,2,3,4,5,6") },
{ njs_str("var a = Array(Infinity)"),
njs_str("RangeError: Invalid array length") },
- { njs_str("var a = Array(1111111111)"),
- njs_str("MemoryError") },
+ { njs_str(NJS_LARGE_ARRAY ".length"),
+ njs_str(NJS_LARGE_ARRAY_LEN) },
+
+ { njs_str("var a = Array(1111111111); a[1111111112] = 1; a.length"),
+ njs_str("1111111113") },
+
+ { njs_str("var a = Array(1111111111); a[1111111112] = 1; a[1111111112]"),
+ njs_str("1") },
+
+ { njs_str("var a = Array(1111111111); a[1] = 2; a[1111111112] = 1; Object.keys(a)"),
+ njs_str("1,1111111112") },
+
+ { njs_str("var a = Array(1111111111); a[1] = 2; a[1111111112] = 1; Object.entries(a)"),
+ njs_str("1,2,1111111112,1") },
{ njs_str("var x = Array(2**32)"),
njs_str("RangeError: Invalid array length") },
{ njs_str("var x = Array(2**28)"),
- njs_str("MemoryError") },
+ njs_str("undefined") },
+
+ { njs_str("Array.prototype[2] = -1; var x = [0,1,3]; x.length = 2; x[2]"),
+ njs_str("-1") },
+ { njs_str("Array.prototype[1] = 1; var x = [0]; x.length = 2; x[1]"),
+ njs_str("1") },
+
+ { njs_str("var a = new Array(4); Object.defineProperty(a, '0', {enumerable:false}); a[2] = 's';"
+ "Array.prototype.length"),
+ njs_str("0") },
+
+ { njs_str("var x = [0, 1, 2]; x[4294967294] = 4294967294; x.length = 2;"
+ "njs.dump([x,x.length,Array.prototype,Array.prototype.length])"),
+ njs_str("[[0,1],2,[],0]") },
+
+#if 0 /* TODO: length 2**53-1. */
{ njs_str("var x = Array(2**20), y = Array(2**12).fill(x);"
"Array.prototype.concat.apply(y[0], y.slice(1))"),
njs_str("RangeError: Invalid array length") },
+#endif
{ njs_str("var a = new Array(3); a"),
njs_str(",,") },
"JSON.stringify(Object.getOwnPropertyDescriptor(o, 'a')).set"),
njs_str("undefined") },
+ { njs_str("var a = []; Object.defineProperty(a, 4294967294, {value:100}); "
+ "[a.hasOwnProperty('4294967294'), a.length, a[4294967294]]"),
+ njs_str("true,4294967295,100") },
+
+ { njs_str("var a = []; Object.defineProperty(a, 'length', {value:4294967294}); "
+ "a.length"),
+ njs_str("4294967294") },
+
+ { njs_str("var a = []; Object.defineProperty(a, 'length', {value:4294967295}); "
+ "a.length"),
+ njs_str("4294967295") },
+
+ { njs_str("njs.dump(Object.defineProperty([], 'length', {value:4294967295}))"),
+ njs_str("[<4294967295 empty items>]") },
+
+ { njs_str("var a = [1]; Object.defineProperty(a, '1', {get:()=>2}); "
+ "[a[0], a[1], a.length]"),
+ njs_str("1,2,2") },
+
+ { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {configurable:false}); "
+ "[a[0], a[1], a.length]"),
+ njs_str("1,2,3") },
+
+ { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {configurable:false}); "
+ "delete a[1]"),
+ njs_str("TypeError: Cannot delete property \"1\" of array") },
+
+ { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {configurable:false}); "
+ "a.length = 1"),
+ njs_str("TypeError: Cannot delete property \"1\" of array") },
+
+ { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {writable:false}); "
+ "a[1]=4"),
+ njs_str("TypeError: Cannot assign to read-only property \"1\" of array") },
+
+ { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {enumerable:false}); "
+ "njs.dump([Object.keys(a), Object.values(a), Object.entries(a)])"),
+ njs_str("[['0','2'],[1,3],[['0',1],['2',3]]]") },
+
+ { njs_str("var a = [1]; Object.defineProperty(a, '1', {get:()=>2});"
+ "Object.defineProperty(a, 'length', {value:1})"),
+ njs_str("TypeError: Cannot delete property \"1\" of array") },
+
+ { njs_str("var a = [1]; Object.defineProperty(a, '1', {get:()=>2});"
+ "Object.defineProperty(a, 'length', {enumerable:true})"),
+ njs_str("TypeError: Cannot redefine property: \"length\"") },
+
+ { njs_str("var a = [1]; Object.defineProperty(a, '1', {get:()=>2, configurable:true});"
+ "Object.defineProperty(a, 'length', {value:1}); a[1]"),
+ njs_str("undefined") },
+
+ { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {enumerable:false});"
+ "Object.getOwnPropertyDescriptor(a, '1').enumerable"),
+ njs_str("false") },
+
+ { njs_str("var args = (function(a, b, c) {return arguments;})(1,2,3); "
+ "Object.defineProperty(args, \"length\", {value:6}); args.length"),
+ njs_str("6") },
+
{ njs_str("var get = 'get'; var o = { get }; o.get"),
njs_str("get") },
{ njs_str("var a = [1]; a[2] = 'x'; JSON.stringify(a)"),
njs_str("[1,null,\"x\"]") },
+ { njs_str("var a = " NJS_LARGE_ARRAY ";"
+ "a[32] = 'a'; a[64] = 'b';"
+ "var s = JSON.stringify(a); "
+ "[s.length,s.substring(162,163),s.match(/null/g).length]"),
+ njs_str("163844,a,32767") },
+
+ { njs_str("var a = " NJS_LARGE_ARRAY ";"
+ "a[2] = 'a'; a[4] = 'b'; a.length = 3;"
+ "JSON.stringify(a)"),
+ njs_str("[null,null,\"a\"]") },
+
+ { njs_str("var a = [1,2,3];"
+ "Object.defineProperty(a, '1', {enumerable:false});"
+ "JSON.stringify(a)"),
+ njs_str("[1,2,3]") },
+
{ njs_str("JSON.stringify({a:\"b\",c:19,e:null,t:true,f:false})"),
njs_str("{\"a\":\"b\",\"c\":19,\"e\":null,\"t\":true,\"f\":false}") },
{ njs_str("var a = [], b = [a]; a[0] = b; njs.dump(a)"),
njs_str("[[[Circular]]]") },
+ { njs_str("var a = []; a.length = 2**31;"
+ "njs.dump(a)"),
+ njs_str("[<2147483648 empty items>]") },
+
+ { njs_str("var a = ['a',,'c']; a.length = 2**31;"
+ "njs.dump(a)"),
+ njs_str("['a',<1 empty items>,'c',<2147483645 empty items>]") },
+
+ { njs_str("var a = [,'b','c']; a.length = 2**31;"
+ "njs.dump(a)"),
+ njs_str("[<1 empty items>,'b','c',<2147483645 empty items>]") },
+
+#if (!NJS_HAVE_MEMORY_SANITIZER) /* False-positive in MSAN? */
+ { njs_str("var a = []; a[2**31] = 'Z'; a[0] = 'A'; njs.dump(a)"),
+ njs_str("['A',<2147483647 empty items>,'Z']") },
+
+ { njs_str("var a = []; a[2**31] = 'Z'; a[0] = 'A'; a.b = 'X'; njs.dump(a)"),
+ njs_str("['A',<2147483647 empty items>,'Z',b:'X']") },
+
+ { njs_str("var a = []; a[2**31] = 'Z'; a[0] = 'A'; a.b = 'X'; a.length = 3; njs.dump(a)"),
+ njs_str("['A',<2 empty items>,b:'X']") },
+#endif
+
+ { njs_str("var a = [1,2,3];Object.defineProperty(a, '1', {get:()=>2});njs.dump(a)"),
+ njs_str("[1,'[Getter]',3]") },
+
+ { njs_str("var a = [1,2,3];Object.defineProperty(a, '1', {enumerable:false});njs.dump(a)"),
+ njs_str("[1,<1 empty items>,3]") },
+
{ njs_str("njs.dump(-0)"),
njs_str("-0") },