From 88b31f342d3b159c8f6b83c5f1c26b04060b6838 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 2 Nov 2022 14:29:15 +0100 Subject: [PATCH] Allow holes at the end of a simple array. Don't unflatten when creating with a = new Array(10). Don't unflatten when deleting the last element. --- jsarray.c | 3 +-- jsgc.c | 2 +- jsobject.c | 4 +-- jsproperty.c | 2 +- jsrun.c | 70 +++++++++++++++++++++++++++++++++------------------- jsvalue.h | 5 ++-- 6 files changed, 52 insertions(+), 34 deletions(-) diff --git a/jsarray.c b/jsarray.c index fbea9e5..f2ec75c 100644 --- a/jsarray.c +++ b/jsarray.c @@ -318,7 +318,7 @@ static void Ap_sort(js_State *J) js_pushvalue(J, array[i].v); js_setindex(J, 0, i); } - for (i = n; i < len; ++i) { + for (i = len-i; i >= n; --i) { js_delindex(J, 0, i); } @@ -351,7 +351,6 @@ static void Ap_splice(js_State *J) if (del < 0) del = 0; - js_newarray(J); /* copy deleted items to return array */ diff --git a/jsgc.c b/jsgc.c index 61eaa0b..64057da 100644 --- a/jsgc.c +++ b/jsgc.c @@ -108,7 +108,7 @@ static void jsG_scanobject(js_State *J, int mark, js_Object *obj) jsG_markobject(J, mark, obj->prototype); if (obj->type == JS_CARRAY && obj->u.a.simple) { int i; - for (i = 0; i < obj->u.a.length; ++i) { + for (i = 0; i < obj->u.a.flat_length; ++i) { js_Value *v = &obj->u.a.array[i]; if (v->type == JS_TMEMSTR && v->u.memstr->gcmark != mark) v->u.memstr->gcmark = mark; diff --git a/jsobject.c b/jsobject.c index 27edbf6..0b74777 100644 --- a/jsobject.c +++ b/jsobject.c @@ -73,7 +73,7 @@ static void Op_hasOwnProperty(js_State *J) } if (self->type == JS_CARRAY && self->u.a.simple) { - if (js_isarrayindex(J, name, &k) && k >= 0 && k < self->u.a.length) { + if (js_isarrayindex(J, name, &k) && k >= 0 && k < self->u.a.flat_length) { js_pushboolean(J, 1); return; } @@ -189,7 +189,7 @@ static void O_getOwnPropertyNames(js_State *J) js_pushliteral(J, "length"); js_setindex(J, -2, i++); if (obj->u.a.simple) { - for (k = 0; k < obj->u.a.length; ++k) { + for (k = 0; k < obj->u.a.flat_length; ++k) { js_itoa(name, k); js_pushstring(J, name); js_setindex(J, -2, i++); diff --git a/jsproperty.c b/jsproperty.c index 4371d9c..b355316 100644 --- a/jsproperty.c +++ b/jsproperty.c @@ -293,7 +293,7 @@ js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own) io->u.iter.n = obj->u.s.length; if (obj->type == JS_CARRAY && obj->u.a.simple) - io->u.iter.n = obj->u.a.length; + io->u.iter.n = obj->u.a.flat_length; return io; } diff --git a/jsrun.c b/jsrun.c index 05c6e32..c41a37f 100644 --- a/jsrun.c +++ b/jsrun.c @@ -528,14 +528,15 @@ void jsR_unflattenarray(js_State *J, js_Object *obj) { obj->properties = NULL; js_throw(J); } - for (i = 0; i < obj->u.a.length; ++i) { + for (i = 0; i < obj->u.a.flat_length; ++i) { js_itoa(name, i); ref = jsV_setproperty(J, obj, name); ref->value = obj->u.a.array[i]; } js_free(J, obj->u.a.array); obj->u.a.simple = 0; - obj->u.a.capacity = 0; + obj->u.a.flat_length = 0; + obj->u.a.flat_capacity = 0; obj->u.a.array = NULL; js_endtry(J); } @@ -553,10 +554,11 @@ static int jsR_hasproperty(js_State *J, js_Object *obj, const char *name) } if (obj->u.a.simple) { if (js_isarrayindex(J, name, &k)) { - if (k >= 0 && k < obj->u.a.length) { + if (k >= 0 && k < obj->u.a.flat_length) { js_pushvalue(J, obj->u.a.array[k]); return 1; } + return 0; } } } @@ -626,9 +628,12 @@ static void jsR_getproperty(js_State *J, js_Object *obj, const char *name) static int jsR_hasindex(js_State *J, js_Object *obj, int k) { char buf[32]; - if (obj->type == JS_CARRAY && obj->u.a.simple && k >= 0 && k < obj->u.a.length) { - js_pushvalue(J, obj->u.a.array[k]); - return 1; + if (obj->type == JS_CARRAY && obj->u.a.simple) { + if (k >= 0 && k < obj->u.a.flat_length) { + js_pushvalue(J, obj->u.a.array[k]); + return 1; + } + return 0; } return jsR_hasproperty(J, obj, js_itoa(buf, k)); } @@ -646,19 +651,21 @@ static void jsR_setarrayindex(js_State *J, js_Object *obj, int k, js_Value *valu assert(k >= 0); if (newlen > JS_ARRAYLIMIT) js_rangeerror(J, "array too large"); - if (newlen > obj->u.a.length) { - assert(newlen == obj->u.a.length + 1); - if (newlen > obj->u.a.capacity) { - int newcap = obj->u.a.capacity; + if (newlen > obj->u.a.flat_length) { + assert(newlen == obj->u.a.flat_length + 1); + if (newlen > obj->u.a.flat_capacity) { + int newcap = obj->u.a.flat_capacity; if (newcap == 0) newcap = 8; while (newcap < newlen) newcap <<= 1; obj->u.a.array = js_realloc(J, obj->u.a.array, newcap * sizeof(js_Value)); - obj->u.a.capacity = newcap; + obj->u.a.flat_capacity = newcap; } - obj->u.a.length = newlen; + obj->u.a.flat_length = newlen; } + if (newlen > obj->u.a.length) + obj->u.a.length = newlen; obj->u.a.array[k] = *value; } @@ -678,26 +685,28 @@ static void jsR_setproperty(js_State *J, js_Object *obj, const char *name, int t if (newlen > JS_ARRAYLIMIT) js_rangeerror(J, "array too large"); if (obj->u.a.simple) { - if (newlen <= obj->u.a.length) { - obj->u.a.length = newlen; - return; - } - jsR_unflattenarray(J, obj); + obj->u.a.length = newlen; + if (newlen <= obj->u.a.flat_length) + obj->u.a.flat_length = newlen; + } else { + jsV_resizearray(J, obj, newlen); } - jsV_resizearray(J, obj, newlen); return; } if (js_isarrayindex(J, name, &k)) { if (obj->u.a.simple) { - if (k >= 0 && k <= obj->u.a.length) { + if (k >= 0 && k <= obj->u.a.flat_length) { jsR_setarrayindex(J, obj, k, value); - return; + } else { + jsR_unflattenarray(J, obj); + if (obj->u.a.length < k + 1) + obj->u.a.length = k + 1; } - jsR_unflattenarray(J, obj); + } else { + if (obj->u.a.length < k + 1) + obj->u.a.length = k + 1; } - if (k + 1 > obj->u.a.length) - obj->u.a.length = k + 1; } } @@ -771,7 +780,7 @@ readonly: static void jsR_setindex(js_State *J, js_Object *obj, int k, int transient) { char buf[32]; - if (obj->type == JS_CARRAY && obj->u.a.simple && k >= 0 && k <= obj->u.a.length) { + if (obj->type == JS_CARRAY && obj->u.a.simple && k >= 0 && k <= obj->u.a.flat_length) { jsR_setarrayindex(J, obj, k, stackidx(J, -1)); } else { jsR_setproperty(J, obj, js_itoa(buf, k), transient); @@ -890,6 +899,16 @@ dontconf: return 0; } +static void jsR_delindex(js_State *J, js_Object *obj, int k) +{ + char buf[32]; + /* Allow deleting last element of a simple array without unflattening */ + if (obj->type == JS_CARRAY && obj->u.a.simple && k == obj->u.a.flat_length - 1) + obj->u.a.flat_length = k; + else + jsR_delproperty(J, obj, js_itoa(buf, k)); +} + /* Registry, global and object property accessors */ const char *js_ref(js_State *J) @@ -1010,8 +1029,7 @@ void js_setindex(js_State *J, int idx, int i) void js_delindex(js_State *J, int idx, int i) { - char buf[32]; - js_delproperty(J, idx, js_itoa(buf, i)); + jsR_delindex(J, js_toobject(J, idx), i); } /* Iterator */ diff --git a/jsvalue.h b/jsvalue.h index 2d8c8f9..e7796c6 100644 --- a/jsvalue.h +++ b/jsvalue.h @@ -93,9 +93,10 @@ struct js_Object char shrstr[16]; } s; struct { - int length; + int length; /* actual length */ int simple; /* true if array has only non-sparse array properties */ - int capacity; + int flat_length; /* used length of simple array part */ + int flat_capacity; /* allocated length of simple array part */ js_Value *array; } a; struct {