Use protected mode for lua callbacks wherever possible
This commit is contained in:
@@ -19,6 +19,8 @@ namespace lua {
|
|||||||
|
|
||||||
class Allocator;
|
class Allocator;
|
||||||
|
|
||||||
|
auto CallProtected(lua_State*, int nargs, int nresults) -> int;
|
||||||
|
|
||||||
class LuaThread {
|
class LuaThread {
|
||||||
public:
|
public:
|
||||||
static auto Start(system_fsm::ServiceLocator&, lv_obj_t* lvgl_root = nullptr)
|
static auto Start(system_fsm::ServiceLocator&, lv_obj_t* lvgl_root = nullptr)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "lauxlib.h"
|
#include "lauxlib.h"
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
|
#include "lua_thread.hpp"
|
||||||
#include "lvgl.h"
|
#include "lvgl.h"
|
||||||
|
|
||||||
#include "database.hpp"
|
#include "database.hpp"
|
||||||
@@ -108,7 +109,7 @@ static auto db_iterate(lua_State* state) -> int {
|
|||||||
} else {
|
} else {
|
||||||
lua_pushnil(state);
|
lua_pushnil(state);
|
||||||
}
|
}
|
||||||
lua_call(state, 1, 0);
|
CallProtected(state, 1, 0);
|
||||||
luaL_unref(state, LUA_REGISTRYINDEX, callback_ref);
|
luaL_unref(state, LUA_REGISTRYINDEX, callback_ref);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -139,7 +140,6 @@ static auto push_iterator(
|
|||||||
lua_pushcclosure(state, db_iterate, 1);
|
lua_pushcclosure(state, db_iterate, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static auto record_text(lua_State* state) -> int {
|
static auto record_text(lua_State* state) -> int {
|
||||||
LuaRecord* data = reinterpret_cast<LuaRecord*>(
|
LuaRecord* data = reinterpret_cast<LuaRecord*>(
|
||||||
luaL_checkudata(state, 1, kDbRecordMetatable));
|
luaL_checkudata(state, 1, kDbRecordMetatable));
|
||||||
|
|||||||
+42
-7
@@ -10,9 +10,11 @@
|
|||||||
|
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "event_queue.hpp"
|
||||||
#include "lua.hpp"
|
#include "lua.hpp"
|
||||||
#include "luavgl.h"
|
#include "luavgl.h"
|
||||||
#include "service_locator.hpp"
|
#include "service_locator.hpp"
|
||||||
|
#include "ui_events.hpp"
|
||||||
|
|
||||||
namespace lua {
|
namespace lua {
|
||||||
|
|
||||||
@@ -63,6 +65,7 @@ auto LuaThread::Start(system_fsm::ServiceLocator& services, lv_obj_t* lvgl_root)
|
|||||||
|
|
||||||
// FIXME: luavgl init should probably be a part of the bridge.
|
// FIXME: luavgl init should probably be a part of the bridge.
|
||||||
if (lvgl_root) {
|
if (lvgl_root) {
|
||||||
|
luavgl_set_pcall(state, CallProtected);
|
||||||
luavgl_set_root(state, lvgl_root);
|
luavgl_set_root(state, lvgl_root);
|
||||||
luaL_requiref(state, "lvgl", luaopen_lvgl, true);
|
luaL_requiref(state, "lvgl", luaopen_lvgl, true);
|
||||||
lua_pop(state, 1);
|
lua_pop(state, 1);
|
||||||
@@ -85,14 +88,46 @@ auto LuaThread::RunScript(const std::string& path) -> bool {
|
|||||||
if (res != LUA_OK) {
|
if (res != LUA_OK) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
res = lua_pcall(state_, 0, 0, 0);
|
CallProtected(state_, 0, 0);
|
||||||
if (res) {
|
|
||||||
const char* msg = lua_tostring(state_, -1);
|
|
||||||
lua_writestring(msg, strlen(msg));
|
|
||||||
lua_writeline();
|
|
||||||
lua_pop(state_, 1);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
} // namespace lua
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "lua.hpp"
|
#include "lua.hpp"
|
||||||
|
#include "lua_thread.hpp"
|
||||||
#include "lvgl.h"
|
#include "lvgl.h"
|
||||||
#include "service_locator.hpp"
|
#include "service_locator.hpp"
|
||||||
|
|
||||||
@@ -49,9 +50,8 @@ static auto property_bind(lua_State* state) -> int {
|
|||||||
// ...and another copy, since we return the original closure.
|
// ...and another copy, since we return the original closure.
|
||||||
lua_pushvalue(state, 2);
|
lua_pushvalue(state, 2);
|
||||||
|
|
||||||
// FIXME: This should ideally be lua_pcall, for safety.
|
|
||||||
p->PushValue(*state);
|
p->PushValue(*state);
|
||||||
lua_call(state, 1, 0); // Invoke the initial binding.
|
CallProtected(state, 1, 0); // Invoke the initial binding.
|
||||||
|
|
||||||
lua_pushstring(state, kBindingsTable);
|
lua_pushstring(state, kBindingsTable);
|
||||||
lua_gettable(state, LUA_REGISTRYINDEX); // REGISTRY[kBindingsTable]
|
lua_gettable(state, LUA_REGISTRYINDEX); // REGISTRY[kBindingsTable]
|
||||||
@@ -229,7 +229,7 @@ auto Property::Update(const LuaValue& v) -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PushValue(*b.first); // push the argument
|
PushValue(*b.first); // push the argument
|
||||||
lua_call(b.first, 1, 0); // invoke the closure
|
CallProtected(b.first, 1, 0); // invoke the closure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ struct OnStorageChange : tinyfsm::Event {
|
|||||||
|
|
||||||
struct OnSystemError : tinyfsm::Event {};
|
struct OnSystemError : tinyfsm::Event {};
|
||||||
|
|
||||||
|
struct OnLuaError : tinyfsm::Event {
|
||||||
|
std::string message;
|
||||||
|
};
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
struct RecordSelected : tinyfsm::Event {
|
struct RecordSelected : tinyfsm::Event {
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ class UiState : public tinyfsm::Fsm<UiState> {
|
|||||||
void react(const audio::QueueUpdate&);
|
void react(const audio::QueueUpdate&);
|
||||||
|
|
||||||
virtual void react(const system_fsm::KeyLockChanged&);
|
virtual void react(const system_fsm::KeyLockChanged&);
|
||||||
|
virtual void react(const OnLuaError&) {}
|
||||||
|
|
||||||
virtual void react(const internal::RecordSelected&) {}
|
virtual void react(const internal::RecordSelected&) {}
|
||||||
virtual void react(const internal::IndexSelected&) {}
|
virtual void react(const internal::IndexSelected&) {}
|
||||||
@@ -124,6 +125,8 @@ class Lua : public UiState {
|
|||||||
void entry() override;
|
void entry() override;
|
||||||
void exit() override;
|
void exit() override;
|
||||||
|
|
||||||
|
void react(const OnLuaError&) override;
|
||||||
|
|
||||||
void react(const internal::IndexSelected&) override;
|
void react(const internal::IndexSelected&) override;
|
||||||
void react(const internal::ShowNowPlaying&) override;
|
void react(const internal::ShowNowPlaying&) override;
|
||||||
void react(const internal::ShowSettingsPage&) override;
|
void react(const internal::ShowSettingsPage&) override;
|
||||||
|
|||||||
+5
-2
@@ -238,8 +238,7 @@ auto Lua::PushLuaScreen(lua_State* s) -> int {
|
|||||||
|
|
||||||
// Call the constructor for this screen.
|
// Call the constructor for this screen.
|
||||||
lua_settop(s, 1); // Make sure the function is actually at top of stack
|
lua_settop(s, 1); // Make sure the function is actually at top of stack
|
||||||
// FIXME: This should ideally be lua_pcall, for safety.
|
lua::CallProtected(s, 0, 1);
|
||||||
lua_call(s, 0, 1);
|
|
||||||
|
|
||||||
// Store the reference for the table the constructor returned.
|
// Store the reference for the table the constructor returned.
|
||||||
new_screen->SetObjRef(s);
|
new_screen->SetObjRef(s);
|
||||||
@@ -262,6 +261,10 @@ void Lua::exit() {
|
|||||||
lv_group_set_default(NULL);
|
lv_group_set_default(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Lua::react(const OnLuaError& err) {
|
||||||
|
ESP_LOGE("lua", "%s", err.message.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
void Lua::react(const internal::IndexSelected& ev) {
|
void Lua::react(const internal::IndexSelected& ev) {
|
||||||
auto db = sServices->database().lock();
|
auto db = sServices->database().lock();
|
||||||
if (!db) {
|
if (!db) {
|
||||||
|
|||||||
Reference in New Issue
Block a user