]> git.kaiwu.me - quickjs.git/commitdiff
fixed slow Array truncation (#585)
authorFabrice Bellard <fabrice@bellard.org>
Sat, 31 Oct 2026 16:46:00 +0000 (17:46 +0100)
committerFabrice Bellard <fabrice@bellard.org>
Sat, 31 Oct 2026 16:46:00 +0000 (17:46 +0100)
quickjs.c
tests/test_builtin.js

index ed81ff89f67ea88654dd55944a7863d41017a65e..12a47bfb245619f4e46104814e6124e814efdb74 100644 (file)
--- a/quickjs.c
+++ b/quickjs.c
@@ -9580,6 +9580,44 @@ static int call_setter(JSContext *ctx, JSObject *setter,
     }
 }
 
+static void delete_property_no_move(JSContext *ctx, JSObject *p, JSShapeProperty *pr_del)
+{
+    JSShape *sh;
+    JSShapeProperty *pr, *lpr, *prop;
+    JSProperty *pr1;
+    intptr_t h, h1;
+
+    sh = p->shape;
+    h1 = pr_del->atom & sh->prop_hash_mask;
+    h = sh->hash_table[h1];
+    prop = get_shape_prop(sh);
+    lpr = NULL;
+    while (h != 0) {
+        pr = &prop[h - 1];
+        if (pr == pr_del) {
+            /* remove property */
+            if (lpr) {
+                lpr->hash_next = pr->hash_next;
+            } else {
+                sh->hash_table[h1] = pr->hash_next;
+            }
+            sh->deleted_prop_count++;
+            /* free the entry */
+            pr1 = &p->prop[h - 1];
+            free_property(ctx->rt, pr1, pr->flags);
+            JS_FreeAtom(ctx, pr->atom);
+            /* put default values */
+            pr->flags = 0;
+            pr->atom = JS_ATOM_NULL;
+            pr1->u.value = JS_UNDEFINED;
+            return;
+        }
+        lpr = pr;
+        h = pr->hash_next;
+    }
+    abort();
+}
+
 /* set the array length and remove the array elements if necessary. */
 static int set_array_length(JSContext *ctx, JSObject *p, JSValue val,
                             int flags)
@@ -9632,7 +9670,7 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSValue val,
                 }
             } else {
                 /* faster to iterate thru all the properties. Need two
-                   passes in case one of the property is not
+                   passes in case one of the properties is not
                    configurable */
                 cur_len = len;
                 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
@@ -9646,19 +9684,26 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSValue val,
                     }
                 }
 
+                if (js_shape_prepare_update(ctx, p, NULL))
+                    return -1;
+                sh = p->shape;
+                
                 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
                     i++, pr++) {
                     if (pr->atom != JS_ATOM_NULL &&
                         JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
                         if (idx >= cur_len) {
-                            /* remove the property */
-                            delete_property(ctx, p, pr->atom);
-                            /* WARNING: the shape may have been modified */
-                            sh = p->shape;
-                            pr = get_shape_prop(sh) + i;
+                            /* remove the property without compaction (cannot fail) */
+                            delete_property_no_move(ctx, p, pr);
                         }
                     }
                 }
+
+                /* compact the properties if too many deleted properties */
+                if (sh->deleted_prop_count >= 8 &&
+                    sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) {
+                    compact_properties(ctx, p);
+                }
             }
         } else {
             cur_len = len;
index a7fb65a32c97377b92266e03022a62dcb58fcb96..808801acc848746caf10d7671a9b9b4eda7bbf2f 100644 (file)
@@ -234,6 +234,14 @@ function test_array()
     /* sort writes back every element, even those already in place */
     assert_throws(TypeError, () => Array.prototype.sort.call("aa"));
     assert_throws(TypeError, () => Object.freeze([1, 2]).sort());
+
+    /* array length modification with possible compaction */
+    a = [0, 1, 2, 3, 4, 5, 6, 7];
+    a[10] = 1;
+    a.length = 0;
+    a[10] = 2;
+    assert(a.length, 11);
+    assert(a.toString(), ",,,,,,,,,,2");
 }
 
 function test_string()