Index: lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h +++ lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h @@ -10,6 +10,7 @@ #define liblldb_Lua_h_ #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/Error.h" #include "lua.hpp" @@ -36,10 +37,13 @@ } llvm::Error Run(llvm::StringRef buffer); + llvm::Error LoadModule(llvm::StringRef filename); private: + llvm::Expected FormatQuoted(llvm::StringRef str); std::mutex m_mutex; lua_State *m_lua_state; + llvm::StringSet<> m_loaded_modules; }; } // namespace lldb_private Index: lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp +++ lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp @@ -7,10 +7,32 @@ //===----------------------------------------------------------------------===// #include "Lua.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Utility/FileSpec.h" #include "llvm/Support/FormatVariadic.h" using namespace lldb_private; +#define uchar(c) ((unsigned char)(c)) + +llvm::Expected Lua::FormatQuoted(llvm::StringRef str) { + lua_getglobal(m_lua_state, "string"); + lua_getfield(m_lua_state, -1, "format"); + lua_pushlstring(m_lua_state, "%q", 2); + lua_pushlstring(m_lua_state, str.data(), str.size()); + if (lua_pcall(m_lua_state, 2, 1, 0) != 0) { + llvm::Error e = llvm::make_error( + llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)), + llvm::inconvertibleErrorCode()); + // Pop error message from the stack. + lua_pop(m_lua_state, 1); + return std::move(e); + } + const char *formatted_str = lua_tostring(m_lua_state, -1); + lua_pop(m_lua_state, 1); + return formatted_str; +} + llvm::Error Lua::Run(llvm::StringRef buffer) { std::lock_guard lock(m_mutex); int error = @@ -26,3 +48,54 @@ lua_pop(m_lua_state, 1); return e; } + +llvm::Error Lua::LoadModule(llvm::StringRef filename) { + FileSpec file(filename); + if (!FileSystem::Instance().Exists(file)) { + return llvm::make_error("invalid path", + llvm::inconvertibleErrorCode()); + } + + llvm::StringRef module_extension = file.GetFileNameExtension().GetStringRef(); + if (module_extension != ".lua") { + return llvm::make_error("invalid extension", + llvm::inconvertibleErrorCode()); + } + + llvm::StringRef module_name = + file.GetFileNameStrippingExtension().GetStringRef(); + if (module_name.empty()) { + return llvm::make_error("invalid module name", + llvm::inconvertibleErrorCode()); + } + + llvm::StringRef module_dir = file.GetDirectory().GetStringRef(); + // Don't clutter the package path and only add the module path if we haven't + // seen it before. + if (!m_loaded_modules.count(module_dir)) { + std::string s = llvm::formatv(";{0}/?.lua", module_dir).str(); + llvm::Expected maybe_quoted = FormatQuoted(s); + if (!maybe_quoted) { + return maybe_quoted.takeError(); + } + if (llvm::Error e = Run( + llvm::formatv("package.path = package.path .. {0}", *maybe_quoted) + .str())) { + return e; + } + m_loaded_modules.insert(module_dir); + } + + if (llvm::Error e = + Run(llvm::formatv("package.loaded.{0} = nil", module_name).str())) { + return e; + } + + llvm::Expected maybe_quoted = FormatQuoted(module_name); + if (!maybe_quoted) { + return maybe_quoted.takeError(); + } + // Import the module. + return Run( + llvm::formatv("{0} = require {1}", module_name, *maybe_quoted).str()); +} Index: lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h +++ lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h @@ -25,6 +25,11 @@ void ExecuteInterpreterLoop() override; + virtual bool + LoadScriptingModule(const char *filename, bool init_session, + lldb_private::Status &error, + StructuredData::ObjectSP *module_sp = nullptr) override; + // Static Functions static void Initialize(); Index: lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp +++ lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp @@ -77,6 +77,18 @@ debugger.PushIOHandler(io_handler_sp); } +bool ScriptInterpreterLua::LoadScriptingModule( + const char *filename, bool init_session, lldb_private::Status &error, + StructuredData::ObjectSP *module_sp) { + + if (llvm::Error e = m_lua->LoadModule(filename)) { + error.SetErrorStringWithFormatv("lua failed to import '{0}': {1}\n", + filename, llvm::toString(std::move(e))); + return false; + } + return true; +} + void ScriptInterpreterLua::Initialize() { static llvm::once_flag g_once_flag; Index: lldb/test/Shell/ScriptInterpreter/Lua/Inputs/testmodule.lua =================================================================== --- /dev/null +++ lldb/test/Shell/ScriptInterpreter/Lua/Inputs/testmodule.lua @@ -0,0 +1,7 @@ +local mymodule = {} + +function mymodule.foo() + print("Hello World!") +end + +return mymodule Index: lldb/test/Shell/ScriptInterpreter/Lua/command_script_import.test =================================================================== --- /dev/null +++ lldb/test/Shell/ScriptInterpreter/Lua/command_script_import.test @@ -0,0 +1,4 @@ +# REQUIRES: lua +# RUN: %lldb --script-language lua -o 'command script import %S/Inputs/testmodule.lua' -o 'script testmodule.foo()' 2>&1 | FileCheck %s +# RUN: %lldb --script-language lua -o 'command script import %S/Inputs/testmodule.lua' -o 'command script import %S/Inputs/testmodule.lua' -o 'script testmodule.foo()' 2>&1 | FileCheck %s +# CHECK: Hello World!