From e1dcf28dac42054f98fca138166d81ea7c13e890 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 7 Oct 2024 23:08:22 +0200 Subject: [PATCH] Move io wrapper to sol --- src/controllers/plugins/PluginController.cpp | 38 +- src/controllers/plugins/api/IOWrapper.cpp | 389 +++++++++---------- src/controllers/plugins/api/IOWrapper.hpp | 33 +- 3 files changed, 223 insertions(+), 237 deletions(-) diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index 3b0c2da13..1ea05b7a8 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -23,6 +23,7 @@ # include # include # include +# include # include # include # include @@ -217,24 +218,7 @@ void PluginController::openLibrariesFor(Plugin *plugin, const QDir &pluginDir) lua_seti(L, -2, 3); lua_pop(L, 2); // remove package, package.searchers - // NOLINTNEXTLINE(*-avoid-c-arrays) - static const luaL_Reg ioLib[] = { - {"close", lua::api::io_close}, - {"flush", lua::api::io_flush}, - {"input", lua::api::io_input}, - {"lines", lua::api::io_lines}, - {"open", lua::api::io_open}, - {"output", lua::api::io_output}, - {"popen", lua::api::io_popen}, // stub - {"read", lua::api::io_read}, - {"tmpfile", lua::api::io_tmpfile}, // stub - {"write", lua::api::io_write}, - // type = realio.type - {nullptr, nullptr}, - }; - // TODO: io.popen stub auto iolibIdx = lua::pushEmptyTable(L, 1); - luaL_setfuncs(L, ioLib, 0); // set ourio.type = realio.type lua_pushvalue(L, iolibIdx); @@ -299,6 +283,26 @@ void PluginController::initSol(sol::state_view &lua, Plugin *plugin) c2["HTTPMethod"] = lua::createEnumTable(lua); c2["EventType"] = lua::createEnumTable(lua); c2["LogLevel"] = lua::createEnumTable(lua); + + sol::table io = g["io"]; + io.set_function( + "open", sol::overload(&lua::api::io_open, &lua::api::io_open_modeless)); + io.set_function("lines", sol::overload(&lua::api::io_lines, + &lua::api::io_lines_noargs)); + io.set_function("input", sol::overload(&lua::api::io_input_argless, + &lua::api::io_input_name, + &lua::api::io_input_file)); + io.set_function("output", sol::overload(&lua::api::io_output_argless, + &lua::api::io_output_name, + &lua::api::io_output_file)); + io.set_function("close", sol::overload(&lua::api::io_close_argless, + &lua::api::io_close_file)); + io.set_function("flush", sol::overload(&lua::api::io_flush_argless, + &lua::api::io_flush_file)); + io.set_function("read", &lua::api::io_read); + io.set_function("write", &lua::api::io_write); + io.set_function("popen", &lua::api::io_popen); + io.set_function("tmpfile", &lua::api::io_tmpfile); } void PluginController::load(const QFileInfo &index, const QDir &pluginDir, diff --git a/src/controllers/plugins/api/IOWrapper.cpp b/src/controllers/plugins/api/IOWrapper.cpp index 4e46b1b9b..36555fc31 100644 --- a/src/controllers/plugins/api/IOWrapper.cpp +++ b/src/controllers/plugins/api/IOWrapper.cpp @@ -2,13 +2,23 @@ # include "controllers/plugins/api/IOWrapper.hpp" # include "Application.hpp" -# include "controllers/plugins/LuaUtilities.hpp" +# include "common/QLogging.hpp" # include "controllers/plugins/PluginController.hpp" # include # include +# include +# include +# include +# include +# include +# include +# include +# include # include +# include +# include namespace chatterino::lua::api { @@ -89,45 +99,28 @@ struct LuaFileMode { } }; -int ioError(lua_State *L, const QString &value, int errnoequiv) +sol::variadic_results ioError(lua_State *L, const QString &value, + int errnoequiv) { - lua_pushnil(L); - lua::push(L, value); - lua::push(L, errnoequiv); - return 3; + sol::variadic_results out; + out.push_back(sol::nil); + out.push_back(sol::make_object(L, value.toStdString())); + out.push_back({L, sol::in_place_type, errnoequiv}); + return out; } -// NOLINTBEGIN(*vararg) -int io_open(lua_State *L) +sol::variadic_results io_open(sol::this_state L, QString filename, + QString strmode) { auto *pl = getApp()->getPlugins()->getPluginByStatePtr(L); if (pl == nullptr) { - luaL_error(L, "internal error: no plugin"); - return 0; + throw std::runtime_error("internal error: no plugin"); } - LuaFileMode mode; - if (lua_gettop(L) == 2) + LuaFileMode mode(strmode); + if (!mode.error.isEmpty()) { - // we have a mode - QString smode; - if (!lua::pop(L, &smode)) - { - return luaL_error( - L, - "io.open mode (2nd argument) must be a string or not present"); - } - mode = LuaFileMode(smode); - if (!mode.error.isEmpty()) - { - return luaL_error(L, mode.error.toStdString().c_str()); - } - } - QString filename; - if (!lua::pop(L, &filename)) - { - return luaL_error(L, - "io.open filename (1st argument) must be a string"); + throw std::runtime_error(mode.error.toStdString()); } QFileInfo file(pl->dataDirectory().filePath(filename)); auto abs = file.absoluteFilePath(); @@ -142,39 +135,35 @@ int io_open(lua_State *L) "Plugin does not have permissions to access given file.", EACCES); } - lua_getfield(L, LUA_REGISTRYINDEX, REG_REAL_IO_NAME); - lua_getfield(L, -1, "open"); - lua_remove(L, -2); // remove LUA_REGISTRYINDEX[REAL_IO_NAME] - lua::push(L, abs); - lua::push(L, mode.toString()); - lua_call(L, 2, 3); - return 3; + + sol::state_view lua(L); + auto open = lua.registry()[REG_REAL_IO_NAME]["open"]; + sol::protected_function_result res = + open(abs.toStdString(), mode.toString().toStdString()); + return res; +} +sol::variadic_results io_open_modeless(sol::this_state L, QString filename) +{ + return io_open(L, std::move(filename), "r"); } -int io_lines(lua_State *L) +sol::variadic_results io_lines_noargs(sol::this_state L) +{ + sol::state_view lua(L); + auto lines = lua.registry()[REG_REAL_IO_NAME]["lines"]; + sol::protected_function_result res = lines(); + return res; +} + +sol::variadic_results io_lines(sol::this_state L, QString filename, + sol::variadic_args args) { auto *pl = getApp()->getPlugins()->getPluginByStatePtr(L); if (pl == nullptr) { - luaL_error(L, "internal error: no plugin"); - return 0; - } - if (lua_gettop(L) == 0) - { - // io.lines() case, just call realio.lines - lua_getfield(L, LUA_REGISTRYINDEX, REG_REAL_IO_NAME); - lua_getfield(L, -1, "lines"); - lua_remove(L, -2); // remove LUA_REGISTRYINDEX[REAL_IO_NAME] - lua_call(L, 0, 1); - return 1; - } - QString filename; - if (!lua::pop(L, &filename)) - { - return luaL_error( - L, - "io.lines filename (1st argument) must be a string or not present"); + throw std::runtime_error("internal error: no plugin"); } + sol::state_view lua(L); QFileInfo file(pl->dataDirectory().filePath(filename)); auto abs = file.absoluteFilePath(); qCDebug(chatterinoLua) << "[" << pl->id << ":" << pl->meta.name @@ -187,184 +176,164 @@ int io_lines(lua_State *L) "Plugin does not have permissions to access given file.", EACCES); } - // Our stack looks like this: - // - {...}[1] - // - {...}[2] - // ... - // We want: - // - REG[REG_REAL_IO_NAME].lines - // - absolute file path - // - {...}[1] - // - {...}[2] - // ... - lua_getfield(L, LUA_REGISTRYINDEX, REG_REAL_IO_NAME); - lua_getfield(L, -1, "lines"); - lua_remove(L, -2); // remove LUA_REGISTRYINDEX[REAL_IO_NAME] - lua_insert(L, 1); // move function to start of stack - lua::push(L, abs); - lua_insert(L, 2); // move file name just after the function - lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); - return lua_gettop(L); + auto lines = lua.registry()[REG_REAL_IO_NAME]["lines"]; + sol::protected_function_result res = lines(abs.toStdString(), args); + return res; } -namespace { - - // This is the code for both io.input and io.output - int globalFileCommon(lua_State *L, bool output) +sol::variadic_results io_input_argless(sol::this_state L) +{ + auto *pl = getApp()->getPlugins()->getPluginByStatePtr(L); + if (pl == nullptr) { - auto *pl = getApp()->getPlugins()->getPluginByStatePtr(L); - if (pl == nullptr) - { - luaL_error(L, "internal error: no plugin"); - return 0; - } - // Three signature cases: - // io.input() - // io.input(file) - // io.input(name) - if (lua_gettop(L) == 0) - { - // We have no arguments, call realio.input() - lua_getfield(L, LUA_REGISTRYINDEX, REG_REAL_IO_NAME); - if (output) - { - lua_getfield(L, -1, "output"); - } - else - { - lua_getfield(L, -1, "input"); - } - lua_remove(L, -2); // remove LUA_REGISTRYINDEX[REAL_IO_NAME] - lua_call(L, 0, 1); - return 1; - } - if (lua_gettop(L) != 1) - { - return luaL_error(L, "Too many arguments given to io.input()."); - } - // Now check if we have a file or name - auto *p = luaL_testudata(L, 1, LUA_FILEHANDLE); - if (p == nullptr) - { - // this is not a file handle, send it to open - luaL_getsubtable(L, LUA_REGISTRYINDEX, REG_C2_IO_NAME); - lua_getfield(L, -1, "open"); - lua_remove(L, -2); // remove io - - lua_pushvalue(L, 1); // dupe arg - if (output) - { - lua_pushstring(L, "w"); - } - else - { - lua_pushstring(L, "r"); - } - lua_call(L, 2, 1); // call ourio.open(arg1, 'r'|'w') - // if this isn't a string ourio.open errors - - // this leaves us with: - // 1. arg - // 2. new_file - lua_remove(L, 1); // remove arg, replacing it with new_file - } - - // file handle, pass it off to realio.input - lua_getfield(L, LUA_REGISTRYINDEX, REG_REAL_IO_NAME); - if (output) - { - lua_getfield(L, -1, "output"); - } - else - { - lua_getfield(L, -1, "input"); - } - lua_remove(L, -2); // remove LUA_REGISTRYINDEX[REAL_IO_NAME] - lua_pushvalue(L, 1); // duplicate arg - lua_call(L, 1, 1); - return 1; + throw std::runtime_error("internal error: no plugin"); } + sol::state_view lua(L); -} // namespace - -int io_input(lua_State *L) -{ - return globalFileCommon(L, false); + auto func = lua.registry()[REG_REAL_IO_NAME]["input"]; + sol::protected_function_result res = func(); + return res; } - -int io_output(lua_State *L) +sol::variadic_results io_input_file(sol::this_state L, sol::userdata file) { - return globalFileCommon(L, true); -} - -int io_close(lua_State *L) -{ - if (lua_gettop(L) > 1) + auto *pl = getApp()->getPlugins()->getPluginByStatePtr(L); + if (pl == nullptr) { - return luaL_error( - L, "Too many arguments for io.close. Expected one or zero."); + throw std::runtime_error("internal error: no plugin"); } - if (lua_gettop(L) == 0) + sol::state_view lua(L); + + auto func = lua.registry()[REG_REAL_IO_NAME]["input"]; + sol::protected_function_result res = func(file); + return res; +} +sol::variadic_results io_input_name(sol::this_state L, QString filename) +{ + auto *pl = getApp()->getPlugins()->getPluginByStatePtr(L); + if (pl == nullptr) { - lua_getfield(L, LUA_REGISTRYINDEX, "_IO_output"); + throw std::runtime_error("internal error: no plugin"); } - lua_getfield(L, -1, "close"); - lua_pushvalue(L, -2); - lua_call(L, 1, 0); - return 0; -} - -int io_flush(lua_State *L) -{ - if (lua_gettop(L) > 1) + sol::state_view lua(L); + auto res = io_open(L, std::move(filename), "r"); + if (res.size() != 1) { - return luaL_error( - L, "Too many arguments for io.flush. Expected one or zero."); + throw std::runtime_error(res.at(1).as()); } - lua_getfield(L, LUA_REGISTRYINDEX, "_IO_output"); - lua_getfield(L, -1, "flush"); - lua_pushvalue(L, -2); - lua_call(L, 1, 0); - return 0; -} - -int io_read(lua_State *L) -{ - if (lua_gettop(L) > 1) + auto obj = res.at(0); + if (obj.get_type() != sol::type::userdata) { - return luaL_error( - L, "Too many arguments for io.read. Expected one or zero."); + throw std::runtime_error("a file must be a userdata."); } - lua_getfield(L, LUA_REGISTRYINDEX, "_IO_input"); - lua_getfield(L, -1, "read"); - lua_insert(L, 1); - lua_insert(L, 2); - lua_call(L, lua_gettop(L) - 1, 1); - return 1; + return io_input_file(L, obj); } -int io_write(lua_State *L) +sol::variadic_results io_output_argless(sol::this_state L) { - lua_getfield(L, LUA_REGISTRYINDEX, "_IO_output"); - lua_getfield(L, -1, "write"); - lua_insert(L, 1); - lua_insert(L, 2); - // (input) - // (input).read - // args - lua_call(L, lua_gettop(L) - 1, 1); - return 1; + auto *pl = getApp()->getPlugins()->getPluginByStatePtr(L); + if (pl == nullptr) + { + throw std::runtime_error("internal error: no plugin"); + } + sol::state_view lua(L); + + auto func = lua.registry()[REG_REAL_IO_NAME]["output"]; + sol::protected_function_result res = func(); + return res; +} +sol::variadic_results io_output_file(sol::this_state L, sol::userdata file) +{ + auto *pl = getApp()->getPlugins()->getPluginByStatePtr(L); + if (pl == nullptr) + { + throw std::runtime_error("internal error: no plugin"); + } + sol::state_view lua(L); + + auto func = lua.registry()[REG_REAL_IO_NAME]["output"]; + sol::protected_function_result res = func(file); + return res; +} +sol::variadic_results io_output_name(sol::this_state L, QString filename) +{ + auto *pl = getApp()->getPlugins()->getPluginByStatePtr(L); + if (pl == nullptr) + { + throw std::runtime_error("internal error: no plugin"); + } + sol::state_view lua(L); + auto res = io_open(L, std::move(filename), "w"); + if (res.size() != 1) + { + throw std::runtime_error(res.at(1).as()); + } + auto obj = res.at(0); + if (obj.get_type() != sol::type::userdata) + { + throw std::runtime_error("internal error: a file must be a userdata."); + } + return io_output_file(L, obj); } -int io_popen(lua_State *L) +bool io_close_argless(sol::this_state L) { - return luaL_error(L, "io.popen: This function is a stub!"); + sol::state_view lua(L); + auto out = lua.registry()["_IO_output"]; + return io_close_file(L, out); } -int io_tmpfile(lua_State *L) +bool io_close_file(sol::this_state L, sol::userdata file) { - return luaL_error(L, "io.tmpfile: This function is a stub!"); + sol::state_view lua(L); + return file["close"](file); +} + +void io_flush_argless(sol::this_state L) +{ + sol::state_view lua(L); + auto out = lua.registry()["_IO_output"]; + io_flush_file(L, out); +} + +void io_flush_file(sol::this_state L, sol::userdata file) +{ + sol::state_view lua(L); + file["flush"](file); +} + +sol::variadic_results io_read(sol::this_state L, sol::variadic_args args) +{ + sol::state_view lua(L); + auto inp = lua.registry()["_IO_input"]; + if (!inp.is()) + { + throw std::runtime_error("Input not set to a file"); + } + sol::protected_function read = inp["read"]; + return read(inp, args); +} + +sol::variadic_results io_write(sol::this_state L, sol::variadic_args args) +{ + sol::state_view lua(L); + auto out = lua.registry()["_IO_output"]; + if (!out.is()) + { + throw std::runtime_error("Output not set to a file"); + } + sol::protected_function write = out["write"]; + return write(out, args); +} + +void io_popen() +{ + throw std::runtime_error("io.popen: This function is a stub!"); +} + +void io_tmpfile() +{ + throw std::runtime_error("io.tmpfile: This function is a stub!"); } // NOLINTEND(*vararg) diff --git a/src/controllers/plugins/api/IOWrapper.hpp b/src/controllers/plugins/api/IOWrapper.hpp index 24ee2801e..1a777ccbc 100644 --- a/src/controllers/plugins/api/IOWrapper.hpp +++ b/src/controllers/plugins/api/IOWrapper.hpp @@ -1,4 +1,7 @@ #pragma once +#include +#include +#include #ifdef CHATTERINO_HAVE_PLUGINS struct lua_State; @@ -20,7 +23,9 @@ const char *const REG_C2_IO_NAME = "c2io"; * @lua@param mode nil|"r"|"w"|"a"|"r+"|"w+"|"a+" * @exposed io.open */ -int io_open(lua_State *L); +sol::variadic_results io_open(sol::this_state L, QString filename, + QString strmode); +sol::variadic_results io_open_modeless(sol::this_state L, QString filename); /** * Equivalent to io.input():lines("l") or a specific iterator over given file @@ -32,7 +37,9 @@ int io_open(lua_State *L); * @lua@param ... * @exposed io.lines */ -int io_lines(lua_State *L); +sol::variadic_results io_lines(sol::this_state L, QString filename, + sol::variadic_args args); +sol::variadic_results io_lines_noargs(sol::this_state L); /** * Opens a file and sets it as default input or if given no arguments returns the default input. @@ -42,7 +49,9 @@ int io_lines(lua_State *L); * @lua@return nil|FILE* * @exposed io.input */ -int io_input(lua_State *L); +sol::variadic_results io_input_argless(sol::this_state L); +sol::variadic_results io_input_file(sol::this_state L, sol::userdata file); +sol::variadic_results io_input_name(sol::this_state L, QString filename); /** * Opens a file and sets it as default output or if given no arguments returns the default output @@ -52,7 +61,9 @@ int io_input(lua_State *L); * @lua@return nil|FILE* * @exposed io.output */ -int io_output(lua_State *L); +sol::variadic_results io_output_argless(sol::this_state L); +sol::variadic_results io_output_file(sol::this_state L, sol::userdata file); +sol::variadic_results io_output_name(sol::this_state L, QString filename); /** * Closes given file or io.output() if not given. @@ -61,7 +72,8 @@ int io_output(lua_State *L); * @lua@param nil|FILE* * @exposed io.close */ -int io_close(lua_State *L); +bool io_close_argless(sol::this_state L); +bool io_close_file(sol::this_state L, sol::userdata file); /** * Flushes the buffer for given file or io.output() if not given. @@ -70,7 +82,8 @@ int io_close(lua_State *L); * @lua@param nil|FILE* * @exposed io.flush */ -int io_flush(lua_State *L); +void io_flush_argless(sol::this_state L); +void io_flush_file(sol::this_state L, sol::userdata file); /** * Reads some data from the default input file @@ -79,7 +92,7 @@ int io_flush(lua_State *L); * @lua@param nil|string * @exposed io.read */ -int io_read(lua_State *L); +sol::variadic_results io_read(sol::this_state L, sol::variadic_args args); /** * Writes some data to the default output file @@ -88,10 +101,10 @@ int io_read(lua_State *L); * @lua@param nil|string * @exposed io.write */ -int io_write(lua_State *L); +sol::variadic_results io_write(sol::this_state L, sol::variadic_args args); -int io_popen(lua_State *L); -int io_tmpfile(lua_State *L); +void io_popen(); +void io_tmpfile(); // NOLINTEND(readability-identifier-naming) } // namespace chatterino::lua::api