Merge pull request #170 from kichikuou/delegate

Rework delegate implementation
This commit is contained in:
Nunuhara Cabbage
2024-08-05 08:04:15 -07:00
committed by GitHub
7 changed files with 230 additions and 317 deletions
-2
View File
@@ -155,7 +155,6 @@ struct function_call {
uint32_t return_address;
int32_t page_slot;
int32_t struct_page;
int32_t delegate;
};
extern struct function_call call_stack[4096];
@@ -184,7 +183,6 @@ int vm_save_image(const char *key, const char *path);
void vm_load_image(const char *key, const char *path);
struct page *vm_load_image_comments(const char *key, const char *path, int *success);
int vm_write_image_comments(const char *key, const char *path, struct page *comments);
void vm_register_delegate_structs(struct page *dg, int dg_i);
#endif /* VM_PRIVATE */
#endif /* SYSTEM4_VM_H */
+4 -1
View File
@@ -33,6 +33,7 @@ enum vm_pointer_type {
// Heap-backed objects. Reference counted.
struct vm_pointer {
int ref;
uint32_t seq;
enum vm_pointer_type type;
union {
struct string *s;
@@ -60,12 +61,13 @@ void heap_ref(int slot);
void heap_unref(int slot);
void exit_unref(int slot);
uint32_t heap_get_seq(int slot);
bool heap_index_valid(int index);
bool page_index_valid(int index);
bool string_index_valid(int index);
struct page *heap_get_page(int index);
struct page *heap_get_struct_page(int index);
struct page *heap_get_delegate_page(int index);
struct string *heap_get_string(int index);
void heap_set_page(int slot, struct page *page);
@@ -87,6 +89,7 @@ void heap_guarantee(unsigned headroom);
#ifdef VM_PRIVATE
extern uint32_t heap_next_seq;
extern int32_t *heap_free_stack;
extern size_t heap_free_ptr;
+16 -25
View File
@@ -50,8 +50,8 @@ enum page_type {
* Multi-dimensional arrays are implemented as a tree of pages (meaning the
* whole array is NOT contiguous in memory).
*
* Delegates: each delegate object is backed by a page storing object/function
* pairs.
* Delegates: each delegate object is backed by a page storing (object,
* function, seq) triples.
*/
struct page {
enum page_type type;
@@ -66,18 +66,11 @@ struct page {
int index;
enum ain_data_type a_type;
};
union {
// array-specific metadata
struct {
int struct_type;
int rank;
} array;
// struct-specific metadata
struct {
int *delegates;
unsigned nr_delegates;
} struc;
};
// array-specific metadata
struct {
int struct_type;
int rank;
} array;
int nr_vars;
union vm_value values[];
};
@@ -120,8 +113,6 @@ int alloc_struct(int no);
void init_struct(int no, int slot);
void delete_struct(int no, int slot);
void create_struct(int no, union vm_value *var);
void struct_register_delegate(int obj, int dg_i);
void struct_unregister_delegate(int obj, int dg_i);
// arrays
enum ain_data_type array_type(enum ain_data_type type);
@@ -140,14 +131,14 @@ int array_find(struct page *page, int start, int end, union vm_value v, int comp
void array_reverse(struct page *page);
// delegates
void delegate_new_from_method(int dg_i, int obj, int fun);
int delegate_numof(int dg_i);
bool delegate_contains(int dg_i, int obj, int fun);
void delegate_erase(int dg_i, int obj, int fun);
void delegate_append(int dg_i, int obj, int fun);
void delegate_plusa(int dg_i, int add_i);
void delegate_minusa(int dg_i, int minus_i);
void delegate_clear(int dg_i);
void delegate_get(int dg_i, int i, int *obj_out, int *fun_out);
struct page *delegate_new_from_method(int obj, int fun);
int delegate_numof(struct page *page);
bool delegate_contains(struct page *dst, int obj, int fun);
void delegate_erase(struct page *page, int obj, int fun);
struct page *delegate_append(struct page *dst, int obj, int fun);
struct page *delegate_plusa(struct page *dst, struct page *add);
struct page *delegate_minusa(struct page *dst, struct page *minus);
struct page *delegate_clear(struct page *page);
bool delegate_get(struct page *page, int i, int *obj_out, int *fun_out);
#endif /* SYSTEM4_PAGE_H */
+9 -8
View File
@@ -30,6 +30,7 @@
struct vm_pointer *heap = NULL;
size_t heap_size = 0;
uint32_t heap_next_seq;
// Heap free list
// This is a list of unused indices into the 'heap' array.
@@ -86,6 +87,7 @@ void heap_init(void)
heap_free_stack[i] = i;
}
heap_free_ptr = 1; // global page at index 0
heap_next_seq = 1;
}
int32_t heap_alloc_slot(enum vm_pointer_type type)
@@ -96,6 +98,7 @@ int32_t heap_alloc_slot(enum vm_pointer_type type)
int32_t slot = heap_free_stack[heap_free_ptr++];
heap[slot].ref = 1;
heap[slot].seq = heap_next_seq++;
heap[slot].type = type;
#ifdef DEBUG_HEAP
heap[slot].alloc_addr = instr_ptr;
@@ -110,6 +113,7 @@ int32_t heap_alloc_slot(enum vm_pointer_type type)
static void heap_free_slot(int32_t slot)
{
heap[slot].seq = 0;
heap_free_stack[--heap_free_ptr] = slot;
}
@@ -212,6 +216,11 @@ void exit_unref(int slot)
heap_free_slot(slot);
}
uint32_t heap_get_seq(int slot)
{
return heap_index_valid(slot) ? heap[slot].seq : 0;
}
bool heap_index_valid(int index)
{
return index >= 0 && (size_t)index < heap_size && heap[index].ref > 0;
@@ -241,14 +250,6 @@ struct string *heap_get_string(int index)
return heap[index].s;
}
struct page *heap_get_struct_page(int index)
{
struct page *page = heap_get_page(index);
if (unlikely(!page || page->type != STRUCT_PAGE))
VM_ERROR("Not a struct page: %d", index);
return page;
}
struct page *heap_get_delegate_page(int index)
{
struct page *page = heap_get_page(index);
+129 -161
View File
@@ -196,37 +196,14 @@ void delete_page_vars(struct page *page)
}
}
static void delegate_delete_object(int dg_i, int obj);
void delete_page(int slot)
{
struct page *page = heap_get_page(slot);
if (!page)
return;
if (page->type == STRUCT_PAGE) {
struct ain_struct *s = &ain->structures[page->index];
if (s->destructor > 0) {
vm_call(s->destructor, slot);
}
// remove the object from all delegates
for (int i = 0; i < page->struc.nr_delegates; i++) {
delegate_delete_object(page->struc.delegates[i], slot);
}
free(page->struc.delegates);
page->struc.delegates = NULL;
page->struc.nr_delegates = 0;
delete_struct(page->index, slot);
}
if (page->type == DELEGATE_PAGE) {
for (int i = 0; i < page->nr_vars; i += 2) {
if (page->values[i].i < 0)
continue;
struct_unregister_delegate(page->values[i].i, slot);
}
}
delete_page_vars(page);
free_page(page);
}
@@ -239,17 +216,11 @@ struct page *copy_page(struct page *src)
if (!src)
return NULL;
struct page *dst = alloc_page(src->type, src->index, src->nr_vars);
if (src->type == ARRAY_PAGE) {
dst->array = src->array;
} else if (src->type == STRUCT_PAGE) {
dst->struc.delegates = NULL;
dst->struc.nr_delegates = 0;
}
dst->array = src->array;
for (int i = 0; i < src->nr_vars; i++) {
dst->values[i] = vm_copy(src->values[i], variable_type(src, i, NULL, NULL));
}
return dst;
}
@@ -257,17 +228,14 @@ int alloc_struct(int no)
{
struct ain_struct *s = &ain->structures[no];
int slot = heap_alloc_slot(VM_PAGE);
struct page *page = alloc_page(STRUCT_PAGE, no, s->nr_members);
page->struc.delegates = NULL;
page->struc.nr_delegates = 0;
heap_set_page(slot, alloc_page(STRUCT_PAGE, no, s->nr_members));
for (int i = 0; i < s->nr_members; i++) {
if (s->members[i].type.data == AIN_STRUCT) {
page->values[i].i = alloc_struct(s->members[i].type.struc);
heap[slot].page->values[i].i = alloc_struct(s->members[i].type.struc);
} else {
page->values[i] = variable_initval(s->members[i].type.data);
heap[slot].page->values[i] = variable_initval(s->members[i].type.data);
}
}
heap_set_page(slot, page);
return slot;
}
@@ -284,6 +252,14 @@ void init_struct(int no, int slot)
}
}
void delete_struct(int no, int slot)
{
struct ain_struct *s = &ain->structures[no];
if (s->destructor > 0) {
vm_call(s->destructor, slot);
}
}
void create_struct(int no, union vm_value *var)
{
var->i = alloc_struct(no);
@@ -646,159 +622,151 @@ void array_reverse(struct page *page)
}
}
void struct_register_delegate(int obj, int dg_i)
struct page *delegate_new_from_method(int obj, int fun)
{
struct page *page = heap_get_struct_page(obj);
// don't add duplicates
for (int i = 0; i < page->struc.nr_delegates; i++) {
if (page->struc.delegates[i] == obj)
return;
}
page->struc.delegates = xrealloc(page->struc.delegates,
(page->struc.nr_delegates + 1) * sizeof(int));
page->struc.delegates[page->struc.nr_delegates++] = dg_i;
struct page *page = alloc_page(DELEGATE_PAGE, 0, 3);
page->values[0].i = obj;
page->values[1].i = fun;
page->values[2].i = heap_get_seq(obj);
return page;
}
void struct_unregister_delegate(int obj, int dg_i)
{
struct page *page = heap_get_struct_page(obj);
for (int i = 0; i < page->struc.nr_delegates; i++) {
if (page->struc.delegates[i] != dg_i)
continue;
if (page->struc.nr_delegates > 1) {
// swap last entry into delegates[i]
page->struc.delegates[i] = page->struc.delegates[page->struc.nr_delegates - 1];
}
page->struc.nr_delegates--;
return;
}
VM_ERROR("delegate is not registered to object");
}
static bool _delegate_contains(struct page *dst, int obj, int fun)
bool delegate_contains(struct page *dst, int obj, int fun)
{
if (!dst)
return false;
for (int i = 0; i < dst->nr_vars; i += 2) {
if (dst->values[i].i == obj && dst->values[i+1].i == fun)
for (int i = 0; i < dst->nr_vars; i += 3) {
if (dst->values[i].i == obj &&
dst->values[i+1].i == fun &&
dst->values[i+2].i == heap_get_seq(obj))
return true;
}
return false;
}
bool delegate_contains(int dg_i, int obj, int fun)
struct page *delegate_append(struct page *dst, int obj, int fun)
{
return _delegate_contains(heap_get_delegate_page(dg_i), obj, fun);
if (!dst)
return delegate_new_from_method(obj, fun);
if (dst->type != DELEGATE_PAGE)
VM_ERROR("Not a delegate");
if (delegate_contains(dst, obj, fun))
return dst;
dst = xrealloc(dst, sizeof(struct page) + sizeof(union vm_value) * (dst->nr_vars + 3));
dst->values[dst->nr_vars+0].i = obj;
dst->values[dst->nr_vars+1].i = fun;
dst->values[dst->nr_vars+2].i = heap_get_seq(obj);
dst->nr_vars += 3;
return dst;
}
void delegate_append(int dg_i, int obj, int fun)
int delegate_numof(struct page *page)
{
struct page *dg = heap_get_delegate_page(dg_i);
if (dg && _delegate_contains(dg, obj, fun))
return;
if (!dg) {
dg = alloc_page(DELEGATE_PAGE, 0, 2);
dg->values[0].i = obj;
dg->values[1].i = fun;
} else {
dg = xrealloc(dg, sizeof(struct page) + sizeof(union vm_value) * (dg->nr_vars + 2));
dg->values[dg->nr_vars+0].i = obj;
dg->values[dg->nr_vars+1].i = fun;
dg->nr_vars += 2;
}
if (obj >= 0)
struct_register_delegate(obj, dg_i);
heap_set_page(dg_i, dg);
}
int delegate_numof(int dg_i)
{
struct page *dg = heap_get_delegate_page(dg_i);
if (!dg)
if (!page)
return 0;
return dg->nr_vars / 2;
}
if (page->type != DELEGATE_PAGE)
VM_ERROR("Not a delegate");
static void delegate_delete_object(int dg_i, int obj)
{
struct page *dg = heap_get_delegate_page(dg_i);
if (!dg) {
WARNING("Tried to delete object from empty delegate");
return;
}
for (int i = 0; i < dg->nr_vars; i += 2) {
if (dg->values[i].i != obj)
continue;
for (int j = i+2; j < dg->nr_vars; j += 2) {
dg->values[j-2].i = dg->values[j+0].i;
dg->values[j-1].i = dg->values[j+1].i;
// garbage collection
for (int i = 0; i < page->nr_vars; i += 3) {
if (heap_get_seq(page->values[i].i) != page->values[i+2].i) {
for (int j = i+3; j < page->nr_vars; j += 3) {
page->values[j-3].i = page->values[j+0].i;
page->values[j-2].i = page->values[j+1].i;
page->values[j-1].i = page->values[j+2].i;
}
page->nr_vars -= 3;
i -= 3;
}
dg->nr_vars -= 2;
i -= 2;
}
return page->nr_vars / 3;
}
void delegate_erase(int dg_i, int obj, int fun)
void delegate_erase(struct page *page, int obj, int fun)
{
struct page *dg = heap_get_delegate_page(dg_i);
if (!dg)
return;
for (int i = 0; i < dg->nr_vars; i += 2) {
if (dg->values[i].i != obj || dg->values[i+1].i != fun)
continue;
if (dg->values[i].i >= 0)
struct_unregister_delegate(obj, dg_i);
for (int j = i+2; j < dg->nr_vars; j += 2) {
dg->values[j-2].i = dg->values[j+0].i;
dg->values[j-1].i = dg->values[j+1].i;
}
dg->nr_vars -= 2;
break;
}
}
void delegate_plusa(int dg_i, int add_i)
{
struct page *add = heap_get_delegate_page(add_i);
if (!add)
return;
for (int i = 0; i < add->nr_vars; i += 2) {
delegate_append(dg_i, add->values[i].i, add->values[i+1].i);
}
}
void delegate_minusa(int dg_i, int minus_i)
{
struct page *minus = heap_get_delegate_page(minus_i);
if (!minus)
return;
for (int i = 0; i < minus->nr_vars; i += 2) {
delegate_erase(dg_i, minus->values[i].i, minus->values[i+1].i);
}
}
void delegate_clear(int dg_i)
{
struct page *page = heap_get_delegate_page(dg_i);
if (!page)
return;
for (int i = 0; i < page->nr_vars; i += 2) {
if (page->values[i].i >= 0)
struct_unregister_delegate(page->values[i].i, dg_i);
page->values[i+0].i = -1;
if (page->type != DELEGATE_PAGE)
VM_ERROR("Not a delegate");
for (int i = 0; i < page->nr_vars; i += 3) {
if (page->values[i].i == obj && page->values[i+1].i == fun) {
for (int j = i+3; j < page->nr_vars; j += 3) {
page->values[j-3].i = page->values[j+0].i;
page->values[j-2].i = page->values[j+1].i;
page->values[j-1].i = page->values[j+2].i;
}
page->nr_vars -= 3;
break;
}
}
}
struct page *delegate_plusa(struct page *dst, struct page *add)
{
if (!add)
return dst;
if ((dst && dst->type != DELEGATE_PAGE) || add->type != DELEGATE_PAGE)
VM_ERROR("Not a delegate");
for (int i = 0; i < add->nr_vars; i += 3) {
if (heap_get_seq(add->values[i].i) == add->values[i+2].i)
dst = delegate_append(dst, add->values[i].i, add->values[i+1].i);
}
return dst;
}
struct page *delegate_minusa(struct page *dst, struct page *minus)
{
if (!dst)
return NULL;
if (!minus)
return dst;
if (dst->type != DELEGATE_PAGE || minus->type != DELEGATE_PAGE)
VM_ERROR("Not a delegate");
for (int i = 0; i < minus->nr_vars; i += 3) {
if (heap_get_seq(minus->values[i].i) == minus->values[i+2].i)
delegate_erase(dst, minus->values[i].i, minus->values[i+1].i);
}
return dst;
}
struct page *delegate_clear(struct page *page)
{
if (!page)
return NULL;
if (page->type != DELEGATE_PAGE)
VM_ERROR("Not a delegate");
for (int i = 0; i < page->nr_vars; i += 3) {
page->values[i].i = -1;
page->values[i+1].i = -1;
page->values[i+2].i = 0;
}
page->index = 0;
page->nr_vars = 0;
return page;
}
void delegate_get(int dg_i, int i, int *obj_out, int *fun_out)
bool delegate_get(struct page *page, int i, int *obj_out, int *fun_out)
{
struct page *dg = heap_get_delegate_page(dg_i);
if (i * 2 >= dg->nr_vars)
VM_ERROR("Invalid delegate index: %d", i);
*obj_out = dg->values[i*2+0].i;
*fun_out = dg->values[i*2+1].i;
if (!page)
return false;
if (page->type != DELEGATE_PAGE)
VM_ERROR("Not a delegate");
while (i*3 < page->nr_vars) {
if (heap_get_seq(page->values[i*3].i) == page->values[i*3+2].i) {
*obj_out = page->values[i*3].i;
*fun_out = page->values[i*3+1].i;
return true;
}
for (int j = (i + 1) * 3; j < page->nr_vars; j += 3) {
page->values[j-3].i = page->values[j+0].i;
page->values[j-2].i = page->values[j+1].i;
page->values[j-1].i = page->values[j+2].i;
}
page->nr_vars -= 3;
}
return false;
}
+17 -29
View File
@@ -101,6 +101,9 @@ static cJSON *heap_item_to_json(int i, possibly_unused void *_)
cJSON_AddItemToArray(item, cJSON_CreateString(heap[i].s->text));
break;
}
if (ain->nr_delegates > 0) {
cJSON_AddItemToArray(item, cJSON_CreateNumber(heap[i].seq));
}
return item;
}
@@ -116,8 +119,6 @@ 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;
}
@@ -147,6 +148,9 @@ static cJSON *vm_image_to_json(const char *key)
cJSON_AddItemToObject(image, "call-stack", call_stack_to_json());
cJSON_AddItemToObject(image, "stack", stack_to_json());
cJSON_AddNumberToObject(image, "ip", instr_ptr);
if (ain->nr_delegates > 0) {
cJSON_AddNumberToObject(image, "next_seq", heap_next_seq);
}
return image;
}
@@ -500,41 +504,31 @@ 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_json_heap(cJSON *json)
{
struct delegate_list delegates = {0};
delete_heap();
cJSON *item;
cJSON_ArrayForEach(item, json) {
type_check(cJSON_Array, item);
if (cJSON_GetArraySize(item) != 3)
if (cJSON_GetArraySize(item) < 3)
invalid_save_data("Invalid heap data");
int slot = type_check(cJSON_Number, cJSON_GetArrayItem(item, 0))->valueint;
int ref = type_check(cJSON_Number, cJSON_GetArrayItem(item, 1))->valueint;
cJSON *value = cJSON_GetArrayItem(item, 2);
int seq = ain->nr_delegates > 0
? type_check(cJSON_Number, cJSON_GetArrayItem(item, 3))->valueint
: slot;
alloc_heap_slot(slot);
heap[slot].ref = ref;
heap[slot].seq = seq;
if (cJSON_IsString(value)) {
load_json_string(slot, value);
} else if (cJSON_IsObject(value)) {
load_json_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;
@@ -542,13 +536,6 @@ static void load_json_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 int resolve_func_symbol(struct rsave_symbol *sym)
@@ -636,8 +623,6 @@ static void load_rsave_struct(int slot, struct rsave_heap_struct *s)
{
int struct_index = resolve_struct_symbol(&s->struct_type);
struct page *page = alloc_page(STRUCT_PAGE, struct_index, s->nr_slots);
page->struc.delegates = NULL;
page->struc.nr_delegates = 0;
// type check
struct ain_struct *as = &ain->structures[struct_index];
@@ -687,13 +672,11 @@ static void load_json_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,
.delegate = delegate ? type_check(cJSON_Number, delegate)->valueint : -1
};
}
}
@@ -723,7 +706,6 @@ static void load_rsave_call_stack(struct rsave *save)
.return_address = return_address,
.page_slot = save->call_frames[i].local_ptr,
.struct_page = save->call_frames[i].struct_ptr,
.delegate = -1,
};
// Calculate return address from the function address and offset
// to make it robust to ain changes.
@@ -793,6 +775,12 @@ static void load_json_image(const char *key, const char *path)
load_json_call_stack(type_check(cJSON_Array, cJSON_GetObjectItem(save, "call-stack")));
load_json_stack(type_check(cJSON_Array, cJSON_GetObjectItem(save, "stack")));
instr_ptr = ip->valueint;
if (ain->nr_delegates > 0) {
cJSON *next_seq = type_check(cJSON_Number, cJSON_GetObjectItem(save, "next_seq"));
heap_next_seq = next_seq->valueint;
} else {
heap_next_seq = heap_size;
}
cJSON_Delete(save);
}
+55 -91
View File
@@ -241,37 +241,15 @@ int vm_copy_page(struct page *page)
return slot;
}
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)
continue;
struct_register_delegate(dg->values[i].i, dg_i);
}
}
int vm_copy_delegate_page(int dg_i)
{
struct page *page = heap_get_page(dg_i);
int slot = vm_copy_page(page);
if (page) {
vm_register_delegate_structs(page, slot);
}
return slot;
}
union vm_value vm_copy(union vm_value v, enum ain_data_type type)
{
switch (type) {
case AIN_STRING:
return (union vm_value) { .i = vm_string_ref(heap_get_string(v.i)) };
case AIN_STRUCT:
case AIN_DELEGATE:
case AIN_ARRAY_TYPE:
return (union vm_value) { .i = vm_copy_page(heap_get_page(v.i)) };
case AIN_DELEGATE:
return (union vm_value) { .i = vm_copy_delegate_page(v.i) };
case AIN_REF_TYPE:
heap_ref(v.i);
return v;
@@ -339,7 +317,6 @@ static void scenario_call(int slot)
.return_address = VM_RETURN,
.page_slot = slot,
.struct_page = -1,
.delegate = -1,
};
call_stack_ptr = 1;
instr_ptr = ain->functions[fno].address;
@@ -364,7 +341,6 @@ static int _function_call(int fno, int return_address)
.return_address = return_address,
.page_slot = slot,
.struct_page = -1,
.delegate = -1,
};
// initialize local variables
for (int i = f->nr_args; i < f->nr_vars; i++) {
@@ -407,24 +383,48 @@ static void vm_execute(void);
static void delegate_call(int dg_no, int return_address)
{
// stack: [arg0, ..., dg_page, dg_index]
int dg_page = stack_peek(1).i;
int dg_index = stack_peek(0).i;
if (dg_no < 0 || dg_no >= ain->nr_delegates)
VM_ERROR("Invalid delegate index");
// stack: [arg0, ..., dg_page, dg_index, [return_value]]
int return_values = (ain->delegates[dg_no].return_type.data != AIN_VOID) ? 1 : 0;
int dg_page = stack_peek(1 + return_values).i;
int dg_index = stack_peek(0 + return_values).i;
int obj, fun;
delegate_get(dg_page, dg_index, &obj, &fun);
if (delegate_get(heap_get_delegate_page(dg_page), dg_index, &obj, &fun)) {
// pop previous return value
if (ain->delegates[dg_no].return_type.data != AIN_VOID) {
stack_pop();
}
// increment dg_index
stack[stack_ptr - 1].i++;
int slot = _function_call(fun, return_address);
int slot = _function_call(fun, instr_ptr + instruction_width(DG_CALL));
// copy arguments into local page
struct ain_function_type *dg = &ain->delegates[dg_no];
for (int i = 0; i < dg->nr_arguments; i++) {
union vm_value arg = stack_peek((dg->nr_arguments + 1) - i);
heap[slot].page->values[i] = vm_copy(arg, dg->variables[i].type.data);
// copy arguments into local page
struct ain_function_type *dg = &ain->delegates[dg_no];
for (int i = 0; i < dg->nr_arguments; i++) {
union vm_value arg = stack_peek((dg->nr_arguments + 1) - i);
heap[slot].page->values[i] = vm_copy(arg, dg->variables[i].type.data);
}
call_stack[call_stack_ptr-1].struct_page = obj;
} else {
// call finished: clean up stack and jump to return address
union vm_value r;
if (return_values) {
r = stack_pop();
}
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);
}
if (return_values) {
stack_push(r);
}
instr_ptr = get_argument(1);
}
call_stack[call_stack_ptr-1].struct_page = obj;
call_stack[call_stack_ptr-1].delegate = dg_no;
}
void vm_call(int fno, int struct_page)
@@ -444,14 +444,6 @@ 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--;
}
@@ -2162,79 +2154,49 @@ static enum opcode execute_instruction(enum opcode opcode)
int obj = stack_pop().i;
int dg_i = stack_pop().i;
delete_page(dg_i);
delegate_append(dg_i, obj, fun);
heap_set_page(dg_i, delegate_new_from_method(obj, fun));
break;
}
case DG_SET: {
int fun = stack_pop().i;
int obj = stack_pop().i;
int dg_i = stack_pop().i;
delegate_append(dg_i, obj, fun);
struct page *dg = heap_get_delegate_page(dg_i);
heap_set_page(dg_i, delegate_append(dg, obj, fun));
break;
}
case DG_CALL: { // DG_TYPE, ADDR
int dg = get_argument(0);
if (dg < 0 || dg >= ain->nr_delegates)
VM_ERROR("Invalid delegate index");
// stack: [arg0, ..., dg_page, dg_index, [return_value]]
int return_values = (ain->delegates[dg].return_type.data != AIN_VOID) ? 1 : 0;
int dg_page = stack_peek(1 + return_values).i;
int dg_index = stack_peek(0 + return_values).i;
if (dg_index < delegate_numof(dg_page)) {
int obj, fun;
delegate_get(dg_page, dg_index, &obj, &fun);
// pop previous return value
if (ain->delegates[dg].return_type.data != AIN_VOID) {
stack_pop();
}
delegate_call(dg, instr_ptr + instruction_width(DG_CALL));
} else {
// call finished: clean up stack and jump to return address
union vm_value r;
if (return_values) {
r = stack_pop();
}
stack_pop(); // dg_index
stack_pop(); // dg_page
for (int i = ain->delegates[dg].nr_variables - 1; i >= 0; i--) {
variable_fini(stack_pop(), ain->delegates[dg].variables[i].type.data);
}
if (return_values) {
stack_push(r);
}
instr_ptr = get_argument(1);
}
delegate_call(get_argument(0), get_argument(1));
break;
}
case DG_NUMOF: {
int dg = stack_pop().i;
stack_push(delegate_numof(dg));
stack_push(delegate_numof(heap_get_delegate_page(dg)));
break;
}
case DG_EXIST: {
int fun = stack_pop().i;
int obj = stack_pop().i;
int dg_i = stack_pop().i;
stack_push(delegate_contains(dg_i, obj, fun));
stack_push(delegate_contains(heap_get_delegate_page(dg_i), obj, fun));
break;
}
case DG_ERASE: {
int fun = stack_pop().i;
int obj = stack_pop().i;
int dg_i = stack_pop().i;
delegate_erase(dg_i, obj, fun);
delegate_erase(heap_get_delegate_page(dg_i), obj, fun);
break;
}
case DG_CLEAR: {
int slot = stack_pop().i;
if (!slot)
break;
delegate_clear(slot);
heap_set_page(slot, delegate_clear(heap_get_delegate_page(slot)));
break;
}
case DG_COPY: {
stack_push(vm_copy_delegate_page(stack_pop().i));
stack_push(vm_copy_page(heap_get_delegate_page(stack_pop().i)));
break;
}
case DG_ASSIGN: {
@@ -2244,21 +2206,24 @@ 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);
vm_register_delegate_structs(new_dg, dst_i);
stack_push(set_i);
break;
}
case DG_PLUSA: {
int add_i = stack_pop().i;
int dst_i = stack_pop().i;
delegate_plusa(dst_i, add_i);
struct page *add = heap_get_delegate_page(add_i);
struct page *dst = heap_get_delegate_page(dst_i);
heap_set_page(dst_i, delegate_plusa(dst, add));
stack_push(add_i);
break;
}
case DG_MINUSA: {
int minus_i = stack_pop().i;
int dst_i = stack_pop().i;
delegate_minusa(dst_i, minus_i);
struct page *minus = heap_get_delegate_page(minus_i);
struct page *dst = heap_get_delegate_page(dst_i);
heap_set_page(dst_i, delegate_minusa(dst, minus));
stack_push(minus_i);
break;
}
@@ -2269,9 +2234,7 @@ static enum opcode execute_instruction(enum opcode opcode)
case DG_NEW_FROM_METHOD: {
int fun = stack_pop().i;
int obj = stack_pop().i;
int dg_i = heap_alloc_page(NULL);
delegate_append(dg_i, obj, fun);
stack_push(dg_i);
stack_push(heap_alloc_page(delegate_new_from_method(obj, fun)));
break;
}
case DG_CALLBEGIN: { // DG_TYPE
@@ -2370,6 +2333,7 @@ int vm_execute_ain(struct ain *program)
// Initialize globals
heap[0].ref = 1;
heap[0].seq = heap_next_seq++;
heap_set_page(0, alloc_page(GLOBAL_PAGE, 0, ain->nr_globals));
for (int i = 0; i < ain->nr_globals; i++) {
if (ain->globals[i].type.data == AIN_STRUCT) {