| //===-- Lua.cpp -----------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Lua.h" |
| #include "lldb/Host/FileSystem.h" |
| #include "lldb/Utility/FileSpec.h" |
| #include "llvm/Support/Error.h" |
| #include "llvm/Support/FormatVariadic.h" |
| |
| using namespace lldb_private; |
| using namespace lldb; |
| |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wreturn-type-c-linkage" |
| |
| // Disable warning C4190: 'LLDBSwigPythonBreakpointCallbackFunction' has |
| // C-linkage specified, but returns UDT 'llvm::Expected<bool>' which is |
| // incompatible with C |
| #if _MSC_VER |
| #pragma warning (push) |
| #pragma warning (disable : 4190) |
| #endif |
| |
| extern "C" llvm::Expected<bool> LLDBSwigLuaBreakpointCallbackFunction( |
| lua_State *L, lldb::StackFrameSP stop_frame_sp, |
| lldb::BreakpointLocationSP bp_loc_sp, StructuredDataImpl *extra_args_impl); |
| |
| extern "C" llvm::Expected<bool> LLDBSwigLuaWatchpointCallbackFunction( |
| lua_State *L, lldb::StackFrameSP stop_frame_sp, lldb::WatchpointSP wp_sp); |
| |
| #if _MSC_VER |
| #pragma warning (pop) |
| #endif |
| |
| #pragma clang diagnostic pop |
| |
| static int lldb_print(lua_State *L) { |
| int n = lua_gettop(L); |
| lua_getglobal(L, "io"); |
| lua_getfield(L, -1, "stdout"); |
| lua_getfield(L, -1, "write"); |
| for (int i = 1; i <= n; i++) { |
| lua_pushvalue(L, -1); // write() |
| lua_pushvalue(L, -3); // io.stdout |
| luaL_tolstring(L, i, nullptr); |
| lua_pushstring(L, i != n ? "\t" : "\n"); |
| lua_call(L, 3, 0); |
| } |
| return 0; |
| } |
| |
| Lua::Lua() : m_lua_state(luaL_newstate()) { |
| assert(m_lua_state); |
| luaL_openlibs(m_lua_state); |
| luaopen_lldb(m_lua_state); |
| lua_pushcfunction(m_lua_state, lldb_print); |
| lua_setglobal(m_lua_state, "print"); |
| } |
| |
| Lua::~Lua() { |
| assert(m_lua_state); |
| lua_close(m_lua_state); |
| } |
| |
| llvm::Error Lua::Run(llvm::StringRef buffer) { |
| int error = |
| luaL_loadbuffer(m_lua_state, buffer.data(), buffer.size(), "buffer") || |
| lua_pcall(m_lua_state, 0, 0, 0); |
| if (error == LUA_OK) |
| return llvm::Error::success(); |
| |
| llvm::Error e = llvm::make_error<llvm::StringError>( |
| llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)), |
| llvm::inconvertibleErrorCode()); |
| // Pop error message from the stack. |
| lua_pop(m_lua_state, 1); |
| return e; |
| } |
| |
| llvm::Error Lua::RegisterBreakpointCallback(void *baton, const char *body) { |
| lua_pushlightuserdata(m_lua_state, baton); |
| const char *fmt_str = "return function(frame, bp_loc, ...) {0} end"; |
| std::string func_str = llvm::formatv(fmt_str, body).str(); |
| if (luaL_dostring(m_lua_state, func_str.c_str()) != LUA_OK) { |
| llvm::Error e = llvm::make_error<llvm::StringError>( |
| llvm::formatv("{0}", lua_tostring(m_lua_state, -1)), |
| llvm::inconvertibleErrorCode()); |
| // Pop error message from the stack. |
| lua_pop(m_lua_state, 2); |
| return e; |
| } |
| lua_settable(m_lua_state, LUA_REGISTRYINDEX); |
| return llvm::Error::success(); |
| } |
| |
| llvm::Expected<bool> |
| Lua::CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp, |
| lldb::BreakpointLocationSP bp_loc_sp, |
| StructuredData::ObjectSP extra_args_sp) { |
| |
| lua_pushlightuserdata(m_lua_state, baton); |
| lua_gettable(m_lua_state, LUA_REGISTRYINDEX); |
| auto *extra_args_impl = [&]() -> StructuredDataImpl * { |
| if (extra_args_sp == nullptr) |
| return nullptr; |
| auto *extra_args_impl = new StructuredDataImpl(); |
| extra_args_impl->SetObjectSP(extra_args_sp); |
| return extra_args_impl; |
| }(); |
| return LLDBSwigLuaBreakpointCallbackFunction(m_lua_state, stop_frame_sp, |
| bp_loc_sp, extra_args_impl); |
| } |
| |
| llvm::Error Lua::RegisterWatchpointCallback(void *baton, const char *body) { |
| lua_pushlightuserdata(m_lua_state, baton); |
| const char *fmt_str = "return function(frame, wp, ...) {0} end"; |
| std::string func_str = llvm::formatv(fmt_str, body).str(); |
| if (luaL_dostring(m_lua_state, func_str.c_str()) != LUA_OK) { |
| llvm::Error e = llvm::make_error<llvm::StringError>( |
| llvm::formatv("{0}", lua_tostring(m_lua_state, -1)), |
| llvm::inconvertibleErrorCode()); |
| // Pop error message from the stack. |
| lua_pop(m_lua_state, 2); |
| return e; |
| } |
| lua_settable(m_lua_state, LUA_REGISTRYINDEX); |
| return llvm::Error::success(); |
| } |
| |
| llvm::Expected<bool> |
| Lua::CallWatchpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp, |
| lldb::WatchpointSP wp_sp) { |
| |
| lua_pushlightuserdata(m_lua_state, baton); |
| lua_gettable(m_lua_state, LUA_REGISTRYINDEX); |
| return LLDBSwigLuaWatchpointCallbackFunction(m_lua_state, stop_frame_sp, |
| wp_sp); |
| } |
| |
| llvm::Error Lua::CheckSyntax(llvm::StringRef buffer) { |
| int error = |
| luaL_loadbuffer(m_lua_state, buffer.data(), buffer.size(), "buffer"); |
| if (error == LUA_OK) { |
| // Pop buffer |
| lua_pop(m_lua_state, 1); |
| return llvm::Error::success(); |
| } |
| |
| llvm::Error e = llvm::make_error<llvm::StringError>( |
| llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)), |
| llvm::inconvertibleErrorCode()); |
| // Pop error message from the stack. |
| lua_pop(m_lua_state, 1); |
| return e; |
| } |
| |
| llvm::Error Lua::LoadModule(llvm::StringRef filename) { |
| FileSpec file(filename); |
| if (!FileSystem::Instance().Exists(file)) { |
| return llvm::make_error<llvm::StringError>("invalid path", |
| llvm::inconvertibleErrorCode()); |
| } |
| |
| ConstString module_extension = file.GetFileNameExtension(); |
| if (module_extension != ".lua") { |
| return llvm::make_error<llvm::StringError>("invalid extension", |
| llvm::inconvertibleErrorCode()); |
| } |
| |
| int error = luaL_loadfile(m_lua_state, filename.data()) || |
| lua_pcall(m_lua_state, 0, 1, 0); |
| if (error != LUA_OK) { |
| llvm::Error e = llvm::make_error<llvm::StringError>( |
| llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)), |
| llvm::inconvertibleErrorCode()); |
| // Pop error message from the stack. |
| lua_pop(m_lua_state, 1); |
| return e; |
| } |
| |
| ConstString module_name = file.GetFileNameStrippingExtension(); |
| lua_setglobal(m_lua_state, module_name.GetCString()); |
| return llvm::Error::success(); |
| } |
| |
| llvm::Error Lua::ChangeIO(FILE *out, FILE *err) { |
| assert(out != nullptr); |
| assert(err != nullptr); |
| |
| lua_getglobal(m_lua_state, "io"); |
| |
| lua_getfield(m_lua_state, -1, "stdout"); |
| if (luaL_Stream *s = static_cast<luaL_Stream *>( |
| luaL_testudata(m_lua_state, -1, LUA_FILEHANDLE))) { |
| s->f = out; |
| lua_pop(m_lua_state, 1); |
| } else { |
| lua_pop(m_lua_state, 2); |
| return llvm::make_error<llvm::StringError>("could not get stdout", |
| llvm::inconvertibleErrorCode()); |
| } |
| |
| lua_getfield(m_lua_state, -1, "stderr"); |
| if (luaL_Stream *s = static_cast<luaL_Stream *>( |
| luaL_testudata(m_lua_state, -1, LUA_FILEHANDLE))) { |
| s->f = out; |
| lua_pop(m_lua_state, 1); |
| } else { |
| lua_pop(m_lua_state, 2); |
| return llvm::make_error<llvm::StringError>("could not get stderr", |
| llvm::inconvertibleErrorCode()); |
| } |
| |
| lua_pop(m_lua_state, 1); |
| return llvm::Error::success(); |
| } |