#include "LuaFunction.h" #include "LuaException.h" #include "LuaHeaders.h" namespace luacpp { LuaFunction LuaFunction::createFromCFunction(lua_State* L, lua_CFunction function, const LuaValueList& upvalues) { LuaFunction func; for (auto& val : upvalues) { val.pushValue(); } lua_pushcclosure(L, function, (int) upvalues.size()); func.setReference(UniqueLuaReference::create(L)); lua_pop(L, 1); return func; } 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& other) : LuaValue(other), _errorFunction(other._errorFunction) { } LuaFunction::~LuaFunction() { } bool LuaFunction::setEnvironment(const LuaTable& table) { if (!table.getReference()->isValid()) { throw LuaException("Table reference is not valid!"); } this->pushValue(); table.pushValue(); bool ret = lua_setfenv(_luaState, -2) != 0; // Pop the function again lua_pop(_luaState, 1); return ret; } LuaValueList LuaFunction::operator()(const LuaValueList& args) { return this->call(args); } void LuaFunction::setReference(const LuaReference& ref) { ref->pushValue(); lua_State* L = ref->getState(); if (lua_type(L, -1) != LUA_TFUNCTION) { lua_pop(L, 1); throw LuaException("Reference does not refere to a function!"); } else { lua_pop(L, 1); LuaValue::setReference(ref); } } LuaValueList LuaFunction::call(const LuaValueList& args) { int err_idx = 0; int stackTop; if (_errorFunction) { // push the error function _errorFunction->pushValue(); err_idx = lua_gettop(_luaState); stackTop = err_idx; } else { stackTop = lua_gettop(_luaState); } // Push the function onto the stack this->pushValue(); // Push the arguments onto the stack for (LuaValueList::const_iterator iter = args.begin(); iter != args.end(); ++iter) { iter->pushValue(); } // actually call the function now! int err = lua_pcall(_luaState, (int) args.size(), LUA_MULTRET, err_idx); if (!err) { int numReturn = lua_gettop(_luaState) - stackTop; LuaValueList values; values.reserve(numReturn); LuaValue val; for (int i = 0; i < numReturn; ++i) { if (convert::popValue(_luaState, 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(_luaState, 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(_luaState, stackTop + 1); std::string err_msg; if (!lua_isstring(_luaState, -1)) { err_msg = "Invalid lua value on stack!"; lua_pop(_luaState, 1); // Remove the value on the stack } else { if (!convert::popValue(_luaState, err_msg)) { err_msg = "Failed to get error message from Lua stack!"; } } if (err_idx != 0) { // Pop the error function lua_pop(_luaState, 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; } } }