ref: 88b31f342d3b159c8f6b83c5f1c26b04060b6838
parent: 7d9888b739040b9d58f1d6d0e42df98975b86d92
author: Tor Andersson <tor.andersson@artifex.com>
date: Wed Nov 2 10:29:15 EDT 2022
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.
--- a/jsarray.c
+++ b/jsarray.c
@@ -318,7 +318,7 @@
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);
}
@@ -350,7 +350,6 @@
del = len - start;
if (del < 0)
del = 0;
-
js_newarray(J);
--- a/jsgc.c
+++ b/jsgc.c
@@ -108,7 +108,7 @@
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;
--- a/jsobject.c
+++ b/jsobject.c
@@ -73,7 +73,7 @@
}
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 @@
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++);
--- a/jsproperty.c
+++ b/jsproperty.c
@@ -293,7 +293,7 @@
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;
}
--- a/jsrun.c
+++ b/jsrun.c
@@ -528,7 +528,7 @@
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];
@@ -535,7 +535,8 @@
}
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 @@
}
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 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 @@
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 @@
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 @@
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 @@
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_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 */
--- a/jsvalue.h
+++ b/jsvalue.h
@@ -93,9 +93,10 @@
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 {