From 490727fdc0f7fc58bd8ab7420cdb9397465a37f6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 16 Jun 2024 20:15:26 +0300 Subject: [PATCH] add lua user types + bytearray --- src/logic/scripting/lua/lua_custom_types.cpp | 86 ++++++++++++++++++++ src/logic/scripting/lua/lua_custom_types.hpp | 39 +++++++++ src/logic/scripting/lua/lua_engine.cpp | 4 +- src/logic/scripting/lua/lua_util.cpp | 9 ++ src/logic/scripting/lua/lua_util.hpp | 58 +++++++++++-- 5 files changed, 186 insertions(+), 10 deletions(-) create mode 100644 src/logic/scripting/lua/lua_custom_types.cpp create mode 100644 src/logic/scripting/lua/lua_custom_types.hpp diff --git a/src/logic/scripting/lua/lua_custom_types.cpp b/src/logic/scripting/lua/lua_custom_types.cpp new file mode 100644 index 00000000..4571f32a --- /dev/null +++ b/src/logic/scripting/lua/lua_custom_types.cpp @@ -0,0 +1,86 @@ +#include "lua_custom_types.hpp" + +#include "lua_util.hpp" + +#include + +using namespace lua; + + +Bytearray::Bytearray(size_t capacity) + : buffer(std::make_unique(capacity)), capacity(capacity) { +} + +Bytearray::~Bytearray() { +} + +static int l_bytearray_meta_meta_call(lua::State* L) { + auto size = tointeger(L, 2); + if (size < 0) { + throw std::runtime_error("size can not be less than 0"); + } + return newuserdata(L, static_cast(size)); +} + +static int l_bytearray_meta_index(lua::State* L) { + auto buffer = touserdata(L, 1); + auto index = tointeger(L, 2)-1; + if (buffer == nullptr || static_cast(index) > buffer->size()) { + return 0; + } + return pushinteger(L, (*buffer)[index]); +} + +static int l_bytearray_meta_newindex(lua::State* L) { + auto buffer = touserdata(L, 1); + auto index = tointeger(L, 2)-1; + if (buffer == nullptr || static_cast(index) > buffer->size()) { + return 0; + } + auto value = tointeger(L, 3); + (*buffer)[index] = static_cast(value); + return 0; +} + +static int l_bytearray_meta_len(lua::State* L) { + if (auto buffer = touserdata(L, 1)) { + return pushinteger(L, buffer->size()); + } + return 0; +} + +static int l_bytearray_meta_tostring(lua::State* L) { + auto& buffer = *touserdata(L, 1); + if (buffer.size() > 128) { + return pushstring(L, "bytearray["+std::to_string(buffer.size())+"]{...}"); + } else { + std::stringstream ss; + ss << "bytearray[" << std::to_string(buffer.size()) << "]{"; + for (size_t i = 0; i < buffer.size(); i++) { + if (i > 0) { + ss << " "; + } + ss << static_cast(buffer[i]); + } + ss << "}"; + return pushstring(L, ss.str()); + } +} + +int Bytearray::createMetatable(lua::State* L) { + createtable(L, 0, 5); + pushcfunction(L, lua::wrap); + setfield(L, "__index"); + pushcfunction(L, lua::wrap); + setfield(L, "__newindex"); + pushcfunction(L, lua::wrap); + setfield(L, "__len"); + pushcfunction(L, lua::wrap); + setfield(L, "__tostring"); + + createtable(L, 0, 1); + pushcfunction(L, lua::wrap); + setfield(L, "__call"); + setmetatable(L); + return 1; +} diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp new file mode 100644 index 00000000..e095e8a3 --- /dev/null +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -0,0 +1,39 @@ +#ifndef LOGIC_SCRIPTING_LUA_LUA_CUSTOM_TYPES_HPP_ +#define LOGIC_SCRIPTING_LUA_LUA_CUSTOM_TYPES_HPP_ + +#include "lua_commons.hpp" + +#include +#include + +namespace lua { + class Userdata { + public: + virtual ~Userdata() {}; + virtual const std::string& getTypeName() const = 0; + }; + + class Bytearray : public Userdata { + std::unique_ptr buffer; + size_t capacity; + public: + Bytearray(size_t capacity); + virtual ~Bytearray(); + + inline ubyte& operator[](size_t index) { + return buffer[index]; + } + + const std::string& getTypeName() const override { + return TYPENAME; + } + + inline size_t size() const { + return capacity; + } + static int createMetatable(lua::State*); + inline static std::string TYPENAME = "bytearray"; + }; +} + +#endif // LOGIC_SCRIPTING_LUA_LUA_CUSTOM_TYPES_HPP_ diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 096109df..75ef541f 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -1,6 +1,7 @@ #include "lua_engine.hpp" #include "api_lua.hpp" +#include "lua_custom_types.hpp" #include "../../../debug/Logger.hpp" #include "../../../util/stringutil.hpp" @@ -45,7 +46,6 @@ static void create_libs(lua::State* L) { addfunc(L, "print", lua::wrap); } -#include void lua::initialize() { logger.info() << LUA_VERSION; logger.info() << LUAJIT_VERSION; @@ -81,6 +81,8 @@ void lua::initialize() { createtable(L, 0, 0); setglobal(L, LAMBDAS_TABLE); + + newusertype(L, "bytearray"); } void lua::finalize() { diff --git a/src/logic/scripting/lua/lua_util.cpp b/src/logic/scripting/lua/lua_util.cpp index 198ba280..745889d2 100644 --- a/src/logic/scripting/lua/lua_util.cpp +++ b/src/logic/scripting/lua/lua_util.cpp @@ -9,6 +9,15 @@ using namespace lua; static int nextEnvironment = 1; +std::unordered_map lua::usertypeNames; + +int lua::userdata_destructor(lua::State* L) { + if (auto obj = touserdata(L, 1)) { + obj->~Userdata(); + } + return 0; +} + std::string lua::env_name(int env) { return "_ENV"+util::mangleid(env); } diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index 3c705eb3..a51d784c 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -2,9 +2,16 @@ #define LOGIC_SCRIPTING_LUA_UTIL_HPP_ #include "lua_commons.hpp" +#include "lua_custom_types.hpp" + +#include +#include +#include namespace lua { inline std::string LAMBDAS_TABLE = "$L"; + extern std::unordered_map usertypeNames; + int userdata_destructor(lua::State* L); std::string env_name(int env); @@ -230,6 +237,12 @@ namespace lua { inline bool isfunction(lua::State* L, int idx) { return lua_isfunction(L, idx); } + inline bool isuserdata(lua::State* L, int idx) { + return lua_isuserdata(L, idx); + } + inline void setfield(lua::State* L, const std::string& name, int idx=-2) { + lua_setfield(L, idx, name.c_str()); + } inline bool toboolean(lua::State* L, int idx) { return lua_toboolean(L, idx); } @@ -245,7 +258,42 @@ namespace lua { inline const void* topointer(lua::State* L, int idx) { return lua_topointer(L, idx); } - inline glm::vec2 tovec2(lua::State* L, int idx) { + inline void setglobal(lua::State* L, const std::string& name) { + lua_setglobal(L, name.c_str()); + } + template + inline T* touserdata(lua::State* L, int idx) { + if (void* rawptr = lua_touserdata(L, idx)) { + return static_cast(rawptr); + } + return nullptr; + } + template + inline int newuserdata(lua::State* L, Args&&... args) { + const auto& found = usertypeNames.find(typeid(T)); + void* ptr = lua_newuserdata(L, sizeof(T)); + new (ptr) T(args...); + + if (found == usertypeNames.end()) { + log_error("usertype is not registred: "+std::string(typeid(T).name())); + } else if (getglobal(L, found->second)) { + setmetatable(L); + } + return 1; + } + + template + inline void newusertype(lua::State* L, const std::string& name) { + usertypeNames[typeid(T)] = name; + func(L); + + pushcfunction(L, userdata_destructor); + setfield(L, "__gc"); + + setglobal(L, name); + } + + inline glm::vec2 tovec2(lua::State* L, int idx) { pushvalue(L, idx); if (!istable(L, idx) || objlen(L, idx) < 2) { throw std::runtime_error("value must be an array of two numbers"); @@ -287,14 +335,6 @@ namespace lua { return true; } - inline void setfield(lua::State* L, const std::string& name, int idx=-2) { - lua_setfield(L, idx, name.c_str()); - } - - inline void setglobal(lua::State* L, const std::string& name) { - lua_setglobal(L, name.c_str()); - } - inline const char* require_string(lua::State* L, int idx) { if (!isstring(L, idx)) { throw luaerror("string expected at "+std::to_string(idx));