JDevlieghere created this revision.
JDevlieghere added a reviewer: LLDB.
Herald added subscribers: abidh, mgorny.
Herald added a project: LLDB.
JDevlieghere edited the summary of this revision.
JDevlieghere added a parent revision: D71232: [lldb/Lua] Add Boilerplate for a 
Lua Script Interpreter.

This implements a very elementary Lua script interpreter. It supports running a 
single command as well as running interactively. It uses editline if available. 
It's still missing a bunch of stuff though. Some things that I intentionally 
ingored for now are that I/O isn't properly hooked up (so every `print` goes to 
stdout) and the non-editline support which is not handling a bunch of corner 
cases. The latter is a matter of reusing existing code in the Python 
interpreter.

Discussion on the mailing list: 
http://lists.llvm.org/pipermail/lldb-dev/2019-December/015812.html


Repository:
  rLLDB LLDB

https://reviews.llvm.org/D71234

Files:
  lldb/include/lldb/Core/IOHandler.h
  lldb/source/Plugins/ScriptInterpreter/Lua/CMakeLists.txt
  lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp

Index: lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
@@ -10,32 +10,188 @@
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Core/StreamFile.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
 #include "lldb/Utility/Stream.h"
 #include "lldb/Utility/StringList.h"
-
+#include "lldb/Utility/Timer.h"
 #include "llvm/Support/Threading.h"
 
+#ifndef LLDB_DISABLE_LIBEDIT
+#include "lldb/Host/Editline.h"
+#endif
+
+#include "lua.hpp"
+
 #include <mutex>
 
 using namespace lldb;
 using namespace lldb_private;
 
+class Lua {
+public:
+  Lua() : m_lua_state(luaL_newstate()) {
+    assert(m_lua_state);
+    luaL_openlibs(m_lua_state);
+  }
+  ~Lua() {
+    assert(m_lua_state);
+    luaL_openlibs(m_lua_state);
+  }
+
+  lua_State *State() { return m_lua_state; }
+
+private:
+  lua_State *m_lua_state = nullptr;
+};
+
+class IOHandlerLuaInterpreter : public IOHandler {
+public:
+  IOHandlerLuaInterpreter(Debugger &debugger, ScriptInterpreterLua *lua)
+      : IOHandler(debugger, IOHandler::Type::LuaInterpreter), m_lua(lua) {
+#ifndef LLDB_DISABLE_LIBEDIT
+
+    bool use_editline = GetInputFILE() && GetOutputFILE() && GetErrorFILE() &&
+                        m_input_sp && m_input_sp->GetIsRealTerminal();
+    if (use_editline) {
+      m_editline_up.reset(new Editline("lua", GetInputFILE(), GetOutputFILE(),
+                                       GetErrorFILE(),
+                                       m_debugger.GetUseColor()));
+      m_editline_up->SetPrompt(">>> ");
+    }
+#endif
+  }
+
+  ~IOHandlerLuaInterpreter() override {}
+
+  void PrintPrompt() { fprintf(GetOutputFILE(), ">>> "); }
+
+  bool GetLine(std::string &line, bool &interrupted) {
+#ifndef LLDB_DISABLE_LIBEDIT
+    if (m_editline_up)
+      return m_editline_up->GetLine(line, interrupted);
+#endif
+
+    PrintPrompt();
+    char buffer[512];
+    char *r = fgets(buffer, sizeof(buffer), GetInputFILE());
+    line = std::string(buffer);
+    return r != nullptr;
+  }
+
+  void Run() override {
+    if (!m_lua) {
+      SetIsDone(true);
+      return;
+    }
+
+    int stdin_fd = GetInputFD();
+    if (stdin_fd < 0) {
+      SetIsDone(true);
+      return;
+    }
+
+    Terminal terminal(stdin_fd);
+    TerminalState terminal_state;
+    const bool is_a_tty = terminal.IsATerminal();
+
+    if (is_a_tty) {
+      terminal_state.Save(stdin_fd, false);
+      terminal.SetCanonical(false);
+      terminal.SetEcho(true);
+    }
+
+    Lua l;
+    std::string line;
+    bool interrupted = false;
+    int error = 0;
+    while (IsActive()) {
+      if (GetLine(line, interrupted)) {
+        error = luaL_loadbuffer(l.State(), line.c_str(), line.size(), "line") ||
+                lua_pcall(l.State(), 0, 0, 0);
+        if (error) {
+          fprintf(GetOutputFILE(), "%s\n", lua_tostring(l.State(), -1));
+          // Pop error message from the stack.
+          lua_pop(l.State(), 1);
+        }
+      } else {
+        SetIsDone(true);
+        break;
+      }
+    }
+
+    if (is_a_tty)
+      terminal_state.Restore();
+
+    SetIsDone(true);
+  }
+
+  void Cancel() override {
+#ifndef LLDB_DISABLE_LIBEDIT
+    if (m_editline_up)
+      m_editline_up->Cancel();
+#endif
+  }
+
+  bool Interrupt() override { return m_lua->Interrupt(); }
+
+  void GotEOF() override {
+#ifndef LLDB_DISABLE_LIBEDIT
+    if (m_editline_up)
+      m_editline_up->Interrupt();
+#endif
+  }
+
+protected:
+#ifndef LLDB_DISABLE_LIBEDIT
+  std::unique_ptr<Editline> m_editline_up;
+#endif
+  ScriptInterpreterLua *m_lua;
+};
+
 ScriptInterpreterLua::ScriptInterpreterLua(Debugger &debugger)
     : ScriptInterpreter(debugger, eScriptLanguageLua) {}
 
 ScriptInterpreterLua::~ScriptInterpreterLua() {}
 
 bool ScriptInterpreterLua::ExecuteOneLine(llvm::StringRef command,
-                                          CommandReturnObject *,
-                                          const ExecuteScriptOptions &) {
-  m_debugger.GetErrorStream().PutCString(
-      "error: the lua script interpreter is not yet implemented.\n");
+                                          CommandReturnObject *result,
+                                          const ExecuteScriptOptions &options) {
+  if (command.empty()) {
+    result->AppendError("empty command passed to lua\n");
+    return false;
+  }
+
+  Lua l;
+
+  // FIXME: Redirecting stdin, stdout and stderr.
+  int success = luaL_dostring(l.State(), command.data());
+  if (success == 0)
+    return true;
+
+  result->AppendErrorWithFormat("lua failed attempting to evaluate '%s'\n",
+                                command.data());
   return false;
 }
 
 void ScriptInterpreterLua::ExecuteInterpreterLoop() {
-  m_debugger.GetErrorStream().PutCString(
-      "error: the lua script interpreter is not yet implemented.\n");
+  static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+  Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+  Debugger &debugger = m_debugger;
+
+  // At the moment, the only time the debugger does not have an input file
+  // handle is when this is called directly from lua, in which case it is
+  // both dangerous and unnecessary (not to mention confusing) to try to embed
+  // a running interpreter loop inside the already running lua interpreter
+  // loop, so we won't do it.
+
+  if (!debugger.GetInputFile().IsValid())
+    return;
+
+  IOHandlerSP io_handler_sp(new IOHandlerLuaInterpreter(debugger, this));
+  if (io_handler_sp) {
+    debugger.PushIOHandler(io_handler_sp);
+  }
 }
 
 void ScriptInterpreterLua::Initialize() {
Index: lldb/source/Plugins/ScriptInterpreter/Lua/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Lua/CMakeLists.txt
+++ lldb/source/Plugins/ScriptInterpreter/Lua/CMakeLists.txt
@@ -4,4 +4,9 @@
   LINK_LIBS
     lldbCore
     lldbInterpreter
-  )
\ No newline at end of file
+  )
+
+find_package(Lua REQUIRED)
+include_directories(${LUA_INCLUDE_DIR})
+
+target_link_libraries(lldbPluginScriptInterpreterLua PRIVATE ${LUA_LIBRARIES})
Index: lldb/include/lldb/Core/IOHandler.h
===================================================================
--- lldb/include/lldb/Core/IOHandler.h
+++ lldb/include/lldb/Core/IOHandler.h
@@ -51,6 +51,7 @@
     REPL,
     ProcessIO,
     PythonInterpreter,
+    LuaInterpreter,
     PythonCode,
     Other
   };
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to