213 Zeilen
5.2 KiB
C++
213 Zeilen
5.2 KiB
C++
/**
|
|
* server/src/lua/method.cpp - Lua method wrapper
|
|
*
|
|
* This file is part of the traintastic source code.
|
|
*
|
|
* Copyright (C) 2019-2020,2022-2024 Reinder Feenstra
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "method.hpp"
|
|
#include "push.hpp"
|
|
#include "check.hpp"
|
|
#include "error.hpp"
|
|
#include "to.hpp"
|
|
#include "../core/abstractmethod.hpp"
|
|
#include "../core/object.hpp"
|
|
|
|
namespace Lua {
|
|
|
|
constexpr char const* methodsGlobal = "methods";
|
|
|
|
struct MethodData
|
|
{
|
|
ObjectPtrWeak object;
|
|
AbstractMethod& method;
|
|
|
|
MethodData(AbstractMethod& _method) :
|
|
object{_method.object().weak_from_this()},
|
|
method{_method}
|
|
{
|
|
}
|
|
};
|
|
|
|
AbstractMethod& Method::check(lua_State* L, int index)
|
|
{
|
|
auto& data = *static_cast<MethodData*>(luaL_checkudata(L, index, metaTableName));
|
|
if(!data.object.expired())
|
|
return data.method;
|
|
|
|
errorDeadObject(L);
|
|
}
|
|
|
|
AbstractMethod* Method::test(lua_State* L, int index)
|
|
{
|
|
auto* data = static_cast<MethodData*>(luaL_testudata(L, index, metaTableName));
|
|
if(!data)
|
|
return nullptr;
|
|
if(!data->object.expired())
|
|
return &data->method;
|
|
|
|
errorDeadObject(L);
|
|
}
|
|
|
|
void Method::push(lua_State* L, AbstractMethod& value)
|
|
{
|
|
lua_getglobal(L, methodsGlobal);
|
|
lua_rawgetp(L, -1, &value);
|
|
if(lua_isnil(L, -1)) // method not in table
|
|
{
|
|
lua_pop(L, 1); // remove nil
|
|
new(lua_newuserdata(L, sizeof(MethodData))) MethodData(value);
|
|
luaL_setmetatable(L, metaTableName);
|
|
lua_pushvalue(L, -1); // copy userdata on stack
|
|
lua_rawsetp(L, -3, &value); // add method to table
|
|
}
|
|
lua_insert(L, lua_gettop(L) - 1); // swap table and userdata
|
|
lua_pop(L, 1); // remove table
|
|
}
|
|
|
|
void Method::registerType(lua_State* L)
|
|
{
|
|
luaL_newmetatable(L, metaTableName);
|
|
lua_pushcfunction(L, __gc);
|
|
lua_setfield(L, -2, "__gc");
|
|
lua_pushcfunction(L, __call);
|
|
lua_setfield(L, -2, "__call");
|
|
lua_pop(L, 1);
|
|
|
|
// weak table for method userdata:
|
|
lua_newtable(L);
|
|
lua_newtable(L); // metatable
|
|
lua_pushliteral(L, "__mode");
|
|
lua_pushliteral(L, "v");
|
|
lua_rawset(L, -3);
|
|
lua_setmetatable(L, -2);
|
|
lua_setglobal(L, methodsGlobal);
|
|
}
|
|
|
|
int Method::__gc(lua_State* L)
|
|
{
|
|
static_cast<MethodData*>(lua_touserdata(L, 1))->~MethodData();
|
|
return 0;
|
|
}
|
|
|
|
int Method::__call(lua_State* L)
|
|
{
|
|
AbstractMethod& method = check(L, 1);
|
|
const int argc = static_cast<int>(method.argumentTypeInfo().size());
|
|
|
|
if(lua_gettop(L) - 1 != argc)
|
|
errorExpectedNArgumentsGotN(L, argc, lua_gettop(L) - 1);
|
|
|
|
Arguments args;
|
|
args.reserve(argc);
|
|
|
|
if(argc > 0)
|
|
{
|
|
int index = 2;
|
|
|
|
for(const auto& info : method.argumentTypeInfo())
|
|
{
|
|
switch(info.type)
|
|
{
|
|
case ValueType::Boolean:
|
|
args.emplace_back(to<bool>(L, index));
|
|
break;
|
|
|
|
case ValueType::Enum:
|
|
args.emplace_back(static_cast<int64_t>(checkEnum(L, index, info.enumName.data())));
|
|
break;
|
|
|
|
case ValueType::Integer:
|
|
args.emplace_back(to<int64_t>(L, index));
|
|
break;
|
|
|
|
case ValueType::Float:
|
|
args.emplace_back(to<double>(L, index));
|
|
break;
|
|
|
|
case ValueType::String:
|
|
args.emplace_back(to<std::string>(L, index));
|
|
break;
|
|
|
|
case ValueType::Object:
|
|
if(lua_isnil(L, index))
|
|
args.emplace_back(ObjectPtr());
|
|
else
|
|
args.emplace_back(Lua::check<::Object>(L, index));
|
|
break;
|
|
|
|
case ValueType::Set:
|
|
args.emplace_back(static_cast<int64_t>(checkSet(L, index, info.setName.data())));
|
|
break;
|
|
|
|
case ValueType::Invalid:
|
|
assert(false);
|
|
errorInternal(L);
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
|
|
assert(args.size() == static_cast<Arguments::size_type>(argc));
|
|
|
|
try
|
|
{
|
|
AbstractMethod::Result result = method.call(args);
|
|
|
|
const auto& typeInfo = method.resultTypeInfo();
|
|
switch(typeInfo.type)
|
|
{
|
|
case ValueType::Invalid:
|
|
return 0; // no return value
|
|
|
|
case ValueType::Boolean:
|
|
Lua::push(L, std::get<bool>(result));
|
|
return 1;
|
|
|
|
case ValueType::Enum:
|
|
case ValueType::Set:
|
|
errorInternal(L); // not yet implemented
|
|
|
|
case ValueType::Integer:
|
|
Lua::push(L, std::get<int64_t>(result));
|
|
return 1;
|
|
|
|
case ValueType::Float:
|
|
Lua::push(L, std::get<double>(result));
|
|
return 1;
|
|
|
|
case ValueType::String:
|
|
Lua::push(L, std::get<std::string>(result));
|
|
return 1;
|
|
|
|
case ValueType::Object:
|
|
Lua::push(L, std::get<ObjectPtr>(result));
|
|
return 1;
|
|
}
|
|
|
|
assert(false);
|
|
return 0;
|
|
}
|
|
catch(const std::exception& e)
|
|
{
|
|
errorException(L, e);
|
|
}
|
|
}
|
|
|
|
}
|