From 7dc67609283609c7fae33cb5c7aee55edf00130e Mon Sep 17 00:00:00 2001 From: kichikuou Date: Mon, 23 Jun 2025 08:31:38 +0900 Subject: [PATCH] Do not call destructors for structs passed by value to HLL functions Some classes in Dungeons & Dolls serialize themselves using `File.Write(this)` in their destructors. Because `File.Write` takes the argument by value, a copy of `this` is created. If the destructor is called on the copied `this`, it falls into an an infinite loop. --- include/vm/page.h | 2 +- src/ffi.c | 2 +- src/hll/File.c | 2 +- src/hll/vmFile.c | 2 +- src/page.c | 17 ++++++++++------- src/vm.c | 2 +- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/include/vm/page.h b/include/vm/page.h index 4e827d2..139a7ff 100644 --- a/include/vm/page.h +++ b/include/vm/page.h @@ -103,7 +103,7 @@ static inline void _page_set_var(struct page *page, int i, union vm_value v) // variables union vm_value variable_initval(enum ain_data_type type); -void variable_fini(union vm_value v, enum ain_data_type type); +void variable_fini(union vm_value v, enum ain_data_type type, bool call_dtor); enum ain_data_type variable_type(struct page *page, int varno, int *struct_type, int *array_rank); void variable_set(struct page *page, int varno, enum ain_data_type type, union vm_value val); diff --git a/src/ffi.c b/src/ffi.c index 529ee21..e1f87f5 100644 --- a/src/ffi.c +++ b/src/ffi.c @@ -380,7 +380,7 @@ void hll_call(int libno, int fno) break; // fallthrough default: - variable_fini(stack[stack_ptr+j], f->arguments[i].type.data); + variable_fini(stack[stack_ptr+j], f->arguments[i].type.data, false); break; } } diff --git a/src/hll/File.c b/src/hll/File.c index acea4fb..016021c 100644 --- a/src/hll/File.c +++ b/src/hll/File.c @@ -62,7 +62,7 @@ static void read_value(union vm_value *v, enum ain_data_type type) v->f = buffer_read_float(&contents); break; case AIN_STRING: - variable_fini(*v, type); + variable_fini(*v, type, true); v->i = heap_alloc_string(buffer_read_string(&contents)); break; case AIN_STRUCT: diff --git a/src/hll/vmFile.c b/src/hll/vmFile.c index 65711d6..0c73a0b 100644 --- a/src/hll/vmFile.c +++ b/src/hll/vmFile.c @@ -58,7 +58,7 @@ static void read_value(struct vm_file *vf, union vm_value *v, enum ain_data_type v->f = buffer_read_float(&vf->buf); break; case AIN_STRING: - variable_fini(*v, type); + variable_fini(*v, type, true); v->i = heap_alloc_string(buffer_read_string(&vf->buf)); break; case AIN_STRUCT: diff --git a/src/page.c b/src/page.c index bafa9cd..fac61c0 100644 --- a/src/page.c +++ b/src/page.c @@ -98,7 +98,7 @@ union vm_value variable_initval(enum ain_data_type type) } } -void variable_fini(union vm_value v, enum ain_data_type type) +void variable_fini(union vm_value v, enum ain_data_type type, bool call_dtor) { switch (type) { case AIN_STRING: @@ -108,7 +108,10 @@ void variable_fini(union vm_value v, enum ain_data_type type) case AIN_REF_TYPE: if (v.i == -1) break; - heap_unref(v.i); + if (call_dtor) + heap_unref(v.i); + else + exit_unref(v.i); break; default: break; @@ -185,14 +188,14 @@ enum ain_data_type variable_type(struct page *page, int varno, int *struct_type, void variable_set(struct page *page, int varno, enum ain_data_type type, union vm_value val) { - variable_fini(page->values[varno], type); + variable_fini(page->values[varno], type, true); page->values[varno] = val; } void delete_page_vars(struct page *page) { for (int i = 0; i < page->nr_vars; i++) { - variable_fini(page->values[i], variable_type(page, i, NULL, NULL)); + variable_fini(page->values[i], variable_type(page, i, NULL, NULL), true); } } @@ -330,7 +333,7 @@ struct page *realloc_array(struct page *src, int rank, union vm_value *dimension // if shrinking array, unref orphaned children if (dimensions->i < src->nr_vars) { for (int i = dimensions->i; i < src->nr_vars; i++) { - variable_fini(src->values[i], variable_type(src, i, NULL, NULL)); + variable_fini(src->values[i], variable_type(src, i, NULL, NULL), true); } } @@ -419,7 +422,7 @@ int array_fill(struct page *dst, int dst_i, int n, union vm_value v) for (int i = 0; i < n; i++) { variable_set(dst, dst_i+i, type, vm_copy(v, type)); } - variable_fini(v, type); + variable_fini(v, type, true); return n; } @@ -478,7 +481,7 @@ struct page *array_erase(struct page *page, int i, bool *success) } // delete variable, shift subsequent variables, then realloc page - variable_fini(page->values[i], array_type(page->a_type)); + variable_fini(page->values[i], array_type(page->a_type), true); for (int j = i + 1; j < page->nr_vars; j++) { page->values[j-1] = page->values[j]; } diff --git a/src/vm.c b/src/vm.c index 5779b51..51d0b99 100644 --- a/src/vm.c +++ b/src/vm.c @@ -421,7 +421,7 @@ static void delegate_call(int dg_no, int return_address) stack_pop(); // dg_index stack_pop(); // dg_page for (int i = ain->delegates[dg_no].nr_variables - 1; i >= 0; i--) { - variable_fini(stack_pop(), ain->delegates[dg_no].variables[i].type.data); + variable_fini(stack_pop(), ain->delegates[dg_no].variables[i].type.data, true); } if (return_values) { stack_push(r);