Implement recursive transfer of values from lua to c++ structures.

E.g. df.global.cursor = { x = 1, y = 2, z = 3 }. The lua data
must be represented by raw lua tables.

For structs, the entries in the table are assigned to matching fields.

For containers, if a 'resize' field is missing or nil, the table is
treated like 1-based lua array, and the container is resized to match
its # length. Otherwise, the field must be either an explicit number,
true or false. If it is true, the size is selected by the highest index
in the table. After that, entries are copied using 0-based indices.

For pointers, the table must match the target object. If the pointer
is null, the object is auto-allocated; this can be controlled using
the 'new' field, the value of which will be passed to df.new().
develop
Alexander Gavrilov 2012-03-28 01:47:52 +08:00 committed by Petr Mrázek
parent d2d16271f0
commit b76bdad50f
3 changed files with 181 additions and 11 deletions

@ -65,9 +65,22 @@ void constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
push_object_internal(state, this, ptr);
}
static void invoke_assign(lua_State *state, type_identity *id, void *ptr, int val_index)
{
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME);
push_object_internal(state, id, ptr);
lua_pushvalue(state, val_index);
lua_call(state, 2, 0);
}
void constructed_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
{
field_error(state, fname_idx, "complex object", "write");
if (lua_istable(state, val_index))
{
invoke_assign(state, this, ptr, val_index);
}
else
field_error(state, fname_idx, "complex object", "write");
}
void enum_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
@ -150,6 +163,47 @@ void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
lua_read(state, fname_idx, ptr, target);
}
static void autovivify_ptr(lua_State *state, int fname_idx, void **pptr,
type_identity *target, int val_index)
{
lua_getfield(state, val_index, "new");
if (!lua_isnil(state, -1))
{
int top = lua_gettop(state);
type_identity *suggested = get_object_identity(state, top, "autovivify", true, true);
if (!is_type_compatible(state, target, 0, suggested, top+1, false))
field_error(state, fname_idx, "incompatible suggested autovivify type", "write");
lua_pop(state, 1);
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME);
lua_swap(state);
lua_call(state, 1, 1);
void *nval = get_object_internal(state, target, top, false);
if (!nval)
field_error(state, fname_idx, "inconsistent autovivify type", "write");
*pptr = nval;
}
else
{
if (!target)
field_error(state, fname_idx, "trying to autovivify void*", "write");
*pptr = target->allocate();
if (!*pptr)
field_error(state, fname_idx, "could not allocate in autovivify", "write");
}
lua_pop(state, 1);
}
void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr,
type_identity *target, int val_index)
{
@ -157,6 +211,13 @@ void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr,
if (lua_isnil(state, val_index))
*pptr = NULL;
else if (lua_istable(state, val_index))
{
if (!*pptr)
autovivify_ptr(state, fname_idx, pptr, target, val_index);
invoke_assign(state, target, *pptr, val_index);
}
else
{
void *nval = get_object_internal(state, target, val_index, false);
@ -435,10 +496,15 @@ static void write_field(lua_State *state, const struct_field_info *field, void *
case struct_field_info::POINTER:
df::pointer_identity::lua_write(state, 2, ptr, field->type, value_idx);
return;
case struct_field_info::STATIC_ARRAY:
case struct_field_info::STL_VECTOR_PTR:
field_error(state, 2, "complex object", "write");
lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME);
read_field(state, field, ptr);
lua_pushvalue(state, value_idx);
lua_call(state, 2, 0);
return;
case struct_field_info::END:
return;

@ -255,8 +255,8 @@ static void fetch_container_details(lua_State *state, int meta, type_identity **
/**
* Check if type1 and type2 are compatible, possibly using additional metatable data.
*/
static bool is_type_compatible(lua_State *state, type_identity *type1, int meta1,
type_identity *type2, int meta2, bool exact_equal)
bool LuaWrapper::is_type_compatible(lua_State *state, type_identity *type1, int meta1,
type_identity *type2, int meta2, bool exact_equal)
{
if (type1 == type2)
return true;
@ -417,9 +417,9 @@ static bool is_valid_metatable(lua_State *state, int objidx, int metaidx)
/**
* Given a DF object reference or type, safely retrieve its identity pointer.
*/
static type_identity *get_object_identity(lua_State *state, int objidx,
const char *ctx, bool allow_type = false,
bool keep_metatable = false)
type_identity *LuaWrapper::get_object_identity(lua_State *state, int objidx,
const char *ctx, bool allow_type,
bool keep_metatable)
{
if (!lua_getmetatable(state, objidx))
luaL_error(state, "Invalid object in %s", ctx);
@ -608,6 +608,31 @@ static int meta_new(lua_State *state)
return 1;
}
static void invoke_resize(lua_State *state, int table, lua_Integer size)
{
lua_getfield(state, table, "resize");
lua_pushvalue(state, table);
lua_pushinteger(state, size);
lua_call(state, 2, 0);
}
static void copy_table(lua_State *state, int dest, int src, int skipkey)
{
lua_pushnil(state);
while (lua_next(state, src))
{
if (lua_equal(state, -2, skipkey))
lua_pop(state, 1);
else
{
lua_pushvalue(state, -2);
lua_swap(state);
lua_settable(state, dest);
}
}
}
/**
* Method: assign data between objects.
*/
@ -618,11 +643,83 @@ static int meta_assign(lua_State *state)
if (argc != 2)
luaL_error(state, "Usage: target:assign(src) or df.assign(target,src)");
type_identity *id1, *id2;
check_type_compatible(state, 1, 2, &id1, &id2, "df.assign()", false, false);
if (!lua_istable(state, 2))
{
type_identity *id1, *id2;
check_type_compatible(state, 1, 2, &id1, &id2, "df.assign()", false, false);
if (!id1->copy(get_object_ref(state, 1), get_object_ref(state, 2)))
luaL_error(state, "No copy support for %s", id1->getFullName().c_str());
}
else
{
type_identity *id = get_object_identity(state, 1, "df.assign()", false);
if (id->isContainer())
{
lua_pushstring(state, "resize");
int resize_str = lua_gettop(state);
if (!id1->copy(get_object_ref(state, 1), get_object_ref(state, 2)))
luaL_error(state, "No copy support for %s", id1->getFullName().c_str());
lua_dup(state);
lua_rawget(state, 2);
if (lua_isnil(state,-1))
{
/*
* nil or missing resize field => 1-based lua array
*/
int size = lua_objlen(state, 2);
lua_pop(state, 1);
invoke_resize(state, 1, size);
for (int i = 1; i <= size; i++)
{
lua_pushinteger(state, i-1);
lua_rawgeti(state, 2, i);
lua_settable(state, 1);
}
}
else
{
if (lua_isboolean(state, -1))
{
// resize=false => just assign
// resize=true => find the largest index
if (lua_toboolean(state, -1))
{
lua_Integer size = 0;
lua_pushnil(state);
while (lua_next(state, 2))
{
lua_pop(state, 1);
if (lua_isnumber(state,-1))
size = std::max(size, lua_tointeger(state,-1)+1);
}
invoke_resize(state, 1, size);
}
}
else
{
// otherwise, must be an explicit number
if (!lua_isnumber(state,-1))
luaL_error(state, "Invalid container.resize value in df.assign()");
invoke_resize(state, 1, lua_tointeger(state, -1));
}
lua_pop(state, 1);
copy_table(state, 1, 2, resize_str);
}
}
else
{
lua_pushstring(state, "new");
copy_table(state, 1, 2, lua_gettop(state));
}
}
return 0;
}

@ -149,6 +149,13 @@ namespace DFHack { namespace LuaWrapper {
*/
uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode);
bool is_type_compatible(lua_State *state, type_identity *type1, int meta1,
type_identity *type2, int meta2, bool exact_equal);
type_identity *get_object_identity(lua_State *state, int objidx,
const char *ctx, bool allow_type = false,
bool keep_metatable = false);
void LookupInTable(lua_State *state, void *id, const char *tname);
void SaveInTable(lua_State *state, void *node, const char *tname);
void SaveTypeInfo(lua_State *state, void *node);