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