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