summaryrefslogtreecommitdiff
path: root/source/luametatex/source/luaoptional/lmtforeign.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/luametatex/source/luaoptional/lmtforeign.c')
-rw-r--r--source/luametatex/source/luaoptional/lmtforeign.c1191
1 files changed, 1191 insertions, 0 deletions
diff --git a/source/luametatex/source/luaoptional/lmtforeign.c b/source/luametatex/source/luaoptional/lmtforeign.c
new file mode 100644
index 000000000..da04eca12
--- /dev/null
+++ b/source/luametatex/source/luaoptional/lmtforeign.c
@@ -0,0 +1,1191 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+/*tex
+
+ In \LUATEX\ we provide an ffi library that is derived from luajit but it got orphaned a few years
+ after it showed up. A problem with such libraries is that they need to be maintained actively
+ because platforms and processors evolve. So, having a mechanism like that makes not much sense in
+ a \TEX\ engine. In \LUAMETATEX\ we therefore don't use that library but have a model for delayed
+ loading of optional libraries. A few interfaces are built in (like zint) but we don't ship any
+ library: you get what is installed on the system (which actually is the whole idea behind using
+ most libraries). Delayed loading is implemented by using function pointers and in practice that
+ is fast enough (after all we use them in \TEX\ as well as \METAPOST\ without much penalty).
+
+ But \unknown\ what if a user wants to use a library and doesn't want to write an interface? We
+ kind of end up again with something ffi. When looking around I ran into an \LUA\ module that
+ was made a few years ago (for 5.1 and 5.2) called 'alien' that also binds to libffi. But that
+ library looks a bit more complex than we need. For instance, mechanisms for callbacks use libffi
+ calls than need some compile time properties that normally come from the h files generated on
+ the system and I don't want to impose on users to also install and compile a whole chain of
+ dependencies. We could (and maybe some day will if users really need it) provide callbacks
+ too but then we also need to keep some system specific 'constants' in sync. We then probaly
+ also need to provide libraries at the contextgarden.
+
+ The basics of an interface as used in this module (running over specs) is given in the \LUA\
+ manual\ and we also use it for callbacks in \LUATEX\ and therefore \LUAMETATEX. There we use
+ varargs but here specification if converted into a recipe that libffi will bind to a function.
+ Some code below looks like the code in alien (after all I took a good look at it). The ffi
+ part is filtered from the ffi.h.in file. The interfaces are sort of what we do with other
+ libraries.
+
+ For the record: when testing, I just used a version of libffi that came with inkscape and I
+ saw several other instances on my system disk. A quick test with loading showed that it is no
+ problem in the ecosystem that we use in \TEX. The buildot on the contextgarden generates
+ binaries for several platforms and one can observe that some platforms (like bsd) are not
+ that downward compatible, so there we have multiple versions. This also means that finding
+ matching libraries can be an issue. In \CONTEXT\ we never depend on external or evolving
+ libraries so it's a user's choice in the end.
+
+ The \LUATEX\ build script and infrastructure are more complex than the \LUAMETATEX\ ones and
+ it's often the libraries that make for the - sometimes incompatible - changes which in turn
+ demands adaptation of the scripts etc in the build farm. We try to avoid that as much as
+ possible but if we ever decide to also provide libraries that match the binaries, but it
+ remains a depencenie that you want to avoid in long running projects.
+
+ Comment: I might look into a vararg variant some day, just for fun. Actually, this module is
+ mostly about the fun, so it will take some time to evolve.
+
+*/
+
+/*tex
+ Because it is an optional module, we use the optional interface.
+*/
+
+# include "luametatex.h"
+# include "lmtoptional.h"
+
+/*tex
+ We need to define a few ffi datatypes and function prototypes. We need to keep an eye on how
+ the library evolves but I assume the api is rather stable. We don't want to depend on a system
+ specific header file.
+*/
+
+typedef struct ffi_type {
+ size_t size;
+ unsigned short alignment;
+ unsigned short type;
+ struct ffi_type **elements;
+} ffi_type;
+
+typedef enum ffi_types {
+ ffi_void_type,
+ ffi_int_type,
+ ffi_float_type,
+ ffi_double_type,
+ ffi_longdouble_type,
+ ffi_uint8_type,
+ ffi_int8_type,
+ ffi_uint16_type,
+ ffi_int16_type,
+ ffi_uint32_type,
+ ffi_int32_type,
+ ffi_uint64_type,
+ ffi_int64_type,
+ ffi_struct_type,
+ ffi_pointer_type,
+ ffi_complex_type, /* unsupported */
+ ffi_last_type,
+} ffi_types;
+
+/*
+ The libffi api document says that the size and alignment should be zero but somehow we do crash
+ when we set the size to some value. Only size_t is now system dependent (e.g. on 32 bit windows
+ it's different).
+
+ We only need to support the architectures and operating systems that the ecosystem runs on so we
+ check a bit differently. We just don't want all these dependencies in the source tree. We have:
+
+ -- 32 64 bit intel linux | freebsd | openbsd
+ -- 64 bit intel osx
+ -- 32 64 bit intel windows mingw
+ -- 64 bit windows msvc
+ -- 64 bit arm msvc
+ -- 32 64 bit arm (rpi etc)
+ -- 64 bit arm darwin
+
+*/
+
+# if PTRDIFF_MAX == 65535
+# define ffi_size_t_type ffi_uint16_type
+# elif PTRDIFF_MAX == 2147483647
+# define ffi_size_t_type ffi_uint32_type
+# elif PTRDIFF_MAX == 9223372036854775807
+# define ffi_size_t_type ffi_uint64_type
+# elif defined(_WIN64)
+# define ffi_size_t_type ffi_uint64_type
+# else
+# define ffi_size_t_type ffi_uint32_type
+# endif
+
+/*tex This comes from the libffi.h* file: */
+
+typedef enum ffi_abi {
+
+# if defined (X86_WIN64)
+
+ FFI_FIRST_ABI = 0,
+ FFI_WIN64, /* sizeof(long double) == 8 - microsoft compilers */
+ FFI_GNUW64, /* sizeof(long double) == 16 - GNU compilers */
+ FFI_LAST_ABI,
+# ifdef __GNUC__
+ FFI_DEFAULT_ABI = FFI_GNUW64
+# else
+ FFI_DEFAULT_ABI = FFI_WIN64
+# endif
+
+# elif defined (X86_64) || (defined (__x86_64__) && defined (X86_DARWIN))
+
+ FFI_FIRST_ABI = 1,
+ FFI_UNIX64,
+ FFI_WIN64,
+ FFI_EFI64 = FFI_WIN64,
+ FFI_GNUW64,
+ FFI_LAST_ABI,
+ FFI_DEFAULT_ABI = FFI_UNIX64
+
+# elif defined (X86_WIN32)
+
+ FFI_FIRST_ABI = 0,
+ FFI_SYSV = 1,
+ FFI_STDCALL = 2,
+ FFI_THISCALL = 3,
+ FFI_FASTCALL = 4,
+ FFI_MS_CDECL = 5,
+ FFI_PASCAL = 6,
+ FFI_REGISTER = 7,
+ FFI_LAST_ABI,
+ FFI_DEFAULT_ABI = FFI_MS_CDECL
+
+# else
+
+ FFI_FIRST_ABI = 0,
+ FFI_SYSV = 1,
+ FFI_THISCALL = 3,
+ FFI_FASTCALL = 4,
+ FFI_STDCALL = 5,
+ FFI_PASCAL = 6,
+ FFI_REGISTER = 7,
+ FFI_MS_CDECL = 8,
+ FFI_LAST_ABI,
+ FFI_DEFAULT_ABI = FFI_SYSV
+
+#endif
+
+} ffi_abi;
+
+typedef enum ffi_status {
+ FFI_OK,
+ FFI_BAD_TYPEDEF,
+ FFI_BAD_ABI
+} ffi_status;
+
+typedef struct {
+ ffi_abi abi;
+ unsigned nargs;
+ ffi_type **arg_types;
+ ffi_type *rtype;
+ unsigned bytes;
+ unsigned flags;
+} ffi_cif;
+
+typedef struct foreign_state_info {
+
+ int initialized;
+ int padding;
+
+ ffi_status (*ffi_prep_cif) (
+ ffi_cif *cif,
+ ffi_abi abi,
+ unsigned int nargs,
+ ffi_type *rtype,
+ ffi_type **atypes
+ );
+
+ void (*ffi_call) (
+ ffi_cif *cif,
+ void (*fn) (void),
+ void *rvalue,
+ void **avalue
+ );
+
+ ffi_type ffi_type_void;
+ ffi_type ffi_type_uint8;
+ ffi_type ffi_type_int8;
+ ffi_type ffi_type_uint16;
+ ffi_type ffi_type_int16;
+ ffi_type ffi_type_uint32;
+ ffi_type ffi_type_int32;
+ ffi_type ffi_type_uint64;
+ ffi_type ffi_type_int64;
+ ffi_type ffi_type_float;
+ ffi_type ffi_type_double;
+ ffi_type ffi_type_pointer;
+ ffi_type ffi_type_size_t;
+
+
+} foreign_state_info;
+
+static foreign_state_info foreign_state = {
+
+ .initialized = 0,
+ .padding = 0,
+
+ .ffi_prep_cif = NULL,
+ .ffi_call = NULL,
+
+ .ffi_type_void = { .size = 1, .alignment = 0, .type = ffi_void_type, .elements = NULL },
+ .ffi_type_uint8 = { .size = sizeof(uint8_t), .alignment = 0, .type = ffi_uint8_type, .elements = NULL },
+ .ffi_type_int8 = { .size = sizeof(int8_t), .alignment = 0, .type = ffi_int8_type, .elements = NULL },
+ .ffi_type_uint16 = { .size = sizeof(uint16_t), .alignment = 0, .type = ffi_uint16_type, .elements = NULL },
+ .ffi_type_int16 = { .size = sizeof(int16_t), .alignment = 0, .type = ffi_int16_type, .elements = NULL },
+ .ffi_type_uint32 = { .size = sizeof(uint32_t), .alignment = 0, .type = ffi_uint32_type, .elements = NULL },
+ .ffi_type_int32 = { .size = sizeof(int32_t), .alignment = 0, .type = ffi_int32_type, .elements = NULL },
+ .ffi_type_uint64 = { .size = sizeof(uint64_t), .alignment = 0, .type = ffi_uint64_type, .elements = NULL },
+ .ffi_type_int64 = { .size = sizeof(int64_t), .alignment = 0, .type = ffi_int64_type, .elements = NULL },
+ .ffi_type_float = { .size = sizeof(float), .alignment = 0, .type = ffi_float_type, .elements = NULL },
+ .ffi_type_double = { .size = sizeof(double), .alignment = 0, .type = ffi_double_type, .elements = NULL },
+ .ffi_type_pointer = { .size = sizeof(void *), .alignment = 0, .type = ffi_pointer_type, .elements = NULL },
+ .ffi_type_size_t = { .size = sizeof(size_t), .alignment = 0, .type = ffi_size_t_type, .elements = NULL },
+
+};
+
+/*tex
+ We use similar names as in other modules:
+*/
+
+#define FOREIGN_METATABLE_LIBRARY "foreign.library"
+#define FOREIGN_METATABLE_FUNCTION "foreign.function"
+#define FOREIGN_METATABLE_POINTER "foreign.pointer"
+
+/*tex
+ First I had some info structure as we have elsewhere but in the end not much was needed so we
+ now have some simple arrays instead.
+*/
+
+typedef enum foreign_type {
+ foreign_type_void,
+ foreign_type_byte, foreign_type_char,
+ foreign_type_short, foreign_type_ushort,
+ foreign_type_int, foreign_type_uint,
+ foreign_type_long, foreign_type_ulong,
+ foreign_type_longlong, foreign_type_ulonglong,
+ foreign_type_float, foreign_type_double,
+ foreign_type_size_t,
+ foreign_type_string,
+ foreign_type_pointer,
+ foreign_type_reference_to_char,
+ foreign_type_reference_to_int,
+ foreign_type_reference_to_uint,
+ foreign_type_reference_to_double,
+ foreign_type_max,
+} foreign_type;
+
+# define foreign_first_value_return_type foreign_type_void
+# define foreign_last_value_return_type foreign_type_pointer
+
+static const char *foreign_typenames[] = {
+ "void",
+ /* basic types */
+ "byte", "char",
+ "short", "ushort",
+ "int", "uint",
+ "long", "ulong",
+ "longlong", "ulonglong",
+ "float", "double",
+ "size_t",
+ "string",
+ "pointer",
+ "reference to char",
+ "reference to int",
+ "reference to uint",
+ "reference to double",
+ NULL,
+};
+
+static ffi_type *foreign_typecodes[] = {
+ &foreign_state.ffi_type_void,
+ &foreign_state.ffi_type_int8, &foreign_state.ffi_type_uint8,
+ &foreign_state.ffi_type_int16, &foreign_state.ffi_type_uint16,
+ &foreign_state.ffi_type_int32, &foreign_state.ffi_type_uint32,
+ &foreign_state.ffi_type_int64, &foreign_state.ffi_type_uint64,
+ &foreign_state.ffi_type_int64, &foreign_state.ffi_type_uint64,
+ &foreign_state.ffi_type_float, &foreign_state.ffi_type_double,
+ &foreign_state.ffi_type_size_t,
+ &foreign_state.ffi_type_pointer, /* string */
+ &foreign_state.ffi_type_pointer, /* pointer */
+ &foreign_state.ffi_type_pointer,
+ &foreign_state.ffi_type_pointer,
+ &foreign_state.ffi_type_pointer,
+ &foreign_state.ffi_type_pointer,
+ NULL,
+};
+
+typedef struct foreign_library {
+ void *library;
+ char *name;
+ ffi_abi abi;
+ int padding;
+} foreign_library;
+
+typedef enum foreign_states {
+ foreign_state_initialized,
+ foreign_state_registered,
+} foreign_states;
+
+typedef struct foreign_function {
+ foreign_library *library;
+ char *name;
+ void *function;
+ foreign_type result_type;
+ int nofarguments;
+ foreign_type *arguments;
+ ffi_type *ffi_result_type;
+ ffi_type **ffi_arguments;
+ ffi_cif cif;
+ ffi_abi abi;
+} foreign_function;
+
+typedef enum foreign_pointer_types {
+ foreign_pointer_state_regular,
+ foreign_pointer_state_buffer,
+} foreign_pointer_types;
+
+typedef struct foreign_pointer {
+ void *ptr;
+ int state;
+ int padding;
+} foreign_pointer;
+
+/*tex
+ We use the already defined helpers instead of setting up loading here. That way we're also
+ consistent in lookups. You need to pass the resolved name (so at the \LUA\ end we wrap the
+ loader to use the library resolver. So no check for loaders here etc.
+*/
+
+
+#ifdef WIN32
+# ifndef WINDOWS
+# define WINDOWS
+# endif
+#endif
+
+#if !defined(WINDOWS) || defined(_WIN64)
+#define FFI_STDCALL FFI_DEFAULT_ABI
+#endif
+
+#ifdef __APPLE__
+#define FFI_SYSV FFI_DEFAULT_ABI
+#endif
+
+typedef struct foreign_abi_entry {
+ const char *name;
+ ffi_abi abi;
+} foreign_abi_entry;
+
+# define foreign_abi_max 3
+
+static foreign_abi_entry foreign_abi_map[] = {
+ { .name = "default", .abi = FFI_DEFAULT_ABI },
+ { .name = "cdecl", .abi = FFI_SYSV },
+ { .name = "stdcall", .abi = FFI_STDCALL },
+};
+
+typedef enum foreign_library_uv_slots {
+ library_name_uv = 1,
+ library_registry_uv = 2,
+
+} foreign_library_uv_slots;
+
+typedef enum foreign_function_uv_slots {
+ function_name_uv = 1,
+ function_finalizer_uv = 2,
+} foreign_function_uv_slots;
+
+static int foreignlib_not_yet_initialized(lua_State *L)
+{
+ return luaL_error(L, "foreign: not yet initialized");
+}
+
+static int foreignlib_allocation_error(lua_State *L)
+{
+ return luaL_error(L, "foreign: allocation error");
+}
+
+static foreign_library *foreignlib_library_check(lua_State *L, int index)
+{
+ return (foreign_library *) luaL_checkudata(L, index, FOREIGN_METATABLE_LIBRARY);
+}
+
+static foreign_function *foreignlib_function_check(lua_State *L, int index)
+{
+ return (foreign_function *) luaL_checkudata(L, index, FOREIGN_METATABLE_FUNCTION);
+}
+
+static foreign_pointer *foreignlib_pointer_check(lua_State *L, int index)
+{
+ return (foreign_pointer *) luaL_checkudata(L, index, FOREIGN_METATABLE_POINTER);
+}
+
+static int foreignlib_library_tostring(lua_State *L)
+{
+ foreign_library *library = foreignlib_library_check(L, 1);
+ if (library) {
+ lua_pushfstring(L, "<foreign.library %s>", library->name ? library->name : "unknown");
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int foreignlib_function_tostring(lua_State *L)
+{
+ foreign_function *function = foreignlib_function_check(L, 1);
+ if (function) {
+ foreign_library *library = function->library;
+ if (library) {
+ lua_pushfstring(L, "<foreign.function %s in library %s>", function->name ? function->name : "unknown", ((library && library->name) ? library->name : "unknown"));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int foreignlib_pointer_tostring(lua_State *L)
+{
+ foreign_pointer *pointer = foreignlib_pointer_check(L, 1);
+ if (! pointer) {
+ return 0;
+ } else {
+ lua_pushfstring(L, pointer->state == foreign_pointer_state_buffer ? "<foreign.buffer %p>" : "<foreign.pointer %p>", pointer->ptr);
+ return 1;
+ }
+}
+
+static int foreignlib_pointer_gc(lua_State *L)
+{
+ foreign_pointer *pointer = foreignlib_pointer_check(L, 1);
+ if (pointer->state == foreign_pointer_state_buffer) {
+ lmt_memory_free(pointer->ptr);
+ /* not needed: */
+ pointer->state = foreign_pointer_state_regular;
+ pointer->ptr = NULL;
+ }
+ return 0;
+}
+
+/*tex
+ We accept numbers as well as names (just in case go symboloc as we do with other modules).
+*/
+
+static int foreignlib_type_found(lua_State *L, int slot, int dflt)
+{
+ switch (lua_type(L, slot)) {
+ case LUA_TNUMBER:
+ {
+ int i = (int) lua_tointeger(L, slot);
+ if (i >= 0 && i < foreign_type_max) {
+ return i;
+ }
+ break;
+ }
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, slot);
+ for (int i = 0; i < foreign_type_max; i++) {
+ if (strcmp(s, foreign_typenames[i]) == 0) {
+ return i;
+ }
+ }
+ break;
+ }
+ }
+ return dflt;
+}
+
+static int foreignlib_abi_found(lua_State *L, int slot, int dflt)
+{
+ switch (lua_type(L, slot)) {
+ case LUA_TNUMBER:
+ {
+ int i = (int) lua_tointeger(L, slot);
+ if (i >= 0 && i < foreign_abi_max) {
+ return foreign_abi_map[i].abi;
+ }
+ break;
+ }
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, slot);
+ for (int i = 0; i < foreign_abi_max; i++) {
+ if (strcmp(s, foreign_abi_map[i].name) == 0) {
+ return foreign_abi_map[i].abi;
+ }
+ }
+ break;
+ }
+ }
+ return dflt;
+}
+
+static int foreignlib_types(lua_State* L)
+{
+ lua_createtable(L, foreign_type_max, 0);
+ for (lua_Integer i = 0; i < foreign_type_max; i++) {
+ lua_pushstring(L, foreign_typenames[i]);
+ lua_rawseti(L, -2, i + 1);
+ }
+ return 1;
+}
+
+static int foreignlib_abivalues(lua_State* L)
+{
+ lua_createtable(L, 0, foreign_abi_max);
+ for (lua_Integer i = 0; i < foreign_abi_max; i++) {
+ lua_pushstring(L, foreign_abi_map[i].name);
+ lua_pushinteger(L, foreign_abi_map[i].abi);
+ lua_rawset(L, -3);
+ }
+ return 1;
+}
+
+static int foreignlib_load(lua_State *L)
+{
+ if (foreign_state.initialized) {
+ size_t len;
+ const char *libraryname = lua_tolstring(L, 1, &len);
+ if (libraryname && len > 0) {
+ foreign_library *library = (foreign_library *) lua_newuserdatauv(L, sizeof(foreign_library), 2);
+ if (library) {
+ void *libraryreference = lmt_library_open_indeed(libraryname);
+ if (libraryreference) {
+ library->name = lmt_memory_malloc(sizeof(char) * (len + 1));
+ if (library->name) {
+ strcpy(library->name, libraryname);
+ library->library = libraryreference;
+ library->abi = foreignlib_abi_found(L, 2, FFI_DEFAULT_ABI);
+ lua_pushvalue(L, 1);
+ lua_setiuservalue(L, -2, library_name_uv);
+ lua_newtable(L);
+ lua_setiuservalue(L, -2, library_registry_uv);
+ luaL_getmetatable(L, FOREIGN_METATABLE_LIBRARY);
+ lua_setmetatable(L, -2);
+ return 1;
+ } else {
+ goto ALLOCATION_ERROR;
+ }
+ } else {
+ return luaL_error(L, "foreign: invalid library");
+ }
+ } else {
+ goto ALLOCATION_ERROR;
+ }
+ } else {
+ return luaL_error(L, "foreign: invalid library name");
+ }
+ ALLOCATION_ERROR:
+ return foreignlib_allocation_error(L);
+ } else {
+ return foreignlib_not_yet_initialized(L);
+ }
+}
+
+static int foreignlib_library_register(lua_State *L)
+{
+ if (foreign_state.initialized) {
+ /* 1:library 2:specification */
+ foreign_library *library = foreignlib_library_check(L, 1);
+ if (lua_type(L, 2) == LUA_TTABLE) {
+ /* 1:library 2:specification -1:name */
+ if (lua_getfield(L, 2, "name") == LUA_TSTRING) {
+ /* 1:library 2:specification -2:name */
+ lua_getiuservalue(L, 1, library_registry_uv);
+ /* 1:library 2:specification -2:name -1:registry */
+ lua_pushvalue(L, -2);
+ /* 1:library 2:specification -3:name -2:registry -1:name */
+ lua_rawget(L, -2);
+ if (lua_type(L, -1) == LUA_TUSERDATA) {
+ /* 1:library 2:specification -3:name -2:registry -1:function */
+ return 1;
+ } else {
+ /* 1:library 2:specification -3:name -2:registry -1:nil */
+ size_t len;
+ const char *functionname = lua_tolstring(L, -3, &len);
+ void *functionreference = lmt_library_find_indeed(library->library, functionname);
+ lua_pop(L, 1);
+ if (functionreference) {
+ /* 1:library 2:specification -2:name -1:registry */
+ foreign_function *function = (foreign_function *) lua_newuserdatauv(L, sizeof(foreign_function), 2);
+ if (function) {
+ /* 1:library 2:specification -3:name -2:registry -1:function */
+ lua_pushvalue(L, -3);
+ /* 1:library 2:specification -4:name -3:registry -2:function -1:name */
+ lua_pushvalue(L, -2);
+ /* 1:library 2:specification -5:name -4:registry -3:function -2:name -1:function */
+ lua_rawset(L, -4);
+ /* 1:library 2:specification -3:name -2:registry -1:function */
+ lua_pushvalue(L, -3);
+ /* 1:library 2:specification -4:name -3:registry -2:function -1:name */
+ lua_setiuservalue(L, -2, function_name_uv);
+ lua_getfield(L, 2, "finalizer");
+ /* 1:library 2:specification -4:name -3:registry -2:function -1:finalizer */
+ lua_setiuservalue(L, -2, function_finalizer_uv);
+ /* 1:library 2:specification -3:name -2:registry -1:function */
+ luaL_getmetatable(L, FOREIGN_METATABLE_FUNCTION);
+ /* 1:library 2:specification -4:name -3:registry -2:function -1:metatable */
+ lua_setmetatable(L, -2);
+ /* 1:library 2:specification -3:name -2:registry -1:function */
+ function->name = (char *) lmt_memory_malloc((size_t) len + 1);
+ if (function->name) {
+ strcpy(function->name, functionname);
+ function->function = functionreference;
+ function->library = library;
+ function->arguments = NULL;
+ function->ffi_arguments = NULL;
+ /* set the return type */
+ lua_getfield(L, 2, "result");
+ function->result_type = foreignlib_type_found(L, -1, foreign_type_void);
+ if (function->result_type >= foreign_first_value_return_type && function->result_type <= foreign_last_value_return_type) {
+ function->ffi_result_type = foreign_typecodes[function->result_type];
+ lua_pop(L, 1);
+ /* set the abi (will move to library) */
+ lua_getfield(L, 2, "abi");
+ function->abi = foreignlib_abi_found(L, -1, library->abi);
+ lua_pop(L, 1);
+ /* set the argument types */
+ switch (lua_getfield(L, 2, "arguments")) {
+ case LUA_TTABLE:
+ {
+ function->nofarguments = (int) lua_rawlen(L, -1);
+ if (function->nofarguments > 0) {
+ function->ffi_arguments = (ffi_type **) lmt_memory_malloc(function->nofarguments * sizeof(ffi_type *));
+ function->arguments = (foreign_type *) lmt_memory_malloc(function->nofarguments * sizeof(foreign_type));
+ if (function->ffi_arguments && function->arguments) {
+ for (lua_Integer i = 0; i < function->nofarguments; i++) {
+ lua_rawgeti(L, -1, i + 1);
+ function->arguments[i] = foreignlib_type_found(L, -1, foreign_type_int); /* maybe issue an error */
+ function->ffi_arguments[i] = foreign_typecodes[function->arguments[i]];
+ lua_pop(L, 1);
+ }
+ } else {
+ goto ALLOCATION_ERROR;
+ }
+ }
+ break;
+ }
+ case LUA_TSTRING:
+ {
+ /* Just one argument, no varag here as it's too ugly otherwise. */
+ function->nofarguments = 1;
+ function->ffi_arguments = (ffi_type **) lmt_memory_malloc(sizeof(ffi_type *));
+ function->arguments = (foreign_type *) lmt_memory_malloc(sizeof(foreign_type));
+ if (function->ffi_arguments && function->arguments) {
+ function->arguments[0] = foreignlib_type_found(L, -1, foreign_type_int); /* maybe issue an error */
+ function->ffi_arguments[0] = foreign_typecodes[function->arguments[0]];
+ } else {
+ goto ALLOCATION_ERROR;
+ }
+ break;
+ }
+ }
+ lua_pop(L, 1);
+ if (foreign_state.ffi_prep_cif(&(function->cif), function->abi, function->nofarguments, function->ffi_result_type, function->ffi_arguments) == FFI_OK) {
+ return 1;
+ } else {
+ return luaL_error(L, "foreign: error in libffi preparation");
+ }
+ } else {
+ return luaL_error(L, "foreign: invalid return type for function %s", functionname);
+ }
+ } else {
+ goto ALLOCATION_ERROR;
+ }
+ }
+ } else {
+ return luaL_error(L, "foreign: unknown function %s", functionname);
+ }
+ }
+ } else {
+ return luaL_error(L, "foreign: function name expected");
+ }
+ } else {
+ return luaL_error(L, "foreign: specification table expected");
+ }
+ ALLOCATION_ERROR:
+ return foreignlib_allocation_error(L);
+ } else {
+ return foreignlib_not_yet_initialized(L);
+ }
+}
+
+static int foreignlib_library_registered(lua_State *L)
+{
+ if (foreign_state.initialized) {
+ foreign_library *library = foreignlib_library_check(L, 1);
+ if (library) {
+ lua_getiuservalue(L, 1, library_registry_uv);
+ if (lua_type(L, 2) == LUA_TSTRING) {
+ lua_pushvalue(L, 2);
+ lua_rawget(L, -2);
+ if (lua_type(L, -1) == LUA_TUSERDATA) {
+ /* 1:library 2:name -3:registry -2:name -1:function */
+ return 1;
+ } else {
+ size_t len;
+ const char *functionname = lua_tolstring(L, 2, &len);
+ return luaL_error(L, "foreign: unknown function %s", functionname);
+ }
+ } else {
+ lua_newtable(L);
+ lua_pushnil(L);
+ while (lua_next(L, -3)) {
+ /* key -2 value -1 | key has to stay*/
+ lua_pushvalue(L, -2);
+ lua_rawset(L, -4);
+ }
+ lua_pop(L, 1);
+ return 1;
+ }
+ }
+ } else {
+ return foreignlib_not_yet_initialized(L);
+ }
+ return 0;
+}
+
+static int foreignlib_library_available(lua_State *L)
+{
+ if (foreign_state.initialized) {
+ foreign_library *library = foreignlib_library_check(L, 1);
+ if (library && lua_type(L, 2) == LUA_TSTRING) {
+ lua_getiuservalue(L, 1, library_registry_uv);
+ lua_pushvalue(L, 2);
+ lua_rawget(L, -2);
+ lua_pushboolean(L, lua_type(L, -1) == LUA_TUSERDATA);
+ return 1;
+ }
+ } else {
+ return foreignlib_not_yet_initialized(L);
+ }
+ return 0;
+}
+
+ /*tex This one is adapted from the alien version (watch the way pointer arguments are returned). */
+
+static int foreignlib_function_call(lua_State *L)
+{
+ int nofreturnvalues = 1; /* we always return at least nil */
+ foreign_function *function = foreignlib_function_check(L, 1);
+ ffi_cif *cif = &(function->cif);
+ int nofarguments = lua_gettop(L) - 1;
+ void **arguments = NULL;
+ int luacall = 0;
+ if (nofarguments != function->nofarguments) {
+ return luaL_error(L, "foreign: function '%s' expects %d arguments", function->name, function->nofarguments);
+ }
+ lua_getiuservalue(L, 1, function_finalizer_uv);
+ luacall = lua_type(L, -1) == LUA_TFUNCTION;
+ if (! luacall) {
+ lua_pop(L, 1);
+ }
+ if (nofarguments > 0) {
+ arguments = lmt_memory_malloc(sizeof(void*) * nofarguments);
+ if (arguments) {
+ for (int i = 0; i < nofarguments; i++) {
+ void *argument = NULL;
+ int slot = i + 2;
+ switch (function->arguments[i]) {
+ case foreign_type_byte : argument = lmt_memory_malloc(sizeof(char)); *((char *) argument) = (signed char) lua_tointeger(L, slot); break;
+ case foreign_type_char : argument = lmt_memory_malloc(sizeof(unsigned char)); *((unsigned char *) argument) = (unsigned char) lua_tointeger(L, slot); break;
+ case foreign_type_short : argument = lmt_memory_malloc(sizeof(short)); *((short *) argument) = (short) lua_tointeger(L, slot); break;
+ case foreign_type_ushort : argument = lmt_memory_malloc(sizeof(unsigned short)); *((unsigned short *) argument) = (unsigned short) lua_tointeger(L, slot); break;
+ case foreign_type_int : argument = lmt_memory_malloc(sizeof(int)); *((int *) argument) = (int) lua_tointeger(L, slot); break;
+ case foreign_type_uint : argument = lmt_memory_malloc(sizeof(unsigned int)); *((unsigned int *) argument) = (unsigned int) lua_tointeger(L, slot); break;
+ case foreign_type_long : argument = lmt_memory_malloc(sizeof(long)); *((long *) argument) = (long) lua_tointeger(L, slot); break;
+ case foreign_type_ulong : argument = lmt_memory_malloc(sizeof(unsigned long)); *((unsigned long *) argument) = (unsigned long) lua_tointeger(L, slot); break;
+ case foreign_type_longlong : argument = lmt_memory_malloc(sizeof(long long)); *((long long *) argument) = (long long) lua_tointeger(L, slot); break;
+ case foreign_type_ulonglong: argument = lmt_memory_malloc(sizeof(unsigned long long)); *((unsigned long long *) argument) = (unsigned long long) lua_tointeger(L, slot); break;
+ case foreign_type_float : argument = lmt_memory_malloc(sizeof(float)); *((float *) argument) = (float) lua_tonumber (L, slot); break;
+ case foreign_type_double : argument = lmt_memory_malloc(sizeof(double)); *((double *) argument) = (double) lua_tonumber (L, slot); break;
+ case foreign_type_size_t : argument = lmt_memory_malloc(sizeof(size_t)); *((size_t *) argument) = (size_t) lua_tointeger(L, slot); break;
+ case foreign_type_string :
+ {
+ argument = lmt_memory_malloc(sizeof(char*));
+ if (argument) {
+ *((const char**) argument) = lua_type(L, slot) == LUA_TSTRING ? lua_tostring(L, slot) : NULL;
+ break;
+ } else {
+ return foreignlib_allocation_error(L);
+ }
+ }
+ case foreign_type_pointer :
+ {
+ /* why not just use the pointers */
+ argument = lmt_memory_malloc(sizeof(char*));
+ if (argument) {
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ {
+ /*tex A packed 5.4 string. */
+ *((const char **) argument) = lua_tostring(L, slot);
+ break;
+ }
+ case LUA_TUSERDATA:
+ {
+ /*tex A constructed array or so. */
+ foreign_pointer *pointer = foreignlib_pointer_check(L, slot);
+ *((void **) argument) = pointer ? pointer->ptr : NULL;
+ break;
+ }
+ default:
+ {
+ *((void **) argument) = NULL;
+ break;
+ }
+ }
+ break;
+ } else {
+ return foreignlib_allocation_error(L);
+ }
+ }
+ case foreign_type_reference_to_char:
+ {
+ argument = lmt_memory_malloc(sizeof(char *));
+ if (argument) {
+ *((char **) argument) = lmt_memory_malloc(sizeof(char));
+ **((char **) argument) = (char) lua_tointeger(L, slot);
+ nofreturnvalues++;
+ break;
+ } else {
+ return foreignlib_allocation_error(L);
+ }
+ }
+ case foreign_type_reference_to_int:
+ {
+ argument = lmt_memory_malloc(sizeof(int *));
+ if (argument) {
+ *((int **) argument) = lmt_memory_malloc(sizeof(int));
+ **((int **) argument) = (int) lua_tointeger(L, slot);
+ nofreturnvalues++;
+ break;
+ } else {
+ return foreignlib_allocation_error(L);
+ }
+ }
+ case foreign_type_reference_to_uint:
+ {
+ argument = lmt_memory_malloc(sizeof(unsigned int *));
+ if (argument) {
+ *((unsigned int **) argument) = lmt_memory_malloc(sizeof(unsigned int));
+ **((unsigned int **) argument) = (unsigned int) lua_tointeger(L, slot);
+ nofreturnvalues++;
+ break;
+ } else {
+ return foreignlib_allocation_error(L);
+ }
+ }
+ case foreign_type_reference_to_double:
+ {
+ argument = lmt_memory_malloc(sizeof(double *));
+ if (argument) {
+ *((double **) argument) = lmt_memory_malloc(sizeof(double));
+ **((double **) argument) = (double) lua_tonumber(L, slot);
+ nofreturnvalues++;
+ break;
+ } else {
+ return foreignlib_allocation_error(L);
+ }
+ }
+ default:
+ return luaL_error(L, "foreign: invalid parameter %d for '%s')", function->arguments[i], function->name);
+ }
+ arguments[i] = argument;
+ }
+ } else {
+ return foreignlib_allocation_error(L);
+ }
+ }
+ switch (function->result_type) {
+ case foreign_type_void : { foreign_state.ffi_call(cif, function->function, NULL, arguments); lua_pushnil (L); break; }
+ case foreign_type_byte : { int r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (signed char) r); break; }
+ case foreign_type_char : { int r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (unsigned char) r); break; }
+ case foreign_type_short : { int r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (short) r); break; }
+ case foreign_type_ushort : { int r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (unsigned short) r); break; }
+ case foreign_type_int : { int r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (int) r); break; }
+ case foreign_type_uint : { int r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (unsigned int) r); break; }
+ case foreign_type_long : { long r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (long) r); break; }
+ case foreign_type_ulong : { unsigned long r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (unsigned long) r); break; }
+ case foreign_type_longlong : { long long r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (lua_Integer) r); break; }
+ case foreign_type_ulonglong: { unsigned long long r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (lua_Integer) r); break; }
+ case foreign_type_float : { float r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushnumber (L, r); break; }
+ case foreign_type_double : { double r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushnumber (L, r); break; }
+ case foreign_type_size_t : { size_t r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, r); break; }
+ case foreign_type_string :
+ {
+ void *str = NULL;
+ foreign_state.ffi_call(cif, function->function, &str, arguments);
+ if (str) {
+ lua_pushstring(L, (char *) str);
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ }
+ case foreign_type_pointer :
+ {
+ void *ptr = NULL;
+ foreign_state.ffi_call(cif, function->function, &ptr, arguments);
+ if (ptr) {
+ foreign_pointer *pointer = (foreign_pointer *) lua_newuserdatauv(L, sizeof(foreign_pointer), 0);
+ luaL_getmetatable(L, FOREIGN_METATABLE_POINTER);
+ lua_setmetatable(L, -2);
+ pointer->ptr = ptr;
+ pointer->state = foreign_pointer_state_regular;
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ }
+ default:
+ return luaL_error(L, "foreign: invalid return value %d for '%s')", function->result_type, function->name);
+ }
+ for (int i = 0; i < nofarguments; i++) {
+ switch (function->arguments[i]) {
+ case foreign_type_reference_to_char : lua_pushinteger(L, **(char **) arguments[i]); break;
+ case foreign_type_reference_to_int : lua_pushinteger(L, **(int **) arguments[i]); break;
+ case foreign_type_reference_to_uint : lua_pushinteger(L, **(unsigned int **) arguments[i]); break;
+ case foreign_type_reference_to_double: lua_pushnumber (L, **(double **) arguments[i]); break;
+ default: break;
+ }
+ lmt_memory_free(arguments[i]); /* not needed for pointers when we just use pointer */
+ }
+ lmt_memory_free(arguments);
+ if (luacall) {
+ lua_call(L, nofreturnvalues, 1);
+ return 1;
+ } else {
+ return nofreturnvalues;
+ }
+}
+
+static int foreignlib_library_gc(lua_State *L)
+{
+ foreign_library *library = foreignlib_library_check(L, 1);
+ if (library->library) {
+ lmt_library_open_indeed(library->library);
+ lmt_memory_free(library->name);
+ }
+ return 0;
+}
+
+static int foreignlib_function_gc(lua_State *L)
+{
+ foreign_function *function = foreignlib_function_check(L, 1);
+ lmt_memory_free(function->name);
+ lmt_memory_free(function->arguments);
+ lmt_memory_free(function->ffi_arguments);
+ return 0;
+}
+
+/* */
+
+static int foreignlib_newbuffer(lua_State *L)
+{
+ size_t size = lua_tointeger(L, 1);
+ foreign_pointer *pointer = (foreign_pointer *) lua_newuserdatauv(L, sizeof(foreign_pointer), 0);
+ luaL_getmetatable(L, FOREIGN_METATABLE_POINTER);
+ lua_setmetatable(L, -2);
+ pointer->ptr = lmt_memory_malloc(size);
+ pointer->state = foreign_pointer_state_buffer;
+ return 1;
+}
+
+static int foreignlib_getbuffer(lua_State *L)
+{
+ foreign_pointer *pointer = foreignlib_pointer_check(L, 1);
+ if (pointer && pointer->state == foreign_pointer_state_buffer && pointer->ptr) {
+ size_t size = lua_tointeger(L, 2);
+ if (size > 0) {
+ lua_pushlstring(L, pointer->ptr, size);
+ } else {
+ lua_pushnil(L);
+ }
+ lmt_memory_free(pointer->ptr);
+ pointer->ptr = NULL;
+ pointer->state = foreign_pointer_state_regular;
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* pointer to array of pointers */
+
+static int foreignlib_totable(lua_State *L)
+{
+ foreign_pointer *pointer = foreignlib_pointer_check(L, 1);
+ if (pointer) {
+ void *ptr = pointer->ptr;
+ if (ptr) {
+ int resulttype = foreignlib_type_found(L, 2, foreign_type_void);
+ int size = (int) luaL_optinteger(L, 3, -1);
+ lua_createtable(L, size > 0 ? size : 0, 0);
+ switch (resulttype) {
+ case foreign_type_void:
+ return 0;
+ case foreign_type_string:
+ {
+ void **ptr = pointer->ptr;
+ if (ptr) {
+ lua_Integer r = 0;
+ lua_newtable(L);
+ if (size < 0) {
+ while (ptr[r]) {
+ lua_pushstring(L, ptr[r]);
+ lua_rawseti(L, -2, ++r);
+ }
+ } else {
+ for (lua_Integer i = 0; i < size; i++) {
+ lua_pushstring(L, ptr[i]);
+ lua_rawseti(L, -2, ++r);
+ }
+ }
+ }
+ break;
+ }
+ case foreign_type_byte : { signed char *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; }
+ case foreign_type_char : { unsigned char *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; }
+ case foreign_type_short : { short *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; }
+ case foreign_type_ushort : { unsigned short *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; }
+ case foreign_type_int : { int *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; }
+ case foreign_type_uint : { unsigned int *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; }
+ case foreign_type_long : { long *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; }
+ case foreign_type_ulong : { unsigned long *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; }
+ case foreign_type_longlong : { long long *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; }
+ case foreign_type_ulonglong: { unsigned long long *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; }
+ case foreign_type_float : { float *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushnumber (L, (lua_Number) p[i]); lua_rawseti(L, -2, i + 1); } break; }
+ case foreign_type_double : { double *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushnumber (L, (lua_Number) p[i]); lua_rawseti(L, -2, i + 1); } break; }
+ case foreign_type_size_t : { size_t *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; }
+ }
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/*tex
+
+ Here we prepare some metatables. Todo: newindex. When we don't use a metatable for the
+ library we can have more keys, like list and so.
+
+ local library = foreign.load("whatever","abi")
+
+ library:register { name = ..., result = ..., arguments = { ... }, abi = ... )
+ library:registered ("name")
+ library:registered ()
+ library:available ("name")
+
+ foreign.load()
+ foreign.abivalues()
+ foreign.types()
+
+ todo: ckeck what this abi does: probably better at lib loading time than per function
+
+*/
+
+static struct luaL_Reg foreignlib_function_methods[] = {
+ { "register", foreignlib_library_register },
+ { "registered", foreignlib_library_registered },
+ { "available", foreignlib_library_available },
+ { NULL, NULL },
+};
+
+static void foreignlib_populate(lua_State *L)
+{
+ luaL_newmetatable(L, FOREIGN_METATABLE_LIBRARY);
+ lua_pushliteral(L, "__gc");
+ lua_pushcfunction(L, foreignlib_library_gc);
+ lua_settable(L, -3);
+ lua_pushliteral(L, "__tostring");
+ lua_pushcfunction(L, foreignlib_library_tostring);
+ lua_settable(L, -3);
+ lua_pushliteral(L, "__index");
+ lua_newtable(L);
+ for (int i = 0; foreignlib_function_methods[i].name; i++) {
+ lua_pushstring(L, foreignlib_function_methods[i].name);
+ lua_pushcfunction(L, foreignlib_function_methods[i].func);
+ lua_settable(L, -3);
+ }
+ lua_settable(L, -3);
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, FOREIGN_METATABLE_FUNCTION);
+ lua_pushliteral(L, "__gc");
+ lua_pushcfunction(L, foreignlib_function_gc);
+ lua_settable(L, -3);
+ lua_pushliteral(L, "__tostring");
+ lua_pushcfunction(L, foreignlib_function_tostring);
+ lua_settable(L, -3);
+ lua_pushliteral(L, "__call");
+ lua_pushcfunction(L, foreignlib_function_call);
+ lua_settable(L, -3);
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, FOREIGN_METATABLE_POINTER);
+ lua_pushliteral(L, "__gc");
+ lua_pushcfunction(L, foreignlib_pointer_gc);
+ lua_settable(L, -3);
+ lua_pushliteral(L, "__tostring");
+ lua_pushcfunction(L, foreignlib_pointer_tostring);
+ lua_settable(L, -3);
+}
+
+/*tex
+ Finally it all somes together in the initializer. We expect the caller to handle the lookup
+ of |libffi| which can have different names per operating system.
+*/
+
+static int foreignlib_initialize(lua_State * L)
+{
+ if (! foreign_state.initialized) {
+ if (lmt_engine_state.permit_loadlib) {
+ /*tex Just an experiment. */
+ const char *filename = lua_tostring(L, 1); /* libffi */
+ if (filename) {
+
+ lmt_library lib = lmt_library_load(filename);
+
+ foreign_state.ffi_prep_cif = lmt_library_find(lib, "ffi_prep_cif");
+ foreign_state.ffi_call = lmt_library_find(lib, "ffi_call" );
+
+ foreign_state.initialized = lmt_library_okay(lib);
+ }
+ if (foreign_state.initialized) {
+ foreignlib_populate(L);
+ }
+ } else {
+ return luaL_error(L, "foreign: use --permitloadlib to enable this");
+ }
+ }
+ lua_pushboolean(L, foreign_state.initialized);
+ return 1;
+}
+
+static struct luaL_Reg foreignlib_function_list[] = {
+ { "initialize", foreignlib_initialize },
+ { "load", foreignlib_load },
+ { "types", foreignlib_types },
+ { "newbuffer", foreignlib_newbuffer },
+ { "getbuffer", foreignlib_getbuffer },
+ { "abivalues", foreignlib_abivalues }, /* mostly for diagnostics */
+ { "totable", foreignlib_totable },
+ { NULL, NULL },
+};
+
+int luaopen_foreign(lua_State * L)
+{
+ lmt_library_register(L, "foreign", foreignlib_function_list);
+ return 0;
+}