]> git.kaiwu.me - njs.git/commitdiff
Fix Array.prototype.slice() of large arrays in the non-fast keys path
authorDmitry Volyntsev <xeioex@nginx.com>
Fri, 12 Jun 2026 01:44:01 +0000 (18:44 -0700)
committerDmitry Volyntsev <xeioexception@gmail.com>
Tue, 16 Jun 2026 23:22:57 +0000 (16:22 -0700)
When the slice result length was 32761 or more, the destination array was
non-fast and slice_copy() reached the keys path, which enumerated every
own index of the source and copied each at its original key, ignoring the
requested [start, start + length) window and the destination-relative
position.  Array.prototype.slice() thus returned wrong results for such
arrays.

Filter the enumerated keys to the window and write them at index - start,
matching the fast-array path.

While here, also remove dead fast object path in slice_copy.

src/njs_array.c
src/test/njs_unit_test.c

index 636dcfca9b5ba9a1e87453053eff79e035ec8571..5c30e833d9413b7338779f5cee0e6b12a231a28b 100644 (file)
@@ -788,6 +788,7 @@ 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)
 {
+    double             idx;
     size_t             size;
     uint32_t           n;
     njs_int_t          ret;
@@ -871,39 +872,24 @@ njs_array_prototype_slice_copy(njs_vm_t *vm, njs_value_t *this,
 
     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++) {
+        idx = njs_string_to_index(&keys->start[n]);
+
+        if (idx < start || idx >= start + length) {
+            continue;
+        }
+
         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);
+        ret = njs_value_property_i64_set(vm, &self, idx - start, &val);
         if (njs_slow_path(ret == NJS_ERROR)) {
             goto done;
         }
index 7a28332c35d0c1ea98a6f89b86e261e0cc7e45c5..8cc0e8bebb7ed66b9268133dc6ae00e46d48ea44 100644 (file)
@@ -5315,6 +5315,13 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("Array.prototype.slice.call({ length: Object.create(null) })"),
       njs_str("TypeError: Cannot convert object to primitive value") },
 
+    /* Large sparse slice goes through the non-fast keys path. */
+
+    { njs_str("var a = []; a[10] = 'a'; a[40000] = 'b'; a.length = 50000;"
+              "var s = a.slice(5, 45000);"
+              "[s.length, s[5], s[39995], (10 in s), (40000 in s)].join(',')"),
+      njs_str("44995,a,b,false,false") },
+
     { njs_str("Array.prototype.slice.call({length:-1})"),
       njs_str("") },