Files
Goober5000andClaude Fable 5 9bf5b6acf4 harden move operations in w_bank and LuaThread
w_bank: add a move constructor.  object_copy_including_array_member
returns by value, and without a move constructor the return fell back
to the shallow copy constructor whenever NRVO was not applied (e.g.
MSVC debug builds), after which the local's destructor deleted the
freshly allocated arrays -- leaving the stored bank dangling and
double-deleting at model unload.  Also add noexcept to the move
operations, replace the destructor call in move-assignment with
explicit delete[]s (using an object after its lifetime has ended is
undefined behavior), guard against self-move, and document that the
defaulted copy constructor is intentionally shallow for
object_copy_including_array_member's copy-then-replace pattern.

LuaThread: declare the defaulted move operations noexcept.  This
exposed that LuaTable was not movable at all (its user-declared
destructor suppressed the implicit moves), so LuaThread's implicit
move spec was formally throwing -- a hard error on gcc 9, which
still requires the spec on a defaulted redeclaration to match the
implicit one.  Restore LuaTable's rule of five and declare the
LuaValue and LuaFunction copy operations noexcept (they only copy a
shared_ptr, a raw pointer, and an enum), making the noexcept claims
hold throughout the hierarchy.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-06 13:33:49 -04:00

125 lines
2.5 KiB
C++

#include "LuaValue.h"
#include "LuaException.h"
namespace {
using namespace luacpp;
ValueType luaToEnumType(int luaType) {
switch (luaType) {
case LUA_TNONE:
return ValueType::NONE;
case LUA_TNIL:
return ValueType::NIL;
case LUA_TBOOLEAN:
return ValueType::BOOLEAN;
case LUA_TLIGHTUSERDATA:
return ValueType::LIGHTUSERDATA;
case LUA_TNUMBER:
return ValueType::NUMBER;
case LUA_TSTRING:
return ValueType::STRING;
case LUA_TTABLE:
return ValueType::TABLE;
case LUA_TFUNCTION:
return ValueType::FUNCTION;
case LUA_TUSERDATA:
return ValueType::USERDATA;
case LUA_TTHREAD:
return ValueType::THREAD;
default:
return ValueType::NONE;
}
}
}
namespace luacpp {
LuaValue LuaValue::createNil(lua_State* L)
{
lua_pushnil(L);
LuaValue val(L);
val.setReference(UniqueLuaReference::create(L));
return val;
}
LuaValue::LuaValue() = default;
LuaValue::LuaValue(lua_State* state) : _luaState(state)
{
Assertion(state != nullptr, "Lua state pointer is not valid!");
}
LuaValue::LuaValue(const LuaValue&) noexcept = default;
LuaValue& LuaValue::operator=(const LuaValue&) noexcept = default;
LuaValue::LuaValue(LuaValue&&) noexcept = default;
LuaValue& LuaValue::operator=(LuaValue&&) noexcept = default;
LuaValue::~LuaValue() = default;
void LuaValue::setReference(const LuaReference& ref)
{
this->_reference = ref;
this->_luaState = ref->getState();
if (isValid()) {
this->_reference->pushValue(_luaState);
this->_luaType = luaToEnumType(lua_type(_luaState, -1));
lua_pop(_luaState, 1);
} else {
_luaType = ValueType::NONE;
}
}
const LuaReference LuaValue::getReference() const {
return _reference;
}
ValueType LuaValue::getValueType() const {
return _luaType;
}
bool LuaValue::isValid() const {
return _reference && _reference->isValid();
}
bool LuaValue::pushValue(lua_State* thread, bool manualStackAllocation) const
{
if (this->_reference->isValid()) {
this->_reference->pushValue(thread, manualStackAllocation);
return true;
} else {
return false;
}
}
lua_State* LuaValue::getLuaState() const { return _luaState; }
namespace convert {
void pushValue(lua_State* luaState, const LuaValue& value) { value.pushValue(luaState); }
bool popValue(lua_State* luaState, LuaValue& target, int stackposition, bool remove)
{
if (!internal::isValidIndex(luaState, stackposition)) {
return false;
}
target.setReference(UniqueLuaReference::create(luaState, stackposition));
if (remove) {
lua_remove(luaState, stackposition);
}
return true;
}
}
}