// SPDX-License-Identifier: MIT // Copyright (c) 2023 John Watts and the LuminaSensum contributors #define DEBUG_MSG 0 // Change to 1 for debug logging #include "vm.h" #include "debug.h" #include "error.h" #include "list.h" #include "object.h" #include <stddef.h> #include <stdio.h> #include <stdlib.h> struct vm_state { Object stack; int stack_base; int stack_next; Object tail_obj; const char *tail_name; StackTrace stack_trace; }; VmState vm_create(void) { struct vm_state *state = calloc(sizeof(struct vm_state), 1); if (state == NULL) abort_print("vm_create: unable to allocate state"); state->stack_base = 0; state->stack_next = 0; state->tail_obj = object_none(); state->tail_name = NULL; state->stack = object_list_create(state, 16); state->stack_trace = stacktrace_create(32); return (VmState)state; } void vm_destroy(VmState *state) { object_drop(*state, &(*state)->stack); free((*state)->stack_trace); free(*state); } void vm_stack_set(VmState state, int index, Object obj) { int offset = state->stack_base + index; vm_abort_if(state, index < 0, "vm_stack_set: negative value"); vm_abort_if(state, offset >= state->stack_next, "vm_stack_set: stack overflow"); object_list_set(state, state->stack, offset, obj); } Object vm_stack_get(VmState state, int index) { int offset = state->stack_base + index; vm_abort_if(state, index < 0, "vm_stack_get: negative value"); vm_abort_if(state, offset >= state->stack_next, "vm_stack_get: stack overflow"); Object obj = object_list_get(state, state->stack, offset); return obj; } void vm_stack_push(VmState state, Object obj) { // stack_next starts at 0, stack_size starts at 1 // Convert stack_size to a max stack offset to compensate int max_stack = object_list_length(state, state->stack) - 1; // Check if stack_next is outside the stack before // writing to it vm_abort_if(state, state->stack_next > max_stack, "vm_stack_push: stack overflow"); object_list_set(state, state->stack, state->stack_next++, obj); } Object vm_stack_pop(VmState state) { vm_abort_if(state, state->stack_next <= state->stack_base, "vm_stack_pop: stack base underflow"); Object obj = object_list_get(state, state->stack, --state->stack_next); object_list_set(state, state->stack, state->stack_next, object_none()); return obj; } void vm_stack_drop(VmState state, int count) { vm_abort_if(state, count < 1, "vm_stack_drop: invalid drop count"); while (count--) { Object obj = vm_stack_pop(state); object_drop(state, &obj); } } int vm_stack_depth(VmState state) { return state->stack_next - state->stack_base; } void vm_call(VmState state, Object obj, const char *name, int arg_count) { int old_base = state->stack_base; state->stack_base = state->stack_next - arg_count; object_hold(state, obj); do { stacktrace_push(state->stack_trace, name); debug_msg("Calling %s\n", name); dispatch_call(state, obj, name); debug_msg("Exiting %s\n", name); stacktrace_pop(state->stack_trace); object_drop(state, &obj); obj = state->tail_obj; name = state->tail_name; state->tail_obj = object_none(); state->tail_name = NULL; } while (name != NULL); state->stack_base = old_base; } void vm_tailcall(VmState state, Object obj, const char *name) { object_hold(state, obj); state->tail_obj = obj; state->tail_name = name; } __attribute__((noreturn)) void vm_abort_msg(VmState state, const char *msg) { stacktrace_print(state->stack_trace); abort_print(msg); } void vm_abort_if(VmState state, int value, const char *msg) { if (value) vm_abort_msg(state, msg); }