/* vim: ts=4 sw=4 sts=4 et tw=78 * Portions copyright (c) 2015-present, Facebook, Inc. All rights reserved. * Portions copyright (c) 2011 James R. McKaskill. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #include "ffi.h" #include #include /* Set to 1 to get extra debugging on print */ #define DEBUG_TOSTRING 0 int jit_key; int ctype_mt_key; int cdata_mt_key; int callback_mt_key; int cmodule_mt_key; int constants_key; int types_key; int gc_key; int callbacks_key; int functions_key; int abi_key; int next_unnamed_key; int niluv_key; int asmname_key; void push_upval(lua_State* L, int* key) { lua_pushlightuserdata(L, key); lua_rawget(L, LUA_REGISTRYINDEX); } void set_upval(lua_State* L, int* key) { lua_pushlightuserdata(L, key); lua_insert(L, -2); lua_rawset(L, LUA_REGISTRYINDEX); } int equals_upval(lua_State* L, int idx, int* key) { int ret; lua_pushvalue(L, idx); push_upval(L, key); ret = lua_rawequal(L, -2, -1); lua_pop(L, 2); return ret; } struct jit* get_jit(lua_State* L) { struct jit* jit; push_upval(L, &jit_key); jit = (struct jit*) lua_touserdata(L, -1); jit->L = L; lua_pop(L, 1); /* still in registry */ return jit; } static int type_error(lua_State* L, int idx, const char* to_type, int to_usr, const struct ctype* to_ct) { luaL_Buffer B; struct ctype ft; assert(to_type || (to_usr && to_ct)); if (to_usr) { to_usr = lua_absindex(L, to_usr); } idx = lua_absindex(L, idx); luaL_buffinit(L, &B); to_cdata(L, idx, &ft); if (ft.type != INVALID_TYPE) { push_type_name(L, -1, &ft); lua_pushfstring(L, "unable to convert argument %d from cdata<%s> to cdata<", idx, lua_tostring(L, -1)); lua_remove(L, -2); luaL_addvalue(&B); } else { lua_pushfstring(L, "unable to convert argument %d from lua<%s> to cdata<", idx, luaL_typename(L, idx)); luaL_addvalue(&B); } if (to_ct) { push_type_name(L, to_usr, to_ct); luaL_addvalue(&B); } else { luaL_addstring(&B, to_type); } luaL_addchar(&B, '>'); luaL_pushresult(&B); return lua_error(L); } static void* userdata_toptr(lua_State* L, int idx) { void* ptr = lua_touserdata(L, idx); // check for FILE* lua_getmetatable(L, idx); luaL_getmetatable(L, LUA_FILEHANDLE); int isfile = lua_rawequal(L, -1, -2); lua_pop(L, 2); if (isfile) { #if LUA_VERSION_NUM == 501 FILE** stream = (FILE**) ptr; return *stream; #else luaL_Stream* stream = (luaL_Stream*) ptr; return stream->f; #endif } return ptr; } static int cdata_tointeger(lua_State* L, int idx, ptrdiff_t* val) { struct ctype ct; void* addr = to_cdata(L, idx, &ct); lua_pop(L, 1); if (ct.pointers) { return 0; } switch (ct.type) { case INT8_TYPE: *val = *(int8_t*)addr; return 1; case INT16_TYPE: *val = *(int16_t*)addr; return 1; case INT32_TYPE: *val = *(int32_t*)addr; return 1; case INT64_TYPE: *val = *(int64_t*)addr; return 1; default: return 0; } } static int64_t check_intptr(lua_State* L, int idx, void* p, struct ctype* ct) { if (ct->type == INVALID_TYPE) { int64_t ret; memset(ct, 0, sizeof(*ct)); ct->base_size = 8; ct->type = INT64_TYPE; ct->is_defined = 1; ret = luaL_checknumber(L, idx); return ret; } else if (ct->pointers) { return (intptr_t) p; } switch (ct->type) { case INTPTR_TYPE: case FUNCTION_PTR_TYPE: return *(intptr_t*) p; case INT64_TYPE: return *(int64_t*) p; case INT32_TYPE: return ct->is_unsigned ? (int64_t) *(uint32_t*) p : (int64_t) *(int32_t*) p; case INT16_TYPE: return ct->is_unsigned ? (int64_t) *(uint16_t*) p : (int64_t) *(int16_t*) p; case INT8_TYPE: return ct->is_unsigned ? (int64_t) *(uint8_t*) p : (int64_t) *(int8_t*) p; default: type_error(L, idx, "intptr_t", 0, NULL); return 0; } } static int get_cfunction_address(lua_State* L, int idx, cfunction* addr); #define TO_NUMBER(TYPE, ALLOW_POINTERS, LUA_TONUMBER) \ TYPE ret = 0; \ void* p; \ struct ctype ct; \ cfunction f; \ \ switch (lua_type(L, idx)) { \ case LUA_TBOOLEAN: \ ret = (TYPE) lua_toboolean(L, idx); \ break; \ \ case LUA_TNUMBER: \ ret = (TYPE) LUA_TONUMBER(L, idx); \ break; \ \ case LUA_TSTRING: \ if (!ALLOW_POINTERS) { \ type_error(L, idx, #TYPE, 0, NULL); \ } \ ret = (TYPE) (intptr_t) lua_tostring(L, idx); \ break; \ \ case LUA_TLIGHTUSERDATA: \ if (!ALLOW_POINTERS) { \ type_error(L, idx, #TYPE, 0, NULL); \ } \ ret = (TYPE) (intptr_t) lua_topointer(L, idx); \ break; \ \ case LUA_TFUNCTION: \ if (!ALLOW_POINTERS) { \ type_error(L, idx, #TYPE, 0, NULL); \ } \ if (!get_cfunction_address(L, idx, &f)) { \ type_error(L, idx, #TYPE, 0, NULL); \ } \ ret = (TYPE) (intptr_t) f; \ break; \ \ case LUA_TUSERDATA: \ p = to_cdata(L, idx, &ct); \ \ if (ct.type == INVALID_TYPE) { \ if (!ALLOW_POINTERS) { \ type_error(L, idx, #TYPE, 0, NULL); \ } \ ret = (TYPE) (intptr_t) userdata_toptr(L, idx); \ } else if (ct.pointers || ct.type == STRUCT_TYPE || ct.type == UNION_TYPE) {\ if (!ALLOW_POINTERS) { \ type_error(L, idx, #TYPE, 0, NULL); \ } \ ret = (TYPE) (intptr_t) p; \ } else if (ct.type == COMPLEX_DOUBLE_TYPE) { \ ret = (TYPE) creal(*(complex_double*) p); \ } else if (ct.type == COMPLEX_FLOAT_TYPE) { \ ret = (TYPE) crealf(*(complex_float*) p); \ } else if (ct.type == DOUBLE_TYPE) { \ ret = (TYPE) *(double*) p; \ } else if (ct.type == FLOAT_TYPE) { \ ret = (TYPE) *(float*) p; \ } else { \ ret = check_intptr(L, idx, p, &ct); \ } \ lua_pop(L, 1); \ break; \ \ case LUA_TNIL: \ ret = (TYPE) 0; \ break; \ \ default: \ type_error(L, idx, #TYPE, 0, NULL); \ } \ static int64_t cast_int64(lua_State* L, int idx, int is_cast) { TO_NUMBER(int64_t, is_cast, lua_tointeger); return ret; } static uint64_t cast_uint64(lua_State* L, int idx, int is_cast) { TO_NUMBER(uint64_t, is_cast, lua_tointeger); return ret; } int32_t check_int32(lua_State* L, int idx) { return (int32_t) cast_int64(L, idx, 0); } uint32_t check_uint32(lua_State* L, int idx) { return (uint32_t) cast_uint64(L, idx, 0); } int64_t check_int64(lua_State* L, int idx) { return cast_int64(L, idx, 0); } uint64_t check_uint64(lua_State* L, int idx) { return cast_uint64(L, idx, 0); } double check_double(lua_State* L, int idx) { TO_NUMBER(double, 0, lua_tonumber); return ret; } float check_float(lua_State* L, int idx) { TO_NUMBER(double, 0, lua_tonumber); return ret; } uintptr_t check_uintptr(lua_State* L, int idx) { TO_NUMBER(uintptr_t, 1, lua_tointeger); return ret; } complex_double check_complex_double(lua_State* L, int idx) { double real = 0, imag = 0; void* p; struct ctype ct; switch (lua_type(L, idx)) { case LUA_TNUMBER: real = (double) lua_tonumber(L, idx); break; case LUA_TTABLE: lua_rawgeti(L, idx, 1); real = check_double(L, -1); lua_pop(L, 1); lua_rawgeti(L, idx, 2); if (lua_isnil(L, -1)) { imag = real; } else { imag = check_double(L, -1); } lua_pop(L, 1); break; case LUA_TUSERDATA: p = to_cdata(L, idx, &ct); if (ct.type == COMPLEX_DOUBLE_TYPE) { real = creal(*(complex_double*) p); imag = cimag(*(complex_double*) p); } else if (ct.type == COMPLEX_FLOAT_TYPE) { real = crealf(*(complex_float*) p); imag = cimagf(*(complex_float*) p); } else if (ct.type == DOUBLE_TYPE) { real = *(double*) p; } else if (ct.type == FLOAT_TYPE) { real = *(float*) p; } else { real = check_intptr(L, idx, p, &ct); } lua_pop(L, 1); break; default: type_error(L, idx, "complex", 0, NULL); } return mk_complex_double(real, imag); } complex_float check_complex_float(lua_State* L, int idx) { complex_double d = check_complex_double(L, idx); return mk_complex_float(creal(d), cimag(d)); } static size_t unpack_vararg(lua_State* L, int i, char* to) { void* p; struct ctype ct; switch (lua_type(L, i)) { case LUA_TBOOLEAN: *(int*) to = lua_toboolean(L, i); return sizeof(int); case LUA_TNUMBER: *(double*) to = lua_tonumber(L, i); // TODO in Lua 5.3: lua_tointeger sometimes should be here return sizeof(double); case LUA_TSTRING: *(const char**) to = lua_tostring(L, i); return sizeof(const char*); case LUA_TLIGHTUSERDATA: *(void**) to = lua_touserdata(L, i); return sizeof(void*); case LUA_TUSERDATA: p = to_cdata(L, i, &ct); lua_pop(L, 1); if (ct.type == INVALID_TYPE) { *(void**) to = userdata_toptr(L, i); return sizeof(void*); } else if (ct.pointers || ct.type == INTPTR_TYPE) { *(void**) to = p; return sizeof(void*); } else if (ct.type == INT32_TYPE) { *(int32_t*) to = *(int32_t*) p; return sizeof(int32_t); } else if (ct.type == INT64_TYPE) { *(int64_t*) to = *(int64_t*) p; return sizeof(int64_t); } goto err; case LUA_TNIL: *(void**) to = NULL; return sizeof(void*); default: goto err; } err: return type_error(L, i, "vararg", 0, NULL); } void unpack_varargs_stack(lua_State* L, int first, int last, char* to) { int i; for (i = first; i <= last; i++) { to += unpack_vararg(L, i, to); } } void unpack_varargs_stack_skip(lua_State* L, int first, int last, int ints_to_skip, int floats_to_skip, char* to) { int i; for (i = first; i <= last; i++) { int type = lua_type(L, i); if (type == LUA_TNUMBER && --floats_to_skip >= 0) { continue; } else if (type != LUA_TNUMBER && --ints_to_skip >= 0) { continue; } to += unpack_vararg(L, i, to); } } void unpack_varargs_float(lua_State* L, int first, int last, int max, char* to) { int i; for (i = first; i <= last && max > 0; i++) { if (lua_type(L, i) == LUA_TNUMBER) { unpack_vararg(L, i, to); to += sizeof(double); max--; } } } void unpack_varargs_int(lua_State* L, int first, int last, int max, char* to) { int i; for (i = first; i <= last && max > 0; i++) { if (lua_type(L, i) != LUA_TNUMBER) { unpack_vararg(L, i, to); to += sizeof(void*); max--; } } } void unpack_varargs_reg(lua_State* L, int first, int last, char* to) { int i; for (i = first; i <= last; i++) { unpack_vararg(L, i, to); to += sizeof(double); } } /* to_enum tries to convert a value at idx to the enum type indicated by to_ct * and uv to_usr. For strings this means it will do a string lookup for the * enum type. It leaves the stack unchanged. Will throw an error if the type * at idx can't be conerted. */ int32_t check_enum(lua_State* L, int idx, int to_usr, const struct ctype* to_ct) { int32_t ret; switch (lua_type(L, idx)) { case LUA_TSTRING: /* lookup string in to_usr to find value */ to_usr = lua_absindex(L, to_usr); lua_pushvalue(L, idx); lua_rawget(L, to_usr); if (lua_isnil(L, -1)) { goto err; } ret = (int32_t) lua_tointeger(L, -1); lua_pop(L, 1); return ret; case LUA_TUSERDATA: return check_int32(L, idx); case LUA_TNIL: return (int32_t) 0; case LUA_TNUMBER: return (int32_t) lua_tointeger(L, idx); default: goto err; } err: return type_error(L, idx, NULL, to_usr, to_ct); } /* to_pointer tries converts a value at idx to a pointer. It fills out ct and * pushes the uv of the found type. It will throw a lua error if it can not * convert the value to a pointer. */ static void* check_pointer(lua_State* L, int idx, struct ctype* ct) { void* p; memset(ct, 0, sizeof(*ct)); idx = lua_absindex(L, idx); switch (lua_type(L, idx)) { case LUA_TNIL: ct->type = VOID_TYPE; ct->pointers = 1; ct->is_null = 1; lua_pushnil(L); return NULL; case LUA_TNUMBER: ct->type = INTPTR_TYPE; ct->is_unsigned = 1; ct->pointers = 0; lua_pushnil(L); return (void*) (uintptr_t) lua_tonumber(L, idx); // TODO in Lua 5.3: maybe change to lua_tointeger case LUA_TLIGHTUSERDATA: ct->type = VOID_TYPE; ct->pointers = 1; lua_pushnil(L); return lua_touserdata(L, idx); case LUA_TSTRING: ct->type = INT8_TYPE; ct->pointers = 1; ct->is_unsigned = IS_CHAR_UNSIGNED; ct->is_array = 1; ct->base_size = 1; ct->const_mask = 2; lua_pushnil(L); return (void*) lua_tolstring(L, idx, &ct->array_size); case LUA_TUSERDATA: p = to_cdata(L, idx, ct); if (ct->type == INVALID_TYPE) { /* some other type of user data */ ct->type = VOID_TYPE; ct->pointers = 1; return userdata_toptr(L, idx); } else if (ct->type == STRUCT_TYPE || ct->type == UNION_TYPE) { return p; } else { return (void*) (intptr_t) check_intptr(L, idx, p, ct); } break; } type_error(L, idx, "pointer", 0, NULL); return NULL; } static int is_void_ptr(const struct ctype* ct) { return ct->type == VOID_TYPE && ct->pointers == 1; } static int is_same_type(lua_State* L, int usr1, int usr2, const struct ctype* t1, const struct ctype* t2) { if (t1->type != t2->type) { return 0; } #if LUA_VERSION_NUM == 501 if (lua_isnil(L, usr1) != lua_isnil(L, usr2)) { int ret; usr1 = lua_absindex(L, usr1); usr2 = lua_absindex(L, usr2); push_upval(L, &niluv_key); ret = lua_rawequal(L, usr1, -1) || lua_rawequal(L, usr2, -1); lua_pop(L, 1); if (ret) { return 1; } } #endif return lua_rawequal(L, usr1, usr2); } static void set_struct(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers); /* to_typed_pointer converts a value at idx to a type tt with target uv to_usr * checking all types. May push a temporary value so that it can create * structs on the fly. */ void* check_typed_pointer(lua_State* L, int idx, int to_usr, const struct ctype* tt) { struct ctype ft; void* p; to_usr = lua_absindex(L, to_usr); idx = lua_absindex(L, idx); if (tt->pointers == 1 && (tt->type == STRUCT_TYPE || tt->type == UNION_TYPE) && lua_type(L, idx) == LUA_TTABLE) { /* need to construct a struct of the target type */ struct ctype ct = *tt; ct.pointers = ct.is_array = 0; p = push_cdata(L, to_usr, &ct); set_struct(L, idx, p, to_usr, &ct, 1); return p; } p = check_pointer(L, idx, &ft); if (tt->pointers == 1 && ft.pointers == 0 && (ft.type == STRUCT_TYPE || ft.type == UNION_TYPE)) { /* auto dereference structs */ ft.pointers = 1; ft.const_mask <<= 1; } if (is_void_ptr(tt)) { /* any pointer can convert to void* */ goto suc; } else if (is_void_ptr(&ft) && (ft.pointers || ft.is_reference)) { /* void* can convert to any pointer */ goto suc; } else if (ft.is_null) { /* NULL can convert to any pointer */ goto suc; } else if (!is_same_type(L, to_usr, -1, tt, &ft)) { /* the base type is different */ goto err; } else if (tt->pointers != ft.pointers) { goto err; } else if (ft.const_mask & ~tt->const_mask) { /* for every const in from it must be in to, there are further rules * for const casting (see the c++ spec), but they are hard to test * quickly */ goto err; } suc: return p; err: type_error(L, idx, NULL, to_usr, tt); return NULL; } /** * gets the address of the wrapped C function for the lua function value at idx * and returns 1 if it exists; otherwise returns 0 and nothing is pushed */ static int get_cfunction_address(lua_State* L, int idx, cfunction* addr) { if (!lua_isfunction(L, idx)) return 0; int top = lua_gettop(L); // Get the last upvalue int n = 2; while (lua_getupvalue(L, idx, n)) { lua_pop(L, 1); n++; } if (!lua_getupvalue(L, idx, n - 1)) return 0; if (!lua_isuserdata(L, -1) || !lua_getmetatable(L, -1)) { lua_pop(L, 1); return 0; } push_upval(L, &callback_mt_key); if (!lua_rawequal(L, -1, -2)) { lua_pop(L, 3); return 0; } /* stack is: * userdata upval * metatable * callback_mt */ cfunction* f = lua_touserdata(L, -3); *addr = f[1]; lua_pop(L, 3); return 1; } /* to_cfunction converts a value at idx with usr table at to_usr and type tt * into a function. Leaves the stack unchanged. */ static cfunction check_cfunction(lua_State* L, int idx, int to_usr, const struct ctype* tt, int check_pointers) { void* p; struct ctype ft; cfunction f; int top = lua_gettop(L); idx = lua_absindex(L, idx); to_usr = lua_absindex(L, to_usr); switch (lua_type(L, idx)) { case LUA_TFUNCTION: if (get_cfunction_address(L, idx, &f)) { return f; } /* Function cdatas are pinned and must be manually cleaned up by * calling func:free(). */ push_upval(L, &callbacks_key); f = compile_callback(L, idx, to_usr, tt); lua_pushboolean(L, 1); lua_rawset(L, -3); lua_pop(L, 1); /* callbacks tbl */ return f; case LUA_TNIL: return NULL; case LUA_TLIGHTUSERDATA: if (check_pointers) { goto err; } else { return (cfunction) lua_touserdata(L, idx); } case LUA_TUSERDATA: p = to_cdata(L, idx, &ft); assert(lua_gettop(L) == top + 1); if (ft.type == INVALID_TYPE) { if (check_pointers) { goto err; } else { lua_pop(L, 1); return (cfunction) lua_touserdata(L, idx); } } else if (ft.is_null) { lua_pop(L, 1); return NULL; } else if (!check_pointers && (ft.pointers || ft.type == INTPTR_TYPE)) { lua_pop(L, 1); return (cfunction) *(void**) p; } else if (ft.type != FUNCTION_PTR_TYPE) { goto err; } else if (!check_pointers) { lua_pop(L, 1); return *(cfunction*) p; } else if (ft.calling_convention != tt->calling_convention) { goto err; } else if (!is_same_type(L, -1, to_usr, &ft, tt)) { goto err; } else { lua_pop(L, 1); return *(cfunction*) p; } default: goto err; } err: type_error(L, idx, NULL, to_usr, tt); return NULL; } /* to_type_cfunction converts a value at idx with uv at to_usr and type tt to * a cfunction. Leaves the stack unchanged. */ cfunction check_typed_cfunction(lua_State* L, int idx, int to_usr, const struct ctype* tt) { return check_cfunction(L, idx, to_usr, tt, 1); } static void set_value(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers); static void set_array(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers) { size_t i, sz, esz; struct ctype et; idx = lua_absindex(L, idx); to_usr = lua_absindex(L, to_usr); switch (lua_type(L, idx)) { case LUA_TSTRING: if (tt->pointers == 1 && tt->type == INT8_TYPE) { const char* str = lua_tolstring(L, idx, &sz); if (!tt->is_variable_array && sz >= tt->array_size) { memcpy(to, str, tt->array_size); } else { /* include nul terminator */ memcpy(to, str, sz+1); } } else { goto err; } break; case LUA_TTABLE: et = *tt; et.pointers--; et.const_mask >>= 1; et.is_array = 0; esz = et.pointers ? sizeof(void*) : et.base_size; lua_rawgeti(L, idx, 2); if (tt->is_variable_array) { /* we have no idea how big the array is, so set values based off * how many items were given to us */ lua_pop(L, 1); for (i = 0; i < lua_rawlen(L, idx); i++) { lua_rawgeti(L, idx, (int) i + 1); set_value(L, -1, (char*) to + esz * i, to_usr, &et, check_pointers); lua_pop(L, 1); } } else if (lua_isnil(L, -1)) { /* there is no second element, so we set the whole array to the * first element (or nil - ie 0) if there is no first element) */ lua_pop(L, 1); lua_rawgeti(L, idx, 1); if (lua_isnil(L, -1)) { memset(to, 0, ctype_size(L, tt)); } else { /* if its still variable we have no idea how many values to set */ for (i = 0; i < tt->array_size; i++) { set_value(L, -1, (char*) to + esz * i, to_usr, &et, check_pointers); } } lua_pop(L, 1); } else { /* there is a second element, so we set each element using the * equiv index in the table initializer */ lua_pop(L, 1); for (i = 0; i < tt->array_size; i++) { lua_rawgeti(L, idx, (int) (i+1)); if (lua_isnil(L, -1)) { /* we've hit the end of the values provided in the * initializer, so memset the rest to zero */ lua_pop(L, 1); memset((char*) to + esz * i, 0, (tt->array_size - i) * esz); break; } else { set_value(L, -1, (char*) to + esz * i, to_usr, &et, check_pointers); lua_pop(L, 1); } } } break; default: goto err; } return; err: type_error(L, idx, NULL, to_usr, tt); } /* pops the member key from the stack, leaves the member user value on the * stack. Returns the member offset. Returns -ve if the member can not be * found. */ static ptrdiff_t get_member(lua_State* L, int usr, const struct ctype* ct, struct ctype* mt) { ptrdiff_t off; lua_rawget(L, usr); if (lua_isnil(L, -1)) { lua_pop(L, 1); return -1; } *mt = *(const struct ctype*) lua_touserdata(L, -1); lua_getuservalue(L, -1); lua_replace(L, -2); if (mt->is_variable_array && ct->variable_size_known) { /* eg char mbr[?] */ size_t sz = (mt->pointers > 1) ? sizeof(void*) : mt->base_size; assert(ct->is_variable_struct && mt->is_array); mt->array_size = ct->variable_increment / sz; mt->is_variable_array = 0; } else if (mt->is_variable_struct && ct->variable_size_known) { /* eg struct {char a; char b[?]} mbr; */ assert(ct->is_variable_struct); mt->variable_size_known = 1; mt->variable_increment = ct->variable_increment; } off = mt->offset; mt->offset = 0; return off; } static void set_struct(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers) { int have_first = 0; int have_other = 0; struct ctype mt; void* p; to_usr = lua_absindex(L, to_usr); idx = lua_absindex(L, idx); switch (lua_type(L, idx)) { case LUA_TTABLE: /* match up to the members based off the table initializers key - this * will match both numbered and named members in the user table * we need a special case for when no entries in the initializer - * zero initialize the c struct, and only one entry in the initializer * - set all members to this value */ memset(to, 0, ctype_size(L, tt)); lua_pushnil(L); while (lua_next(L, idx)) { ptrdiff_t off; if (!have_first && lua_tonumber(L, -2) == 1 && lua_tonumber(L, -1) != 0) { have_first = 1; } else if (!have_other && (lua_type(L, -2) != LUA_TNUMBER || lua_tonumber(L, -2) != 1)) { have_other = 1; } lua_pushvalue(L, -2); off = get_member(L, to_usr, tt, &mt); assert(off >= 0); set_value(L, -2, (char*) to + off, -1, &mt, check_pointers); /* initializer value, mt usr */ lua_pop(L, 2); } /* if we only had a single non zero value then initialize all members to that value */ if (!have_other && have_first && tt->type != UNION_TYPE) { size_t i, sz; ptrdiff_t off; lua_rawgeti(L, idx, 1); sz = lua_rawlen(L, to_usr); for (i = 2; i < sz; i++) { lua_pushinteger(L, i); off = get_member(L, to_usr, tt, &mt); assert(off >= 0); set_value(L, -2, (char*) to + off, -1, &mt, check_pointers); lua_pop(L, 1); /* mt usr */ } lua_pop(L, 1); /* initializer table */ } break; case LUA_TUSERDATA: if (check_pointers) { p = check_typed_pointer(L, idx, to_usr, tt); } else { struct ctype ct; p = check_pointer(L, idx, &ct); } memcpy(to, p, tt->base_size); lua_pop(L, 1); break; default: goto err; } return; err: type_error(L, idx, NULL, to_usr, tt); } static void set_value(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers) { int top = lua_gettop(L); if (tt->is_array) { set_array(L, idx, to, to_usr, tt, check_pointers); } else if (tt->pointers || tt->is_reference) { union { uint8_t c[sizeof(void*)]; void* p; } u; if (lua_istable(L, idx)) { luaL_error(L, "Can't set a pointer member to a struct that's about to be freed"); } if (check_pointers) { u.p = check_typed_pointer(L, idx, to_usr, tt); } else { struct ctype ct; u.p = check_pointer(L, idx, &ct); } #ifndef ALLOW_MISALIGNED_ACCESS if ((uintptr_t) to & PTR_ALIGN_MASK) { memcpy(to, u.c, sizeof(void*)); } else #endif { *(void**) to = u.p; } lua_pop(L, 1); } else if (tt->is_bitfield) { uint64_t hi_mask = UINT64_C(0) - (UINT64_C(1) << (tt->bit_offset + tt->bit_size)); uint64_t low_mask = (UINT64_C(1) << tt->bit_offset) - UINT64_C(1); uint64_t val = check_uint64(L, idx); val &= (UINT64_C(1) << tt->bit_size) - 1; val <<= tt->bit_offset; *(uint64_t*) to = val | (*(uint64_t*) to & (hi_mask | low_mask)); } else if (tt->type == STRUCT_TYPE || tt->type == UNION_TYPE) { set_struct(L, idx, to, to_usr, tt, check_pointers); } else { #ifndef ALLOW_MISALIGNED_ACCESS union { uint8_t c[8]; _Bool b; uint64_t u64; float f; double d; cfunction func; } misalign; void* origto = to; if ((uintptr_t) origto & (tt->base_size - 1)) { to = misalign.c; } #endif switch (tt->type) { case BOOL_TYPE: *(_Bool*) to = (cast_int64(L, idx, !check_pointers) != 0); break; case INT8_TYPE: if (tt->is_unsigned) { *(uint8_t*) to = (uint8_t) cast_uint64(L, idx, !check_pointers); } else { *(int8_t*) to = (int8_t) cast_int64(L, idx, !check_pointers); } break; case INT16_TYPE: if (tt->is_unsigned) { *(uint16_t*) to = (uint16_t) cast_uint64(L, idx, !check_pointers); } else { *(int16_t*) to = (int16_t) cast_int64(L, idx, !check_pointers); } break; case INT32_TYPE: if (tt->is_unsigned) { *(uint32_t*) to = (uint32_t) cast_uint64(L, idx, !check_pointers); } else { *(int32_t*) to = (int32_t) cast_int64(L, idx, !check_pointers); } break; case INT64_TYPE: if (tt->is_unsigned) { *(uint64_t*) to = cast_uint64(L, idx, !check_pointers); } else { *(int64_t*) to = cast_int64(L, idx, !check_pointers); } break; case FLOAT_TYPE: *(float*) to = (float) check_double(L, idx); break; case DOUBLE_TYPE: *(double*) to = check_double(L, idx); break; case COMPLEX_FLOAT_TYPE: *(complex_float*) to = check_complex_float(L, idx); break; case COMPLEX_DOUBLE_TYPE: *(complex_double*) to = check_complex_double(L, idx); break; case INTPTR_TYPE: *(uintptr_t*) to = check_uintptr(L, idx); break; case ENUM_TYPE: *(int32_t*) to = check_enum(L, idx, to_usr, tt); break; case FUNCTION_PTR_TYPE: *(cfunction*) to = check_cfunction(L, idx, to_usr, tt, check_pointers); break; default: goto err; } #ifndef ALLOW_MISALIGNED_ACCESS if ((uintptr_t) origto & (tt->base_size - 1)) { memcpy(origto, misalign.c, tt->base_size); } #endif } assert(lua_gettop(L) == top); return; err: type_error(L, idx, NULL, to_usr, tt); } static int ffi_typeof(lua_State* L) { struct ctype ct; check_ctype(L, 1, &ct); push_ctype(L, -1, &ct); return 1; } static void setmintop(lua_State* L, int idx) { if (lua_gettop(L) < idx) { lua_settop(L, idx); } } /* warning: in the case that it finds an array size, it removes that index */ static void get_variable_array_size(lua_State* L, int idx, struct ctype* ct) { /* we only care about the variable buisness for the variable array * directly ie ffi.new('char[?]') or the struct that contains the variable * array ffi.new('struct {char v[?]}'). A pointer to the struct doesn't * care about the variable size (it treats it as a zero sized array). */ if (ct->is_variable_array) { assert(ct->is_array); ct->array_size = (size_t) luaL_checknumber(L, idx); ct->is_variable_array = 0; lua_remove(L, idx); } else if (ct->is_variable_struct && !ct->variable_size_known) { assert(ct->type == STRUCT_TYPE && !ct->is_array); ct->variable_increment *= (size_t) luaL_checknumber(L, idx); ct->variable_size_known = 1; lua_remove(L, idx); } } static int is_scalar(struct ctype* ct) { int type = ct->type; if (ct->pointers || ct->is_reference) { return !ct->is_array; } return type != STRUCT_TYPE && type != UNION_TYPE && !IS_COMPLEX(type); } static int should_pack(lua_State *L, int ct_usr, struct ctype* ct, int idx) { struct ctype argt; ct_usr = lua_absindex(L, ct_usr); if (IS_COMPLEX(ct->type)) { return 0; } switch (lua_type(L, idx)) { case LUA_TTABLE: return 0; case LUA_TSTRING: return ct->type == STRUCT_TYPE; case LUA_TUSERDATA: // don't pack if the argument is a cdata with the same type to_cdata(L, idx, &argt); int same = is_same_type(L, ct_usr, -1, ct, &argt); lua_pop(L, 1); return !same; default: return 1; } } static int do_new(lua_State* L, int is_cast) { int cargs, i; void* p; struct ctype ct; int check_ptrs = !is_cast; check_ctype(L, 1, &ct); /* don't push a callback when we have a c function, as cb:set needs a * compiled callback from a lua function to work */ if (!ct.pointers && ct.type == FUNCTION_PTR_TYPE && (lua_isnil(L, 2) || lua_isfunction(L, 2))) { // Get the bound C function if this is a ffi lua function cfunction func; if (get_cfunction_address(L, 2, &func)) { p = push_cdata(L, -1, &ct); *(cfunction*) p = func; return 1; } /* Function cdatas are pinned and must be manually cleaned up by * calling func:free(). */ compile_callback(L, 2, -1, &ct); push_upval(L, &callbacks_key); lua_pushvalue(L, -2); lua_pushboolean(L, 1); lua_rawset(L, -3); lua_pop(L, 1); /* callbacks tbl */ return 1; } /* this removes the vararg argument if its needed, and errors if its invalid */ if (!is_cast) { get_variable_array_size(L, 2, &ct); } p = push_cdata(L, -1, &ct); /* if the user mt has a __gc function then call ffi.gc on this value */ if (push_user_mt(L, -2, &ct)) { push_upval(L, &gc_key); lua_pushvalue(L, -3); /* user_mt.__gc */ lua_pushliteral(L, "__gc"); lua_rawget(L, -4); lua_rawset(L, -3); /* gc_upval[cdata] = user_mt.__gc */ lua_pop(L, 2); /* user_mt and gc_upval */ } /* stack is: * ctype arg * ctor args ... 0+ * ctype usr * cdata */ cargs = lua_gettop(L) - 3; if (cargs == 0) { return 1; } int scalar = is_scalar(&ct); if (scalar && cargs > 1) { return luaL_error(L, "too many initializers"); } if (cargs > 1 || (!scalar && should_pack(L, -2, &ct, 2))) { lua_createtable(L, cargs, 0); lua_replace(L, 1); for (i = 1; i <= cargs; i++) { lua_pushvalue(L, i + 1); lua_rawseti(L, 1, i); } assert(lua_gettop(L) == cargs + 3); set_value(L, 1, p, -2, &ct, check_ptrs); return 1; } set_value(L, 2, p, -2, &ct, check_ptrs); return 1; } static int ffi_new(lua_State* L) { return do_new(L, 0); } static int ffi_cast(lua_State* L) { return do_new(L, 1); } static int ctype_new(lua_State* L) { return do_new(L, 0); } static int ctype_call(lua_State* L) { struct ctype ct; int top = lua_gettop(L); check_ctype(L, 1, &ct); if (push_user_mt(L, -1, &ct)) { lua_pushstring(L, "__new"); lua_rawget(L, -2); if (!lua_isnil(L, -1)) { lua_insert(L, 1); // function at bottom of stack under args lua_pop(L, 2); lua_call(L, top, 1); return 1; } lua_pop(L, 2); } lua_pop(L, 1); assert(lua_gettop(L) == top); return do_new(L, 0); } static int ffi_sizeof(lua_State* L) { struct ctype ct; check_ctype(L, 1, &ct); get_variable_array_size(L, 2, &ct); lua_pushinteger(L, ctype_size(L, &ct)); return 1; } static int ffi_alignof(lua_State* L) { struct ctype ct, mt; lua_settop(L, 2); check_ctype(L, 1, &ct); /* if no member is specified then we return the alignment of the type */ if (lua_isnil(L, 2)) { lua_pushinteger(L, ct.align_mask + 1); return 1; } /* get the alignment of the member */ lua_pushvalue(L, 2); if (get_member(L, -2, &ct, &mt) < 0) { push_type_name(L, 3, &ct); return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2)); } lua_pushinteger(L, mt.align_mask + 1); return 1; } static int ffi_offsetof(lua_State* L) { ptrdiff_t off; struct ctype ct, mt; lua_settop(L, 2); check_ctype(L, 1, &ct); lua_pushvalue(L, 2); off = get_member(L, -2, &ct, &mt); /* this replaces the member key at -1 with the mbr usr value */ if (off < 0) { push_type_name(L, 3, &ct); return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2)); } lua_pushinteger(L, off); if (!mt.is_bitfield) { return 1; } lua_pushinteger(L, mt.bit_offset); lua_pushinteger(L, mt.bit_size); return 3; } static int ffi_istype(lua_State* L) { struct ctype tt, ft; check_ctype(L, 1, &tt); to_cdata(L, 2, &ft); if (ft.type == INVALID_TYPE) { goto fail; } if (!is_same_type(L, 3, 4, &tt, &ft)) { goto fail; } if (tt.pointers != ft.pointers) { goto fail; } if (tt.is_array != ft.is_array) { goto fail; } if (tt.is_array && tt.array_size != ft.array_size) { goto fail; } if (tt.calling_convention != ft.calling_convention) { goto fail; } lua_pushboolean(L, 1); return 1; fail: lua_pushboolean(L, 0); return 1; } static int cdata_gc(lua_State* L) { struct ctype ct; check_cdata(L, 1, &ct); lua_settop(L, 1); /* call the gc func if there is any registered */ lua_pushvalue(L, 1); lua_rawget(L, lua_upvalueindex(2)); if (!lua_isnil(L, -1)) { lua_pushvalue(L, 1); lua_pcall(L, 1, 0, 0); } /* unset the closure */ lua_pushvalue(L, 1); lua_pushnil(L); lua_rawset(L, lua_upvalueindex(1)); return 0; } static int callback_free(lua_State* L) { cfunction* p = (cfunction*) lua_touserdata(L, 1); // FIXME: temporarily disabled to prevent SIGTRAP on exit // free_code(get_jit(L), L, *p); return 0; } static int cdata_free(lua_State* L) { struct ctype ct; cfunction* p = (cfunction*) check_cdata(L, 1, &ct); lua_settop(L, 1); /* unset the closure */ lua_pushvalue(L, 1); lua_pushnil(L); lua_rawset(L, lua_upvalueindex(1)); if (ct.is_jitted) { free_code(get_jit(L), L, *p); *p = NULL; } return 0; } static int cdata_set(lua_State* L) { struct ctype ct; cfunction* p = (cfunction*) check_cdata(L, 1, &ct); luaL_checktype(L, 2, LUA_TFUNCTION); if (!ct.is_jitted) { luaL_error(L, "can't set the function for a non-lua callback"); } if (*p == NULL) { luaL_error(L, "can't set the function for a free'd callback"); } push_func_ref(L, *p); lua_pushvalue(L, 2); lua_rawseti(L, -2, CALLBACK_FUNC_USR_IDX); /* remove the closure for this callback as it embeds the function pointer * value */ lua_pushvalue(L, 1); lua_pushboolean(L, 1); lua_rawset(L, lua_upvalueindex(1)); return 0; } static int cdata_call(lua_State* L) { struct ctype ct; int top = lua_gettop(L); cfunction* p = (cfunction*) check_cdata(L, 1, &ct); if (push_user_mt(L, -1, &ct)) { lua_pushliteral(L, "__call"); lua_rawget(L, -2); if (!lua_isnil(L, -1)) { lua_insert(L, 1); lua_pop(L, 2); /* ct_usr, user_mt */ lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); return lua_gettop(L); } } if (ct.pointers || ct.type != FUNCTION_PTR_TYPE) { return luaL_error(L, "only function callbacks are callable"); } lua_pushvalue(L, 1); lua_rawget(L, lua_upvalueindex(1)); if (!lua_isfunction(L, -1)) { lua_pop(L, 1); compile_function(L, *p, -1, &ct); assert(lua_gettop(L) == top + 2); /* uv, closure */ /* closures[func] = closure */ lua_pushvalue(L, 1); lua_pushvalue(L, -2); lua_rawset(L, lua_upvalueindex(1)); lua_replace(L, 1); } else { lua_replace(L, 1); } lua_pop(L, 1); /* uv */ assert(lua_gettop(L) == top); lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); return lua_gettop(L); } static int user_mt_key; static int ffi_metatype(lua_State* L) { struct ctype ct; lua_settop(L, 2); check_ctype(L, 1, &ct); if (lua_type(L, 2) != LUA_TTABLE && lua_type(L, 2) != LUA_TNIL) { return luaL_argerror(L, 2, "metatable must be a table or nil"); } lua_pushlightuserdata(L, &user_mt_key); lua_pushvalue(L, 2); lua_rawset(L, 3); /* user[user_mt_key] = mt */ /* return the passed in ctype */ push_ctype(L, 3, &ct); return 1; } /* push_user_mt returns 1 if the type has a user metatable and pushes it onto * the stack, otherwise it returns 0 and pushes nothing */ int push_user_mt(lua_State* L, int ct_usr, const struct ctype* ct) { if (ct->type != STRUCT_TYPE && ct->type != UNION_TYPE && !IS_COMPLEX(ct->type)) { return 0; } if (!lua_istable(L, ct_usr)) { return 0; } ct_usr = lua_absindex(L, ct_usr); lua_pushlightuserdata(L, &user_mt_key); lua_rawget(L, ct_usr); if (lua_isnil(L, -1)) { lua_pop(L, 1); return 0; } return 1; } static int ffi_gc(lua_State* L) { struct ctype ct; lua_settop(L, 2); check_cdata(L, 1, &ct); push_upval(L, &gc_key); lua_pushvalue(L, 1); lua_pushvalue(L, 2); lua_rawset(L, -3); /* return the cdata back */ lua_settop(L, 1); return 1; } /* lookup_cdata_index returns the offset of the found type and user value on * the stack if valid. Otherwise returns -ve and doesn't touch the stack. */ static ptrdiff_t lookup_cdata_index(lua_State* L, int idx, int ct_usr, struct ctype* ct) { struct ctype mt; ptrdiff_t off; ct_usr = lua_absindex(L, ct_usr); int type = lua_type(L, idx); switch (type) { case LUA_TNUMBER: case LUA_TUSERDATA: /* possibilities are array, pointer */ if (!ct->pointers || is_void_ptr(ct)) { return -1; } // unbox cdata if (type == LUA_TUSERDATA) { if (!cdata_tointeger(L, idx, &off)) { return -1; } } else { off = lua_tointeger(L, idx); } ct->is_array = 0; ct->pointers--; ct->const_mask >>= 1; ct->is_reference = 0; lua_pushvalue(L, ct_usr); return (ct->pointers ? sizeof(void*) : ct->base_size) * off; case LUA_TSTRING: /* possibilities are struct/union, pointer to struct/union */ if ((ct->type != STRUCT_TYPE && ct->type != UNION_TYPE) || ct->is_array || ct->pointers > 1) { return -1; } lua_pushvalue(L, idx); off = get_member(L, ct_usr, ct, &mt); if (off < 0) { return -1; } *ct = mt; return off; default: return -1; } } static int cdata_newindex(lua_State* L) { struct ctype tt; char* to; ptrdiff_t off; lua_settop(L, 3); to = (char*) check_cdata(L, 1, &tt); off = lookup_cdata_index(L, 2, -1, &tt); if (off < 0) { if (!push_user_mt(L, -1, &tt)) { goto err; } lua_pushliteral(L, "__newindex"); lua_rawget(L, -2); if (lua_isnil(L, -1)) { goto err; } lua_insert(L, 1); lua_settop(L, 4); lua_call(L, 3, LUA_MULTRET); return lua_gettop(L); } if (tt.const_mask & 1) { return luaL_error(L, "can't set const data"); } set_value(L, 3, to + off, -1, &tt, 1); return 0; err: push_type_name(L, 4, &tt); return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2)); } static int cdata_index(lua_State* L) { void* to; struct ctype ct; char* data; ptrdiff_t off; lua_settop(L, 2); data = (char*) check_cdata(L, 1, &ct); assert(lua_gettop(L) == 3); if (!ct.pointers) { switch (ct.type) { case FUNCTION_PTR_TYPE: /* Callbacks use the same metatable as standard cdata values, but have set * and free members. So instead of mt.__index = mt, we do the equiv here. */ lua_getmetatable(L, 1); lua_pushvalue(L, 2); lua_rawget(L, -2); return 1; /* This provides the .re and .im virtual members */ case COMPLEX_DOUBLE_TYPE: case COMPLEX_FLOAT_TYPE: if (!lua_isstring(L, 2)) { luaL_error(L, "invalid member for complex number"); } else if (strcmp(lua_tostring(L, 2), "re") == 0) { lua_pushnumber(L, ct.type == COMPLEX_DOUBLE_TYPE ? creal(*(complex_double*) data) : crealf(*(complex_float*) data)); } else if (strcmp(lua_tostring(L, 2), "im") == 0) { lua_pushnumber(L, ct.type == COMPLEX_DOUBLE_TYPE ? cimag(*(complex_double*) data) : cimagf(*(complex_float*) data)); } else { luaL_error(L, "invalid member for complex number"); } return 1; } } off = lookup_cdata_index(L, 2, -1, &ct); if (off < 0) { assert(lua_gettop(L) == 3); if (!push_user_mt(L, -1, &ct)) { goto err; } lua_pushliteral(L, "__index"); lua_rawget(L, -2); if (lua_isnil(L, -1)) { goto err; } if (lua_istable(L, -1)) { lua_pushvalue(L, 2); lua_gettable(L, -2); return 1; } lua_insert(L, 1); lua_settop(L, 3); lua_call(L, 2, LUA_MULTRET); return lua_gettop(L); err: push_type_name(L, 3, &ct); return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2)); } assert(lua_gettop(L) == 4); /* ct, key, ct_usr, mbr_usr */ data += off; if (ct.is_array) { /* push a reference to the array */ ct.is_reference = 1; to = push_cdata(L, -1, &ct); *(void**) to = data; return 1; } else if (ct.is_bitfield) { if (ct.type == INT64_TYPE) { struct ctype rt; uint64_t val = *(uint64_t*) data; val >>= ct.bit_offset; val &= (UINT64_C(1) << ct.bit_size) - 1; memset(&rt, 0, sizeof(rt)); rt.base_size = 8; rt.type = INT64_TYPE; rt.is_unsigned = 1; rt.is_defined = 1; to = push_cdata(L, 0, &rt); *(uint64_t*) to = val; return 1; } else if (ct.type == BOOL_TYPE) { uint64_t val = *(uint64_t*) data; lua_pushboolean(L, (int) (val & (UINT64_C(1) << ct.bit_offset))); return 1; } else { uint64_t val = *(uint64_t*) data; val >>= ct.bit_offset; val &= (UINT64_C(1) << ct.bit_size) - 1; lua_pushinteger(L, val); return 1; } } else if (ct.pointers) { #ifndef ALLOW_MISALIGNED_ACCESS union { uint8_t c[8]; void* p; } misalignbuf; if ((uintptr_t) data & PTR_ALIGN_MASK) { memcpy(misalignbuf.c, data, sizeof(void*)); data = misalignbuf.c; } #endif to = push_cdata(L, -1, &ct); *(void**) to = *(void**) data; return 1; } else if (ct.type == STRUCT_TYPE || ct.type == UNION_TYPE) { /* push a reference to the member */ ct.is_reference = 1; to = push_cdata(L, -1, &ct); *(void**) to = data; return 1; } else if (ct.type == FUNCTION_PTR_TYPE) { cfunction* pf = (cfunction*) push_cdata(L, -1, &ct); *pf = *(cfunction*) data; return 1; } else { #ifndef ALLOW_MISALIGNED_ACCESS union { uint8_t c[8]; double d; float f; uint64_t u64; } misalignbuf; assert(ct.base_size <= 8); if ((uintptr_t) data & (ct.base_size - 1)) { memcpy(misalignbuf.c, data, ct.base_size); data = misalignbuf.c; } #endif switch (ct.type) { case BOOL_TYPE: lua_pushboolean(L, *(_Bool*) data); break; case INT8_TYPE: lua_pushinteger(L, ct.is_unsigned ? (lua_Integer) *(uint8_t*) data : (lua_Integer) *(int8_t*) data); break; case INT16_TYPE: lua_pushinteger(L, ct.is_unsigned ? (lua_Integer) *(uint16_t*) data : (lua_Integer) *(int16_t*) data); break; case ENUM_TYPE: case INT32_TYPE: lua_pushinteger(L, ct.is_unsigned ? (lua_Integer) *(uint32_t*) data : (lua_Integer) *(int32_t*) data); break; case INT64_TYPE: to = push_cdata(L, -1, &ct); *(int64_t*) to = *(int64_t*) data; break; case INTPTR_TYPE: to = push_cdata(L, -1, &ct); *(intptr_t*) to = *(intptr_t*) data; break; case FLOAT_TYPE: lua_pushnumber(L, *(float*) data); break; case DOUBLE_TYPE: lua_pushnumber(L, *(double*) data); break; case COMPLEX_DOUBLE_TYPE: to = push_cdata(L, -1, &ct); *(complex_double*) to = *(complex_double*) data; break; case COMPLEX_FLOAT_TYPE: to = push_cdata(L, -1, &ct); *(complex_float*) to = *(complex_float*) data; break; default: luaL_error(L, "internal error: invalid member type"); } return 1; } } static complex_double check_complex(lua_State* L, int idx, void* p, struct ctype* ct) { if (ct->type == INVALID_TYPE) { double d = luaL_checknumber(L, idx); #ifdef HAVE_COMPLEX return d; #else complex_double c; c.real = d; c.imag = 0; return c; #endif } else if (ct->type == COMPLEX_DOUBLE_TYPE) { return *(complex_double*) p; } else if (ct->type == COMPLEX_FLOAT_TYPE) { complex_float* f = (complex_float*) p; #ifdef HAVE_COMPLEX return *f; #else complex_double d; d.real = f->real; d.imag = f->imag; return d; #endif } else { complex_double dummy; type_error(L, idx, "complex", 0, NULL); memset(&dummy, 0, sizeof(dummy)); return dummy; } } static int rank(const struct ctype* ct) { if (ct->pointers) { return 5; } switch (ct->type) { case COMPLEX_DOUBLE_TYPE: return 7; case COMPLEX_FLOAT_TYPE: return 6; case INTPTR_TYPE: return sizeof(intptr_t) >= sizeof(int64_t) ? 4 : 1; case INT64_TYPE: return ct->is_unsigned ? 3 : 2; case INT32_TYPE: case INT16_TYPE: case INT8_TYPE: return 2; default: return 0; } } static void push_complex(lua_State* L, complex_double res, int ct_usr, const struct ctype* ct) { if (ct->type == COMPLEX_DOUBLE_TYPE) { complex_double* p = (complex_double*) push_cdata(L, ct_usr, ct); *p = res; } else { complex_float* p = (complex_float*) push_cdata(L, ct_usr, ct); #ifdef HAVE_COMPLEX *p = (complex float) res; #else p->real = (float) res.real; p->imag = (float) res.imag; #endif } } static void push_number(lua_State* L, int64_t val, int ct_usr, const struct ctype* ct) { if ((ct->pointers || ct->type == INTPTR_TYPE) && sizeof(intptr_t) != sizeof(int64_t)) { intptr_t* p = (intptr_t*) push_cdata(L, ct_usr, ct); *p = val; } else { int64_t* p = (int64_t*) push_cdata(L, ct_usr, ct); *p = val; } } static int call_user_op(lua_State* L, const char* opfield, int idx, int ct_usr, const struct ctype* ct) { idx = lua_absindex(L, idx); if (push_user_mt(L, ct_usr, ct)) { lua_pushstring(L, opfield); lua_rawget(L, -2); if (!lua_isnil(L, -1)) { int top = lua_gettop(L); lua_pushvalue(L, idx); lua_call(L, 1, LUA_MULTRET); return lua_gettop(L) - top + 1; } lua_pop(L, 2); } return -1; } static int cdata_unm(lua_State* L) { struct ctype ct; void* p; int64_t val; int ret; lua_settop(L, 1); p = to_cdata(L, 1, &ct); ret = call_user_op(L, "__unm", 1, 2, &ct); if (ret >= 0) { return ret; } val = check_intptr(L, 1, p, &ct); if (ct.pointers) { luaL_error(L, "can't negate a pointer value"); } else { memset(&ct, 0, sizeof(ct)); ct.type = INT64_TYPE; ct.base_size = 8; ct.is_defined = 1; push_number(L, -val, 0, &ct); } return 1; } /* returns -ve if no binop was called otherwise returns the number of return * arguments */ static int call_user_binop(lua_State* L, const char* opfield, int lidx, int lusr, const struct ctype* lt, int ridx, int rusr, const struct ctype* rt) { lidx = lua_absindex(L, lidx); ridx = lua_absindex(L, ridx); if (push_user_mt(L, lusr, lt)) { lua_pushstring(L, opfield); lua_rawget(L, -2); if (!lua_isnil(L, -1)) { int top = lua_gettop(L); lua_pushvalue(L, lidx); lua_pushvalue(L, ridx); lua_call(L, 2, LUA_MULTRET); return lua_gettop(L) - top + 1; } lua_pop(L, 2); /* user_mt and user_mt.op */ } if (push_user_mt(L, rusr, rt)) { lua_pushstring(L, opfield); lua_rawget(L, -2); if (!lua_isnil(L, -1)) { int top = lua_gettop(L); lua_pushvalue(L, lidx); lua_pushvalue(L, ridx); lua_call(L, 2, LUA_MULTRET); return lua_gettop(L) - top + 1; } lua_pop(L, 2); /* user_mt and user_mt.op */ } return -1; } static int cdata_concat(lua_State* L) { struct ctype lt, rt; int ret; lua_settop(L, 2); to_cdata(L, 1, <); to_cdata(L, 2, &rt); ret = call_user_binop(L, "__concat", 1, 3, <, 2, 4, &rt); if (ret >= 0) { return ret; } return luaL_error(L, "NYI"); } static int cdata_len(lua_State* L) { struct ctype ct; int ret; lua_settop(L, 1); to_cdata(L, 1, &ct); ret = call_user_op(L, "__len", 1, 2, &ct); if (ret >= 0) { return ret; } push_type_name(L, 2, &ct); return luaL_error(L, "type %s does not implement the __len metamethod", lua_tostring(L, -1)); } static int cdata_pairs(lua_State* L) { struct ctype ct; int ret; lua_settop(L, 1); to_cdata(L, 1, &ct); ret = call_user_op(L, "__pairs", 1, 2, &ct); if (ret >= 0) { return ret; } push_type_name(L, 2, &ct); return luaL_error(L, "type %s does not implement the __pairs metamethod", lua_tostring(L, -1)); } static int cdata_ipairs(lua_State* L) { struct ctype ct; int ret; lua_settop(L, 1); to_cdata(L, 1, &ct); ret = call_user_op(L, "__ipairs", 1, 2, &ct); if (ret >= 0) { return ret; } push_type_name(L, 2, &ct); return luaL_error(L, "type %s does not implement the __ipairs metamethod", lua_tostring(L, -1)); } static int cdata_add(lua_State* L) { struct ctype lt, rt, ct; void *lp, *rp; int ct_usr; int ret; lua_settop(L, 2); lp = to_cdata(L, 1, <); rp = to_cdata(L, 2, &rt); assert(lua_gettop(L) == 4); ret = call_user_binop(L, "__add", 1, 3, <, 2, 4, &rt); if (ret >= 0) { return ret; } assert(lua_gettop(L) == 4); ct_usr = rank(<) > rank(&rt) ? 3 : 4; ct = rank(<) > rank(&rt) ? lt : rt; if (IS_COMPLEX(ct.type)) { complex_double left, right, res; left = check_complex(L, 1, lp, <); right = check_complex(L, 2, rp, &rt); assert(lua_gettop(L) == 4); #ifdef HAVE_COMPLEX res = left + right; #else res.real = left.real + right.real; res.imag = left.imag + right.imag; #endif push_complex(L, res, ct_usr, &ct); return 1; } else { int64_t left = check_intptr(L, 1, lp, <); int64_t right = check_intptr(L, 2, rp, &rt); assert(lua_gettop(L) == 4); /* note due to 2s complement it doesn't matter if we do the addition as int or uint, * but the result needs to be uint64_t if either of the sources are */ if (lt.pointers && rt.pointers) { luaL_error(L, "can't add two pointers"); } else if (lt.pointers) { int64_t res = left + (lt.pointers > 1 ? sizeof(void*) : lt.base_size) * right; lt.is_array = 0; push_number(L, res, 3, <); } else if (rt.pointers) { int64_t res = right + (rt.pointers > 1 ? sizeof(void*) : rt.base_size) * left; rt.is_array = 0; push_number(L, res, 4, &rt); } else { push_number(L, left + right, ct_usr, &ct); } return 1; } } static int cdata_sub(lua_State* L) { struct ctype lt, rt, ct; void *lp, *rp; int ct_usr; int ret; lua_settop(L, 2); lp = to_cdata(L, 1, <); rp = to_cdata(L, 2, &rt); ret = call_user_binop(L, "__sub", 1, 3, <, 2, 4, &rt); if (ret >= 0) { return ret; } ct_usr = rank(<) > rank(&rt) ? 3 : 4; ct = rank(<) > rank(&rt) ? lt : rt; if (IS_COMPLEX(ct.type)) { complex_double left, right, res; left = check_complex(L, 1, lp, <); right = check_complex(L, 2, rp, &rt); #ifdef HAVE_COMPLEX res = left - right; #else res.real = left.real - right.real; res.imag = left.imag - right.imag; #endif push_complex(L, res, ct_usr, &ct); return 1; } else { int64_t left = check_intptr(L, 1, lp, <); int64_t right = check_intptr(L, 2, rp, &rt); if (rt.pointers) { luaL_error(L, "NYI: can't subtract a pointer value"); } else if (lt.pointers) { int64_t res = left - (lt.pointers > 1 ? sizeof(void*) : lt.base_size) * right; lt.is_array = 0; push_number(L, res, 3, <); } else { int64_t res = left - right; push_number(L, res, ct_usr, &ct); } return 1; } } /* TODO fix for unsigned */ #define NUMBER_ONLY_BINOP(OPSTR, DO_NORMAL, DO_COMPLEX) \ struct ctype lt, rt, ct; \ void *lp, *rp; \ int ct_usr; \ int ret; \ \ lua_settop(L, 2); \ \ lp = to_cdata(L, 1, <); \ rp = to_cdata(L, 2, &rt); \ \ ret = call_user_binop(L, OPSTR, 1, 3, <, 2, 4, &rt); \ if (ret >= 0) { \ return ret; \ } \ \ ct_usr = rank(<) > rank(&rt) ? 3 : 4; \ ct = rank(<) > rank(&rt) ? lt : rt; \ \ if (IS_COMPLEX(ct.type)) { \ complex_double res; \ complex_double left = check_complex(L, 1, lp, <); \ complex_double right = check_complex(L, 2, rp, &rt); \ \ DO_COMPLEX(left, right, res); \ push_complex(L, res, ct_usr, &ct); \ \ } else if (lt.pointers || rt.pointers) { \ luaL_error(L, "can't operate on a pointer value"); \ \ } else { \ int64_t res; \ int64_t left = check_intptr(L, 1, lp, <); \ int64_t right = check_intptr(L, 2, rp, &rt); \ \ DO_NORMAL(left, right, res); \ push_number(L, res, ct_usr, &ct); \ } \ \ return 1 #define MUL(l,r,s) s = l * r #define DIV(l,r,s) s = l / r #define MOD(l,r,s) s = l % r #define POW(l,r,s) s = pow(l, r) #ifdef HAVE_COMPLEX #define MULC(l,r,s) s = l * r #define DIVC(l,r,s) s = l / r #define MODC(l,r,s) (void) l, (void) r, memset(&s, 0, sizeof(s)), luaL_error(L, "NYI: complex mod") #define POWC(l,r,s) s = cpow(l, r) #else #define MULC(l,r,s) s.real = l.real * r.real - l.imag * r.imag, s.imag = l.real * r.imag + l.imag * r.real #define DIVC(l,r,s) s.real = (l.real * r.real + l.imag * r.imag) / (r.real * r.real + r.imag * r.imag), \ s.imag = (l.imag * r.real - l.real * r.imag) / (r.real * r.real + r.imag * r.imag) #define MODC(l,r,s) (void) l, (void) r, memset(&s, 0, sizeof(s)), luaL_error(L, "NYI: complex mod") #define POWC(l,r,s) (void) l, (void) r, memset(&s, 0, sizeof(s)), luaL_error(L, "NYI: complex pow") #endif static int cdata_mul(lua_State* L) { NUMBER_ONLY_BINOP("__mul", MUL, MULC); } static int cdata_div(lua_State* L) { NUMBER_ONLY_BINOP("__div", DIV, DIVC); } static int cdata_mod(lua_State* L) { NUMBER_ONLY_BINOP("__mod", MOD, MODC); } static int cdata_pow(lua_State* L) { NUMBER_ONLY_BINOP("__pow", POW, POWC); } #define COMPARE_BINOP(OPSTR, OP, OPC) \ struct ctype lt, rt; \ void *lp, *rp; \ int ret, res; \ \ lua_settop(L, 2); \ \ lp = to_cdata(L, 1, <); \ rp = to_cdata(L, 2, &rt); \ \ ret = call_user_binop(L, OPSTR, 1, 3, <, 2, 4, &rt); \ if (ret >= 0) { \ return ret; \ } \ \ if (IS_COMPLEX(lt.type) || IS_COMPLEX(rt.type)) { \ complex_double left = check_complex(L, 1, lp, <); \ complex_double right = check_complex(L, 2, rp, &rt); \ \ res = OPC(left, right); \ \ lua_pushboolean(L, res); \ \ } else { \ int64_t left = check_intptr(L, 1, lp, <); \ int64_t right = check_intptr(L, 2, rp, &rt); \ \ if (lt.pointers && rt.pointers) { \ if (is_void_ptr(<) || is_void_ptr(&rt) || is_same_type(L, 3, 4, <, &rt)) { \ res = OP((uint64_t) left, (uint64_t) right); \ } else { \ goto err; \ } \ \ } else if (lt.is_null && rt.type == FUNCTION_PTR_TYPE) { \ res = OP((uint64_t) left, (uint64_t) right); \ \ } else if (rt.is_null && lt.type == FUNCTION_PTR_TYPE) { \ res = OP((uint64_t) left, (uint64_t) right); \ \ } else if (lt.pointers && rt.type == INTPTR_TYPE && rt.is_unsigned) {\ res = OP((uint64_t) left, (uint64_t) right); \ \ } else if (rt.pointers && lt.type == INTPTR_TYPE && lt.is_unsigned) {\ res = OP((uint64_t) left, (uint64_t) right); \ \ } else if (rt.pointers || lt.pointers) { \ goto err; \ \ } else if (lt.is_unsigned && rt.is_unsigned) { \ res = OP((uint64_t) left, (uint64_t) right); \ \ } else if (lt.is_unsigned) { \ res = OP((int64_t) (uint64_t) left, right); \ \ } else if (rt.is_unsigned) { \ res = OP(left, (int64_t) (uint64_t) right); \ \ } else { \ res = OP(left, right); \ } \ \ lua_pushboolean(L, res); \ } \ return 1 #define EQ(l, r) (l) == (r) #define LT(l, r) (l) < (r) #define LE(l, r) (l) <= (r) #ifdef HAVE_COMPLEX #define EQC(l, r) (l) == (r) #else #define EQC(l, r) (l).real == (r).real && (l).imag == (r).imag #endif #define LEC(l, r) EQC(l, r), luaL_error(L, "complex numbers are non-orderable") #define LTC(l, r) EQC(l, r), luaL_error(L, "complex numbers are non-orderable") static int cdata_eq(lua_State* L) { COMPARE_BINOP("__eq", EQ, EQC); err: lua_pushboolean(L, 0); return 1; } static int cdata_lt(lua_State* L) { COMPARE_BINOP("__lt", LT, LTC); err: lua_getuservalue(L, 1); lua_getuservalue(L, 2); push_type_name(L, -2, <); push_type_name(L, -2, <); return luaL_error(L, "trying to compare incompatible types %s and %s", lua_tostring(L, -2), lua_tostring(L, -1)); } static int cdata_le(lua_State* L) { COMPARE_BINOP("__le", LE, LEC); err: lua_getuservalue(L, 1); lua_getuservalue(L, 2); push_type_name(L, -2, <); push_type_name(L, -2, <); return luaL_error(L, "trying to compare incompatible types %s and %s", lua_tostring(L, -2), lua_tostring(L, -1)); } static const char* etype_tostring(int type) { switch (type) { case VOID_TYPE: return "void"; case DOUBLE_TYPE: return "double"; case FLOAT_TYPE: return "float"; case COMPLEX_DOUBLE_TYPE: return "complex double"; case COMPLEX_FLOAT_TYPE: return "complex float"; case BOOL_TYPE: return "bool"; case INT8_TYPE: return "int8"; case INT16_TYPE: return "int16"; case INT32_TYPE: return "int32"; case INT64_TYPE: return "int64"; case INTPTR_TYPE: return "intptr"; case ENUM_TYPE: return "enum"; case UNION_TYPE: return "union"; case STRUCT_TYPE: return "struct"; case FUNCTION_PTR_TYPE: return "function ptr"; case FUNCTION_TYPE: return "function"; default: return "invalid"; } } static void print_type(lua_State* L, const struct ctype* ct) { lua_pushfstring(L, " sz %d %d %d align %d ptr %d %d %d type %s%s %d %d %d name %d call %d %d var %d %d %d bit %d %d %d %d jit %d", /* sz */ ct->base_size, ct->array_size, ct->offset, /* align */ ct->align_mask, /* ptr */ ct->is_array, ct->pointers, ct->const_mask, /* type */ ct->is_unsigned ? "u" : "", etype_tostring(ct->type), ct->is_reference, ct->is_defined, ct->is_null, /* name */ ct->has_member_name, /* call */ ct->calling_convention, ct->has_var_arg, /* var */ ct->is_variable_array, ct->is_variable_struct, ct->variable_size_known, /* bit */ ct->is_bitfield, ct->has_bitfield, ct->bit_offset, ct->bit_size, /* jit */ ct->is_jitted); } static int ctype_tostring(lua_State* L) { struct ctype ct; assert(lua_type(L, 1) == LUA_TUSERDATA); lua_settop(L, 1); check_ctype(L, 1, &ct); assert(lua_gettop(L) == 2); push_type_name(L, -1, &ct); lua_pushfstring(L, "ctype<%s>", lua_tostring(L, -1)); if (DEBUG_TOSTRING) { print_type(L, &ct); lua_concat(L, 2); } return 1; } static int cdata_tostring(lua_State* L) { struct ctype ct; char buf[64]; void* p; int ret; lua_settop(L, 1); p = to_cdata(L, 1, &ct); ret = call_user_op(L, "__tostring", 1, 2, &ct); if (ret >= 0) { return ret; } if (ct.pointers > 0 || ct.is_reference || ct.type == STRUCT_TYPE || ct.type == UNION_TYPE) { push_type_name(L, -1, &ct); lua_pushfstring(L, "cdata<%s>: %p", lua_tostring(L, -1), p); if (DEBUG_TOSTRING) { print_type(L, &ct); lua_concat(L, 2); } return 1; } switch (ct.type) { case COMPLEX_DOUBLE_TYPE: { complex_double c = *(complex_double*) p; lua_pushfstring(L, "%f+%fi", creal(c), cimag(c)); } return 1; case COMPLEX_FLOAT_TYPE: { complex_float c = *(complex_float*) p; lua_pushfstring(L, "%f+%fi", crealf(c), cimagf(c)); } return 1; case FUNCTION_PTR_TYPE: p = *(void**) p; push_type_name(L, -1, &ct); lua_pushfstring(L, "cdata<%s>: %p", lua_tostring(L, -1), *(void**) p); return 1; case INTPTR_TYPE: lua_pushfstring(L, "%p", *(uintptr_t*) p); return 1; case INT64_TYPE: sprintf(buf, ct.is_unsigned ? "%"PRIu64 : "%"PRId64, *(uint64_t*) p); lua_pushstring(L, buf); return 1; default: sprintf(buf, ct.is_unsigned ? "%"PRId64 : "%"PRId64, (int64_t) check_intptr(L, 1, p, &ct)); lua_pushstring(L, buf); return 1; } } static int ffi_errno(lua_State* L) { struct jit* jit = get_jit(L); if (!lua_isnoneornil(L, 1)) { lua_pushinteger(L, jit->last_errno); jit->last_errno = luaL_checknumber(L, 1); } else { lua_pushinteger(L, jit->last_errno); } return 1; } static int ffi_type(lua_State* L) { if (lua_isuserdata(L, 1) && lua_getmetatable(L, 1)) { if (equals_upval(L, -1, &cdata_mt_key) || equals_upval(L, -1, &ctype_mt_key)) { lua_pushstring(L, "cdata"); return 1; } lua_pop(L, 1); /* mt */ } /* call the old _G.type, we use an upvalue as _G.type is set * to this function */ lua_pushvalue(L, lua_upvalueindex(1)); lua_insert(L, 1); lua_call(L, lua_gettop(L)-1, LUA_MULTRET); return lua_gettop(L); } static int ffi_number(lua_State* L) { struct ctype ct; void* data = to_cdata(L, 1, &ct); if (ct.type != INVALID_TYPE) { lua_pushinteger(L, check_intptr(L, 1, data, &ct)); return 1; } else { /* call the old _G.tonumber, we use an upvalue as _G.tonumber is set * to this function */ lua_pushvalue(L, lua_upvalueindex(1)); lua_insert(L, 1); lua_call(L, lua_gettop(L)-1, LUA_MULTRET); return lua_gettop(L); } } static int ffi_string(lua_State* L) { struct ctype ct; char* data; lua_settop(L, 2); data = (char*) check_cdata(L, 1, &ct); if (is_void_ptr(&ct)) { lua_pushlstring(L, data, (size_t) luaL_checknumber(L, 2)); return 1; } else if (ct.type == INT8_TYPE && ct.pointers == 1) { size_t sz; if (lua_isuserdata(L, 2)) { ptrdiff_t val; if (!cdata_tointeger(L, 2, &val)) { type_error(L, 2, "int", 0, NULL); } sz = (size_t) val; } else if (!lua_isnil(L, 2)) { sz = (size_t) luaL_checknumber(L, 2); } else if (ct.is_array && !ct.is_variable_array) { char* nul = memchr(data, '\0', ct.array_size); sz = nul ? nul - data : ct.array_size; } else { sz = strlen(data); } lua_pushlstring(L, data, sz); return 1; } return luaL_error(L, "unable to convert cdata to string"); } static int ffi_copy(lua_State* L) { struct ctype ft, tt; char *to, *from; setmintop(L, 3); to = (char*) check_pointer(L, 1, &tt); from = (char*) check_pointer(L, 2, &ft); if (!lua_isnoneornil(L, 3)) { memcpy(to, from, (size_t) luaL_checknumber(L, 3)); } else if (ft.type == INT8_TYPE && ft.pointers == 1) { size_t sz = ft.is_array ? ft.array_size : strlen(from); memcpy(to, from, sz); to[sz] = '\0'; } return 0; } static int ffi_fill(lua_State* L) { struct ctype ct; void* to; size_t sz; int val = 0; setmintop(L, 3); to = check_pointer(L, 1, &ct); sz = (size_t) luaL_checknumber(L, 2); if (!lua_isnoneornil(L, 3)) { val = (int) luaL_checkinteger(L, 3); } memset(to, val, sz); return 0; } static int ffi_abi(lua_State* L) { luaL_checkstring(L, 1); push_upval(L, &abi_key); lua_pushvalue(L, 1); lua_rawget(L, -2); lua_pushboolean(L, lua_toboolean(L, -1)); return 1; } static int ffi_load(lua_State* L) { const char* libname = luaL_checkstring(L, 1); void** lib = (void**) lua_newuserdata(L, sizeof(void*)); *lib = LoadLibraryA(libname); #ifdef LIB_FORMAT_1 if (!*lib) { libname = lua_pushfstring(L, LIB_FORMAT_1, lua_tostring(L, 1)); *lib = LoadLibraryA(libname); lua_pop(L, 1); } #endif #ifdef LIB_FORMAT_2 if (!*lib) { libname = lua_pushfstring(L, LIB_FORMAT_2, lua_tostring(L, 1)); *lib = LoadLibraryA(libname); lua_pop(L, 1); } #endif if (!*lib) { return luaL_error(L, "could not load library %s", lua_tostring(L, 1)); } lua_newtable(L); lua_setuservalue(L, -2); push_upval(L, &cmodule_mt_key); lua_setmetatable(L, -2); return 1; } static void* find_symbol(lua_State* L, int modidx, const char* asmname) { size_t i; void** libs; size_t num; void* sym = NULL; libs = (void**) lua_touserdata(L, modidx); num = lua_rawlen(L, modidx) / sizeof(void*); for (i = 0; i < num && sym == NULL; i++) { if (libs[i]) { sym = GetProcAddressA(libs[i], asmname); } } return sym; } /* pushes the user table */ static void* lookup_global(lua_State* L, int modidx, int nameidx, const char** pname, struct ctype* ct) { int top = lua_gettop(L); void* sym; modidx = lua_absindex(L, modidx); nameidx = lua_absindex(L, nameidx); *pname = luaL_checkstring(L, nameidx); /* get the ctype */ push_upval(L, &functions_key); lua_pushvalue(L, nameidx); lua_rawget(L, -2); if (lua_isnil(L, -1)) { luaL_error(L, "missing declaration for function/global %s", *pname); return NULL; } /* leave just the ct_usr on the stack */ *ct = *(const struct ctype*) lua_touserdata(L, -1); lua_getuservalue(L, -1); lua_replace(L, top + 1); lua_pop(L, 1); assert(lua_gettop(L) == top + 1); /* get the assembly name */ push_upval(L, &asmname_key); lua_pushvalue(L, nameidx); lua_rawget(L, -2); if (lua_isstring(L, -1)) { *pname = lua_tostring(L, -1); } lua_pop(L, 2); sym = find_symbol(L, modidx, *pname); assert(lua_gettop(L) == top + 1); return sym; } static int cmodule_index(lua_State* L) { const char* asmname; struct ctype ct; void *sym; lua_settop(L, 2); /* see if we have already loaded the function */ lua_getuservalue(L, 1); lua_pushvalue(L, 2); lua_rawget(L, -2); if (!lua_isnil(L, -1)) { return 1; } lua_pop(L, 2); /* check the constants table */ push_upval(L, &constants_key); lua_pushvalue(L, 2); lua_rawget(L, -2); if (!lua_isnil(L, -1)) { return 1; } lua_pop(L, 2); /* lookup_global pushes the ct_usr */ sym = lookup_global(L, 1, 2, &asmname, &ct); #if defined _WIN32 && !defined _WIN64 && (defined __i386__ || defined _M_IX86) if (!sym && ct.type == FUNCTION_TYPE) { ct.calling_convention = STD_CALL; lua_pushfstring(L, "_%s@%d", asmname, x86_return_size(L, -1, &ct)); sym = find_symbol(L, 1, lua_tostring(L, -1)); lua_pop(L, 1); } if (!sym && ct.type == FUNCTION_TYPE) { ct.calling_convention = FAST_CALL; lua_pushfstring(L, "@%s@%d", asmname, x86_return_size(L, -1, &ct)); sym = find_symbol(L, 1, lua_tostring(L, -1)); lua_pop(L, 1); } #endif if (!sym) { return luaL_error(L, "failed to find function/global %s", asmname); } assert(lua_gettop(L) == 3); /* module, name, ct_usr */ if (ct.type == FUNCTION_TYPE) { compile_function(L, (cfunction) sym, -1, &ct); assert(lua_gettop(L) == 4); /* module, name, ct_usr, function */ /* set module usr value[luaname] = function to cache for next time */ lua_getuservalue(L, 1); lua_pushvalue(L, 2); lua_pushvalue(L, -3); lua_rawset(L, -3); lua_pop(L, 1); /* module uv */ return 1; } /* extern const char* foo; and extern const char foo[]; */ if (ct.pointers == 1 && ct.type == INT8_TYPE) { char* str = (char*) sym; if (!ct.is_array) { str = *(char**) sym; } lua_pushstring(L, str); return 1; } /* extern struct foo foo[], extern void* foo[]; and extern struct foo foo; */ if (ct.is_array || (!ct.pointers && (ct.type == UNION_TYPE || ct.type == STRUCT_TYPE))) { void* p; ct.is_reference = 1; p = push_cdata(L, -1, &ct); *(void**) p = sym; return 1; } /* extern void* foo; and extern void (*foo)(); */ if (ct.pointers || ct.type == FUNCTION_PTR_TYPE) { void* p = push_cdata(L, -1, &ct); *(void**) p = *(void**) sym; return 1; } switch (ct.type) { case COMPLEX_DOUBLE_TYPE: case COMPLEX_FLOAT_TYPE: case INTPTR_TYPE: case INT64_TYPE: { /* TODO: complex float/double need to be references if .re and * .imag are setable */ void* p = push_cdata(L, -1, &ct); memcpy(p, sym, ct.base_size); return 1; } case DOUBLE_TYPE: lua_pushnumber(L, *(double*) sym); return 1; case FLOAT_TYPE: lua_pushnumber(L, *(float*) sym); return 1; case BOOL_TYPE: lua_pushboolean(L, *(bool*) sym); return 1; case INT8_TYPE: lua_pushinteger(L, ct.is_unsigned ? (lua_Integer) *(uint8_t*) sym : (lua_Integer) *(int8_t*) sym); return 1; case INT16_TYPE: lua_pushinteger(L, ct.is_unsigned ? (lua_Integer) *(uint16_t*) sym : (lua_Integer) *(int16_t*) sym); return 1; case INT32_TYPE: case ENUM_TYPE: lua_pushinteger(L, ct.is_unsigned ? (lua_Integer) *(uint32_t*) sym : (lua_Integer) *(int32_t*) sym); return 1; } return luaL_error(L, "NYI - global value type"); } static int cmodule_newindex(lua_State* L) { const char* name; void* sym; struct ctype ct; lua_settop(L, 3); /* pushes the ct_usr */ sym = lookup_global(L, 1, 2, &name, &ct); assert(lua_gettop(L) == 4); /* module, name, value, ct_usr */ if (sym == NULL) { return luaL_error(L, "failed to find global %s", name); } if (ct.type == FUNCTION_TYPE || ct.is_array || (ct.const_mask & 1)) { return luaL_error(L, "can not set global %s", name); } set_value(L, 3, sym, -1, &ct, 1); return 0; } static int jit_gc(lua_State* L) { size_t i; struct jit* jit = get_jit(L); dasm_free(jit); for (i = 0; i < jit->pagenum; i++) { FreePage(jit->pages[i], jit->pages[i]->size); } free(jit->pages); free(jit->globals); return 0; } static int ffi_debug(lua_State* L) { lua_newtable(L); push_upval(L, &ctype_mt_key); lua_setfield(L, -2, "ctype_mt"); push_upval(L, &cdata_mt_key); lua_setfield(L, -2, "cdata_mt"); push_upval(L, &cmodule_mt_key); lua_setfield(L, -2, "cmodule_mt"); push_upval(L, &constants_key); lua_setfield(L, -2, "constants"); push_upval(L, &types_key); lua_setfield(L, -2, "types"); push_upval(L, &jit_key); lua_setfield(L, -2, "jit"); push_upval(L, &gc_key); lua_setfield(L, -2, "gc"); push_upval(L, &callbacks_key); lua_setfield(L, -2, "callbacks"); push_upval(L, &functions_key); lua_setfield(L, -2, "functions"); push_upval(L, &abi_key); lua_setfield(L, -2, "abi"); push_upval(L, &next_unnamed_key); lua_setfield(L, -2, "next_unnamed"); return 1; } static int do64(lua_State* L, int is_unsigned) { lua_Number low, high; struct ctype ct; int64_t val; lua_settop(L, 2); if (!lua_isnil(L, 2)) { high = luaL_checknumber(L, 1); low = luaL_checknumber(L, 2); } else { high = 0; low = luaL_checknumber(L, 1); } val = ((int64_t) (uint32_t) high << 32) | (int64_t) (uint32_t) low; if (!is_unsigned && (high < 0 || low < 0)) { val = -val; } memset(&ct, 0, sizeof(ct)); ct.type = INT64_TYPE; ct.is_unsigned = is_unsigned; ct.is_defined = 1; ct.base_size = sizeof(int64_t); push_number(L, (int64_t) val, 0, &ct); return 1; } static int ffi_i64(lua_State* L) { return do64(L, 0); } static int ffi_u64(lua_State* L) { return do64(L, 1); } static const luaL_Reg cdata_mt[] = { {"__gc", &cdata_gc}, {"__call", &cdata_call}, {"free", &cdata_free}, {"set", &cdata_set}, {"__index", &cdata_index}, {"__newindex", &cdata_newindex}, {"__add", &cdata_add}, {"__sub", &cdata_sub}, {"__mul", &cdata_mul}, {"__div", &cdata_div}, {"__mod", &cdata_mod}, {"__pow", &cdata_pow}, {"__unm", &cdata_unm}, {"__eq", &cdata_eq}, {"__lt", &cdata_lt}, {"__le", &cdata_le}, {"__tostring", &cdata_tostring}, {"__concat", &cdata_concat}, {"__len", &cdata_len}, {"__pairs", &cdata_pairs}, {"__ipairs", &cdata_ipairs}, {NULL, NULL} }; static const luaL_Reg callback_mt[] = { {"__gc", &callback_free}, {NULL, NULL} }; static const luaL_Reg ctype_mt[] = { {"__call", &ctype_call}, {"__new", &ctype_new}, {"__tostring", &ctype_tostring}, {NULL, NULL} }; static const luaL_Reg cmodule_mt[] = { {"__index", &cmodule_index}, {"__newindex", &cmodule_newindex}, {NULL, NULL} }; static const luaL_Reg jit_mt[] = { {"__gc", &jit_gc}, {NULL, NULL} }; static const luaL_Reg ffi_reg[] = { {"cdef", &ffi_cdef}, {"load", &ffi_load}, {"new", &ffi_new}, {"typeof", &ffi_typeof}, {"cast", &ffi_cast}, {"metatype", &ffi_metatype}, {"gc", &ffi_gc}, {"sizeof", &ffi_sizeof}, {"alignof", &ffi_alignof}, {"offsetof", &ffi_offsetof}, {"istype", &ffi_istype}, {"errno", &ffi_errno}, {"string", &ffi_string}, {"copy", &ffi_copy}, {"fill", &ffi_fill}, {"abi", &ffi_abi}, {"debug", &ffi_debug}, {"i64", &ffi_i64}, {"u64", &ffi_u64}, {NULL, NULL} }; /* leaves the usr table on the stack */ static void push_builtin(lua_State* L, struct ctype* ct, const char* name, int type, int size, int align, int is_unsigned) { memset(ct, 0, sizeof(*ct)); ct->type = type; ct->base_size = size; ct->align_mask = align; ct->is_defined = 1; ct->is_unsigned = is_unsigned; if (IS_COMPLEX(type)) { lua_newtable(L); } else { lua_pushnil(L); } push_upval(L, &types_key); push_ctype(L, -2, ct); lua_setfield(L, -2, name); lua_pop(L, 2); /* types, usr table */ } static void push_builtin_undef(lua_State* L, struct ctype* ct, const char* name, int type) { memset(ct, 0, sizeof(*ct)); ct->type = type; push_upval(L, &types_key); push_ctype(L, 0, ct); lua_setfield(L, -2, name); lua_pop(L, 1); /* types */ } static void add_typedef(lua_State* L, const char* from, const char* to) { struct ctype ct; struct parser P; P.line = 1; P.align_mask = DEFAULT_ALIGN_MASK; P.next = P.prev = from; push_upval(L, &types_key); parse_type(L, &P, &ct); parse_argument(L, &P, -1, &ct, NULL, NULL); push_ctype(L, -1, &ct); /* stack is at +4: types, type usr, arg usr, ctype */ lua_setfield(L, -4, to); lua_pop(L, 3); /* types, type usr, arg usr */ } static int setup_upvals(lua_State* L) { struct jit* jit = get_jit(L); /* jit setup */ { dasm_init(jit, 64); #ifdef _WIN32 { SYSTEM_INFO si; GetSystemInfo(&si); jit->align_page_size = si.dwAllocationGranularity - 1; } #else jit->align_page_size = sysconf(_SC_PAGE_SIZE) - 1; #endif jit->globals = (void**) malloc(64 * sizeof(void*)); dasm_setupglobal(jit, jit->globals, 64); compile_globals(jit, L); } /* ffi.C */ { #ifdef _WIN32 size_t sz = sizeof(HMODULE) * 6; HMODULE* libs = lua_newuserdata(L, sz); memset(libs, 0, sz); /* exe */ GetModuleHandle(NULL); /* lua dll */ #ifdef LUA_DLL_NAME #define STR2(tok) #tok #define STR(tok) STR2(tok) libs[1] = LoadLibraryA(STR(LUA_DLL_NAME)); #undef STR #undef STR2 #endif /* crt */ #ifdef UNDER_CE libs[2] = LoadLibraryA("coredll.dll"); #else GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (char*) &_fmode, &libs[2]); libs[3] = LoadLibraryA("kernel32.dll"); libs[4] = LoadLibraryA("user32.dll"); libs[5] = LoadLibraryA("gdi32.dll"); #endif jit->lua_dll = libs[1]; jit->kernel32_dll = libs[3]; #else /* !_WIN32 */ size_t sz = sizeof(void*) * 5; void** libs = lua_newuserdata(L, sz); memset(libs, 0, sz); libs[0] = LoadLibraryA(NULL); /* exe */ libs[1] = LoadLibraryA("libc.so"); #ifdef __GNUC__ libs[2] = LoadLibraryA("libgcc.so"); #endif libs[3] = LoadLibraryA("libm.so"); libs[4] = LoadLibraryA("libdl.so"); #endif lua_newtable(L); lua_setuservalue(L, -2); push_upval(L, &cmodule_mt_key); lua_setmetatable(L, -2); lua_setfield(L, 1, "C"); } /* setup builtin types */ { complex_double* pc; struct {char ch; uint16_t v;} a16; struct {char ch; uint32_t v;} a32; struct {char ch; uint64_t v;} a64; struct {char ch; float v;} af; struct {char ch; double v;} ad; #ifdef HAVE_LONG_DOUBLE struct {char ch; long double v;} ald; #endif struct {char ch; uintptr_t v;} aptr; struct ctype ct; struct {char ch; complex_float v;} cf; struct {char ch; complex_double v;} cd; #if defined HAVE_LONG_DOUBLE && defined HAVE_COMPLEX struct {char ch; complex long double v;} cld; #endif push_builtin(L, &ct, "void", VOID_TYPE, 0, 0, 0); push_builtin(L, &ct, "bool", BOOL_TYPE, sizeof(_Bool), sizeof(_Bool) -1, 1); push_builtin(L, &ct, "uint8_t", INT8_TYPE, sizeof(uint8_t), 0, 1); push_builtin(L, &ct, "int8_t", INT8_TYPE, sizeof(int8_t), 0, 0); push_builtin(L, &ct, "uint16_t", INT16_TYPE, sizeof(uint16_t), ALIGNOF(a16), 1); push_builtin(L, &ct, "int16_t", INT16_TYPE, sizeof(int16_t), ALIGNOF(a16), 0); push_builtin(L, &ct, "uint32_t", INT32_TYPE, sizeof(uint32_t), ALIGNOF(a32), 1); push_builtin(L, &ct, "int32_t", INT32_TYPE, sizeof(int32_t), ALIGNOF(a32), 0); push_builtin(L, &ct, "uint64_t", INT64_TYPE, sizeof(uint64_t), ALIGNOF(a64), 1); push_builtin(L, &ct, "int64_t", INT64_TYPE, sizeof(int64_t), ALIGNOF(a64), 0); push_builtin(L, &ct, "float", FLOAT_TYPE, sizeof(float), ALIGNOF(af), 0); push_builtin(L, &ct, "double", DOUBLE_TYPE, sizeof(double), ALIGNOF(ad), 0); #ifdef HAVE_LONG_DOUBLE push_builtin(L, &ct, "long double", LONG_DOUBLE_TYPE, sizeof(long double), ALIGNOF(ald), 0); #else push_builtin_undef(L, &ct, "long double", LONG_DOUBLE_TYPE); #endif push_builtin(L, &ct, "uintptr_t", INTPTR_TYPE, sizeof(uintptr_t), ALIGNOF(aptr), 1); push_builtin(L, &ct, "intptr_t", INTPTR_TYPE, sizeof(uintptr_t), ALIGNOF(aptr), 0); push_builtin(L, &ct, "complex float", COMPLEX_FLOAT_TYPE, sizeof(complex_float), ALIGNOF(cf), 0); push_builtin(L, &ct, "complex double", COMPLEX_DOUBLE_TYPE, sizeof(complex_double), ALIGNOF(cd), 0); #if defined HAVE_LONG_DOUBLE && defined HAVE_COMPLEX push_builtin(L, &ct, "complex long double", COMPLEX_LONG_DOUBLE_TYPE, sizeof(complex long double), ALIGNOF(cld), 0); #else push_builtin_undef(L, &ct, "complex long double", COMPLEX_LONG_DOUBLE_TYPE); #endif /* add NULL and i constants */ push_upval(L, &constants_key); memset(&ct, 0, sizeof(ct)); ct.type = VOID_TYPE; ct.is_defined = 1; ct.pointers = 1; ct.is_null = 1; /* add ffi.C.NULL */ push_cdata(L, 0, &ct); lua_setfield(L, -2, "NULL"); /* add ffi.NULL */ push_cdata(L, 0, &ct); lua_setfield(L, 1, "NULL"); memset(&ct, 0, sizeof(ct)); ct.type = COMPLEX_DOUBLE_TYPE; ct.is_defined = 1; ct.base_size = sizeof(complex_double); pc = (complex_double*) push_cdata(L, 0, &ct); #ifdef HAVE_COMPLEX *pc = 1i; #else pc->real = 0; pc->imag = 1; #endif lua_setfield(L, -2, "i"); lua_pop(L, 1); /* constants */ } assert(lua_gettop(L) == 1); /* setup builtin typedefs */ { add_typedef(L, "bool", "_Bool"); if (sizeof(uint32_t) == sizeof(size_t)) { add_typedef(L, "uint32_t", "size_t"); add_typedef(L, "int32_t", "ssize_t"); } else if (sizeof(uint64_t) == sizeof(size_t)) { add_typedef(L, "uint64_t", "size_t"); add_typedef(L, "int64_t", "ssize_t"); } if (sizeof(int32_t) == sizeof(intptr_t)) { add_typedef(L, "int32_t", "intptr_t"); add_typedef(L, "int32_t", "ptrdiff_t"); } else if (sizeof(int64_t) == sizeof(intptr_t)) { add_typedef(L, "int64_t", "intptr_t"); add_typedef(L, "int64_t", "ptrdiff_t"); } if (sizeof(uint8_t) == sizeof(wchar_t)) { add_typedef(L, "uint8_t", "wchar_t"); } else if (sizeof(uint16_t) == sizeof(wchar_t)) { add_typedef(L, "uint16_t", "wchar_t"); } else if (sizeof(uint32_t) == sizeof(wchar_t)) { add_typedef(L, "uint32_t", "wchar_t"); } if (sizeof(va_list) == sizeof(char*)) { add_typedef(L, "char*", "va_list"); } else { struct {char ch; va_list v;} av; lua_pushfstring(L, "struct {char data[%d] __attribute__((align(%d)));}", (int) sizeof(va_list), (int) ALIGNOF(av) + 1); add_typedef(L, lua_tostring(L, -1), "va_list"); lua_pop(L, 1); } add_typedef(L, "va_list", "__builtin_va_list"); add_typedef(L, "va_list", "__gnuc_va_list"); } assert(lua_gettop(L) == 1); /* setup ABI params table */ push_upval(L, &abi_key); #if defined ARCH_X86 || defined ARCH_ARM lua_pushboolean(L, 1); lua_setfield(L, -2, "32bit"); #elif defined ARCH_X64 || defined ARCH_PPC64 lua_pushboolean(L, 1); lua_setfield(L, -2, "64bit"); #else #error #endif #if defined ARCH_X86 || defined ARCH_X64 || defined ARCH_ARM || defined ARCH_PPC64 lua_pushboolean(L, 1); lua_setfield(L, -2, "le"); #else #error #endif #if defined ARCH_X86 || defined ARCH_X64 || defined ARCH_PPC64 lua_pushboolean(L, 1); lua_setfield(L, -2, "fpu"); #elif defined ARCH_ARM lua_pushboolean(L, 1); lua_setfield(L, -2, "softfp"); #else #error #endif lua_pop(L, 1); /* abi tbl */ /* GC table - shouldn't pin cdata values */ push_upval(L, &gc_key); lua_newtable(L); lua_pushliteral(L, "k"); lua_setfield(L, -2, "__mode"); lua_setmetatable(L, -2); lua_pop(L, 1); /* gc table */ /* ffi.os */ #if defined OS_CE lua_pushliteral(L, "WindowsCE"); #elif defined OS_WIN lua_pushliteral(L, "Windows"); #elif defined OS_OSX lua_pushliteral(L, "OSX"); #elif defined OS_LINUX lua_pushliteral(L, "Linux"); #elif defined OS_BSD lua_pushliteral(L, "BSD"); #elif defined OS_POSIX lua_pushliteral(L, "POSIX"); #else lua_pushliteral(L, "Other"); #endif lua_setfield(L, 1, "os"); /* ffi.arch */ #if defined ARCH_X86 lua_pushliteral(L, "x86"); #elif defined ARCH_X64 lua_pushliteral(L, "x64"); #elif defined ARCH_ARM lua_pushliteral(L, "arm"); #elif defined ARCH_PPC64 lua_pushliteral(L, "ppc64"); #else # error #endif lua_setfield(L, 1, "arch"); assert(lua_gettop(L) == 1); return 0; } static void setup_mt(lua_State* L, const luaL_Reg* mt, int upvals) { lua_pushboolean(L, 1); lua_setfield(L, -upvals-2, "__metatable"); luaL_setfuncs(L, mt, upvals); } int luaopen_ffi(lua_State* L) { lua_settop(L, 0); lua_newtable(L); set_upval(L, &niluv_key); lua_newtable(L); setup_mt(L, ctype_mt, 0); set_upval(L, &ctype_mt_key); lua_newtable(L); set_upval(L, &callbacks_key); lua_newtable(L); set_upval(L, &gc_key); lua_newtable(L); push_upval(L, &callbacks_key); push_upval(L, &gc_key); setup_mt(L, cdata_mt, 2); set_upval(L, &cdata_mt_key); lua_newtable(L); setup_mt(L, callback_mt, 0); set_upval(L, &callback_mt_key); lua_newtable(L); setup_mt(L, cmodule_mt, 0); set_upval(L, &cmodule_mt_key); memset(lua_newuserdata(L, sizeof(struct jit)), 0, sizeof(struct jit)); lua_newtable(L); setup_mt(L, jit_mt, 0); lua_setmetatable(L, -2); set_upval(L, &jit_key); lua_newtable(L); set_upval(L, &constants_key); lua_newtable(L); set_upval(L, &types_key); lua_newtable(L); set_upval(L, &functions_key); lua_newtable(L); set_upval(L, &asmname_key); lua_newtable(L); set_upval(L, &abi_key); lua_pushinteger(L, 1); set_upval(L, &next_unnamed_key); assert(lua_gettop(L) == 0); /* ffi table */ lua_newtable(L); luaL_setfuncs(L, ffi_reg, 0); /* setup_upvals(ffi tbl) */ lua_pushcfunction(L, &setup_upvals); lua_pushvalue(L, 1); lua_call(L, 1, 0); assert(lua_gettop(L) == 1); lua_getglobal(L, "tonumber"); lua_pushcclosure(L, &ffi_number, 1); lua_pushvalue(L, -1); lua_setglobal(L, "tonumber"); lua_setfield(L, -2, "number"); /* ffi.number */ lua_getglobal(L, "type"); lua_pushcclosure(L, &ffi_type, 1); lua_pushvalue(L, -1); lua_setglobal(L, "type"); lua_setfield(L, -2, "type"); /* ffi.type */ return 1; }