Index: lldb/include/lldb/Core/IOHandler.h =================================================================== --- lldb/include/lldb/Core/IOHandler.h +++ lldb/include/lldb/Core/IOHandler.h @@ -51,6 +51,7 @@ REPL, ProcessIO, PythonInterpreter, + LuaInterpreter, PythonCode, Other }; Index: lldb/source/Plugins/ScriptInterpreter/Lua/CMakeLists.txt =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Lua/CMakeLists.txt +++ lldb/source/Plugins/ScriptInterpreter/Lua/CMakeLists.txt @@ -4,4 +4,9 @@ LINK_LIBS lldbCore lldbInterpreter - ) \ No newline at end of file + ) + +find_package(Lua REQUIRED) +include_directories(${LUA_INCLUDE_DIR}) + +target_link_libraries(lldbPluginScriptInterpreterLua PRIVATE ${LUA_LIBRARIES}) Index: lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp +++ lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp @@ -10,32 +10,188 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/StreamFile.h" +#include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StringList.h" - +#include "lldb/Utility/Timer.h" #include "llvm/Support/Threading.h" +#ifndef LLDB_DISABLE_LIBEDIT +#include "lldb/Host/Editline.h" +#endif + +#include "lua.hpp" + #include using namespace lldb; using namespace lldb_private; +class Lua { +public: + Lua() : m_lua_state(luaL_newstate()) { + assert(m_lua_state); + luaL_openlibs(m_lua_state); + } + ~Lua() { + assert(m_lua_state); + luaL_openlibs(m_lua_state); + } + + lua_State *State() { return m_lua_state; } + +private: + lua_State *m_lua_state = nullptr; +}; + +class IOHandlerLuaInterpreter : public IOHandler { +public: + IOHandlerLuaInterpreter(Debugger &debugger, ScriptInterpreterLua *lua) + : IOHandler(debugger, IOHandler::Type::LuaInterpreter), m_lua(lua) { +#ifndef LLDB_DISABLE_LIBEDIT + + bool use_editline = GetInputFILE() && GetOutputFILE() && GetErrorFILE() && + m_input_sp && m_input_sp->GetIsRealTerminal(); + if (use_editline) { + m_editline_up.reset(new Editline("lua", GetInputFILE(), GetOutputFILE(), + GetErrorFILE(), + m_debugger.GetUseColor())); + m_editline_up->SetPrompt(">>> "); + } +#endif + } + + ~IOHandlerLuaInterpreter() override {} + + void PrintPrompt() { fprintf(GetOutputFILE(), ">>> "); } + + bool GetLine(std::string &line, bool &interrupted) { +#ifndef LLDB_DISABLE_LIBEDIT + if (m_editline_up) + return m_editline_up->GetLine(line, interrupted); +#endif + + PrintPrompt(); + char buffer[512]; + char *r = fgets(buffer, sizeof(buffer), GetInputFILE()); + line = std::string(buffer); + return r != nullptr; + } + + void Run() override { + if (!m_lua) { + SetIsDone(true); + return; + } + + int stdin_fd = GetInputFD(); + if (stdin_fd < 0) { + SetIsDone(true); + return; + } + + Terminal terminal(stdin_fd); + TerminalState terminal_state; + const bool is_a_tty = terminal.IsATerminal(); + + if (is_a_tty) { + terminal_state.Save(stdin_fd, false); + terminal.SetCanonical(false); + terminal.SetEcho(true); + } + + Lua l; + std::string line; + bool interrupted = false; + int error = 0; + while (IsActive()) { + if (GetLine(line, interrupted)) { + error = luaL_loadbuffer(l.State(), line.c_str(), line.size(), "line") || + lua_pcall(l.State(), 0, 0, 0); + if (error) { + fprintf(GetOutputFILE(), "%s\n", lua_tostring(l.State(), -1)); + // Pop error message from the stack. + lua_pop(l.State(), 1); + } + } else { + SetIsDone(true); + break; + } + } + + if (is_a_tty) + terminal_state.Restore(); + + SetIsDone(true); + } + + void Cancel() override { +#ifndef LLDB_DISABLE_LIBEDIT + if (m_editline_up) + m_editline_up->Cancel(); +#endif + } + + bool Interrupt() override { return m_lua->Interrupt(); } + + void GotEOF() override { +#ifndef LLDB_DISABLE_LIBEDIT + if (m_editline_up) + m_editline_up->Interrupt(); +#endif + } + +protected: +#ifndef LLDB_DISABLE_LIBEDIT + std::unique_ptr m_editline_up; +#endif + ScriptInterpreterLua *m_lua; +}; + ScriptInterpreterLua::ScriptInterpreterLua(Debugger &debugger) : ScriptInterpreter(debugger, eScriptLanguageLua) {} ScriptInterpreterLua::~ScriptInterpreterLua() {} bool ScriptInterpreterLua::ExecuteOneLine(llvm::StringRef command, - CommandReturnObject *, - const ExecuteScriptOptions &) { - m_debugger.GetErrorStream().PutCString( - "error: the lua script interpreter is not yet implemented.\n"); + CommandReturnObject *result, + const ExecuteScriptOptions &options) { + if (command.empty()) { + result->AppendError("empty command passed to lua\n"); + return false; + } + + Lua l; + + // FIXME: Redirecting stdin, stdout and stderr. + int success = luaL_dostring(l.State(), command.data()); + if (success == 0) + return true; + + result->AppendErrorWithFormat("lua failed attempting to evaluate '%s'\n", + command.data()); return false; } void ScriptInterpreterLua::ExecuteInterpreterLoop() { - m_debugger.GetErrorStream().PutCString( - "error: the lua script interpreter is not yet implemented.\n"); + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION); + + Debugger &debugger = m_debugger; + + // At the moment, the only time the debugger does not have an input file + // handle is when this is called directly from lua, in which case it is + // both dangerous and unnecessary (not to mention confusing) to try to embed + // a running interpreter loop inside the already running lua interpreter + // loop, so we won't do it. + + if (!debugger.GetInputFile().IsValid()) + return; + + IOHandlerSP io_handler_sp(new IOHandlerLuaInterpreter(debugger, this)); + if (io_handler_sp) { + debugger.PushIOHandler(io_handler_sp); + } } void ScriptInterpreterLua::Initialize() {