[njs] Fixed Array.prototype.concat() when array is changed while iterating.
Dmitry Volyntsev
xeioex at nginx.com
Fri Jan 14 14:57:23 UTC 2022
details: https://hg.nginx.org/njs/rev/2c1382bab643
branches:
changeset: 1804:2c1382bab643
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Thu Jan 13 16:20:58 2022 +0000
description:
Fixed Array.prototype.concat() when array is changed while iterating.
Previously, the flat array may be converted to a slow one as a
side-effect of a custom getter invocation for a proto array object.
The function erroneously assumed that the this array remains flat
while iterating.
The fix is to eliminate the micro-optimization which uses direct
pointers.
The problem is similar to the previous commits.
diffstat:
src/njs_array.c | 63 +++++++++--------------------------------------
src/test/njs_unit_test.c | 11 ++++++++
2 files changed, 24 insertions(+), 50 deletions(-)
diffs (128 lines):
diff -r d940c6aaec5d -r 2c1382bab643 src/njs_array.c
--- a/src/njs_array.c Thu Jan 13 15:59:08 2022 +0000
+++ b/src/njs_array.c Thu Jan 13 16:20:58 2022 +0000
@@ -1780,7 +1780,7 @@ njs_array_prototype_concat(njs_vm_t *vm,
int64_t k, len, length;
njs_int_t ret;
njs_uint_t i;
- njs_value_t this, retval, *value, *e;
+ njs_value_t this, retval, *e;
njs_array_t *array, *keys;
ret = njs_value_to_object(vm, &args[0]);
@@ -1819,26 +1819,18 @@ njs_array_prototype_concat(njs_vm_t *vm,
return NJS_ERROR;
}
- if (njs_is_fast_array(&this) && njs_is_fast_array(e)
- && (length + len) <= NJS_ARRAY_LARGE_OBJECT_LENGTH)
- {
+ if (njs_is_fast_array(e)) {
for (k = 0; k < len; k++, length++) {
- value = &njs_array_start(e)[k];
-
- if (njs_slow_path(!njs_is_valid(value))) {
- ret = njs_value_property_i64(vm, e, k, &retval);
- if (njs_slow_path(ret == NJS_ERROR)) {
- return ret;
+ ret = njs_value_property_i64(vm, e, k, &retval);
+ if (njs_slow_path(ret != NJS_OK)) {
+ if (ret == NJS_ERROR) {
+ return NJS_ERROR;
}
- if (ret == NJS_DECLINED) {
- njs_set_invalid(&retval);
- }
-
- value = &retval;
+ njs_set_invalid(&retval);
}
- ret = njs_array_add(vm, array, value);
+ ret = njs_array_add(vm, array, &retval);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
@@ -1847,27 +1839,6 @@ njs_array_prototype_concat(njs_vm_t *vm,
continue;
}
- if (njs_fast_object(len)) {
- for (k = 0; k < len; k++, length++) {
- ret = njs_value_property_i64(vm, e, k, &retval);
- if (njs_slow_path(ret == NJS_ERROR)) {
- return ret;
- }
-
- if (ret != NJS_OK) {
- continue;
- }
-
- ret = njs_value_property_i64_set(vm, &this, length,
- &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;
@@ -1879,9 +1850,9 @@ njs_array_prototype_concat(njs_vm_t *vm,
return ret;
}
- idx = njs_string_to_index(&keys->start[k]) + length;
-
if (ret == NJS_OK) {
+ idx = njs_string_to_index(&keys->start[k]) + length;
+
ret = njs_value_property_i64_set(vm, &this, idx, &retval);
if (njs_slow_path(ret == NJS_ERROR)) {
njs_array_destroy(vm, keys);
@@ -1902,17 +1873,9 @@ njs_array_prototype_concat(njs_vm_t *vm,
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 {
- ret = njs_value_property_i64_set(vm, &this, length, e);
- if (njs_slow_path(ret == NJS_ERROR)) {
- return ret;
- }
+ ret = njs_value_property_i64_set(vm, &this, length, e);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
}
length++;
diff -r d940c6aaec5d -r 2c1382bab643 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Thu Jan 13 15:59:08 2022 +0000
+++ b/src/test/njs_unit_test.c Thu Jan 13 16:20:58 2022 +0000
@@ -12834,6 +12834,17 @@ static njs_unit_test_t njs_test[] =
"Object.getOwnPropertyDescriptor(o, Symbol.isConcatSpreadable).value"),
njs_str("true") },
+ { njs_str("var a = [1];"
+ "var b = [2, /**/, 4, 5];"
+ "Object.defineProperty(b.__proto__, 1, {"
+ " get: () => {"
+ " b.length = 10**6;"
+ " return 3;"
+ " }"
+ "});"
+ "a.concat(b)"),
+ njs_str("1,2,3,4,5") },
+
{ njs_str("var o = {}, n = 5381 /* NJS_DJB_HASH_INIT */;"
"while(n--) o[Symbol()] = 'test'; o[''];"),
njs_str("undefined") },
More information about the nginx-devel
mailing list