#include "LuaFunction.h" #include "LuaException.h" #include "LuaHeaders.h" namespace luacpp { namespace { int stdFunctionDestructor(lua_State* L) { // The userdata is the function object so we call the destructor here to free up the internal resources const auto luaPtr = lua_touserdata(L, 1); const auto funcPtr = static_cast(luaPtr); funcPtr->~LuaFunctionObject(); return 0; } int stdFunctionCaller(lua_State* L) { const auto numArgs = lua_gettop(L); // Push the actual function object on the stack lua_pushvalue(L, lua_upvalueindex(1)); const auto funcObj = static_cast(lua_touserdata(L, -1)); lua_pop(L, 1); // We have the pointer so we can remove this from the stack again LuaValueList params; params.reserve(numArgs); for (int i = 1; i <= numArgs; ++i) { LuaValue val; val.setReference(UniqueLuaReference::create(L, i)); params.push_back(val); } const auto ret = (*funcObj)(L, params); // Prepare to return values. Clear the stack lua_settop(L, 0); for (const auto& retVal : ret) { retVal.pushValue(L); } // Done! return static_cast(ret.size()); } } LuaFunction LuaFunction::createFromCFunction(lua_State* L, lua_CFunction function, const LuaValueList& upvalues) { LuaFunction func; for (auto& val : upvalues) { val.pushValue(L); } lua_pushcclosure(L, function, (int)upvalues.size()); func.setReference(UniqueLuaReference::create(L)); lua_pop(L, 1); return func; } LuaFunction LuaFunction::createFromStdFunction(lua_State* L, LuaFunctionObject function) { auto userdataMetatable = LuaTable::create(L); userdataMetatable.addValue("__gc", stdFunctionDestructor); // Create storage for the function object in Lua and initialize our data there via placement new auto functionStorage = lua_newuserdata(L, sizeof(function)); new (functionStorage) LuaFunctionObject(std::move(function)); userdataMetatable.pushValue(L); lua_setmetatable(L, -2); LuaValue funcValue; funcValue.setReference(UniqueLuaReference::create(L)); lua_pop(L, 1); // Value is referenced. We can remove it from the stack now return LuaFunction::createFromCFunction(L, stdFunctionCaller, { funcValue }); } LuaFunction LuaFunction::createFromCode(lua_State* L, std::string const& code, std::string const& name) { int load_err = luaL_loadbuffer(L, code.c_str(), code.length(), name.c_str()); if (!load_err) { LuaFunction func; func.setReference(UniqueLuaReference::create(L)); lua_pop(L, 1); return func; } else { // Get the error message size_t len; const char* err = lua_tolstring(L, -1, &len); lua_pop(L, 1); throw LuaException(std::string(err, len)); } } LuaFunction::LuaFunction() : LuaValue(), _errorFunction(nullptr) { } LuaFunction::LuaFunction(const LuaFunction&) noexcept = default; LuaFunction& LuaFunction::operator=(const LuaFunction&) noexcept = default; LuaFunction::LuaFunction(LuaFunction&&) noexcept = default; LuaFunction& LuaFunction::operator=(LuaFunction&&) noexcept = default; LuaFunction::~LuaFunction() = default; bool LuaFunction::setEnvironment(const LuaTable& table) { if (!table.getReference()->isValid()) { throw LuaException("Table reference is not valid!"); } this->pushValue(_luaState); table.pushValue(_luaState); bool ret = lua_setfenv(_luaState, -2) != 0; // Pop the function again lua_pop(_luaState, 1); return ret; } LuaValueList LuaFunction::operator()(lua_State* L, const LuaValueList& args) const { return this->call(L, args); } void LuaFunction::setReference(const LuaReference& ref) { if (ref == nullptr) { // If not a valid reference then let the base class handle everything LuaValue::setReference(ref); return; } lua_State* L = ref->getState(); ref->pushValue(L); if (lua_type(L, -1) != LUA_TFUNCTION) { lua_pop(L, 1); throw LuaException("Reference does not refer to a function!"); } else { lua_pop(L, 1); LuaValue::setReference(ref); } } LuaValueList LuaFunction::call(lua_State* L, const LuaValueList& args) const { int err_idx = 0; int stackTop; if (_errorFunction) { // push the error function _errorFunction->pushValue(L); err_idx = lua_gettop(L); stackTop = err_idx; } else { stackTop = lua_gettop(L); } if(!lua_checkstack(L, (int)args.size() + 1)) throw LuaException("Lua Stack Overflow!"); // Push the function onto the stack this->pushValue(L, true); // Push the arguments onto the stack for (const auto& arg : args) { arg.pushValue(L, true); } // actually call the function now! int err = lua_pcall(L, (int) args.size(), LUA_MULTRET, err_idx); if (!err) { int numReturn = lua_gettop(L) - stackTop; LuaValueList values; values.reserve(numReturn); LuaValue val; for (int i = 0; i < numReturn; ++i) { if (convert::popValue(L, val)) { // Add values at the begin as the last return value is on top // of the stack. values.insert(values.begin(), val); } } if (err_idx != 0) { // Remove the error function lua_pop(L, 1); } return values; } else { // Make sure that there is exactly one parameter left on the stack // If the error function didn't return anything then this will push nil // If it pushed more than one value then this will discard all of them except the last one lua_settop(L, stackTop + 1); std::string err_msg; if (!lua_isstring(L, -1)) { err_msg = "Invalid lua value on stack!"; lua_pop(L, 1); // Remove the value on the stack } else { if (!convert::popValue(L, err_msg)) { err_msg = "Failed to get error message from Lua stack!"; } } if (err_idx != 0) { // Pop the error function lua_pop(L, 1); } // Throw exception with generated message throw LuaException(err_msg); } } bool ::luacpp::convert::popValue(lua_State* luaState, LuaFunction& target, int stackposition, bool remove) { if (!internal::isValidIndex(luaState, stackposition)) { return false; } if (!lua_isfunction(luaState, stackposition)) { return false; } else { target.setReference(UniqueLuaReference::create(luaState, stackposition)); if (remove) { lua_remove(luaState, stackposition); } return true; } } }