1 //===-- ScriptInterpreterLua.cpp ------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "ScriptInterpreterLua.h" 10 #include "Lua.h" 11 #include "lldb/Core/Debugger.h" 12 #include "lldb/Core/PluginManager.h" 13 #include "lldb/Core/StreamFile.h" 14 #include "lldb/Interpreter/CommandReturnObject.h" 15 #include "lldb/Utility/Stream.h" 16 #include "lldb/Utility/StringList.h" 17 #include "lldb/Utility/Timer.h" 18 #include "llvm/Support/FormatAdapters.h" 19 20 using namespace lldb; 21 using namespace lldb_private; 22 23 LLDB_PLUGIN_DEFINE(ScriptInterpreterLua) 24 25 class IOHandlerLuaInterpreter : public IOHandlerDelegate, 26 public IOHandlerEditline { 27 public: 28 IOHandlerLuaInterpreter(Debugger &debugger, 29 ScriptInterpreterLua &script_interpreter) 30 : IOHandlerEditline(debugger, IOHandler::Type::LuaInterpreter, "lua", 31 ">>> ", "..> ", true, debugger.GetUseColor(), 0, 32 *this, nullptr), 33 m_script_interpreter(script_interpreter) { 34 llvm::cantFail(m_script_interpreter.GetLua().ChangeIO( 35 debugger.GetOutputFile().GetStream(), 36 debugger.GetErrorFile().GetStream())); 37 llvm::cantFail(m_script_interpreter.EnterSession(debugger.GetID())); 38 } 39 40 ~IOHandlerLuaInterpreter() override { 41 llvm::cantFail(m_script_interpreter.LeaveSession()); 42 } 43 44 void IOHandlerInputComplete(IOHandler &io_handler, 45 std::string &data) override { 46 if (llvm::StringRef(data).rtrim() == "quit") { 47 io_handler.SetIsDone(true); 48 return; 49 } 50 51 if (llvm::Error error = m_script_interpreter.GetLua().Run(data)) { 52 *GetOutputStreamFileSP() << llvm::toString(std::move(error)); 53 } 54 } 55 56 private: 57 ScriptInterpreterLua &m_script_interpreter; 58 }; 59 60 ScriptInterpreterLua::ScriptInterpreterLua(Debugger &debugger) 61 : ScriptInterpreter(debugger, eScriptLanguageLua), 62 m_lua(std::make_unique<Lua>()) {} 63 64 ScriptInterpreterLua::~ScriptInterpreterLua() {} 65 66 bool ScriptInterpreterLua::ExecuteOneLine(llvm::StringRef command, 67 CommandReturnObject *result, 68 const ExecuteScriptOptions &options) { 69 if (command.empty()) { 70 if (result) 71 result->AppendError("empty command passed to lua\n"); 72 return false; 73 } 74 75 llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>> 76 io_redirect_or_error = ScriptInterpreterIORedirect::Create( 77 options.GetEnableIO(), m_debugger, result); 78 if (!io_redirect_or_error) { 79 if (result) 80 result->AppendErrorWithFormatv( 81 "failed to redirect I/O: {0}\n", 82 llvm::fmt_consume(io_redirect_or_error.takeError())); 83 else 84 llvm::consumeError(io_redirect_or_error.takeError()); 85 return false; 86 } 87 88 ScriptInterpreterIORedirect &io_redirect = **io_redirect_or_error; 89 90 if (llvm::Error e = 91 m_lua->ChangeIO(io_redirect.GetOutputFile()->GetStream(), 92 io_redirect.GetErrorFile()->GetStream())) { 93 result->AppendErrorWithFormatv("lua failed to redirect I/O: {0}\n", 94 llvm::toString(std::move(e))); 95 return false; 96 } 97 98 if (llvm::Error e = m_lua->Run(command)) { 99 result->AppendErrorWithFormatv( 100 "lua failed attempting to evaluate '{0}': {1}\n", command, 101 llvm::toString(std::move(e))); 102 return false; 103 } 104 105 io_redirect.Flush(); 106 return true; 107 } 108 109 void ScriptInterpreterLua::ExecuteInterpreterLoop() { 110 static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); 111 Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION); 112 113 // At the moment, the only time the debugger does not have an input file 114 // handle is when this is called directly from lua, in which case it is 115 // both dangerous and unnecessary (not to mention confusing) to try to embed 116 // a running interpreter loop inside the already running lua interpreter 117 // loop, so we won't do it. 118 if (!m_debugger.GetInputFile().IsValid()) 119 return; 120 121 IOHandlerSP io_handler_sp(new IOHandlerLuaInterpreter(m_debugger, *this)); 122 m_debugger.RunIOHandlerAsync(io_handler_sp); 123 } 124 125 bool ScriptInterpreterLua::LoadScriptingModule( 126 const char *filename, bool init_session, lldb_private::Status &error, 127 StructuredData::ObjectSP *module_sp) { 128 129 FileSystem::Instance().Collect(filename); 130 if (llvm::Error e = m_lua->LoadModule(filename)) { 131 error.SetErrorStringWithFormatv("lua failed to import '{0}': {1}\n", 132 filename, llvm::toString(std::move(e))); 133 return false; 134 } 135 return true; 136 } 137 138 void ScriptInterpreterLua::Initialize() { 139 static llvm::once_flag g_once_flag; 140 141 llvm::call_once(g_once_flag, []() { 142 PluginManager::RegisterPlugin(GetPluginNameStatic(), 143 GetPluginDescriptionStatic(), 144 lldb::eScriptLanguageLua, CreateInstance); 145 }); 146 } 147 148 void ScriptInterpreterLua::Terminate() {} 149 150 llvm::Error ScriptInterpreterLua::EnterSession(user_id_t debugger_id) { 151 if (m_session_is_active) 152 return llvm::Error::success(); 153 154 const char *fmt_str = 155 "lldb.debugger = lldb.SBDebugger.FindDebuggerWithID({0}); " 156 "lldb.target = lldb.debugger:GetSelectedTarget(); " 157 "lldb.process = lldb.target:GetProcess(); " 158 "lldb.thread = lldb.process:GetSelectedThread(); " 159 "lldb.frame = lldb.thread:GetSelectedFrame()"; 160 return m_lua->Run(llvm::formatv(fmt_str, debugger_id).str()); 161 } 162 163 llvm::Error ScriptInterpreterLua::LeaveSession() { 164 if (!m_session_is_active) 165 return llvm::Error::success(); 166 167 m_session_is_active = false; 168 169 llvm::StringRef str = "lldb.debugger = nil; " 170 "lldb.target = nil; " 171 "lldb.process = nil; " 172 "lldb.thread = nil; " 173 "lldb.frame = nil"; 174 return m_lua->Run(str); 175 } 176 177 lldb::ScriptInterpreterSP 178 ScriptInterpreterLua::CreateInstance(Debugger &debugger) { 179 return std::make_shared<ScriptInterpreterLua>(debugger); 180 } 181 182 lldb_private::ConstString ScriptInterpreterLua::GetPluginNameStatic() { 183 static ConstString g_name("script-lua"); 184 return g_name; 185 } 186 187 const char *ScriptInterpreterLua::GetPluginDescriptionStatic() { 188 return "Lua script interpreter"; 189 } 190 191 lldb_private::ConstString ScriptInterpreterLua::GetPluginName() { 192 return GetPluginNameStatic(); 193 } 194 195 uint32_t ScriptInterpreterLua::GetPluginVersion() { return 1; } 196 197 Lua &ScriptInterpreterLua::GetLua() { return *m_lua; } 198