From 413dc34c478f7e7859af53f9f8e086cdc6c0caa8 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sat, 13 Jul 2024 23:41:47 +0200 Subject: [PATCH] Convert lua::peek to return a PeekResult --- src/controllers/plugins/LuaUtilities.cpp | 77 ++++++++++++++++-------- src/controllers/plugins/LuaUtilities.hpp | 76 ++++++++++++++--------- 2 files changed, 99 insertions(+), 54 deletions(-) diff --git a/src/controllers/plugins/LuaUtilities.cpp b/src/controllers/plugins/LuaUtilities.cpp index 9d86b1430..c9e50a51c 100644 --- a/src/controllers/plugins/LuaUtilities.cpp +++ b/src/controllers/plugins/LuaUtilities.cpp @@ -156,31 +156,35 @@ StackIdx push(lua_State *L, const api::CompletionEvent &ev) return idx; } -bool peek(lua_State *L, int *out, StackIdx idx) +PeekResult peek(lua_State *L, int *out, StackIdx idx) { StackGuard guard(L); if (lua_isnumber(L, idx) == 0) { - return false; + return { + false, + {QString("Expected an integer got %1").arg(luaL_typename(L, idx))}}; } *out = lua_tointeger(L, idx); - return true; + return {}; } -bool peek(lua_State *L, bool *out, StackIdx idx) +PeekResult peek(lua_State *L, bool *out, StackIdx idx) { StackGuard guard(L); if (!lua_isboolean(L, idx)) { - return false; + return { + false, + {QString("Expected a boolean got %1").arg(luaL_typename(L, idx))}}; } *out = bool(lua_toboolean(L, idx)); - return true; + return {}; } -bool peek(lua_State *L, double *out, StackIdx idx) +PeekResult peek(lua_State *L, double *out, StackIdx idx) { StackGuard guard(L); int ok{0}; @@ -188,76 +192,91 @@ bool peek(lua_State *L, double *out, StackIdx idx) if (ok != 0) { *out = v; + return {}; } - return ok != 0; + + return PeekResult::ofTypeError(L, idx, "integer or float"); } -bool peek(lua_State *L, QString *out, StackIdx idx) +PeekResult peek(lua_State *L, QString *out, StackIdx idx) { StackGuard guard(L); size_t len{0}; const char *str = lua_tolstring(L, idx, &len); if (str == nullptr) { - return false; + return PeekResult::ofTypeError(L, idx, "string"); } if (len >= INT_MAX) { - assert(false && "string longer than INT_MAX, shit's fucked, yo"); + return {false, + {QString("Strings bigger than 2.1 gigabytes are not allowed")}}; } *out = QString::fromUtf8(str, int(len)); - return true; + return {}; } -bool peek(lua_State *L, QByteArray *out, StackIdx idx) +PeekResult peek(lua_State *L, QByteArray *out, StackIdx idx) { StackGuard guard(L); size_t len{0}; const char *str = lua_tolstring(L, idx, &len); if (str == nullptr) { - return false; + return PeekResult::ofTypeError(L, idx, "string"); } if (len >= INT_MAX) { - assert(false && "string longer than INT_MAX, shit's fucked, yo"); + return {false, + {QString("Strings bigger than 2.1 gigabytes are not allowed")}}; } *out = QByteArray(str, int(len)); - return true; + return {}; } -bool peek(lua_State *L, std::string *out, StackIdx idx) +PeekResult peek(lua_State *L, std::string *out, StackIdx idx) { StackGuard guard(L); size_t len{0}; const char *str = lua_tolstring(L, idx, &len); if (str == nullptr) { - return false; + return PeekResult::ofTypeError(L, idx, "string"); } if (len >= INT_MAX) { - assert(false && "string longer than INT_MAX, shit's fucked, yo"); + return {false, + {QString("Strings bigger than 2.1 gigabytes are not allowed")}}; } *out = std::string(str, len); - return true; + return {}; } -bool peek(lua_State *L, api::CompletionList *out, StackIdx idx) +PeekResult peek(lua_State *L, api::CompletionList *out, StackIdx idx) { StackGuard guard(L); int typ = lua_getfield(L, idx, "values"); if (typ != LUA_TTABLE) { lua_pop(L, 1); - return false; + auto res = PeekResult::ofTypeError(L, idx, "string"); + res.errorReason.emplace_back("While processing CompletionList.values"); + return res; } - if (!lua::pop(L, &out->values, -1)) + auto pres = lua::pop(L, &out->values, -1); + if (!pres) { - return false; + pres.errorReason.emplace_back("While processing CompletionList.values"); + return pres; } lua_getfield(L, idx, "hide_others"); - return lua::pop(L, &out->hideOthers); + pres = lua::pop(L, &out->hideOthers); + if (!pres) + { + pres.errorReason.emplace_back( + "While processing CompletionList.hide_others"); + } + return pres; } QString toString(lua_State *L, StackIdx idx) @@ -290,5 +309,13 @@ void PeekResult::throwAsLuaError(lua_State *L) assert(false && "unreachable"); } +PeekResult PeekResult::ofTypeError(lua_State *L, StackIdx idx, + const QString &expect) +{ + return PeekResult{ + .ok = false, + .errorReason = { + QString("Expected %1, got %2").arg(expect, luaL_typename(L, idx))}}; +} } // namespace chatterino::lua #endif diff --git a/src/controllers/plugins/LuaUtilities.hpp b/src/controllers/plugins/LuaUtilities.hpp index 82a1df64a..d59c9d91d 100644 --- a/src/controllers/plugins/LuaUtilities.hpp +++ b/src/controllers/plugins/LuaUtilities.hpp @@ -1,9 +1,10 @@ #pragma once #ifdef CHATTERINO_HAVE_PLUGINS - # include "common/QLogging.hpp" +# include + extern "C" { # include # include @@ -19,7 +20,6 @@ extern "C" { # include # include struct lua_State; -class QJsonObject; namespace chatterino { struct CommandContext; } // namespace chatterino @@ -83,16 +83,19 @@ struct PeekResult { * As luaL_error never returns this function does not either. */ [[noreturn]] void throwAsLuaError(lua_State *L); + + static PeekResult ofTypeError(lua_State *L, StackIdx idx, + const QString &expect); }; // returns OK? -bool peek(lua_State *L, int *out, StackIdx idx = -1); -bool peek(lua_State *L, bool *out, StackIdx idx = -1); -bool peek(lua_State *L, double *out, StackIdx idx = -1); -bool peek(lua_State *L, QString *out, StackIdx idx = -1); -bool peek(lua_State *L, QByteArray *out, StackIdx idx = -1); -bool peek(lua_State *L, std::string *out, StackIdx idx = -1); -bool peek(lua_State *L, api::CompletionList *out, StackIdx idx = -1); +PeekResult peek(lua_State *L, int *out, StackIdx idx = -1); +PeekResult peek(lua_State *L, bool *out, StackIdx idx = -1); +PeekResult peek(lua_State *L, double *out, StackIdx idx = -1); +PeekResult peek(lua_State *L, QString *out, StackIdx idx = -1); +PeekResult peek(lua_State *L, QByteArray *out, StackIdx idx = -1); +PeekResult peek(lua_State *L, std::string *out, StackIdx idx = -1); +PeekResult peek(lua_State *L, api::CompletionList *out, StackIdx idx = -1); /** * @brief Converts Lua object at stack index idx to a string. @@ -170,12 +173,12 @@ StackIdx push(lua_State *L, std::optional val) } template -bool peek(lua_State *L, std::optional *out, StackIdx idx = -1) +PeekResult peek(lua_State *L, std::optional *out, StackIdx idx = -1) { if (lua_isnil(L, idx)) { *out = std::nullopt; - return true; + return {}; } *out = T(); @@ -183,7 +186,7 @@ bool peek(lua_State *L, std::optional *out, StackIdx idx = -1) } template -bool peek(lua_State *L, std::vector *vec, StackIdx idx = -1) +PeekResult peek(lua_State *L, std::vector *vec, StackIdx idx = -1) { StackGuard guard(L); @@ -192,37 +195,44 @@ bool peek(lua_State *L, std::vector *vec, StackIdx idx = -1) lua::stackDump(L, "!table"); qCDebug(chatterinoLua) << "value is not a table, type is" << lua_type(L, idx); - return false; + return {false, + {QString("Expected value to be a table, its actual type is %1") + .arg(lua_typename(L, lua_type(L, idx)))}}; } auto len = lua_rawlen(L, idx); if (len == 0) { qCDebug(chatterinoLua) << "value has 0 length"; - return true; + return {}; } if (len > 1'000'000) { qCDebug(chatterinoLua) << "value is too long"; - return false; + return { + false, + {QString("Table is too long, %1 > 1_000_000 elements").arg(len)}}; } // count like lua for (int i = 1; i <= len; i++) { lua_geti(L, idx, i); std::optional obj; - if (!lua::peek(L, &obj)) + PeekResult pres = lua::peek(L, &obj); + if (!pres) { - //lua_seti(L, LUA_REGISTRYINDEX, 1); // lazy qCDebug(chatterinoLua) << "Failed to convert lua object into c++: at array index " << i << ":"; - stackDump(L, "bad conversion into string"); - return false; + stackDump(L, "bad conversion"); + pres.errorReason.push_back( + QString("At element %1 of a table").arg(i)); + + return pres; } lua_pop(L, 1); vec->push_back(obj.value()); } - return true; + return {}; } /** @@ -230,21 +240,29 @@ bool peek(lua_State *L, std::vector *vec, StackIdx idx = -1) */ template , bool>::type = true> -bool peek(lua_State *L, T *out, StackIdx idx = -1) +PeekResult peek(lua_State *L, T *out, StackIdx idx = -1) { std::string tmp; - if (!lua::peek(L, &tmp, idx)) + auto pres = lua::peek(L, &tmp, idx); + if (!pres) { - return false; + std::string type = boost::typeindex::type_id().pretty_name(); + pres.errorReason.push_back(QString("While converting to enum %1") + .arg(QString::fromStdString(type))); + return pres; } std::optional opt = magic_enum::enum_cast(tmp); if (opt.has_value()) { *out = opt.value(); - return true; + return {}; } - return false; + std::string type = boost::typeindex::type_id().pretty_name(); + return {.ok = false, + .errorReason = {QString("Invalid enum value \"%1\" for enum %2") + .arg(QString::fromStdString(tmp), + QString::fromStdString(type))}}; } /** @@ -305,19 +323,19 @@ StackIdx push(lua_State *L, T inp) * @brief Converts a Lua object into c++ and removes it from the stack. * If peek fails, the object is still removed from the stack. * - * Relies on bool peek(lua_State*, T*, StackIdx) existing. + * Relies on PeekResult peek(lua_State*, T*, StackIdx) existing. */ template -bool pop(lua_State *L, T *out, StackIdx idx = -1) +PeekResult pop(lua_State *L, T *out, StackIdx idx = -1) { StackGuard guard(L, -1); - auto ok = peek(L, out, idx); + auto res = peek(L, out, idx); if (idx < 0) { idx = lua_gettop(L) + idx + 1; } lua_remove(L, idx); - return ok; + return res; } /**