Merge remote-tracking branch 'lethosor/lua-ref-target' into develop

develop
lethosor 2020-07-15 00:04:37 -04:00
commit db004f484d
6 changed files with 75 additions and 12 deletions

@ -158,7 +158,9 @@ that don't fit any of the other reference types. Such
references can only appear as a value of a pointer field,
or as a result of calling the ``_field()`` method.
They behave as structs with one field ``value`` of the right type.
They behave as structs with a ``value`` field of the right type. If the
object's XML definition has a ``ref-target`` attribute, they will also have
a read-only ``ref_target`` field set to the corresponding type object.
To make working with numeric buffers easier, they also allow
numeric indices. Note that other than excluding negative values

@ -536,6 +536,7 @@ static void field_reference(lua_State *state, const struct_field_info *field, vo
case struct_field_info::PRIMITIVE:
case struct_field_info::SUBSTRUCT:
push_object_internal(state, field->type, ptr);
get_object_ref_header(state, -1)->field_info = field;
return;
case struct_field_info::POINTER:
@ -706,6 +707,17 @@ static type_identity *find_primitive_field(lua_State *state, int field, const ch
*/
static int meta_primitive_index(lua_State *state)
{
const char *attr = lua_tostring(state, -1);
if (strcmp(attr, "ref_target") == 0) {
const struct_field_info *field_info = get_object_ref_header(state, 1)->field_info;
if (field_info && field_info->extra && field_info->extra->ref_target) {
LookupInTable(state, field_info->extra->ref_target, &DFHACK_TYPEID_TABLE_TOKEN);
} else {
lua_pushnil(state);
}
return 1;
}
uint8_t *ptr = get_object_addr(state, 1, 2, "read");
auto type = find_primitive_field(state, 2, "read", &ptr);
if (!type)
@ -1304,6 +1316,8 @@ static void MakePrimitiveMetatable(lua_State *state, type_identity *type)
{
EnableMetaField(state, base+2, "value", type);
AssociateId(state, base+3, 1, "value");
EnableMetaField(state, base+2, "ref_target", NULL);
}
// Add the iteration metamethods

@ -170,18 +170,24 @@ void LuaWrapper::push_object_ref(lua_State *state, void *ptr)
// stack: [metatable]
auto ref = (DFRefHeader*)lua_newuserdata(state, sizeof(DFRefHeader));
ref->ptr = ptr;
ref->field_info = NULL;
lua_swap(state);
lua_setmetatable(state, -2);
// stack: [userdata]
}
void *LuaWrapper::get_object_ref(lua_State *state, int val_index)
DFRefHeader *LuaWrapper::get_object_ref_header(lua_State *state, int val_index)
{
assert(!lua_islightuserdata(state, val_index));
auto ref = (DFRefHeader*)lua_touserdata(state, val_index);
return ref->ptr;
return ref;
}
void *LuaWrapper::get_object_ref(lua_State *state, int val_index)
{
return get_object_ref_header(state, val_index)->ptr;
}
/**

@ -126,6 +126,7 @@ namespace LuaWrapper {
*/
struct DFRefHeader {
void *ptr;
const struct_field_info *field_info;
};
/**
@ -133,15 +134,7 @@ namespace LuaWrapper {
*/
void push_object_ref(lua_State *state, void *ptr);
DFHACK_EXPORT void *get_object_ref(lua_State *state, int val_index);
/*
* The system might be extended to carry some simple
* objects inline inside the reference buffer.
*/
inline bool is_self_contained(DFRefHeader *ptr) {
void **pp = &ptr->ptr;
return **(void****)pp == (pp + 1);
}
DFHACK_EXPORT DFRefHeader *get_object_ref_header(lua_State *state, int val_index);
/**
* Report an error while accessing a field (index = field name).

@ -69,6 +69,19 @@ function expect.error(func, ...)
return true
end
end
function expect.error_match(func, matcher, ...)
local ok, err = pcall(func, ...)
if ok then
return false, 'no error raised by function call'
elseif type(matcher) == 'string' then
if not tostring(err):match(matcher) then
return false, ('error "%s" did not match "%s"'):format(err, matcher)
end
elseif not matcher(err) then
return false, ('error "%s" did not satisfy matcher'):format(err)
end
return true
end
function expect.pairs_contains(table, key, comment)
for k, v in pairs(table) do
if k == key then

@ -0,0 +1,35 @@
function test.get()
dfhack.with_temp_object(df.unit:new(), function(unit)
expect.eq(unit:_field('hist_figure_id').ref_target, df.historical_figure)
end)
end
function test.get_nil()
dfhack.with_temp_object(df.coord:new(), function(coord)
expect.nil_(coord:_field('x').ref_target)
end)
end
function test.get_non_primitive()
dfhack.with_temp_object(df.unit:new(), function(unit)
expect.error_match(function()
return unit:_field('status').ref_target
end, 'not found')
end)
end
function test.set()
dfhack.with_temp_object(df.unit:new(), function(unit)
expect.error_match(function()
unit:_field('hist_figure_id').ref_target = df.coord
end, 'builtin property or method')
end)
end
function test.set_non_primitive()
dfhack.with_temp_object(df.unit:new(), function(unit)
expect.error_match(function()
unit:_field('status').ref_target = df.coord
end, 'not found')
end)
end