diff --git a/library/LuaTools.test.cpp b/library/LuaTools.test.cpp new file mode 100644 index 0000000000..304ebf8899 --- /dev/null +++ b/library/LuaTools.test.cpp @@ -0,0 +1,74 @@ +// Regression test for https://github.com/DFHack/dfhack/issues/5450 +// (fixed by https://github.com/DFHack/dfhack/pull/5451) +// +// df::identity_traits::get() returns a pointer to a *const* instance of +// a type_identity subclass for most types. When the df_object constraint +// required a pointer to non-const type_identity, Lua::Push rejected those +// types and fell back to the bool overload, so e.g. a pointer to a vector +// arrived in Lua as a boolean. + +#include "LuaTools.h" +#include "LuaWrapper.h" + +#include +#include + +#include +#include + +// The constraint must accept types whose identity is exposed through a +// pointer-to-const. This is the compile-time half of the regression check: +// it fails to build if df_object ever becomes too strict again. +static_assert(DFHack::Lua::df_object>); +static_assert(DFHack::Lua::df_object); +static_assert(DFHack::Lua::df_object); + +class LuaPushTest : public testing::Test { +protected: + lua_State *L = nullptr; + + void SetUp() override + { + L = luaL_newstate(); + ASSERT_NE(nullptr, L); + luaL_openlibs(L); + DFHack::LuaWrapper::AttachDFGlobals(L); + } + + void TearDown() override + { + if (L) + lua_close(L); + } +}; + +TEST_F(LuaPushTest, PushVectorPointer) +{ + std::vector vec{ 7, 42 }; + + // NB: intentionally avoids df_object-constrained calls below so that + // this test still compiles if the constraint regresses - in that case + // Push() falls back to the bool overload and the checks fail at runtime + DFHack::Lua::Push(L, &vec); + + // the pointer must arrive as a wrapped DF object, not a boolean + ASSERT_EQ(LUA_TUSERDATA, lua_type(L, -1)); + ASSERT_EQ(DFHack::Lua::OBJ_REF, DFHack::Lua::IsDFObject(L, -1)); + + // the same pointer must round-trip back out of Lua + EXPECT_EQ(static_cast(&vec), + DFHack::Lua::GetDFObject(L, df::identity_traits>::get(), -1)); + + // and the pushed object must behave like the vector it wraps + lua_len(L, -1); + EXPECT_EQ(lua_Integer(vec.size()), lua_tointeger(L, -1)); + lua_pop(L, 1); + + lua_geti(L, -1, 0); + EXPECT_EQ(7, lua_tointeger(L, -1)); + lua_pop(L, 1); + + lua_geti(L, -1, 1); + EXPECT_EQ(42, lua_tointeger(L, -1)); + lua_pop(L, 1); +} diff --git a/library/include/LuaWrapper.h b/library/include/LuaWrapper.h index 258d4d254a..c913d05893 100644 --- a/library/include/LuaWrapper.h +++ b/library/include/LuaWrapper.h @@ -236,7 +236,7 @@ namespace LuaWrapper { void IndexStatics(lua_State *state, int meta_idx, int ftable_idx, struct_identity *pstruct); - void AttachDFGlobals(lua_State *state); + DFHACK_EXPORT void AttachDFGlobals(lua_State *state); DFHACK_EXPORT void notify_deprecated(lua_State* state, std::string_view name, std::string_view message); }}