xref: /llvm-project/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp (revision fa1b4a96a0110c87cdd83eb4ea3e9a0d002c3c3d)
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