diff --git a/src/littlelambda.cpp b/src/littlelambda.cpp index 7c2fa29..c94d266 100644 --- a/src/littlelambda.cpp +++ b/src/littlelambda.cpp @@ -50,15 +50,17 @@ static bool _try_parse_as(const char* start, const char* end, T& out, V... v) { return false; } +lam_hooks::~lam_hooks() = default; + struct lam_vm { - ugc_t gc; - lam_hooks hooks; - lam_env* root; - std::unordered_map imports; + ugc_t gc{}; + lam_hooks* hooks{}; + lam_env* root{}; + std::unordered_map imports{}; struct { - lam_u64 alloc_count; - lam_u64 free_count; - lam_u64 gc_iter_count; + lam_u64 alloc_count{}; + lam_u64 free_count{}; + lam_u64 gc_iter_count{}; } gc_stats; }; @@ -66,7 +68,8 @@ struct lam_vm { // Register T with the garbage collector template static T* callocPlus(lam_vm* vm, size_t extra) { - void* p = calloc(1, sizeof(T) + extra); + void* p = vm->hooks->mem_alloc(sizeof(T) + extra); + memset(p, 0, sizeof(T) + extra); auto o = reinterpret_cast(p); ugc_register(&vm->gc, &o->header); vm->gc_stats.alloc_count += 1; @@ -351,7 +354,7 @@ struct lam_env_impl : lam_env { static lam_env* lam_new_env(lam_vm* vm, lam_env* parent, const char* name) { assert(parent == nullptr || vm == static_cast(parent)->vm); - auto* d = callocPlus(vm, sizeof(lam_env_impl)); + auto* d = callocPlus(vm, 0); return new (d) lam_env_impl(vm, parent, name); } @@ -744,7 +747,7 @@ static lam_env* lam_make_env_builtin(lam_vm* vm) { auto modname = a[0].as_symbol(); auto it = env->vm->imports.find(modname->val()); if (it == env->vm->imports.end()) { - lam_result result = env->vm->hooks.import(env->vm, modname->val()); + lam_result result = env->vm->hooks->import(env->vm, modname->val()); if (result.code == 0) { return result.value; } else { @@ -1221,15 +1224,17 @@ lam_result lam_vm_import(lam_vm* vm, const char* name, const void* data, size_t } lam_vm* lam_vm_new(lam_hooks* hooks) { - void* addr = hooks->alloc(sizeof(lam_vm)); + hooks->init(); + void* addr = hooks->mem_alloc(sizeof(lam_vm)); lam_vm* vm = new (addr) lam_vm; ugc_init(&vm->gc, &lam_ugc_visit, &lam_ugc_free); vm->gc.userdata = vm; - memcpy(&vm->hooks, hooks, sizeof(lam_hooks)); + vm->hooks = hooks; vm->root = lam_make_env_builtin(vm); return vm; } void lam_vm_delete(lam_vm* vm) { + vm->hooks->quit(); // TODO } diff --git a/src/littlelambda.h b/src/littlelambda.h index 60c1a6a..e42811c 100644 --- a/src/littlelambda.h +++ b/src/littlelambda.h @@ -319,13 +319,12 @@ void lam_print(lam_value val, const char* end = nullptr); /// Functionality provided by external systems. struct lam_hooks { - using alloc_func = void*(size_t); - using free_func = void(void*); - using import_func = lam_result(lam_vm* vm, const char* modname); - - alloc_func* alloc; - free_func* free; - import_func* import; + virtual ~lam_hooks(); + virtual void* mem_alloc(size_t size) =0; + virtual void mem_free(void* addr) = 0; + virtual void init() = 0; + virtual void quit() = 0; + virtual lam_result import(lam_vm* vm, const char* modname) = 0; }; /// Initialize a vm. diff --git a/src/test.cpp b/src/test.cpp index 5bd3676..f894203 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -1,9 +1,23 @@ #define _CRT_SECURE_NO_WARNINGS #include -#include #include +#include #include #include +#if __cpp_lib_stacktrace >= 202011L +#include // C++23 feature +#else +namespace std { +struct stacktrace_entry { + std::string description() { return ""; } +}; +struct stacktrace { + static stacktrace current() { return {}; } + struct stacktrace_entry* begin() { return nullptr; } + stacktrace_entry* end() { return nullptr; } +}; +} // namespace std +#endif #include "littlelambda.h" static int slurp_file(const char* path, std::vector& buf) { @@ -63,10 +77,40 @@ static lam_result import_impl(lam_vm* vm, const char* modname) { return lam_result::fail(1, "Import failed"); } +struct DebugHooks : lam_hooks { + DebugHooks() {} + + void* mem_alloc(size_t size) override { + void* p = malloc(size); + assert(allocs.find(p) == allocs.end()); + allocs[p] = std::stacktrace::current(); + return p; + } + void mem_free(void* addr) override { + auto it = allocs.find(addr); + assert(it != allocs.end()); + allocs.erase(it); + free(addr); + } + void init() override { + //assert(allocs.empty()); + allocs.clear(); + } + void quit() override { + /*for (auto it : allocs) { + printf("%p\n", it.first); + for (auto p : it.second) { + printf("\t%s\n", p.description().c_str()); + } + }*/ + } + lam_result import(lam_vm* vm, const char* modname) override { return import_impl(vm, modname); } + std::unordered_map allocs; +}; int main() { - lam_hooks hooks = {&malloc, &free, &import_impl}; + DebugHooks hooks; if (1) { lam_vm* vm = lam_vm_new(&hooks); read_and_eval(vm, "module.ll");