Fix ResumeSave/ResumeLoad bugs with delegates

When loading a delegate, the stored objects must be registered to it.

Also, delegate_call cannot use vm_execute because the VM cannot be
resumed from such a state.

Incidentally, this means that ResumeSave cannot be called from within a
constructor/destructor (since they also use vm_execute), but this is
probably not a problem.
This commit is contained in:
Nunuhara Cabbage
2023-04-14 15:08:45 -07:00
parent 983944a776
commit 75d6386e25
3 changed files with 46 additions and 20 deletions
+26 -1
View File
@@ -114,6 +114,8 @@ static cJSON *funcall_to_json(struct function_call *call)
cJSON_AddNumberToObject(json, "return-address", call->return_address);
cJSON_AddNumberToObject(json, "local-page", call->page_slot);
cJSON_AddNumberToObject(json, "struct-page", call->struct_page);
if (call->delegate >= 0)
cJSON_AddNumberToObject(json, "delegate", call->delegate);
return json;
}
@@ -266,8 +268,20 @@ static void alloc_heap_slot(int slot)
heap_free_ptr++;
}
struct delegate_list {
int *slots;
int n;
};
void delegate_list_add(struct delegate_list *list, int slot)
{
list->slots = xrealloc_array(list->slots, list->n, list->n+1, sizeof(int));
list->slots[list->n++] = slot;
}
static void load_heap(cJSON *json)
{
struct delegate_list delegates = {0};
delete_heap();
cJSON *item;
@@ -287,6 +301,8 @@ static void load_heap(cJSON *json)
load_string(slot, value);
} else if (cJSON_IsObject(value)) {
load_page(slot, value);
if (heap[slot].page->type == DELEGATE_PAGE)
delegate_list_add(&delegates, slot);
} else if (cJSON_IsNull(value)) {
heap[slot].type = VM_PAGE;
heap[slot].page = NULL;
@@ -294,6 +310,13 @@ static void load_heap(cJSON *json)
invalid_save_data("Invalid heap data");
}
}
for (int i = 0; i < delegates.n; i++) {
int slot = delegates.slots[i];
struct page *page = heap_get_delegate_page(slot);
vm_register_delegate_structs(page, slot);
}
free(delegates.slots);
}
static void load_call_stack(cJSON *json)
@@ -302,11 +325,13 @@ static void load_call_stack(cJSON *json)
cJSON *item;
cJSON_ArrayForEach(item, json) {
type_check(cJSON_Object, item);
cJSON *delegate = cJSON_GetObjectItem(item, "delegate");
call_stack[call_stack_ptr++] = (struct function_call) {
.fno = type_check(cJSON_Number, cJSON_GetObjectItem(item, "function"))->valueint,
.return_address = type_check(cJSON_Number, cJSON_GetObjectItem(item, "return-address"))->valueint,
.page_slot = type_check(cJSON_Number, cJSON_GetObjectItem(item, "local-page"))->valueint,
.struct_page = type_check(cJSON_Number, cJSON_GetObjectItem(item, "struct-page"))->valueint
.struct_page = type_check(cJSON_Number, cJSON_GetObjectItem(item, "struct-page"))->valueint,
.delegate = delegate ? type_check(cJSON_Number, delegate)->valueint : -1
};
}
}
+19 -18
View File
@@ -239,7 +239,7 @@ int vm_copy_page(struct page *page)
return slot;
}
static void register_delegate_structs(struct page *dg, int dg_i)
void vm_register_delegate_structs(struct page *dg, int dg_i)
{
for (int i = 0; i < dg->nr_vars; i += 2) {
if (dg->values[i].i < 0)
@@ -254,7 +254,7 @@ int vm_copy_delegate_page(int dg_i)
int slot = vm_copy_page(page);
if (page) {
register_delegate_structs(page, slot);
vm_register_delegate_structs(page, slot);
}
return slot;
@@ -316,7 +316,8 @@ static void scenario_call(int slot)
.call_address = instr_ptr,
.return_address = VM_RETURN,
.page_slot = slot,
.struct_page = -1
.struct_page = -1,
.delegate = -1,
};
call_stack_ptr = 1;
instr_ptr = ain->functions[fno].address;
@@ -340,7 +341,8 @@ static int _function_call(int fno, int return_address)
.call_address = instr_ptr,
.return_address = return_address,
.page_slot = slot,
.struct_page = -1
.struct_page = -1,
.delegate = -1,
};
// initialize local variables
for (int i = f->nr_args; i < f->nr_vars; i++) {
@@ -378,10 +380,8 @@ static void method_call(int fno, int return_address)
static void vm_execute(void);
static void delegate_call(int dg_no)
static void delegate_call(int dg_no, int return_address)
{
size_t saved_ip = instr_ptr;
// stack: [arg0, ..., dg_page, dg_index]
int dg_page = stack_peek(1).i;
int dg_index = stack_peek(0).i;
@@ -389,7 +389,7 @@ static void delegate_call(int dg_no)
int obj, fun;
delegate_get(dg_page, dg_index, &obj, &fun);
int slot = _function_call(fun, VM_RETURN);
int slot = _function_call(fun, return_address);
// copy arguments into local page
struct ain_function_type *dg = &ain->delegates[dg_no];
@@ -399,8 +399,7 @@ static void delegate_call(int dg_no)
}
call_stack[call_stack_ptr-1].struct_page = obj;
vm_execute();
instr_ptr = saved_ip;
call_stack[call_stack_ptr-1].delegate = dg_no;
}
void vm_call(int fno, int struct_page)
@@ -420,6 +419,14 @@ static void function_return(void)
{
heap_unref(call_stack[call_stack_ptr-1].page_slot);
instr_ptr = call_stack[call_stack_ptr-1].return_address;
if (call_stack[call_stack_ptr-1].delegate >= 0) {
const int dg = call_stack[call_stack_ptr-1].delegate;
if (ain->delegates[dg].return_type.data != AIN_VOID) {
stack[stack_ptr-2].i++;
} else {
stack[stack_ptr-1].i++;
}
}
call_stack_ptr--;
}
@@ -2134,13 +2141,7 @@ static enum opcode execute_instruction(enum opcode opcode)
if (ain->delegates[dg].return_type.data != AIN_VOID) {
stack_pop();
}
delegate_call(dg);
if (ain->delegates[dg].return_type.data != AIN_VOID) {
stack[stack_ptr-2].i++;
} else {
stack[stack_ptr-1].i++;
}
instr_ptr += 10;
delegate_call(dg, instr_ptr + instruction_width(DG_CALL));
} else {
// call finished: clean up stack and jump to return address
union vm_value r;
@@ -2196,7 +2197,7 @@ static enum opcode execute_instruction(enum opcode opcode)
struct page *new_dg = copy_page(set);
delete_page(dst_i);
heap_set_page(dst_i, new_dg);
register_delegate_structs(new_dg, dst_i);
vm_register_delegate_structs(new_dg, dst_i);
stack_push(set_i);
break;
}