v4k-git-backup/tools/luaffi/ctype.c

269 lines
7.1 KiB
C

/* vim: ts=4 sw=4 sts=4 et tw=78
* Copyright (c) 2011 James R. McKaskill. See license in ffi.h
*/
#include "ffi.h"
static int to_define_key;
static void update_on_definition(lua_State* L, int ct_usr, int ct_idx)
{
ct_usr = lua_absindex(L, ct_usr);
ct_idx = lua_absindex(L, ct_idx);
lua_pushlightuserdata(L, &to_define_key);
lua_rawget(L, ct_usr);
if (lua_isnil(L, -1)) {
lua_pop(L, 1); /* pop the nil */
/* {} */
lua_newtable(L);
/* {__mode='k'} */
lua_newtable(L);
lua_pushliteral(L, "k");
lua_setfield(L, -2, "__mode");
/* setmetatable({}, {__mode='k'}) */
lua_setmetatable(L, -2);
/* usr[TO_UPDATE_KEY] = setmetatable({}, {__mode='k'}) */
lua_pushlightuserdata(L, &to_define_key);
lua_pushvalue(L, -2);
lua_rawset(L, ct_usr);
/* leave the table on the stack */
}
/* to_update[ctype or cdata] = true */
lua_pushvalue(L, ct_idx);
lua_pushboolean(L, 1);
lua_rawset(L, -3);
/* pop the to_update table */
lua_pop(L, 1);
}
void set_defined(lua_State* L, int ct_usr, struct ctype* ct)
{
ct_usr = lua_absindex(L, ct_usr);
ct->is_defined = 1;
/* update ctypes and cdatas that were created before the definition came in */
lua_pushlightuserdata(L, &to_define_key);
lua_rawget(L, ct_usr);
if (!lua_isnil(L, -1)) {
lua_pushnil(L);
while (lua_next(L, -2)) {
struct ctype* upd = (struct ctype*) lua_touserdata(L, -2);
upd->base_size = ct->base_size;
upd->align_mask = ct->align_mask;
upd->is_defined = 1;
upd->is_variable_struct = ct->is_variable_struct;
upd->variable_increment = ct->variable_increment;
assert(!upd->variable_size_known);
lua_pop(L, 1);
}
lua_pop(L, 1);
/* usr[TO_UPDATE_KEY] = nil */
lua_pushlightuserdata(L, &to_define_key);
lua_pushnil(L);
lua_rawset(L, ct_usr);
} else {
lua_pop(L, 1);
}
}
struct ctype* push_ctype(lua_State* L, int ct_usr, const struct ctype* ct)
{
struct ctype* ret;
ct_usr = lua_absindex(L, ct_usr);
ret = (struct ctype*) lua_newuserdata(L, sizeof(struct ctype));
*ret = *ct;
push_upval(L, &ctype_mt_key);
lua_setmetatable(L, -2);
#if LUA_VERSION_NUM == 501
if (!ct_usr || lua_isnil(L, ct_usr)) {
push_upval(L, &niluv_key);
lua_setfenv(L, -2);
}
#endif
if (ct_usr && !lua_isnil(L, ct_usr)) {
lua_pushvalue(L, ct_usr);
lua_setuservalue(L, -2);
}
if (!ct->is_defined && ct_usr && !lua_isnil(L, ct_usr)) {
update_on_definition(L, ct_usr, -1);
}
return ret;
}
size_t ctype_size(lua_State* L, const struct ctype* ct)
{
if (ct->pointers - ct->is_array) {
return sizeof(void*) * (ct->is_array ? ct->array_size : 1);
} else if (!ct->is_defined || ct->type == VOID_TYPE) {
return luaL_error(L, "can't calculate size of an undefined type");
} else if (ct->variable_size_known) {
assert(ct->is_variable_struct && !ct->is_array);
return ct->base_size + ct->variable_increment;
} else if (ct->is_variable_array || ct->is_variable_struct) {
return luaL_error(L, "internal error: calc size of variable type with unknown size");
} else {
return ct->base_size * (ct->is_array ? ct->array_size : 1);
}
}
void* push_cdata(lua_State* L, int ct_usr, const struct ctype* ct)
{
struct cdata* cd;
size_t sz = ct->is_reference ? sizeof(void*) : ctype_size(L, ct);
ct_usr = lua_absindex(L, ct_usr);
/* This is to stop valgrind from complaining. Bitfields are accessed in 8
* byte chunks so that the code doesn't have to deal with different access
* patterns, but this means that occasionally it will read past the end of
* the struct. As its not setting the bits past the end (only reading and
* then writing the bits back) and the read is aligned its a non-issue,
* but valgrind complains nonetheless.
*/
if (ct->has_bitfield) {
sz = ALIGN_UP(sz, 7);
}
cd = (struct cdata*) lua_newuserdata(L, sizeof(struct cdata) + sz);
*(struct ctype*) &cd->type = *ct;
memset(cd+1, 0, sz);
/* TODO: handle cases where lua_newuserdata returns a pointer that is not
* aligned */
#if 0
assert((uintptr_t) (cd + 1) % 8 == 0);
#endif
#if LUA_VERSION_NUM == 501
if (!ct_usr || lua_isnil(L, ct_usr)) {
push_upval(L, &niluv_key);
lua_setfenv(L, -2);
}
#endif
if (ct_usr && !lua_isnil(L, ct_usr)) {
lua_pushvalue(L, ct_usr);
lua_setuservalue(L, -2);
}
push_upval(L, &cdata_mt_key);
lua_setmetatable(L, -2);
if (!ct->is_defined && ct_usr && !lua_isnil(L, ct_usr)) {
update_on_definition(L, ct_usr, -1);
}
return cd+1;
}
void push_callback(lua_State* L, cfunction f)
{
cfunction* pf = (cfunction*) lua_newuserdata(L, sizeof(cfunction));
*pf = f;
push_upval(L, &callback_mt_key);
lua_setmetatable(L, -2);
}
/* returns the value as a ctype, pushes the user value onto the stack */
void check_ctype(lua_State* L, int idx, struct ctype* ct)
{
if (lua_isstring(L, idx)) {
struct parser P;
P.line = 1;
P.prev = P.next = lua_tostring(L, idx);
P.align_mask = DEFAULT_ALIGN_MASK;
parse_type(L, &P, ct);
parse_argument(L, &P, -1, ct, NULL, NULL);
lua_remove(L, -2); /* remove the user value from parse_type */
} else if (lua_getmetatable(L, idx)) {
if (!equals_upval(L, -1, &ctype_mt_key)
&& !equals_upval(L, -1, &cdata_mt_key)) {
goto err;
}
lua_pop(L, 1); /* pop the metatable */
*ct = *(struct ctype*) lua_touserdata(L, idx);
lua_getuservalue(L, idx);
} else {
goto err;
}
return;
err:
luaL_error(L, "expected cdata, ctype or string for arg #%d", idx);
}
/* to_cdata returns the struct cdata* and pushes the user value onto the
* stack. If the index is not a ctype then ct is not touched, a nil is pushed,
* NULL is returned, and ct->type is set to INVALID_TYPE. Also dereferences
* references */
void* to_cdata(lua_State* L, int idx, struct ctype* ct)
{
struct cdata* cd;
ct->type = INVALID_TYPE;
if (!lua_isuserdata(L, idx) || !lua_getmetatable(L, idx)) {
lua_pushnil(L);
return NULL;
}
if (!equals_upval(L, -1, &cdata_mt_key)) {
lua_pop(L, 1); /* mt */
lua_pushnil(L);
return NULL;
}
lua_pop(L, 1); /* mt */
cd = (struct cdata*) lua_touserdata(L, idx);
*ct = cd->type;
lua_getuservalue(L, idx);
if (ct->is_reference) {
ct->is_reference = 0;
return *(void**) (cd+1);
} else if (ct->pointers && !ct->is_array) {
return *(void**) (cd+1);
} else {
return cd + 1;
}
}
/* check_cdata returns the struct cdata* and pushes the user value onto the
* stack. Also dereferences references. */
void* check_cdata(lua_State* L, int idx, struct ctype* ct)
{
void* p = to_cdata(L, idx, ct);
if (ct->type == INVALID_TYPE) {
luaL_error(L, "expected cdata for arg #%d", idx);
}
return p;
}