mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 18:31:26 -10:00
254 lines
6.0 KiB
C++
254 lines
6.0 KiB
C++
#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<LuaFunctionObject*>(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<LuaFunctionObject*>(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<int>(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&) = default;
|
|
LuaFunction& LuaFunction::operator=(const LuaFunction&) = 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;
|
|
}
|
|
}
|
|
|
|
}
|