// 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) { vm_abort_if(state, index < 0, "object_list_get: index too small"); vm_abort_if(state, index >= list->length, "object_list_get: index too large"); object_drop(state, &(list->elems[index])); list->elems[index] = obj; } Object object_list_get(VmState state, ObjectList list, int index) { vm_abort_if(state, index < 0, "object_list_get: index too small"); vm_abort_if(state, index >= list->length, "object_list_get: index too large"); 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; }