diff --git a/ffitest/gtk.janet b/ffitest/gtk.janet index e6de3672..529b7921 100644 --- a/ffitest/gtk.janet +++ b/ffitest/gtk.janet @@ -2,10 +2,10 @@ # An even more sophisticated macro wrapper could add # better doc strings, better parameter checking, etc. -(defn defnative-context +(defn ffi-context "Load a dynamic library and set it as the context for following declarations" [location] - (setdyn :raw-native (raw-native location))) + (setdyn :raw-native (ffi/native location))) (defmacro defnative "Declare a native binding" @@ -16,12 +16,12 @@ (def $sig (symbol name "-signature-")) (def $pointer (symbol name "-raw-pointer-")) ~(upscope - (def ,$pointer :private (as-macro ,assert (,native-lookup (,dyn :raw-native) ,raw-symbol))) - (def ,$sig :private (,native-signature :default ,ret-type ,;signature-args)) + (def ,$pointer :private (as-macro ,assert (,ffi/lookup (,dyn :raw-native) ,raw-symbol))) + (def ,$sig :private (,ffi/signature :default ,ret-type ,;signature-args)) (defn ,name [,;defn-args] - (,native-call ,$pointer ,$sig ,;defn-args)))) + (,ffi/call ,$pointer ,$sig ,;defn-args)))) -(defnative-context "/usr/lib/libgtk-3.so") +(ffi-context "/usr/lib/libgtk-3.so") (defnative gtk-application-new :ptr [:ptr :uint]) (defnative g-signal-connect-data :ulong [:ptr :ptr :ptr :ptr :ptr :int]) @@ -38,7 +38,7 @@ # Limitation is that we cannot generate arbitrary closures to pass into apis. # However, any stubs we need we would simply need to compile ourselves, so # Janet includes a common stub out of the box. -(def cb (native-trampoline :default)) +(def cb (ffi/trampoline :default)) (defn on-active [app] diff --git a/ffitest/test.janet b/ffitest/test.janet index bbfd6c09..0ec27735 100644 --- a/ffitest/test.janet +++ b/ffitest/test.janet @@ -1,72 +1,72 @@ -(def native-loc "ffitest/so.so") -(def native-source-loc "ffitest/so.c") +(def ffi/loc "ffitest/so.so") +(def ffi/source-loc "ffitest/so.c") -(os/execute ["cc" native-source-loc "-shared" "-o" native-loc] :px) -(def module (raw-native native-loc)) +(os/execute ["cc" ffi/source-loc "-shared" "-o" ffi/loc] :px) +(def module (ffi/native ffi/loc)) -(def int-fn-sig (native-signature :default :int :int :int)) -(def int-fn-pointer (native-lookup module "int_fn")) +(def int-fn-sig (ffi/signature :default :int :int :int)) +(def int-fn-pointer (ffi/lookup module "int_fn")) (defn int-fn [x y] - (native-call int-fn-pointer int-fn-sig x y)) + (ffi/call int-fn-pointer int-fn-sig x y)) -(def double-fn-sig (native-signature :default :double :double :double :double)) -(def double-fn-pointer (native-lookup module "double_fn")) +(def double-fn-sig (ffi/signature :default :double :double :double :double)) +(def double-fn-pointer (ffi/lookup module "double_fn")) (defn double-fn [x y z] - (native-call double-fn-pointer double-fn-sig x y z)) + (ffi/call double-fn-pointer double-fn-sig x y z)) -(def double-many-sig (native-signature :default :double :double :double :double :double :double :double)) -(def double-many-pointer (native-lookup module "double_many")) +(def double-many-sig (ffi/signature :default :double :double :double :double :double :double :double)) +(def double-many-pointer (ffi/lookup module "double_many")) (defn double-many [x y z w a b] - (native-call double-many-pointer double-many-sig x y z w a b)) + (ffi/call double-many-pointer double-many-sig x y z w a b)) -(def double-lots-sig (native-signature :default :double +(def double-lots-sig (ffi/signature :default :double :double :double :double :double :double :double :double :double :double :double)) -(def double-lots-pointer (native-lookup module "double_lots")) +(def double-lots-pointer (ffi/lookup module "double_lots")) (defn double-lots [a b c d e f g h i j] - (native-call double-lots-pointer double-lots-sig a b c d e f g h i j)) + (ffi/call double-lots-pointer double-lots-sig a b c d e f g h i j)) -(def float-fn-sig (native-signature :default :double :float :float :float)) -(def float-fn-pointer (native-lookup module "float_fn")) +(def float-fn-sig (ffi/signature :default :double :float :float :float)) +(def float-fn-pointer (ffi/lookup module "float_fn")) (defn float-fn [x y z] - (native-call float-fn-pointer float-fn-sig x y z)) + (ffi/call float-fn-pointer float-fn-sig x y z)) -(def intint-fn-sig (native-signature :default :int :double [:int :int])) -(def intint-fn-pointer (native-lookup module "intint_fn")) +(def intint-fn-sig (ffi/signature :default :int :double [:int :int])) +(def intint-fn-pointer (ffi/lookup module "intint_fn")) (defn intint-fn [x ii] - (native-call intint-fn-pointer intint-fn-sig x ii)) + (ffi/call intint-fn-pointer intint-fn-sig x ii)) -(def return-struct-sig (native-signature :default [:int :int] :int)) -(def return-struct-pointer (native-lookup module "return_struct")) +(def return-struct-sig (ffi/signature :default [:int :int] :int)) +(def return-struct-pointer (ffi/lookup module "return_struct")) (defn return-struct-fn [i] - (native-call return-struct-pointer return-struct-sig i)) + (ffi/call return-struct-pointer return-struct-sig i)) -(def intintint (native-struct :int :int :int)) -(def intintint-fn-sig (native-signature :default :int :double intintint)) -(def intintint-fn-pointer (native-lookup module "intintint_fn")) +(def intintint (ffi/struct :int :int :int)) +(def intintint-fn-sig (ffi/signature :default :int :double intintint)) +(def intintint-fn-pointer (ffi/lookup module "intintint_fn")) (defn intintint-fn [x iii] - (native-call intintint-fn-pointer intintint-fn-sig x iii)) + (ffi/call intintint-fn-pointer intintint-fn-sig x iii)) -(def big (native-struct :s64 :s64 :s64)) -(def struct-big-fn-sig (native-signature :default big :int :double)) -(def struct-big-fn-pointer (native-lookup module "struct_big")) +(def big (ffi/struct :s64 :s64 :s64)) +(def struct-big-fn-sig (ffi/signature :default big :int :double)) +(def struct-big-fn-pointer (ffi/lookup module "struct_big")) (defn struct-big-fn [i d] - (native-call struct-big-fn-pointer struct-big-fn-sig i d)) + (ffi/call struct-big-fn-pointer struct-big-fn-sig i d)) -(def void-fn-pointer (native-lookup module "void_fn")) -(def void-fn-sig (native-signature :default :void)) +(def void-fn-pointer (ffi/lookup module "void_fn")) +(def void-fn-sig (ffi/signature :default :void)) (defn void-fn [] - (native-call void-fn-pointer void-fn-sig)) + (ffi/call void-fn-pointer void-fn-sig)) # # Call functions @@ -95,8 +95,8 @@ (defn check-round-trip [t value] - (def buf (native-write t value)) - (def same-value (native-read t buf)) + (def buf (ffi/write t value)) + (def same-value (ffi/read t buf)) (assert (deep= value same-value) (string/format "round trip %j (got %j)" value same-value))) @@ -121,7 +121,7 @@ (check-round-trip :s32 0) (check-round-trip :s32 -1234567) -(def s (native-struct :s8 :s8 :s8 :float)) +(def s (ffi/struct :s8 :s8 :s8 :float)) (check-round-trip s [1 3 5 123.5]) (check-round-trip s [-1 -3 -5 -123.5]) diff --git a/src/core/corelib.c b/src/core/corelib.c index 322b1411..dcde0c45 100644 --- a/src/core/corelib.c +++ b/src/core/corelib.c @@ -42,64 +42,6 @@ extern size_t janet_core_image_size; #define JDOC(x) NULL #endif -/* Use LoadLibrary on windows or dlopen on posix to load dynamic libaries - * with native code. */ -#if defined(JANET_NO_DYNAMIC_MODULES) -typedef int Clib; -#define load_clib(name) ((void) name, 0) -#define symbol_clib(lib, sym) ((void) lib, (void) sym, NULL) -#define error_clib() "dynamic libraries not supported" -#define free_clib(c) ((void) (c), 0) -#elif defined(JANET_WINDOWS) -#include -typedef HINSTANCE Clib; -#define load_clib(name) LoadLibrary((name)) -#define free_clib(c) FreeLibrary((c)) -#define symbol_clib(lib, sym) GetProcAddress((lib), (sym)) -static char error_clib_buf[256]; -static char *error_clib(void) { - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - error_clib_buf, sizeof(error_clib_buf), NULL); - error_clib_buf[strlen(error_clib_buf) - 1] = '\0'; - return error_clib_buf; -} -#else -#include -typedef void *Clib; -#define load_clib(name) dlopen((name), RTLD_NOW) -#define free_clib(lib) dlclose((lib)) -#define symbol_clib(lib, sym) dlsym((lib), (sym)) -#define error_clib() dlerror() -#endif - -static char *get_processed_name(const char *name) { - if (name[0] == '.') return (char *) name; - const char *c; - for (c = name; *c; c++) { - if (*c == '/') return (char *) name; - } - size_t l = (size_t)(c - name); - char *ret = janet_malloc(l + 3); - if (NULL == ret) { - JANET_OUT_OF_MEMORY; - } - ret[0] = '.'; - ret[1] = '/'; - memcpy(ret + 2, name, l + 1); - return ret; -} - -typedef struct { - Clib clib; - int closed; -} JanetAbstractNative; - -static const JanetAbstractType janet_native_type = { - "core/native", - JANET_ATEND_NAME -}; - JanetModule janet_native(const char *name, const uint8_t **error) { char *processed_name = get_processed_name(name); Clib lib = load_clib(processed_name); @@ -350,48 +292,6 @@ JANET_CORE_FN(janet_core_native, return janet_wrap_table(env); } -JANET_CORE_FN(janet_core_raw_native, - "(raw-native path)", - "Load a shared object or dll from the given path, and do not extract" - " or run any code from it. This is different than `native`, which will " - "run initialization code to get a module table. Returns a `core/native`.") { - janet_fixarity(argc, 1); - const char *path = janet_getcstring(argv, 0); - char *processed_name = get_processed_name(path); - Clib lib = load_clib(processed_name); - if (path != processed_name) janet_free(processed_name); - if (!lib) janet_panic(error_clib()); - JanetAbstractNative *anative = janet_abstract(&janet_native_type, sizeof(JanetAbstractNative)); - anative->clib = lib; - anative->closed = 0; - return janet_wrap_abstract(anative); -} - -JANET_CORE_FN(janet_core_native_lookup, - "(native-lookup native symbol-name)", - "Lookup a symbol from a native object. All symbol lookups will return a raw pointer " - "if the symbol is found, else nil.") { - janet_fixarity(argc, 2); - JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type); - const char *sym = janet_getcstring(argv, 1); - if (anative->closed) janet_panic("native object already closed"); - void *value = symbol_clib(anative->clib, sym); - if (NULL == value) return janet_wrap_nil(); - return janet_wrap_pointer(value); -} - -JANET_CORE_FN(janet_core_native_close, - "(native-close native)", - "Free a native object. Dereferencing pointers to symbols in the object will have undefined " - "behavior after freeing.") { - janet_fixarity(argc, 1); - JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type); - if (anative->closed) janet_panic("native object already closed"); - anative->closed = 1; - free_clib(anative->clib); - return janet_wrap_nil(); -} - JANET_CORE_FN(janet_core_describe, "(describe x)", "Returns a string that is a human-readable description of `x`. " @@ -1011,9 +911,6 @@ static const uint32_t cmp_asm[] = { static void janet_load_libs(JanetTable *env) { JanetRegExt corelib_cfuns[] = { JANET_CORE_REG("native", janet_core_native), - JANET_CORE_REG("raw-native", janet_core_raw_native), - JANET_CORE_REG("native-lookup", janet_core_native_lookup), - JANET_CORE_REG("native-close", janet_core_native_close), JANET_CORE_REG("describe", janet_core_describe), JANET_CORE_REG("string", janet_core_string), JANET_CORE_REG("symbol", janet_core_symbol), diff --git a/src/core/ffi.c b/src/core/ffi.c index 0e814733..76f0ab88 100644 --- a/src/core/ffi.c +++ b/src/core/ffi.c @@ -194,6 +194,16 @@ static const JanetAbstractType janet_struct_type = { JANET_ATEND_GCMARK }; +typedef struct { + Clib clib; + int closed; +} JanetAbstractNative; + +static const JanetAbstractType janet_native_type = { + "core/ffi-native", + JANET_ATEND_NAME +}; + static JanetFFIType prim_type(JanetFFIPrimType pt) { JanetFFIType t; t.prim = pt; @@ -352,7 +362,7 @@ static JanetFFIType decode_ffi_type(Janet x) { } JANET_CORE_FN(cfun_ffi_struct, - "(native-struct & types)", + "(ffi/struct & types)", "Create a struct type descriptor that can be used to pass structs into native functions. ") { janet_arity(argc, 1, -1); return janet_wrap_abstract(build_struct_type(argc, argv)); @@ -555,7 +565,7 @@ static JanetFFIWordSpec sysv64_classify(JanetFFIType type) { #endif JANET_CORE_FN(cfun_ffi_signature, - "(native-signature calling-convention ret-type & arg-types)", + "(ffi/signature calling-convention ret-type & arg-types)", "Create a function signature object that can be used to make calls " "with raw function pointers.") { janet_arity(argc, 2, -1); @@ -989,7 +999,7 @@ static Janet janet_ffi_win64(JanetFFISignature *signature, void *function_pointe #endif JANET_CORE_FN(cfun_ffi_call, - "(native-call pointer signature & args)", + "(ffi/call pointer signature & args)", "Call a raw pointer as a function pointer. The function signature specifies " "how Janet values in `args` are converted to native machine types.") { janet_arity(argc, 2, -1); @@ -1011,7 +1021,7 @@ JANET_CORE_FN(cfun_ffi_call, } JANET_CORE_FN(cfun_ffi_buffer_write, - "(native-write ffi-type data &opt buffer)", + "(ffi/write ffi-type data &opt buffer)", "Append a native tyep to a buffer such as it would appear in memory. This can be used " "to pass pointers to structs in the ffi, or send C/C++/native structs over the network " "or to files. Returns a modifed buffer or a new buffer if one is not supplied.") { @@ -1028,9 +1038,9 @@ JANET_CORE_FN(cfun_ffi_buffer_write, JANET_CORE_FN(cfun_ffi_buffer_read, - "(native-read ffi-type bytes &opt offset)", + "(ffi/read ffi-type bytes &opt offset)", "Parse a native struct out of a buffer and convert it to normal Janet data structures. " - "This function is the inverse of `native-write`. `bytes` can also be a raw pointer, although " + "This function is the inverse of `ffi/write`. `bytes` can also be a raw pointer, although " "this is unsafe.") { janet_arity(argc, 2, 3); JanetFFIType type = decode_ffi_type(argv[0]); @@ -1047,13 +1057,13 @@ JANET_CORE_FN(cfun_ffi_buffer_read, } JANET_CORE_FN(cfun_ffi_get_callback_trampoline, - "(native-trampoline cc)", + "(ffi/trampoline cc)", "Get a native function pointer that can be used as a callback and passed to C libraries. " "This callback trampoline has the signature `void trampoline(void *ctx, void *userdata)` in " "the given calling convention. This is the only function signature supported. " "It is up to the programmer to ensure that the `userdata` argument contains a janet function " "the will be called with one argument, `ctx` which is an opaque pointer. This pointer can " - "be further inspected with `native-read`.") { + "be further inspected with `ffi/read`.") { janet_arity(argc, 0, 1); JanetFFICallingConvention cc = JANET_FFI_CC_DEFAULT; if (argc >= 1) cc = decode_ffi_cc(janet_getkeyword(argv, 0)); @@ -1070,14 +1080,59 @@ JANET_CORE_FN(cfun_ffi_get_callback_trampoline, } } +JANET_CORE_FN(janet_core_raw_native, + "(ffi/native path)", + "Load a shared object or dll from the given path, and do not extract" + " or run any code from it. This is different than `native`, which will " + "run initialization code to get a module table. Returns a `core/native`.") { + janet_fixarity(argc, 1); + const char *path = janet_getcstring(argv, 0); + char *processed_name = get_processed_name(path); + Clib lib = load_clib(processed_name); + if (path != processed_name) janet_free(processed_name); + if (!lib) janet_panic(error_clib()); + JanetAbstractNative *anative = janet_abstract(&janet_native_type, sizeof(JanetAbstractNative)); + anative->clib = lib; + anative->closed = 0; + return janet_wrap_abstract(anative); +} + +JANET_CORE_FN(janet_core_native_lookup, + "(ffi/lookup native symbol-name)", + "Lookup a symbol from a native object. All symbol lookups will return a raw pointer " + "if the symbol is found, else nil.") { + janet_fixarity(argc, 2); + JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type); + const char *sym = janet_getcstring(argv, 1); + if (anative->closed) janet_panic("native object already closed"); + void *value = symbol_clib(anative->clib, sym); + if (NULL == value) return janet_wrap_nil(); + return janet_wrap_pointer(value); +} + +JANET_CORE_FN(janet_core_native_close, + "(ffi/close native)", + "Free a native object. Dereferencing pointers to symbols in the object will have undefined " + "behavior after freeing.") { + janet_fixarity(argc, 1); + JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type); + if (anative->closed) janet_panic("native object already closed"); + anative->closed = 1; + free_clib(anative->clib); + return janet_wrap_nil(); +} + void janet_lib_ffi(JanetTable *env) { JanetRegExt ffi_cfuns[] = { - JANET_CORE_REG("native-signature", cfun_ffi_signature), - JANET_CORE_REG("native-call", cfun_ffi_call), - JANET_CORE_REG("native-struct", cfun_ffi_struct), - JANET_CORE_REG("native-write", cfun_ffi_buffer_write), - JANET_CORE_REG("native-read", cfun_ffi_buffer_read), - JANET_CORE_REG("native-trampoline", cfun_ffi_get_callback_trampoline), + JANET_CORE_REG("ffi/native", janet_core_raw_native), + JANET_CORE_REG("ffi/lookup", janet_core_native_lookup), + JANET_CORE_REG("ffi/close", janet_core_native_close), + JANET_CORE_REG("ffi/signature", cfun_ffi_signature), + JANET_CORE_REG("ffi/call", cfun_ffi_call), + JANET_CORE_REG("ffi/struct", cfun_ffi_struct), + JANET_CORE_REG("ffi/write", cfun_ffi_buffer_write), + JANET_CORE_REG("ffi/read", cfun_ffi_buffer_read), + JANET_CORE_REG("ffi/trampoline", cfun_ffi_get_callback_trampoline), JANET_REG_END }; janet_core_cfuns_ext(env, NULL, ffi_cfuns); diff --git a/src/core/util.c b/src/core/util.c index c4cea1dd..7568299f 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -884,6 +884,35 @@ int janet_cryptorand(uint8_t *out, size_t n) { #endif } +/* Dynamic library loading */ + +char *get_processed_name(const char *name) { + if (name[0] == '.') return (char *) name; + const char *c; + for (c = name; *c; c++) { + if (*c == '/') return (char *) name; + } + size_t l = (size_t)(c - name); + char *ret = janet_malloc(l + 3); + if (NULL == ret) { + JANET_OUT_OF_MEMORY; + } + ret[0] = '.'; + ret[1] = '/'; + memcpy(ret + 2, name, l + 1); + return ret; +} + +#if defined(JANET_WINDOWS) +static char error_clib_buf[256]; +char *error_clib(void) { + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + error_clib_buf, sizeof(error_clib_buf), NULL); + error_clib_buf[strlen(error_clib_buf) - 1] = '\0'; + return error_clib_buf; +} +#endif /* Alloc function macro fills */ void *(janet_malloc)(size_t size) { diff --git a/src/core/util.h b/src/core/util.h index 012a0677..3ac3e449 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -127,6 +127,31 @@ int janet_gettime(struct timespec *spec); #define strdup(x) _strdup(x) #endif +/* Use LoadLibrary on windows or dlopen on posix to load dynamic libaries + * with native code. */ +#if defined(JANET_NO_DYNAMIC_MODULES) +typedef int Clib; +#define load_clib(name) ((void) name, 0) +#define symbol_clib(lib, sym) ((void) lib, (void) sym, NULL) +#define error_clib() "dynamic libraries not supported" +#define free_clib(c) ((void) (c), 0) +#elif defined(JANET_WINDOWS) +#include +typedef HINSTANCE Clib; +#define load_clib(name) LoadLibrary((name)) +#define free_clib(c) FreeLibrary((c)) +#define symbol_clib(lib, sym) GetProcAddress((lib), (sym)) +char *error_clib(void); +#else +#include +typedef void *Clib; +#define load_clib(name) dlopen((name), RTLD_NOW) +#define free_clib(lib) dlclose((lib)) +#define symbol_clib(lib, sym) dlsym((lib), (sym)) +#define error_clib() dlerror() +#endif +char *get_processed_name(const char *name); + #define RETRY_EINTR(RC, CALL) do { (RC) = CALL; } while((RC) < 0 && errno == EINTR) /* Initialize builtin libraries */