diff --git a/lang/bytecode.c b/lang/bytecode.c index c3b3175..d97167f 100644 --- a/lang/bytecode.c +++ b/lang/bytecode.c @@ -47,6 +47,7 @@ const char *dispatch = (const char *)pos_code; struct object *obj = vm_stack_pop(state); vm_call(obj, dispatch, arg_count, state); + object_drop(&obj); break; } case OP_NULL: { @@ -64,10 +65,7 @@ break; } case OP_DROP: { - unsigned char count = *pos_code++; - while (count--) { - (void)vm_stack_pop(state); - } + vm_stack_drop(state, *pos_code++); break; } default: diff --git a/lang/func.c b/lang/func.c index 7d5bd6d..0317c07 100644 --- a/lang/func.c +++ b/lang/func.c @@ -16,6 +16,7 @@ struct func *func = malloc(sizeof(struct func)); abort_if(!func, "unable to allocate func"); func->obj.class_data = &func_class; + func->obj.ref_count = 1; return (struct object *)func; } diff --git a/lang/main.c b/lang/main.c index 02259ce..26db195 100644 --- a/lang/main.c +++ b/lang/main.c @@ -15,15 +15,13 @@ struct object *numB = number_create(3); vm_stack_push(&state, NULL); vm_stack_push(&state, numB); + numB = NULL; vm_call(numA, "Add", 2, &state); struct object *numC = vm_stack_get(&state, 0); printf("numC value is %i\n", number_value(numC)); - printf("numA is %p\n", (void *)numA); - printf("numB is %p\n", (void *)numB); - printf("numC is %p\n", (void *)numC); - printf("numA is %p\n", (void *)numA); - printf("numB is %p\n", (void *)numB); - printf("numC is %p\n", (void *)numC); + vm_stack_drop(&state, 2); + object_drop(&numA); + object_drop(&numC); } static void test_func(void) { @@ -34,12 +32,10 @@ struct object *funcA = func_create(); vm_stack_push(&state, NULL); vm_call(funcA, "Call", 1, &state); - printf("funcA is %p\n", (void *)funcA); - printf("funcA is %p\n", (void *)funcA); - struct object *numA = vm_stack_get(&state, 0); - printf("numA is %p\n", (void *)numA); + struct object *numA = vm_stack_pop(&state); printf("code return value is %i\n", number_value(numA)); - printf("numA is %p\n", (void *)numA); + object_drop(&numA); + object_drop(&funcA); } int lang_main(void) { diff --git a/lang/number.c b/lang/number.c index 9c4888d..bb5f439 100644 --- a/lang/number.c +++ b/lang/number.c @@ -16,6 +16,7 @@ struct number *num = malloc(sizeof(struct number)); abort_if(!num, "unable to allocate number"); num->obj.class_data = &num_class; + num->obj.ref_count = 1; num->value = value; return (struct object *)num; } @@ -45,6 +46,8 @@ struct number *numB = (struct number *)arg1; int added = numA->value + numB->value; vm_stack_set(state, 0, number_create(added)); + object_drop(&arg1); + numB = NULL; } static void number_minus( @@ -58,6 +61,8 @@ struct number *numB = (struct number *)arg1; int subbed = numA->value - numB->value; vm_stack_set(state, 0, number_create(subbed)); + object_drop(&arg1); + numB = NULL; } static struct object_call calls[] = {{.name = "Add", .handler = number_add}, diff --git a/lang/object.c b/lang/object.c index a08dd4d..dff08ad 100644 --- a/lang/object.c +++ b/lang/object.c @@ -6,6 +6,23 @@ #include #include +void object_hold(struct object *obj) { + abort_if(obj == NULL, "object_hold holding NULL"); + atomic_fetch_add_explicit(&obj->ref_count, 1, memory_order_relaxed); +} + +void object_drop(struct object **objptr) { + abort_if(*objptr == NULL, "object_drop dropping NULL"); + struct object *obj = *objptr; + atomic_int count = atomic_fetch_sub_explicit( + &obj->ref_count, 1, memory_order_relaxed); + if (count == 1) { + // We were the last user of the object, clean it up + obj->class_data->cleanup(obj); + } + *objptr = NULL; +} + void dispatch_call(struct object *obj, const char *name, int arg_count, struct vm_state *state) { struct object_call *call = obj->class_data->calls; diff --git a/lang/object.h b/lang/object.h index 89ebfcb..2fc91fe 100644 --- a/lang/object.h +++ b/lang/object.h @@ -5,6 +5,7 @@ #define OBJECT_H #include "vm.h" +#include // Forward declare the class as we need it in object struct object_class; @@ -14,6 +15,7 @@ // Make sure to compile with -fno-strict-aliasing struct object { struct object_class *class_data; + atomic_int ref_count; }; // Dispatchable object call @@ -30,6 +32,13 @@ *calls; // Points to array terminated by call with NULL name }; +// Adds a reference to an object +void object_hold(struct object *obj); + +// Drops a reference to an object, possibly cleaning up the object +// Sets objptr to NULL +void object_drop(struct object **objptr); + // Calls a method on an object void dispatch_call(struct object *obj, const char *name, int arg_count, struct vm_state *state); diff --git a/lang/vm.c b/lang/vm.c index 32c608b..5f01af3 100644 --- a/lang/vm.c +++ b/lang/vm.c @@ -7,11 +7,19 @@ void vm_stack_set(struct vm_state *state, int index, struct object *obj) { struct object **stack_pos = &state->stack_base[index]; + struct object *old = *stack_pos; + if (old != NULL) { + object_drop(stack_pos); + } *stack_pos = obj; } struct object *vm_stack_get(struct vm_state *state, int index) { - return state->stack_base[index]; + struct object *obj = state->stack_base[index]; + if (obj != NULL) { + object_hold(obj); + } + return obj; } void vm_stack_push(struct vm_state *state, struct object *obj) { @@ -19,7 +27,18 @@ } struct object *vm_stack_pop(struct vm_state *state) { - return *--state->stack_next; + struct object *obj = *--state->stack_next; + *state->stack_next = NULL; + return obj; +} + +void vm_stack_drop(struct vm_state *state, int count) { + while (count--) { + struct object *obj = vm_stack_pop(state); + if (obj != NULL) { + object_drop(&obj); + } + } } void vm_call(struct object *obj, const char *name, int arg_count, diff --git a/lang/vm.h b/lang/vm.h index 85fafca..dfc77e3 100644 --- a/lang/vm.h +++ b/lang/vm.h @@ -15,6 +15,7 @@ struct object *vm_stack_get(struct vm_state *state, int index); void vm_stack_push(struct vm_state *state, struct object *obj); struct object *vm_stack_pop(struct vm_state *state); +void vm_stack_drop(struct vm_state *state, int count); void vm_call(struct object *obj, const char *name, int arg_count, struct vm_state *state);