Avoid stack overflow when freeing deeply nested values
This commit is contained in:
1 parent
7316e5ce4b
commit
33d7bce321
1 file changed
+60
-51
@@ -185,14 +185,6 @@ int jv_invalid_has_msg(jv inv) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jvp_invalid_free(jv x) {
|
|
||||||
assert(JVP_HAS_KIND(x, JV_KIND_INVALID));
|
|
||||||
if (JVP_HAS_FLAGS(x, JVP_FLAGS_INVALID_MSG) && jvp_refcnt_dec(x.u.ptr)) {
|
|
||||||
jv_free(((jvp_invalid*)x.u.ptr)->errmsg);
|
|
||||||
jv_mem_free(x.u.ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Numbers
|
* Numbers
|
||||||
*/
|
*/
|
||||||
@@ -847,17 +839,6 @@ static jv jvp_array_new(unsigned size) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jvp_array_free(jv a) {
|
|
||||||
assert(JVP_HAS_KIND(a, JV_KIND_ARRAY));
|
|
||||||
if (jvp_refcnt_dec(a.u.ptr)) {
|
|
||||||
jvp_array* array = jvp_array_ptr(a);
|
|
||||||
for (int i=0; i<array->length; i++) {
|
|
||||||
jv_free(array->elements[i]);
|
|
||||||
}
|
|
||||||
jv_mem_free(array);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int jvp_array_length(jv a) {
|
static int jvp_array_length(jv a) {
|
||||||
assert(JVP_HAS_KIND(a, JV_KIND_ARRAY));
|
assert(JVP_HAS_KIND(a, JV_KIND_ARRAY));
|
||||||
return a.size;
|
return a.size;
|
||||||
@@ -905,7 +886,7 @@ static jv* jvp_array_write(jv* a, int i) {
|
|||||||
new_array->elements[j] = JV_NULL;
|
new_array->elements[j] = JV_NULL;
|
||||||
}
|
}
|
||||||
new_array->length = new_length;
|
new_array->length = new_length;
|
||||||
jvp_array_free(*a);
|
jv_free(*a);
|
||||||
jv new_jv = {JVP_FLAGS_ARRAY, 0, 0, new_length, {&new_array->refcnt}};
|
jv new_jv = {JVP_FLAGS_ARRAY, 0, 0, new_length, {&new_array->refcnt}};
|
||||||
*a = new_jv;
|
*a = new_jv;
|
||||||
return &new_array->elements[i];
|
return &new_array->elements[i];
|
||||||
@@ -1707,20 +1688,6 @@ static jv* jvp_object_read(jv object, jv key) {
|
|||||||
else return &slot->value;
|
else return &slot->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jvp_object_free(jv o) {
|
|
||||||
assert(JVP_HAS_KIND(o, JV_KIND_OBJECT));
|
|
||||||
if (jvp_refcnt_dec(o.u.ptr)) {
|
|
||||||
for (int i=0; i<jvp_object_size(o); i++) {
|
|
||||||
struct object_slot* slot = jvp_object_get_slot(o, i);
|
|
||||||
if (jv_get_kind(slot->string) != JV_KIND_NULL) {
|
|
||||||
jvp_string_free(slot->string);
|
|
||||||
jv_free(slot->value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jv_mem_free(jvp_object_ptr(o));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int jvp_object_rehash(jv *objectp) {
|
static int jvp_object_rehash(jv *objectp) {
|
||||||
jv object = *objectp;
|
jv object = *objectp;
|
||||||
assert(JVP_HAS_KIND(object, JV_KIND_OBJECT));
|
assert(JVP_HAS_KIND(object, JV_KIND_OBJECT));
|
||||||
@@ -1765,7 +1732,7 @@ static jv jvp_object_unshare(jv object) {
|
|||||||
int* new_buckets = jvp_object_buckets(new_object);
|
int* new_buckets = jvp_object_buckets(new_object);
|
||||||
memcpy(new_buckets, old_buckets, sizeof(int) * jvp_object_size(new_object)*2);
|
memcpy(new_buckets, old_buckets, sizeof(int) * jvp_object_size(new_object)*2);
|
||||||
|
|
||||||
jvp_object_free(object);
|
jv_free(object);
|
||||||
assert(jvp_refcnt_unshared(new_object.u.ptr));
|
assert(jvp_refcnt_unshared(new_object.u.ptr));
|
||||||
return new_object;
|
return new_object;
|
||||||
}
|
}
|
||||||
@@ -2002,24 +1969,66 @@ jv jv_copy(jv j) {
|
|||||||
return j;
|
return j;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free children iteratively via a heap-allocated buffer to avoid
|
||||||
|
* recursion through jv_free, which would cause stack overflow on
|
||||||
|
* deeply nested values.
|
||||||
|
*/
|
||||||
void jv_free(jv j) {
|
void jv_free(jv j) {
|
||||||
switch(JVP_KIND(j)) {
|
jv* pending = NULL;
|
||||||
case JV_KIND_ARRAY:
|
size_t len = 0, cap = 0;
|
||||||
jvp_array_free(j);
|
while (1) {
|
||||||
break;
|
switch (JVP_KIND(j)) {
|
||||||
case JV_KIND_STRING:
|
case JV_KIND_ARRAY:
|
||||||
jvp_string_free(j);
|
if (jvp_refcnt_dec(j.u.ptr)) {
|
||||||
break;
|
jvp_array* arr = jvp_array_ptr(j);
|
||||||
case JV_KIND_OBJECT:
|
if (len + arr->length > cap) {
|
||||||
jvp_object_free(j);
|
cap = (len + arr->length) * 2;
|
||||||
break;
|
pending = jv_mem_realloc(pending, cap * sizeof(jv));
|
||||||
case JV_KIND_INVALID:
|
}
|
||||||
jvp_invalid_free(j);
|
for (int i = 0; i < arr->length; i++)
|
||||||
break;
|
pending[len++] = arr->elements[i];
|
||||||
case JV_KIND_NUMBER:
|
jv_mem_free(arr);
|
||||||
jvp_number_free(j);
|
}
|
||||||
break;
|
break;
|
||||||
|
case JV_KIND_OBJECT:
|
||||||
|
if (jvp_refcnt_dec(j.u.ptr)) {
|
||||||
|
int sz = jvp_object_size(j);
|
||||||
|
if (len + sz > cap) {
|
||||||
|
cap = (len + sz) * 2;
|
||||||
|
pending = jv_mem_realloc(pending, cap * sizeof(jv));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < sz; i++) {
|
||||||
|
struct object_slot* slot = jvp_object_get_slot(j, i);
|
||||||
|
if (jv_get_kind(slot->string) != JV_KIND_NULL) {
|
||||||
|
jvp_string_free(slot->string);
|
||||||
|
pending[len++] = slot->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jv_mem_free(jvp_object_ptr(j));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JV_KIND_INVALID:
|
||||||
|
if (JVP_HAS_FLAGS(j, JVP_FLAGS_INVALID_MSG) && jvp_refcnt_dec(j.u.ptr)) {
|
||||||
|
if (len + 1 > cap) {
|
||||||
|
cap = (len + 1) * 2;
|
||||||
|
pending = jv_mem_realloc(pending, cap * sizeof(jv));
|
||||||
|
}
|
||||||
|
pending[len++] = ((jvp_invalid*)j.u.ptr)->errmsg;
|
||||||
|
jv_mem_free(j.u.ptr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JV_KIND_STRING:
|
||||||
|
jvp_string_free(j);
|
||||||
|
break;
|
||||||
|
case JV_KIND_NUMBER:
|
||||||
|
jvp_number_free(j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (len == 0) break;
|
||||||
|
j = pending[--len];
|
||||||
}
|
}
|
||||||
|
jv_mem_free(pending);
|
||||||
}
|
}
|
||||||
|
|
||||||
int jv_get_refcnt(jv j) {
|
int jv_get_refcnt(jv j) {
|
||||||
|
|||||||
Reference in new issue
Block a user