Newer
Older
Tardis / lang / object.c
// SPDX-License-Identifier: MIT
// Copyright (c) 2023 John Watts and the LuminaSensum contributors

#include "object.h"
#include "error.h"
#include "vm.h"
#include <stdatomic.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>

struct object {
	struct object_class *class_data;
	atomic_int ref_count;
	char priv_data[];
};

Object object_none(void) { return (Object)NULL; }

Object object_create(VmState state, struct object_class *class, int priv_size) {
	(void)state;
	vm_abort_if(state, priv_size < 1, "object_create: priv_size too small");
	size_t size = sizeof(struct object) + priv_size;
	struct object *obj = calloc(size, 1);
	vm_abort_if(state, obj == NULL,
		"object_create: not enough memory for an objects");
	obj->class_data = class;
	obj->ref_count = 1;
	return (Object)obj;
}

char *object_priv(VmState state, Object object, struct object_class *class) {
	(void)state;
	vm_abort_if(
		state, object == object_none(), "object_priv: no priv on none");
	struct object *obj = (struct object *)object;
	vm_abort_if(state, obj->class_data != class,
		"object_priv: incompatible class");
	return obj->priv_data;
}

void object_hold(VmState state, Object obj) {
	(void)state;
	if (obj == object_none())
		return;
	atomic_fetch_add_explicit(&obj->ref_count, 1, memory_order_relaxed);
}

void object_drop(VmState state, Object *objptr) {
	if (*objptr == object_none())
		return;
	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(state, obj);
		free(obj);
	}
	*objptr = object_none();
}

void dispatch_call(VmState state, Object obj, const char *name) {
	if (obj == object_none())
		vm_abort_msg(state, "dispatch_call: cannot dispatch on none");
	struct object_call *call = obj->class_data->calls;
	while (call->name != NULL) {
		if (strcmp(call->name, name) == 0) {
			call->handler(state, obj, call->priv);
			return;
		}
		++call;
	}
	vm_abort_msg(state, "dispatch_call: no call found to dispatch");
}

struct object_list {
	int length;
	atomic_int ref_count;
	Object elems[];
};

ObjectList object_list_create(VmState state, int length) {
	size_t elem_size = sizeof(Object) * length;
	size_t size = sizeof(struct object_list) + elem_size;
	ObjectList list = calloc(size, 1);
	vm_abort_if(
		state, list == NULL, "object_list_create: not enough memory");
	list->length = length;
	list->ref_count = 1;
	for (int i = 0; i < length; ++i)
		list->elems[i] = object_none();
	return list;
}

int object_list_length(VmState state, ObjectList list) {
	(void)state;
	return list->length;
}

void object_list_set(VmState state, ObjectList list, int index, Object obj) {
	object_drop(state, &(list->elems[index]));
	list->elems[index] = obj;
}

Object object_list_get(VmState state, ObjectList list, int index) {
	Object obj = list->elems[index];
	object_hold(state, obj);
	return obj;
}

void object_list_hold(VmState state, ObjectList list) {
	(void)state;
	atomic_fetch_add_explicit(&list->ref_count, 1, memory_order_relaxed);
}

void object_list_drop(VmState state, ObjectList *listptr) {
	(void)state;
	ObjectList list = (ObjectList)*listptr;
	atomic_int count = atomic_fetch_sub_explicit(
		&list->ref_count, 1, memory_order_relaxed);
	if (count == 1) {
		// We were the last user of the list, clean it up
		for (int i = 0; i < list->length; ++i)
			object_drop(state, &(list->elems[i]));
		free(list);
	}
	*listptr = NULL;
}