diff --git a/CMakeLists.txt b/CMakeLists.txt index ca6fdf7..10fe745 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,6 +169,7 @@ if (NOT FZL_SKIP_VM) src/vm/reader/vm_rd_sections_sz.c src/vm/fzl_vm.c src/vm/fzl_vm_env.c + src/vm/fzl_vm_extern.c src/vm/fzl_vm_reader.c src/vm/fzl_vm_debug.c src/vm/main.c) diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index 6bc7184..fb41615 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include diff --git a/src/vm/fzl_vm_extern.c b/src/vm/fzl_vm_extern.c index 7cefb9b..118cb04 100644 --- a/src/vm/fzl_vm_extern.c +++ b/src/vm/fzl_vm_extern.c @@ -87,10 +87,37 @@ bool prepare_sym(fzl_vm_reader_t *reader, void *sym, fzl_extern_fn_t *fn, "specified"); return false; } + memset(out, 0, sizeof(fzl_vm_sym_t)); - out->func_ptr = sym; - // TODO: fill the FFI CIF struct values - return false; + out->func_ptr = sym; + out->arg_types = malloc(sizeof(ffi_type *) * fn->args_count); + if (!out->arg_types) + { + snprintf(ex_err, sizeof(ex_err), "out of memory"); + return false; + } + + memset(out->arg_types, 0, sizeof(ffi_type *) * fn->args_count); + for (size_t i = 0; i < fn->args_count; i++) + { + fzl_extern_arg_t *arg = &fn->args[i]; + if (arg->is_out) + out->arg_types[i] = &ffi_type_pointer; + else + out->arg_types[i] = resolve_type(reader, &arg->type); + } + + ffi_type *ret = + fn->has_return ? resolve_type(reader, &fn->ret_type) : &ffi_type_void; + ffi_status res = ffi_prep_cif(&out->cif, FFI_DEFAULT_ABI, fn->args_count, ret, + out->arg_types); + if (res != FFI_OK) + { + snprintf(ex_err, sizeof(ex_err), "failed to prepare FFI"); + return false; + } + + return true; } fzl_vm_extern_t *fzl_vm_init_externs(fzl_vm_reader_t *reader) diff --git a/src/vm/handlers/group_fn.c b/src/vm/handlers/group_fn.c index 9d1c35e..42e1bcd 100644 --- a/src/vm/handlers/group_fn.c +++ b/src/vm/handlers/group_fn.c @@ -1,5 +1,8 @@ +#include +#include #include #include +#include #include // Call function (store return address and jump into function init) @@ -28,7 +31,31 @@ int op_ret(fzl_vm_t *vm) // Call externally defined function int op_call_extern(fzl_vm_t *vm) { - (void)vm; - // TODO: implement + size_t fn_n = vm->reader->ops[++vm->ip]; + if (fn_n >= vm->reader->extern_fns_count) + { + snprintf(last_err, sizeof(last_err), + "runtime error - unknown external function"); + print_err_data(vm); + return -1; + } + + fzl_vm_sym_t *fn = &vm->prep_fns->syms[fn_n]; + if (!fn) + { + snprintf(last_err, sizeof(last_err), + "runtime error - failed to load extern function"); + print_err_data(vm); + return -1; + } + + if (vm->dtop < fn->cif.nargs) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected %u valued on dstack", fn->cif.nargs); + print_err_data(vm); + return -1; + } + return 0; } diff --git a/src/vm/main.c b/src/vm/main.c index 5c420ee..420f574 100644 --- a/src/vm/main.c +++ b/src/vm/main.c @@ -145,6 +145,7 @@ int main(int argc, char **argv) if (!vm->prep_fns) { printf("%sError%s: %s\n", FZL_CERROR, FZL_CRST, fzl_vm_externs_geterror()); + fzl_vm_free_externs(vm->prep_fns); return -1; } @@ -163,6 +164,7 @@ int main(int argc, char **argv) printf("%sError%s: %s\n", FZL_CERROR, FZL_CRST, err); free(args); + fzl_vm_free_externs(vm->prep_fns); fzl_vm_freereader(reader); fzl_freevm(vm); return -1; @@ -170,6 +172,7 @@ int main(int argc, char **argv) } free(args); + fzl_vm_free_externs(vm->prep_fns); fzl_vm_freereader(reader); fzl_freevm(vm);