Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fc6406e97 | ||
|
|
d09b4f2761 | ||
|
|
2488e9fb07 | ||
|
|
899c22eece | ||
|
|
1c57525604 | ||
|
|
5de995a3c2 | ||
|
|
7759136aa8 | ||
|
|
d38365f10c |
@@ -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)
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <vm/fzl_vm_env.h>
|
||||
#include <vm/fzl_vm_extern.h>
|
||||
#include <vm/fzl_vm_reader.h>
|
||||
|
||||
// VM descriptor
|
||||
typedef struct
|
||||
{
|
||||
fzl_vm_reader_t *reader;
|
||||
fzl_vm_extern_t *prep_fns;
|
||||
|
||||
size_t ip;
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#define FZL_VM_EXTERN_H
|
||||
|
||||
#include <ffi.h>
|
||||
#include <stddef.h>
|
||||
#include <vm/fzl_vm_reader.h>
|
||||
|
||||
// Loaded extern function definition
|
||||
typedef struct
|
||||
@@ -9,6 +11,25 @@ typedef struct
|
||||
void *func_ptr;
|
||||
ffi_cif cif;
|
||||
ffi_type **arg_types;
|
||||
} fzl_vm_sym_t;
|
||||
|
||||
// Extern object descriptor
|
||||
typedef struct
|
||||
{
|
||||
void **libs;
|
||||
size_t libs_loaded;
|
||||
|
||||
fzl_vm_sym_t *syms;
|
||||
size_t syms_loaded;
|
||||
} fzl_vm_extern_t;
|
||||
|
||||
// Load externs
|
||||
fzl_vm_extern_t *fzl_vm_init_externs(fzl_vm_reader_t *reader);
|
||||
|
||||
// Get last error from externs loader
|
||||
char *fzl_vm_externs_geterror();
|
||||
|
||||
// Close and free externs
|
||||
void fzl_vm_free_externs(fzl_vm_extern_t *ex);
|
||||
|
||||
#endif // FZL_VM_EXTERN_H
|
||||
|
||||
@@ -131,7 +131,8 @@ fzl_an_res_t analyze_call(fzl_analyzer_t *analyzer, fzl_node_t *call_node)
|
||||
return (fzl_an_res_t){.error = true};
|
||||
}
|
||||
|
||||
if (sym.node->type != FZL_NODETYPE_FUNCTION)
|
||||
if (sym.node->type != FZL_NODETYPE_FUNCTION &&
|
||||
sym.node->type != FZL_NODETYPE_EXTERN_FN)
|
||||
{
|
||||
snprintf(an_problem, sizeof(an_problem), "'%s' is not callable",
|
||||
callee_name);
|
||||
@@ -146,22 +147,24 @@ fzl_an_res_t analyze_call(fzl_analyzer_t *analyzer, fzl_node_t *call_node)
|
||||
return (fzl_an_res_t){.error = true};
|
||||
}
|
||||
|
||||
size_t offset = is_method ? 1 : 0;
|
||||
if ((sym.node->fn.args_count - offset) != call_node->call.args_count)
|
||||
size_t offset = is_method ? 1 : 0;
|
||||
size_t args_count = sym.node->type == FZL_NODETYPE_EXTERN_FN
|
||||
? sym.node->ext_fn.args_count
|
||||
: sym.node->fn.args_count;
|
||||
if ((args_count - offset) != call_node->call.args_count)
|
||||
{
|
||||
if (is_method)
|
||||
{
|
||||
snprintf(an_problem, sizeof(an_problem),
|
||||
"method '%s.%s' is expecting %zu argument(s), got %zu",
|
||||
tmp_classname, tmp_fnname, sym.node->fn.args_count,
|
||||
tmp_classname, tmp_fnname, args_count,
|
||||
call_node->call.args_count - offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(an_problem, sizeof(an_problem),
|
||||
"function '%s' is expecting %zu argument(s), got %zu",
|
||||
callee_name, sym.node->fn.args_count,
|
||||
call_node->call.args_count - offset);
|
||||
callee_name, args_count, call_node->call.args_count - offset);
|
||||
}
|
||||
an_push_error(analyzer, call_node->token, an_problem);
|
||||
|
||||
@@ -174,9 +177,12 @@ fzl_an_res_t analyze_call(fzl_analyzer_t *analyzer, fzl_node_t *call_node)
|
||||
return (fzl_an_res_t){.error = true};
|
||||
}
|
||||
|
||||
fzl_node_t **args = sym.node->type == FZL_NODETYPE_EXTERN_FN
|
||||
? sym.node->ext_fn.args
|
||||
: sym.node->fn.args;
|
||||
for (size_t i = offset; i < sym.node->fn.args_count; i++)
|
||||
{
|
||||
fzl_node_t *arg = sym.node->fn.args[i];
|
||||
fzl_node_t *arg = args[i];
|
||||
bool found = false;
|
||||
fzl_type_t req_type = an_toktotype(analyzer, arg->var.type, &found);
|
||||
char *req_classname = NULL;
|
||||
@@ -237,14 +243,28 @@ fzl_an_res_t analyze_call(fzl_analyzer_t *analyzer, fzl_node_t *call_node)
|
||||
}
|
||||
|
||||
bool type_found = false;
|
||||
fzl_type_t type = an_toktotype(analyzer, sym.node->fn.type, &type_found);
|
||||
fzl_token_t *ret_tok = NULL;
|
||||
void *ret_name = NULL;
|
||||
|
||||
if (sym.node->type == FZL_NODETYPE_EXTERN_FN)
|
||||
{
|
||||
ret_tok = sym.node->ext_fn.type;
|
||||
ret_name = sym.node->ext_fn.name;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_tok = sym.node->fn.type;
|
||||
ret_name = sym.node->fn.name;
|
||||
}
|
||||
|
||||
fzl_type_t type = an_toktotype(analyzer, ret_tok, &type_found);
|
||||
char *classname = sym.classname;
|
||||
|
||||
if (!type_found)
|
||||
{
|
||||
snprintf(an_problem, sizeof(an_problem), "symbol '%s' not found",
|
||||
(char *)sym.node->fn.type->value);
|
||||
an_push_error(analyzer, sym.node->fn.type, an_problem);
|
||||
(char *)ret_name);
|
||||
an_push_error(analyzer, ret_tok, an_problem);
|
||||
return (fzl_an_res_t){.error = true};
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <string.h>
|
||||
#include <vm/fzl_vm.h>
|
||||
#include <vm/fzl_vm_env.h>
|
||||
#include <vm/fzl_vm_extern.h>
|
||||
#include <vm/fzl_vm_object.h>
|
||||
#include <vm/fzl_vm_reader.h>
|
||||
#include <vm/internals/opcode.h>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <common/fzl_opcodes.h>
|
||||
#include <common/fzl_value.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <vm/fzl_vm.h>
|
||||
@@ -22,6 +23,14 @@ void fzl_vm_print_ops(fzl_vm_t *vm)
|
||||
|
||||
switch (vm->reader->ops[i])
|
||||
{
|
||||
case FZL_OPCODE_CALL_EXTERN:
|
||||
{
|
||||
size_t fn_n = vm->reader->ops[++i];
|
||||
printf("CALL_EXTERN FN=#%zu (%s)\n", fn_n,
|
||||
vm->reader->extern_fns[fn_n].used_name);
|
||||
break;
|
||||
}
|
||||
|
||||
case FZL_OPCODE_STORE_LOCAL:
|
||||
{
|
||||
size_t depth = vm->reader->ops[++i];
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
#include <common/fzl_extern.h>
|
||||
#include <common/fzl_types.h>
|
||||
#include <dlfcn.h>
|
||||
#include <ffi.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <vm/fzl_vm_extern.h>
|
||||
#include <vm/fzl_vm_reader.h>
|
||||
|
||||
char ex_err[256];
|
||||
|
||||
// Resolve specified type reference
|
||||
ffi_type *resolve_type(fzl_vm_reader_t *reader, fzl_extern_type_ref_t *ref)
|
||||
{
|
||||
if (ref->is_primitive)
|
||||
{
|
||||
switch (ref->primitive)
|
||||
{
|
||||
case FZL_TYPE_INT8:
|
||||
return &ffi_type_sint8;
|
||||
|
||||
case FZL_TYPE_INT16:
|
||||
return &ffi_type_sint16;
|
||||
|
||||
case FZL_TYPE_INT32:
|
||||
return &ffi_type_sint32;
|
||||
|
||||
case FZL_TYPE_INT64:
|
||||
return &ffi_type_sint64;
|
||||
|
||||
case FZL_TYPE_UINT8:
|
||||
case FZL_TYPE_BOOL:
|
||||
return &ffi_type_uint8;
|
||||
|
||||
case FZL_TYPE_UINT16:
|
||||
return &ffi_type_uint16;
|
||||
|
||||
case FZL_TYPE_UINT32:
|
||||
return &ffi_type_uint32;
|
||||
|
||||
case FZL_TYPE_UINT64:
|
||||
return &ffi_type_uint64;
|
||||
|
||||
case FZL_TYPE_INT:
|
||||
return &ffi_type_sint;
|
||||
|
||||
case FZL_TYPE_UINT:
|
||||
return &ffi_type_uint;
|
||||
|
||||
case FZL_TYPE_CHAR:
|
||||
return &ffi_type_schar;
|
||||
|
||||
case FZL_TYPE_STRING:
|
||||
case FZL_TYPE_HANDLE:
|
||||
case FZL_TYPE_STRUCT:
|
||||
return &ffi_type_pointer;
|
||||
|
||||
default:
|
||||
return &ffi_type_void;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fzl_extern_type_t *t = &reader->extern_types[ref->type_slot];
|
||||
if (!t)
|
||||
return &ffi_type_void;
|
||||
|
||||
switch (t->kind)
|
||||
{
|
||||
case FZL_EXTERN_KIND_STRUCT:
|
||||
case FZL_EXTERN_KIND_HANDLE:
|
||||
case FZL_EXTERN_KIND_REF:
|
||||
return &ffi_type_pointer;
|
||||
|
||||
case FZL_EXTERN_KIND_ALIAS:
|
||||
return resolve_type(reader, &t->alias_target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Preapre sym, put result in out and return operation result
|
||||
bool prepare_sym(fzl_vm_reader_t *reader, void *sym, fzl_extern_fn_t *fn,
|
||||
fzl_vm_sym_t *out)
|
||||
{
|
||||
if (!reader || !fn || !sym || !out)
|
||||
{
|
||||
snprintf(ex_err, sizeof(ex_err),
|
||||
"cannot prepare sym: function def, symbol handle or outvar is not "
|
||||
"specified");
|
||||
return false;
|
||||
}
|
||||
memset(out, 0, sizeof(fzl_vm_sym_t));
|
||||
|
||||
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)
|
||||
{
|
||||
if (!reader)
|
||||
{
|
||||
snprintf(ex_err, sizeof(ex_err), "reader is not provided");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fzl_vm_extern_t *ex = malloc(sizeof(fzl_vm_extern_t));
|
||||
if (!ex)
|
||||
{
|
||||
snprintf(ex_err, sizeof(ex_err), "out of memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(ex, 0, sizeof(fzl_vm_extern_t));
|
||||
|
||||
if (reader->extern_libs_count < 1 || reader->extern_fns_count < 1)
|
||||
return ex;
|
||||
|
||||
ex->libs = malloc(sizeof(void *) * reader->extern_libs_count);
|
||||
if (!ex->libs)
|
||||
{
|
||||
snprintf(ex_err, sizeof(ex_err), "out of memory");
|
||||
free(ex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(ex->libs, 0, sizeof(void *) * reader->extern_libs_count);
|
||||
for (uint16_t i = 0; i < reader->extern_libs_count; i++)
|
||||
{
|
||||
void *lib = dlopen(reader->extern_libs[i], RTLD_NOW);
|
||||
if (!lib)
|
||||
{
|
||||
snprintf(ex_err, sizeof(ex_err), "cannot open library '%s'",
|
||||
reader->extern_libs[i]);
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
for (size_t j = 0; j < ex->libs_loaded; j++)
|
||||
if (ex->libs[j])
|
||||
dlclose(ex->libs[j]);
|
||||
}
|
||||
|
||||
free(ex->libs);
|
||||
free(ex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ex->libs[ex->libs_loaded++] = lib;
|
||||
}
|
||||
|
||||
ex->syms = malloc(sizeof(fzl_vm_sym_t) * reader->extern_fns_count);
|
||||
if (!ex->syms)
|
||||
{
|
||||
snprintf(ex_err, sizeof(ex_err), "out of memory");
|
||||
|
||||
for (size_t i = 0; i < ex->libs_loaded; i++)
|
||||
if (ex->libs[i])
|
||||
dlclose(ex->libs[i]);
|
||||
|
||||
free(ex->libs);
|
||||
free(ex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(ex->syms, 0, sizeof(fzl_vm_sym_t) * reader->extern_fns_count);
|
||||
for (uint16_t i = 0; i < reader->extern_fns_count; i++)
|
||||
{
|
||||
fzl_extern_fn_t *fn = &reader->extern_fns[i];
|
||||
char *fnname = fn->symbol_name != NULL ? fn->symbol_name : fn->used_name;
|
||||
|
||||
void *sym = dlsym(ex->libs[fn->lib_slot], fnname);
|
||||
if (!sym)
|
||||
{
|
||||
snprintf(ex_err, sizeof(ex_err), "couldn't find symbol '%s' in lib '%s'",
|
||||
fnname, reader->extern_libs[fn->lib_slot]);
|
||||
|
||||
for (size_t j = 0; j < ex->libs_loaded; j++)
|
||||
if (ex->libs[j])
|
||||
dlclose(ex->libs[j]);
|
||||
|
||||
free(ex->libs);
|
||||
|
||||
for (size_t j = 0; j < ex->syms_loaded; j++)
|
||||
{
|
||||
if (ex->syms->arg_types)
|
||||
free(ex->syms->arg_types);
|
||||
}
|
||||
|
||||
free(ex->syms);
|
||||
free(ex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!prepare_sym(reader, sym, fn, &ex->syms[ex->syms_loaded++]))
|
||||
{
|
||||
if (!ex_err[0])
|
||||
{
|
||||
snprintf(ex_err, sizeof(ex_err),
|
||||
"failed to preapre FFI structure for sym '%s'", fnname);
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < ex->libs_loaded; j++)
|
||||
if (ex->libs[j])
|
||||
dlclose(ex->libs[j]);
|
||||
|
||||
free(ex->libs);
|
||||
free(ex->syms);
|
||||
free(ex);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
char *fzl_vm_externs_geterror() { return ex_err; }
|
||||
|
||||
void fzl_vm_free_externs(fzl_vm_extern_t *ex)
|
||||
{
|
||||
if (!ex)
|
||||
return;
|
||||
|
||||
if (ex->libs)
|
||||
{
|
||||
for (size_t i = 0; i < ex->libs_loaded; i++)
|
||||
if (ex->libs[i])
|
||||
dlclose(ex->libs[i]);
|
||||
|
||||
free(ex->libs);
|
||||
}
|
||||
|
||||
if (ex->syms)
|
||||
{
|
||||
for (size_t i = 0; i < ex->syms_loaded; i++)
|
||||
free(ex->syms[i].arg_types);
|
||||
|
||||
free(ex->syms);
|
||||
}
|
||||
|
||||
free(ex);
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
#include <common/fzl_value.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <vm/fzl_vm.h>
|
||||
#include <vm/fzl_vm_extern.h>
|
||||
#include <vm/internals/opcode.h>
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <vm/fzl_vm.h>
|
||||
#include <vm/fzl_vm_extern.h>
|
||||
#include <vm/fzl_vm_reader.h>
|
||||
|
||||
typedef struct
|
||||
@@ -140,6 +141,18 @@ int main(int argc, char **argv)
|
||||
return -1;
|
||||
}
|
||||
|
||||
vm->prep_fns = fzl_vm_init_externs(reader);
|
||||
if (!vm->prep_fns)
|
||||
{
|
||||
printf("%sError%s: %s\n", FZL_CERROR, FZL_CRST, fzl_vm_externs_geterror());
|
||||
|
||||
free(args);
|
||||
fzl_vm_free_externs(vm->prep_fns);
|
||||
fzl_vm_freereader(reader);
|
||||
fzl_freevm(vm);
|
||||
return -1;
|
||||
}
|
||||
|
||||
vm->allow_infinite = args->allow_infinite;
|
||||
vm->max_iter = args->max_iter;
|
||||
|
||||
@@ -155,6 +168,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;
|
||||
@@ -162,6 +176,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
free(args);
|
||||
fzl_vm_free_externs(vm->prep_fns);
|
||||
fzl_vm_freereader(reader);
|
||||
fzl_freevm(vm);
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
extern "jopalib"
|
||||
{
|
||||
type JopaLibType handle;
|
||||
|
||||
fn jopalib(int a, str b) JopaLibType;
|
||||
}
|
||||
|
||||
JopaLibType a = jopalib(42, "jopa");
|
||||
printline(a);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
extern "libmath" {
|
||||
fn double_num(int num) int;
|
||||
}
|
||||
|
||||
int initial = 42;
|
||||
int result = double_num(initial);
|
||||
printline($"initial value: {initial}, result of external function: {result}");
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
extern "math" {
|
||||
fn dnum(int num) int = "double_num";
|
||||
}
|
||||
|
||||
int initial = 42;
|
||||
int result = dnum(initial);
|
||||
printline($"initial value: {initial}, result of external function: {result}");
|
||||
|
||||
Reference in New Issue
Block a user