Skip to content

Commit

Permalink
Fix extensible class ability to lookup static properties (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
kunitoki authored Oct 4, 2023
1 parent dd586f2 commit 2c93749
Show file tree
Hide file tree
Showing 10 changed files with 718 additions and 231 deletions.
285 changes: 167 additions & 118 deletions Source/LuaBridge/detail/CFunctions.h

Large diffs are not rendered by default.

14 changes: 12 additions & 2 deletions Source/LuaBridge/detail/ClassInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,12 @@ template <class T, auto = typeName<T>().find_first_of('.')>
*/
[[nodiscard]] inline const void* getIndexFallbackKey()
{
return reinterpret_cast<void*>(0x81ca);
return reinterpret_cast<void*>(0x81ca);
}

[[nodiscard]] inline const void* getIndexExtensibleKey()
{
return reinterpret_cast<void*>(0x81cb);
}

//=================================================================================================
Expand All @@ -152,7 +157,12 @@ template <class T, auto = typeName<T>().find_first_of('.')>
*/
[[nodiscard]] inline const void* getNewIndexFallbackKey()
{
return reinterpret_cast<void*>(0x8107);
return reinterpret_cast<void*>(0x8107);
}

[[nodiscard]] inline const void* getNewIndexExtensibleKey()
{
return reinterpret_cast<void*>(0x8108);
}

//=================================================================================================
Expand Down
2 changes: 1 addition & 1 deletion Source/LuaBridge/detail/FuncTraits.h
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ inline static constexpr bool is_proxy_member_function_v =
template <class T, class F>
inline static constexpr bool is_const_proxy_function_v =
is_proxy_member_function_v<T, F> &&
std::is_const_v<std::remove_pointer_t<function_argument_or_void_t<0, F>>>;
std::is_const_v<std::remove_reference_t<std::remove_pointer_t<function_argument_or_void_t<0, F>>>>;

//=================================================================================================
/**
Expand Down
23 changes: 23 additions & 0 deletions Source/LuaBridge/detail/LuaRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,29 @@ class LuaRefBase
return metatable.isTable() && metatable["__call"].isFunction();
}

/**
* @brief Get the name of the class, only if it is a C++ registered class via the library.
*
* @returns An optional string containing the name used to register the class with `beginClass`, nullopt in case it's not a registered class.
*/
std::optional<std::string> getClassName()
{
if (! isUserdata())
return std::nullopt;

const StackRestore stackRestore(m_L);

impl().push(m_L);
if (! lua_getmetatable(m_L, -1))
return std::nullopt;

lua_rawgetp(m_L, -1, detail::getTypeKey());
if (lua_isstring(m_L, -1))
return lua_tostring(m_L, -1);

return std::nullopt;
}

//=============================================================================================
/**
* @brief Perform a safe explicit conversion to the type T.
Expand Down
119 changes: 57 additions & 62 deletions Source/LuaBridge/detail/Namespace.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,24 +169,25 @@ class Namespace : public detail::Registrar
std::string type_name = std::string(trueConst ? "const " : "") + name;

// Stack: namespace table (ns)

lua_newtable(L); // Stack: ns, const table (co)
lua_pushvalue(L, -1); // Stack: ns, co, co
lua_setmetatable(L, -2); // co.__metatable = co. Stack: ns, co

pushunsigned(L, options.toUnderlying());
pushunsigned(L, options.toUnderlying()); // Stack: ns, co, options
lua_rawsetp(L, -2, detail::getClassOptionsKey()); // co [classOptionsKey] = options. Stack: ns, co

lua_pushstring(L, type_name.c_str());
lua_pushstring(L, type_name.c_str()); // Stack: ns, co, name
lua_rawsetp(L, -2, detail::getTypeKey()); // co [typeKey] = name. Stack: ns, co

lua_pushcfunction_x(L, &detail::index_metamethod);
rawsetfield(L, -2, "__index");
lua_pushcfunction_x(L, &detail::index_metamethod<true>); // Stack: ns, co, im
rawsetfield(L, -2, "__index"); // Stack: ns, co

lua_pushcfunction_x(L, &detail::newindex_object_metamethod);
rawsetfield(L, -2, "__newindex");
lua_pushcfunction_x(L, &detail::newindex_metamethod<true>); // Stack: ns, co, nim
rawsetfield(L, -2, "__newindex"); // Stack: ns, co

lua_newtable(L);
lua_rawsetp(L, -2, detail::getPropgetKey());
lua_newtable(L); // Stack: ns, co, tb
lua_rawsetp(L, -2, detail::getPropgetKey()); // Stack: ns, co

if (! options.test(visibleMetatables))
{
Expand Down Expand Up @@ -229,17 +230,21 @@ class Namespace : public detail::Registrar
LUABRIDGE_ASSERT(name != nullptr);

// Stack: namespace table (ns), const table (co), class table (cl)
lua_newtable(L); // Stack: ns, co, cl, visible static table (vst)
lua_newtable(L); // Stack: ns, co, cl, st, static metatable (st)
lua_pushvalue(L, -1); // Stack: ns, co, cl, vst, st, st
lua_setmetatable(L, -3); // st.__metatable = mt. Stack: ns, co, cl, vst, st
lua_insert(L, -2); // Stack: ns, co, cl, st, vst
rawsetfield(L, -5, name); // ns [name] = vst. Stack: ns, co, cl, st

lua_pushcfunction_x(L, &detail::index_metamethod);

lua_newtable(L); // Stack: ns, co, cl, static table (st)
lua_newtable(L); // Stack: ns, co, cl, st, static metatable (mt)
lua_pushvalue(L, -1); // Stack: ns, co, cl, st, mt, mt
lua_setmetatable(L, -3); // st.__metatable = mt. Stack: ns, co, cl, st, mt
lua_insert(L, -2); // Stack: ns, co, cl, st, mt, st
rawsetfield(L, -5, name); // ns [name] = st. Stack: ns, co, cl, st, mt

pushunsigned(L, options.toUnderlying()); // Stack: ns, co, cl, st, mt, options
lua_rawsetp(L, -2, detail::getClassOptionsKey()); // st [classOptionsKey] = options. Stack: ns, co, cl, st, mt

lua_pushcfunction_x(L, &detail::index_metamethod<false>);
rawsetfield(L, -2, "__index");

lua_pushcfunction_x(L, &detail::newindex_static_metamethod);
lua_pushcfunction_x(L, &detail::newindex_metamethod<false>);
rawsetfield(L, -2, "__newindex");

lua_newtable(L); // Stack: ns, co, cl, st, proget table (pg)
Expand Down Expand Up @@ -307,43 +312,39 @@ class Namespace : public detail::Registrar
lua_pop(L, 1); // Stack: ns

createConstTable(name, true, options); // Stack: ns, const table (co)
++m_stackSize;
#if !defined(LUABRIDGE_ON_LUAU)
lua_pushcfunction_x(L, &detail::gc_metamethod<T>); // Stack: ns, co, function
rawsetfield(L, -2, "__gc"); // co ["__gc"] = function. Stack: ns, co
#endif
++m_stackSize;
lua_pushcfunction_x(L, &detail::tostring_metamethod<T>);
rawsetfield(L, -2, "__tostring");

createClassTable(name, options); // Stack: ns, co, class table (cl)
++m_stackSize;
#if !defined(LUABRIDGE_ON_LUAU)
lua_pushcfunction_x(L, &detail::gc_metamethod<T>); // Stack: ns, co, cl, function
rawsetfield(L, -2, "__gc"); // cl ["__gc"] = function. Stack: ns, co, cl
#endif

lua_pushcfunction_x(L, &detail::tostring_metamethod<T>);
rawsetfield(L, -2, "__tostring");
++m_stackSize;

createStaticTable(name, options); // Stack: ns, co, cl, st
++m_stackSize;

lua_pushvalue(L, -1); // Stack: ns, co, cl, st, st
lua_rawsetp(L, -2, detail::getStaticKey()); // cl [staticKey] = st. Stack: ns, co, cl, st
lua_pushvalue(L, -1); // Stack: ns, co, cl, st, st
lua_rawsetp(L, -3, detail::getStaticKey()); // co [staticKey] = st. Stack: ns, co, cl, st

// Map T back to its tables.
lua_pushvalue(L, -1); // Stack: ns, co, cl, st, st
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getStaticRegistryKey<T>()); // Stack: ns, co, cl, st
lua_pushvalue(L, -2); // Stack: ns, co, cl, st, cl
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey<T>()); // Stack: ns, co, cl, st
lua_pushvalue(L, -3); // Stack: ns, co, cl, st, co
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getConstRegistryKey<T>()); // Stack: ns, co, cl, st

// Setup class extensibility
if (options.test(extensibleClass))
{
lua_pushcfunction_x(L, &detail::newindex_extended_class); // Stack: ns, co, cl, fn
lua_rawsetp(L, -2, detail::getNewIndexFallbackKey()); // Stack: ns, co, cl

lua_pushvalue(L, -1); // Stack: ns, co, cl, st, st
lua_pushcclosure_x(L, &detail::index_extended_class, 1); // Stack: ns, co, cl, fn
lua_rawsetp(L, -3, detail::getIndexFallbackKey()); // Stack: ns, co, cl
}
}
else
{
Expand Down Expand Up @@ -380,24 +381,31 @@ class Namespace : public detail::Registrar
LUABRIDGE_ASSERT(lua_istable(L, -1)); // Stack: namespace table (ns)

createConstTable(name, true, options); // Stack: ns, const table (co)
++m_stackSize;
#if !defined(LUABRIDGE_ON_LUAU)
lua_pushcfunction_x(L, &detail::gc_metamethod<T>); // Stack: ns, co, function
rawsetfield(L, -2, "__gc"); // co ["__gc"] = function. Stack: ns, co
#endif
++m_stackSize;
lua_pushcfunction_x(L, &detail::tostring_metamethod<T>);
rawsetfield(L, -2, "__tostring");

createClassTable(name, options); // Stack: ns, co, class table (cl)
++m_stackSize;
#if !defined(LUABRIDGE_ON_LUAU)
lua_pushcfunction_x(L, &detail::gc_metamethod<T>); // Stack: ns, co, cl, function
rawsetfield(L, -2, "__gc"); // cl ["__gc"] = function. Stack: ns, co, cl
#endif
lua_pushcfunction_x(L, &detail::tostring_metamethod<T>);
rawsetfield(L, -2, "__tostring");
++m_stackSize;

createStaticTable(name, options); // Stack: ns, co, cl, st
++m_stackSize;

lua_pushvalue(L, -1); // Stack: ns, co, cl, st, st
lua_rawsetp(L, -2, detail::getStaticKey()); // cl [staticKey] = st. Stack: ns, co, cl, st
lua_pushvalue(L, -1); // Stack: ns, co, cl, st, st
lua_rawsetp(L, -3, detail::getStaticKey()); // co [staticKey] = st. Stack: ns, co, cl, st

lua_rawgetp(L, LUA_REGISTRYINDEX, staticKey); // Stack: ns, co, cl, st, parent st (pst) | nil
if (lua_isnil(L, -1)) // Stack: ns, co, cl, st, nil
{
Expand Down Expand Up @@ -425,17 +433,6 @@ class Namespace : public detail::Registrar
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey<T>()); // Stack: ns, co, cl, st
lua_pushvalue(L, -3); // Stack: ns, co, cl, st, co
lua_rawsetp(L, LUA_REGISTRYINDEX, detail::getConstRegistryKey<T>()); // Stack: ns, co, cl, st

// Setup class extensibility
if (options.test(extensibleClass))
{
lua_pushcfunction_x(L, &detail::newindex_extended_class); // Stack: ns, co, cl, fn
lua_rawsetp(L, -2, detail::getNewIndexFallbackKey()); // Stack: ns, co, cl

lua_pushvalue(L, -1); // Stack: ns, co, cl, st, st
lua_pushcclosure_x(L, &detail::index_extended_class, 1); // Stack: ns, co, cl, fn
lua_rawsetp(L, -3, detail::getIndexFallbackKey()); // Stack: ns, co, cl
}
}

//=========================================================================================
Expand Down Expand Up @@ -1493,27 +1490,25 @@ class Namespace : public detail::Registrar
{
LUABRIDGE_ASSERT(lua_istable(L, -1));

{
lua_pushvalue(L, -1); // Stack: ns, ns
lua_pushvalue(L, -1); // Stack: ns, ns

// ns.__metatable = ns
lua_setmetatable(L, -2); // Stack: ns
// ns.__metatable = ns
lua_setmetatable(L, -2); // Stack: ns

// ns.__index = index_metamethod
lua_pushcfunction_x(L, &detail::index_metamethod);
rawsetfield(L, -2, "__index"); // Stack: ns
// ns.__index = index_static_metamethod
lua_pushcfunction_x(L, &detail::index_metamethod<false>);
rawsetfield(L, -2, "__index"); // Stack: ns

lua_newtable(L); // Stack: ns, mt, propget table (pg)
lua_rawsetp(L, -2, detail::getPropgetKey()); // ns [propgetKey] = pg. Stack: ns
lua_newtable(L); // Stack: ns, mt, propget table (pg)
lua_rawsetp(L, -2, detail::getPropgetKey()); // ns [propgetKey] = pg. Stack: ns

lua_newtable(L); // Stack: ns, mt, propset table (ps)
lua_rawsetp(L, -2, detail::getPropsetKey()); // ns [propsetKey] = ps. Stack: ns
lua_newtable(L); // Stack: ns, mt, propset table (ps)
lua_rawsetp(L, -2, detail::getPropsetKey()); // ns [propsetKey] = ps. Stack: ns

if (! options.test(visibleMetatables))
{
lua_pushboolean(L, 0);
rawsetfield(L, -2, "__metatable");
}
if (! options.test(visibleMetatables))
{
lua_pushboolean(L, 0);
rawsetfield(L, -2, "__metatable");
}

++m_stackSize;
Expand Down Expand Up @@ -1548,12 +1543,12 @@ class Namespace : public detail::Registrar
// ns.__metatable = ns
lua_setmetatable(L, -2); // Stack: pns, ns

// ns.__index = index_metamethod
lua_pushcfunction_x(L, &detail::index_metamethod);
// ns.__index = index_static_metamethod
lua_pushcfunction_x(L, &detail::index_metamethod<false>);
rawsetfield(L, -2, "__index"); // Stack: pns, ns

// ns.__newindex = newindex_static_metamethod
lua_pushcfunction_x(L, &detail::newindex_static_metamethod);
lua_pushcfunction_x(L, &detail::newindex_metamethod<false>);
rawsetfield(L, -2, "__newindex"); // Stack: pns, ns

lua_newtable(L); // Stack: pns, ns, propget table (pg)
Expand Down
35 changes: 15 additions & 20 deletions Source/LuaBridge/detail/Userdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,8 @@ class Userdata
const void* registryClassKey,
bool canBeConst)
{
index = lua_absindex(L, index);

lua_getmetatable(L, index); // Stack: object metatable (ot) | nil
if (!lua_istable(L, -1))
const int result = lua_getmetatable(L, index); // Stack: object metatable (ot) | nil
if (result == 0 || !lua_istable(L, -1))
{
lua_rawgetp(L, LUA_REGISTRYINDEX, registryClassKey); // Stack: ot | nil, registry metatable (rt) | nil
return throwBadArg(L, index);
Expand All @@ -85,15 +83,10 @@ class Userdata
// -> canBeConst = false, isConst = true
// -> 'Class' registry table, 'const Class' object table
// -> 'expected Class, got const Class'
bool isConst = lua_isnil(L, -1); // Stack: ot | nil, nil, rt
if (isConst && canBeConst)
{
lua_rawgetp(L, LUA_REGISTRYINDEX, registryConstKey); // Stack: ot, nil, rt
}
else
{
lua_rawgetp(L, LUA_REGISTRYINDEX, registryClassKey); // Stack: ot, co, rt
}
const bool isConst = lua_isnil(L, -1); // Stack: ot | nil, nil, rt
lua_rawgetp(L, LUA_REGISTRYINDEX, (isConst && canBeConst)
? registryConstKey
: registryClassKey); // Stack: ot, co | nil, rt

lua_insert(L, -3); // Stack: rt, ot, co | nil
lua_pop(L, 1); // Stack: rt, ot
Expand Down Expand Up @@ -122,21 +115,19 @@ class Userdata
// no return
}

static bool isInstance(lua_State* L, int index, const void* registryClassKey)
static bool isInstance(lua_State* L, int index, const void* registryKey)
{
index = lua_absindex(L, index);

int result = lua_getmetatable(L, index); // Stack: object metatable (ot) | nothing
const auto result = lua_getmetatable(L, index); // Stack: object metatable (ot) | nil
if (result == 0)
return false; // Nothing was pushed on the stack
return false;

if (!lua_istable(L, -1))
{
lua_pop(L, 1); // Stack: -
return false;
}

lua_rawgetp(L, LUA_REGISTRYINDEX, registryClassKey); // Stack: ot, rt
lua_rawgetp(L, LUA_REGISTRYINDEX, registryKey); // Stack: ot, rt
lua_insert(L, -2); // Stack: rt, ot

for (;;)
Expand All @@ -152,12 +143,15 @@ class Userdata

if (lua_isnil(L, -1)) // Stack: rt, ot, nil
{
// Drop the object metatable because it may be some parent metatable
lua_pop(L, 3); // Stack: -
return false;
}

lua_remove(L, -2); // Stack: rt, pot
}

// no return
}

static Userdata* throwBadArg(lua_State* L, int index)
Expand Down Expand Up @@ -254,7 +248,8 @@ class Userdata
template <class T>
static bool isInstance(lua_State* L, int index)
{
return isInstance(L, index, detail::getClassRegistryKey<T>());
return isInstance(L, index, detail::getClassRegistryKey<T>())
|| isInstance(L, index, detail::getConstRegistryKey<T>());
}

protected:
Expand Down
Loading

0 comments on commit 2c93749

Please sign in to comment.