Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 69 additions & 53 deletions Source/LuaBridge/detail/CFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,44 @@ inline bool is_metamethod(std::string_view method_name)
"__unm"
);

if (method_name.size() <= 2 || method_name[0] != '_' || method_name[1] != '_')
return false;

auto result = std::lower_bound(metamethods.begin(), metamethods.end(), method_name);
return result != metamethods.end() && *result == method_name;
}

/**
* @brief Make super method name.
*/
inline std::string make_super_method_name(const char* name)
{
LUABRIDGE_ASSERT(name != nullptr);

return (std::string_view(name).find("_") == 0u)
? (std::string("super") + name)
: (std::string("super_") + name);
}

//=================================================================================================
/**
* @brief Make super method name.
*/
inline Options get_class_options(lua_State* L, int index)
{
LUABRIDGE_ASSERT(lua_istable(L, index)); // Stack: mt

Options options = defaultOptions;

lua_rawgetp(L, index, getClassOptionsKey()); // Stack: mt, ifb (may be nil)
if (lua_isnumber(L, -1))
options = Options::fromUnderlying(lua_tointeger(L, -1));

lua_pop(L, 1);

return options;
}

//=================================================================================================
/**
* @brief __index metamethod for a namespace or class static and non-static members.
Expand Down Expand Up @@ -196,28 +230,16 @@ inline int index_metamethod(lua_State* L)

// Protect internal meta methods
const char* key = lua_tostring(L, 2);
if (key != nullptr)
if (key != nullptr && is_metamethod(key))
{
const auto name = std::string_view(key);
if (name.size() > 2 && name[0] == '_' && name[1] == '_' && is_metamethod(name))
{
lua_pushnil(L);
return 1;
}
lua_pushnil(L);
return 1;
}

for (;;)
{
// Obtain class options
Options options = defaultOptions;

lua_rawgetp(L, -1, getClassOptionsKey()); // Stack: mt, ifb (may be nil)
if (lua_isnumber(L, -1))
options = Options::fromUnderlying(lua_tointeger(L, -1));

lua_pop(L, 1);

// If we allow method overriding, we need to prioritise it
const Options options = get_class_options(L, -1); // Stack: mt
if (options.test(allowOverridingMethods))
{
if (auto result = try_call_index_fallback(L))
Expand Down Expand Up @@ -289,15 +311,18 @@ inline int index_metamethod(lua_State* L)

inline std::optional<int> try_call_newindex_fallback(lua_State* L, const char* key)
{
LUABRIDGE_ASSERT(key != nullptr);
LUABRIDGE_ASSERT(lua_istable(L, -1)); // Stack: mt

lua_rawgetp(L, -1, getNewIndexFallbackKey()); // Stack: mt, nifb (may be nil)
lua_rawgetp(L, -1, getNewIndexFallbackKey()); // Stack: mt, nifb | nil
if (! lua_iscfunction(L, -1))
{
lua_pop(L, 1); // Stack: mt
return std::nullopt;
}

const bool is_key_metamethod = is_metamethod(key);

lua_pushvalue(L, -2); // Stack: mt, nifb, mt

for (;;)
Expand All @@ -318,49 +343,49 @@ inline std::optional<int> try_call_newindex_fallback(lua_State* L, const char* k
lua_pushvalue(L, 2); // Stack: mt, nifb, mt, ct, field name
lua_rawget(L, -2); // Stack: mt, nifb, mt, ct, field | nil

if (! lua_isnil(L, -1)) // Stack: mt, nifb, mt, ct, nil
if (! lua_isnil(L, -1)) // Stack: mt, nifb, mt, ct, field
{
Options options = defaultOptions;
lua_rawgetp(L, -2, getClassOptionsKey()); // Stack: mt, nifb, mt, ct, field, opt | nil
if (lua_isnumber(L, -1))
options = Options::fromUnderlying(lua_tointeger(L, -1));
lua_pop(L, 1); // Stack: mt, nifb, mt, ct, field

// Obtain class options
const Options options = get_class_options(L, -2); // Stack: mt, nifb, mt, ct, field,
if (! options.test(allowOverridingMethods))
{
lua_pop(L, 5); // Stack: -
luaL_error(L, "immutable member '%s'", key);
return 0;
}

lua_getmetatable(L, 1); // Stack: mt, nifb, mt, ct, field, mt2
lua_pushvalue(L, -2); // Stack: mt, nifb, mt, ct, field, mt2, field
rawsetfield(L, -2, (std::string("super_") + key).c_str()); // Stack: mt, nifb, mt, ct, field, mt2
rawsetfield(L, -2, make_super_method_name(key).c_str()); // Stack: mt, nifb, mt, ct, field, mt2

lua_pop(L, 3); // Stack: mt, nifb, mt
lua_pop(L, 2); // Stack: mt, nifb, mt, ct
break;
}

lua_pop(L, 2); // Stack: mt, nifb, mt
lua_pop(L, 1); // Stack: mt, nifb, mt, ct

lua_rawgetp(L, -1, getParentKey()); // Stack: mt, nifb, mt, parent mt | nil
if (lua_isnil(L, -1)) // Stack: mt, nifb, mt
lua_rawgetp(L, -2, getParentKey()); // Stack: mt, nifb, mt, ct, parent mt (pmt) | nil
if (lua_isnil(L, -1)) // Stack: mt, nifb, mt, ct, nil
{
lua_pop(L, 1); // Stack: mt, nifb, mt
lua_pop(L, 1); // Stack: mt, nifb, mt, ct
break;
}

LUABRIDGE_ASSERT(lua_istable(L, -1)); // Stack: mt, nifb, mt, parent mt
lua_remove(L, -2); // Stack: mt, nifb, parent mt
LUABRIDGE_ASSERT(lua_istable(L, -1)); // Stack: mt, nifb, mt, ct, pmt
lua_remove(L, -2); // Stack: mt, nifb, mt, pmt
lua_remove(L, -2); // Stack: mt, nifb, pmt
}

lua_pop(L, 1); // Stack: mt, nifb
if (is_key_metamethod)
{
lua_remove(L, -2); // Stack: mt, nifb, ct
}
else
{
lua_pop(L, 2); // Stack: mt, nifb
lua_pushvalue(L, 1); // Stack: mt, nifb, arg1
}

lua_pushvalue(L, 2); // Stack: mt, nifb, arg1 | ct, arg2
lua_pushvalue(L, 3); // Stack: mt, nifb, arg1 | ct, arg2, arg3
lua_call(L, 3, 0); // Stack: mt

lua_remove(L, -2); // Stack: nifb
lua_pushvalue(L, 1); // Stack: nifb, arg1
lua_pushvalue(L, 2); // Stack: nifb, arg1, arg2
lua_pushvalue(L, 3); // Stack: nifb, arg1, arg2, arg3
lua_call(L, 3, 0); // Stack: -
return 0;
}

Expand All @@ -382,11 +407,7 @@ inline int newindex_metamethod(lua_State* L, bool pushSelf)
// Try in the property set table
lua_rawgetp(L, -1, getPropsetKey()); // Stack: mt, propset table (ps) | nil
if (lua_isnil(L, -1)) // Stack: mt, nil
{
lua_pop(L, 2); // Stack: -
luaL_error(L, "no member named '%s'", key);
return 0;
}

LUABRIDGE_ASSERT(lua_istable(L, -1));

Expand Down Expand Up @@ -414,11 +435,7 @@ inline int newindex_metamethod(lua_State* L, bool pushSelf)
// Try in the parent
lua_rawgetp(L, -1, getParentKey()); // Stack: mt, parent mt | nil
if (lua_isnil(L, -1)) // Stack: mt, nil
{
lua_pop(L, 2); // Stack: -
luaL_error(L, "no writable member '%s'", key);
return 0;
}

LUABRIDGE_ASSERT(lua_istable(L, -1)); // Stack: mt, parent mt
lua_remove(L, -2); // Stack: parent mt
Expand Down Expand Up @@ -510,7 +527,7 @@ inline int newindex_extended_class(lua_State* L)
template <class C>
static int tostring_metamethod(lua_State* L)
{
const void* ptr = lua_topointer(L, -1);
const void* ptr = lua_topointer(L, 1);

lua_getmetatable(L, -1); // Stack: metatable (mt)
lua_rawgetp(L, -1, getTypeKey()); // Stack: mt, classname (cn)
Expand Down Expand Up @@ -1003,8 +1020,7 @@ inline int try_overload_functions(lua_State* L)
if (err == LUABRIDGE_LUA_OK)
{
// calculate number of return values and return
const int nresults = lua_gettop(L) - nargs - 4; // 4: overloads, errors, key, table
return nresults;
return lua_gettop(L) - nargs - 4; // 4: overloads, errors, key, table
}
else if (err == LUA_ERRRUN)
{
Expand Down
2 changes: 2 additions & 0 deletions Source/LuaBridge/detail/Namespace.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,8 @@ class Namespace : public detail::Registrar
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
Expand Down
1 change: 1 addition & 0 deletions Tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ add_subdirectory(${LUABRIDGE_RAVI_LOCATION} ravi)
set (LUABRIDGE_TEST_SOURCE_FILES
Source/AmalgamateTests.cpp
Source/ArrayTests.cpp
Source/ClassExtensibleTests.cpp
Source/ClassTests.cpp
Source/CoroutineTests.cpp
Source/EnumTests.cpp
Expand Down
Loading