mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-30 02:48:14 +03:00
Merge pull request #170 from kichikuou/delegate
Rework delegate implementation
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user