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