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 19 using namespace lldb; 20 using namespace lldb_private; 21 22 LLDB_PLUGIN_DEFINE(ScriptInterpreterLua) 23 24 class IOHandlerLuaInterpreter : public IOHandlerDelegate, 25 public IOHandlerEditline { 26 public: 27 IOHandlerLuaInterpreter(Debugger &debugger, 28 ScriptInterpreterLua &script_interpreter) 29 : IOHandlerEditline(debugger, IOHandler::Type::LuaInterpreter, "lua", 30 ">>> ", "..> ", true, debugger.GetUseColor(), 0, 31 *this, nullptr), 32 m_script_interpreter(script_interpreter) { 33 llvm::cantFail(m_script_interpreter.GetLua().ChangeIO( 34 debugger.GetOutputFile().GetStream(), 35 debugger.GetErrorFile().GetStream())); 36 llvm::cantFail(m_script_interpreter.EnterSession(debugger.GetID())); 37 } 38 39 ~IOHandlerLuaInterpreter() override { 40 llvm::cantFail(m_script_interpreter.LeaveSession()); 41 } 42 43 void IOHandlerInputComplete(IOHandler &io_handler, 44 std::string &data) override { 45 if (llvm::StringRef(data).rtrim() == "quit") { 46 io_handler.SetIsDone(true); 47 return; 48 } 49 50 if (llvm::Error error = m_script_interpreter.GetLua().Run(data)) { 51 *GetOutputStreamFileSP() << llvm::toString(std::move(error)); 52 } 53 } 54 55 private: 56 ScriptInterpreterLua &m_script_interpreter; 57 }; 58 59 ScriptInterpreterLua::ScriptInterpreterLua(Debugger &debugger) 60 : ScriptInterpreter(debugger, eScriptLanguageLua), 61 m_lua(std::make_unique<Lua>()) {} 62 63 ScriptInterpreterLua::~ScriptInterpreterLua() {} 64 65 bool ScriptInterpreterLua::ExecuteOneLine(llvm::StringRef command, 66 CommandReturnObject *result, 67 const ExecuteScriptOptions &options) { 68 if (llvm::Error e = m_lua->Run(command)) { 69 result->AppendErrorWithFormatv( 70 "lua failed attempting to evaluate '{0}': {1}\n", command, 71 llvm::toString(std::move(e))); 72 return false; 73 } 74 return true; 75 } 76 77 void ScriptInterpreterLua::ExecuteInterpreterLoop() { 78 static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); 79 Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION); 80 81 // At the moment, the only time the debugger does not have an input file 82 // handle is when this is called directly from lua, in which case it is 83 // both dangerous and unnecessary (not to mention confusing) to try to embed 84 // a running interpreter loop inside the already running lua interpreter 85 // loop, so we won't do it. 86 if (!m_debugger.GetInputFile().IsValid()) 87 return; 88 89 IOHandlerSP io_handler_sp(new IOHandlerLuaInterpreter(m_debugger, *this)); 90 m_debugger.RunIOHandlerAsync(io_handler_sp); 91 } 92 93 bool ScriptInterpreterLua::LoadScriptingModule( 94 const char *filename, bool init_session, lldb_private::Status &error, 95 StructuredData::ObjectSP *module_sp) { 96 97 FileSystem::Instance().Collect(filename); 98 if (llvm::Error e = m_lua->LoadModule(filename)) { 99 error.SetErrorStringWithFormatv("lua failed to import '{0}': {1}\n", 100 filename, llvm::toString(std::move(e))); 101 return false; 102 } 103 return true; 104 } 105 106 void ScriptInterpreterLua::Initialize() { 107 static llvm::once_flag g_once_flag; 108 109 llvm::call_once(g_once_flag, []() { 110 PluginManager::RegisterPlugin(GetPluginNameStatic(), 111 GetPluginDescriptionStatic(), 112 lldb::eScriptLanguageLua, CreateInstance); 113 }); 114 } 115 116 void ScriptInterpreterLua::Terminate() {} 117 118 llvm::Error ScriptInterpreterLua::EnterSession(user_id_t debugger_id) { 119 if (m_session_is_active) 120 return llvm::Error::success(); 121 122 const char *fmt_str = 123 "lldb.debugger = lldb.SBDebugger.FindDebuggerWithID({0}); " 124 "lldb.target = lldb.debugger:GetSelectedTarget(); " 125 "lldb.process = lldb.target:GetProcess(); " 126 "lldb.thread = lldb.process:GetSelectedThread(); " 127 "lldb.frame = lldb.thread:GetSelectedFrame()"; 128 return m_lua->Run(llvm::formatv(fmt_str, debugger_id).str()); 129 } 130 131 llvm::Error ScriptInterpreterLua::LeaveSession() { 132 if (!m_session_is_active) 133 return llvm::Error::success(); 134 135 m_session_is_active = false; 136 137 llvm::StringRef str = "lldb.debugger = nil; " 138 "lldb.target = nil; " 139 "lldb.process = nil; " 140 "lldb.thread = nil; " 141 "lldb.frame = nil"; 142 return m_lua->Run(str); 143 } 144 145 lldb::ScriptInterpreterSP 146 ScriptInterpreterLua::CreateInstance(Debugger &debugger) { 147 return std::make_shared<ScriptInterpreterLua>(debugger); 148 } 149 150 lldb_private::ConstString ScriptInterpreterLua::GetPluginNameStatic() { 151 static ConstString g_name("script-lua"); 152 return g_name; 153 } 154 155 const char *ScriptInterpreterLua::GetPluginDescriptionStatic() { 156 return "Lua script interpreter"; 157 } 158 159 lldb_private::ConstString ScriptInterpreterLua::GetPluginName() { 160 return GetPluginNameStatic(); 161 } 162 163 uint32_t ScriptInterpreterLua::GetPluginVersion() { return 1; } 164 165 Lua &ScriptInterpreterLua::GetLua() { return *m_lua; } 166