diff options
| author | cooljqln <cooljqln@noreply.codeberg.org> | 2024-05-03 04:48:17 +0000 |
|---|---|---|
| committer | cooljqln <cooljqln@noreply.codeberg.org> | 2024-05-03 04:48:17 +0000 |
| commit | 3ceb8025ee4330c177101ed30ec17dfb0002f41e (patch) | |
| tree | 58350210f15df7d00d967cac6f30eeceeb031a3c /src/tangara/lua/lua_thread.cpp | |
| parent | 964da15a0b84f8e5f00e8abac2f7dfda0bf60488 (diff) | |
| parent | 9fafd797a5504f458b5fcae4a1d28a68da936315 (diff) | |
| download | tangara-fw-3ceb8025ee4330c177101ed30ec17dfb0002f41e.tar.gz | |
Merge pull request 'Break dependency cycles with our components by merging co-dependent components together' (#68) from jqln/component-merge into main
Reviewed-on: https://codeberg.org/cool-tech-zone/tangara-fw/pulls/68
Diffstat (limited to 'src/tangara/lua/lua_thread.cpp')
| -rw-r--r-- | src/tangara/lua/lua_thread.cpp | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/src/tangara/lua/lua_thread.cpp b/src/tangara/lua/lua_thread.cpp new file mode 100644 index 00000000..77a46b45 --- /dev/null +++ b/src/tangara/lua/lua_thread.cpp @@ -0,0 +1,189 @@ +/* + * Copyright 2023 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "lua/lua_thread.hpp" + +#include <iostream> +#include <memory> + +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "lua.hpp" + +#include "events/event_queue.hpp" +#include "lua/bridge.hpp" +#include "memory_resource.hpp" +#include "system_fsm/service_locator.hpp" +#include "ui/ui_events.hpp" + +namespace lua { + +[[maybe_unused]] static constexpr char kTag[] = "lua"; + +class Allocator { + public: + Allocator() : total_allocated_(0) {} + + auto alloc(void* ptr, size_t osize, size_t nsize) -> void* { + total_allocated_ = total_allocated_ - osize + nsize; + // ESP_LOGI(kTag, "lua realloc -> %u KiB", total_allocated_ / 1024); + if (nsize == 0) { + heap_caps_free(ptr); + return NULL; + } else { + return heap_caps_realloc(ptr, nsize, MALLOC_CAP_SPIRAM); + } + } + + private: + size_t total_allocated_; +}; + +static auto lua_alloc(void* ud, + void* ptr, + size_t osize, + size_t nsize) -> void* { + Allocator* instance = reinterpret_cast<Allocator*>(ud); + return instance->alloc(ptr, osize, nsize); +} + +static int lua_panic(lua_State* L) { + ESP_LOGE(kTag, "!! PANIC !! %s", lua_tostring(L, -1)); + return 0; +} + +auto LuaThread::Start(system_fsm::ServiceLocator& services) -> LuaThread* { + auto alloc = std::make_unique<Allocator>(); + lua_State* state = lua_newstate(lua_alloc, alloc.get()); + if (!state) { + return nullptr; + } + + luaL_openlibs(state); + lua_atpanic(state, lua_panic); + + return new LuaThread(alloc, state); +} + +LuaThread::LuaThread(std::unique_ptr<Allocator>& alloc, lua_State* state) + : alloc_(std::move(alloc)), state_(state) {} + +LuaThread::~LuaThread() { + lua_close(state_); +} + +auto LuaThread::RunScript(const std::string& path) -> bool { + int res = luaL_loadfilex(state_, path.c_str(), NULL); + if (res != LUA_OK) { + return false; + } + CallProtected(state_, 0, 0); + return true; +} + +auto LuaThread::RunString(const std::string& script) -> bool { + int res = luaL_loadstring(state_, script.c_str()); + if (res != LUA_OK) { + return false; + } + CallProtected(state_, 0, 0); + return true; +} + +auto LuaThread::DumpStack() -> void { + int top = lua_gettop(state_); + std::cout << "stack size: " << top << std::endl; + for (size_t i = 1; i <= top; i++) { + std::cout << "[" << i << "]\t" << luaL_typename(state_, i); + switch (lua_type(state_, i)) { + case LUA_TNUMBER: + std::cout << "\t("; + if (lua_isinteger(state_, i)) { + std::cout << lua_tointeger(state_, i); + } else { + std::cout << lua_tonumber(state_, i); + } + std::cout << ")"; + break; + case LUA_TSTRING: + std::cout << "\t('" << lua_tostring(state_, i) << "')"; + break; + case LUA_TBOOLEAN: + std::cout << "\t(" << lua_toboolean(state_, i) << ")"; + break; + case LUA_TNIL: + // Value is implied. + break; + case LUA_TTABLE: + lua_pushnil(state_); + while (lua_next(state_, i) != 0) { + // Keys + std::cout << std::endl << "\t\t" << luaL_typename(state_, -2); + if (lua_type(state_, -2) == LUA_TSTRING) { + std::cout << "\t(" << lua_tostring(state_, -2) << ")"; + } else if (lua_type(state_, -2) == LUA_TNUMBER) { + std::cout << "\t(" << lua_tonumber(state_, -2) << ")"; + } + + // Values + std::cout << "\t\t" << luaL_typename(state_, -1); + if (lua_type(state_, -1) == LUA_TSTRING) { + std::cout << "\t(" << lua_tostring(state_, -1) << ")"; + } else if (lua_type(state_, -1) == LUA_TNUMBER) { + std::cout << "\t(" << lua_tonumber(state_, -1) << ")"; + } + // Pop the value; we don't care about it. Leave the key on the stack + // for the next call to lua_next. + lua_pop(state_, 1); + } + break; + default: + std::cout << "\t(" << lua_topointer(state_, i) << ")"; + break; + } + std::cout << std::endl; + } +} + +static int msg_handler(lua_State* L) { + if (!lua_isstring(L, 1)) { + return 1; + } + + const char* msg = lua_tostring(L, 1); + if (msg == NULL) { /* is error object not a string? */ + if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */ + lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */ + return 1; /* that is the message */ + else + msg = lua_pushfstring(L, "(error object is a %s value)", + luaL_typename(L, 1)); + } + + /* append a standard traceback */ + luaL_traceback(L, L, msg, 1); + return 1; +} + +auto CallProtected(lua_State* s, int nargs, int nresults) -> int { + int base = lua_gettop(s) - nargs; + // Place our message handler under the function to be called. + lua_pushcfunction(s, msg_handler); + lua_insert(s, base); + + // Invoke the function. + int ret = lua_pcall(s, nargs, nresults, base); + if (ret != LUA_OK) { + events::Ui().Dispatch(ui::OnLuaError{.message = lua_tostring(s, -1)}); + } + + // Clean up our message handler + lua_remove(s, base); + + return ret; +} + +} // namespace lua |
