[njs] Fixed Array.prototype.slice() when array is changed while iterating.
Dmitry Volyntsev
xeioex at nginx.com
Fri Jan 14 14:57:21 UTC 2022
details: https://hg.nginx.org/njs/rev/d940c6aaec5d
branches:
changeset: 1803:d940c6aaec5d
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Thu Jan 13 15:59:08 2022 +0000
description:
Fixed Array.prototype.slice() 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 (9578cc729205) commit.
This closes #445 issue on Github.
diffstat:
src/njs_array.c | 49 +++++++++++------------------------------------
src/test/njs_unit_test.c | 10 +++++++++
2 files changed, 22 insertions(+), 37 deletions(-)
diffs (95 lines):
diff -r 9578cc729205 -r d940c6aaec5d src/njs_array.c
--- a/src/njs_array.c Wed Jan 12 17:59:42 2022 +0000
+++ b/src/njs_array.c Thu Jan 13 15:59:08 2022 +0000
@@ -729,7 +729,7 @@ njs_array_prototype_slice_copy(njs_vm_t
uint32_t n;
njs_int_t ret;
njs_array_t *array, *keys;
- njs_value_t *value, retval, self;
+ njs_value_t *value, *last, retval, self;
const u_char *src, *end;
njs_slice_prop_t string_slice;
njs_string_prop_t string;
@@ -748,34 +748,7 @@ njs_array_prototype_slice_copy(njs_vm_t
n = 0;
if (njs_fast_path(array->object.fast_array)) {
- if (njs_fast_path(njs_is_fast_array(this))) {
- value = njs_array_start(this);
-
- do {
- if (njs_fast_path(njs_is_valid(&value[start]))) {
- array->start[n++] = value[start++];
-
- } else {
-
- /* src value may be in Array.prototype object. */
-
- ret = njs_value_property_i64(vm, this, start++,
- &array->start[n]);
- if (njs_slow_path(ret == NJS_ERROR)) {
- return NJS_ERROR;
- }
-
- if (ret != NJS_OK) {
- njs_set_invalid(&array->start[n]);
- }
-
- n++;
- }
-
- length--;
- } while (length != 0);
-
- } else if (njs_is_string(this) || njs_is_object_string(this)) {
+ if (njs_is_string(this) || njs_is_object_string(this)) {
if (njs_is_object_string(this)) {
this = njs_object_value(this);
@@ -816,16 +789,18 @@ njs_array_prototype_slice_copy(njs_vm_t
} else if (njs_is_object(this)) {
- do {
- value = &array->start[n++];
- ret = njs_value_property_i64(vm, this, start++, value);
-
- if (ret != NJS_OK) {
+ 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);
}
-
- length--;
- } while (length != 0);
+ }
} else {
diff -r 9578cc729205 -r d940c6aaec5d src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Wed Jan 12 17:59:42 2022 +0000
+++ b/src/test/njs_unit_test.c Thu Jan 13 15:59:08 2022 +0000
@@ -4525,6 +4525,16 @@ static njs_unit_test_t njs_test[] =
"Array.prototype.slice.call(1, 0, 2)"),
njs_str(",") },
+ { njs_str("var a = [1, /**/, 3, 4];"
+ "Object.defineProperty(a.__proto__, 1, {"
+ " get: () => {"
+ " a.length = 10**6;"
+ " return 2;"
+ " }"
+ "});"
+ "a.slice(1)"),
+ njs_str("2,3,4") },
+
{ njs_str("Array.prototype.pop()"),
njs_str("undefined") },
More information about the nginx-devel
mailing list