From 38a5f60ff2b406da457ecbd0f1db59e206cd47de Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 2 Sep 2024 21:32:11 +0200 Subject: [PATCH] Add some documentation on implementation of plugin APIs. --- src/controllers/plugins/README.md | 69 +++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/controllers/plugins/README.md diff --git a/src/controllers/plugins/README.md b/src/controllers/plugins/README.md new file mode 100644 index 000000000..4962d45d0 --- /dev/null +++ b/src/controllers/plugins/README.md @@ -0,0 +1,69 @@ +# Lua wrapper + +The `chatterino::lua` namespace contains a wrapper for most used Lua functions. +Most of them live in `src/controllers/plugins/LuaUtilities.hpp`. + +- When doing any kind of complex work with the Lua stack, use + `lua::StackGuard`, which will abort the program should the stack size not be + what you expect. +- When possible use `lua::push(lua_State *, T)`, + `lua::peek(lua_State*, T*, StackIdx)` or `lua::pop(lua_State *, T*)` instead + of Lua's APIs directly. + +# Error paths + +When you create error paths that call `luaL_error` or `lua_error` directly, you +should be careful of local variables. Consider this: + +```cpp +// XXX: INCORRECT +void Foo::frobulate(lua_State *L) +{ + // stack size check omitted + + QString somestring; + if (!lua::pop(L, &somestring)) + { + return luaL_error(L, "Foo:frobulate failed to get somestring (1st argument), expected a string"); + } + int someint; + if (!lua::pop(L, &someint)) + { + // Error path 2 + return luaL_error(L, "Foo:frobulate failed to get somesint (2nd argument), expected an integer"); + } + // ... +} +``` + +If `Foo:frobulate()` gets called with a valid string but an invalid int +argument _error path 2_ is invoked. This means that the function returns +without ever calling `QString::~QString()` on `somestring` which leaks memory +that object owns. To solve this you can use the `drop()` utility function to +call destructor of your object: + +```cpp +void Foo::frobulate(lua_State *L) +{ + // stack size check omitted + + QString somestring; + if (!lua::pop(L, &somestring)) + { + drop(somestring); + return luaL_error(L, "Foo:frobulate failed to get somestring (1st argument), expected a string"); + } + int someint; + if (!lua::pop(L, &someint)) + { + // Error path 2 + drop(somestring); + return luaL_error(L, "Foo:frobulate failed to get somesint (2nd argument), expected an integer"); + } + // ... +} +``` + +This code example properly deallocates all memory held by local variables. Note +that you should not use `drop()` on trivial objects that is objects that do not +need any explicitly created destructor.