1 //===-- Lua.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 "Lua.h" 10 #include "lldb/Host/FileSystem.h" 11 #include "lldb/Utility/FileSpec.h" 12 #include "llvm/Support/Error.h" 13 #include "llvm/Support/FormatVariadic.h" 14 15 using namespace lldb_private; 16 using namespace lldb; 17 18 #pragma clang diagnostic push 19 #pragma clang diagnostic ignored "-Wreturn-type-c-linkage" 20 21 // Disable warning C4190: 'LLDBSwigPythonBreakpointCallbackFunction' has 22 // C-linkage specified, but returns UDT 'llvm::Expected<bool>' which is 23 // incompatible with C 24 #if _MSC_VER 25 #pragma warning (push) 26 #pragma warning (disable : 4190) 27 #endif 28 29 extern "C" llvm::Expected<bool> LLDBSwigLuaBreakpointCallbackFunction( 30 lua_State *L, lldb::StackFrameSP stop_frame_sp, 31 lldb::BreakpointLocationSP bp_loc_sp, StructuredDataImpl *extra_args_impl); 32 33 extern "C" llvm::Expected<bool> LLDBSwigLuaWatchpointCallbackFunction( 34 lua_State *L, lldb::StackFrameSP stop_frame_sp, lldb::WatchpointSP wp_sp); 35 36 #if _MSC_VER 37 #pragma warning (pop) 38 #endif 39 40 #pragma clang diagnostic pop 41 42 static int lldb_print(lua_State *L) { 43 int n = lua_gettop(L); 44 lua_getglobal(L, "io"); 45 lua_getfield(L, -1, "stdout"); 46 lua_getfield(L, -1, "write"); 47 for (int i = 1; i <= n; i++) { 48 lua_pushvalue(L, -1); // write() 49 lua_pushvalue(L, -3); // io.stdout 50 luaL_tolstring(L, i, nullptr); 51 lua_pushstring(L, i != n ? "\t" : "\n"); 52 lua_call(L, 3, 0); 53 } 54 return 0; 55 } 56 57 Lua::Lua() : m_lua_state(luaL_newstate()) { 58 assert(m_lua_state); 59 luaL_openlibs(m_lua_state); 60 luaopen_lldb(m_lua_state); 61 lua_pushcfunction(m_lua_state, lldb_print); 62 lua_setglobal(m_lua_state, "print"); 63 } 64 65 Lua::~Lua() { 66 assert(m_lua_state); 67 lua_close(m_lua_state); 68 } 69 70 llvm::Error Lua::Run(llvm::StringRef buffer) { 71 int error = 72 luaL_loadbuffer(m_lua_state, buffer.data(), buffer.size(), "buffer") || 73 lua_pcall(m_lua_state, 0, 0, 0); 74 if (error == LUA_OK) 75 return llvm::Error::success(); 76 77 llvm::Error e = llvm::make_error<llvm::StringError>( 78 llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)), 79 llvm::inconvertibleErrorCode()); 80 // Pop error message from the stack. 81 lua_pop(m_lua_state, 1); 82 return e; 83 } 84 85 llvm::Error Lua::RegisterBreakpointCallback(void *baton, const char *body) { 86 lua_pushlightuserdata(m_lua_state, baton); 87 const char *fmt_str = "return function(frame, bp_loc, ...) {0} end"; 88 std::string func_str = llvm::formatv(fmt_str, body).str(); 89 if (luaL_dostring(m_lua_state, func_str.c_str()) != LUA_OK) { 90 llvm::Error e = llvm::make_error<llvm::StringError>( 91 llvm::formatv("{0}", lua_tostring(m_lua_state, -1)), 92 llvm::inconvertibleErrorCode()); 93 // Pop error message from the stack. 94 lua_pop(m_lua_state, 2); 95 return e; 96 } 97 lua_settable(m_lua_state, LUA_REGISTRYINDEX); 98 return llvm::Error::success(); 99 } 100 101 llvm::Expected<bool> 102 Lua::CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp, 103 lldb::BreakpointLocationSP bp_loc_sp, 104 StructuredData::ObjectSP extra_args_sp) { 105 106 lua_pushlightuserdata(m_lua_state, baton); 107 lua_gettable(m_lua_state, LUA_REGISTRYINDEX); 108 auto *extra_args_impl = [&]() -> StructuredDataImpl * { 109 if (extra_args_sp == nullptr) 110 return nullptr; 111 auto *extra_args_impl = new StructuredDataImpl(); 112 extra_args_impl->SetObjectSP(extra_args_sp); 113 return extra_args_impl; 114 }(); 115 return LLDBSwigLuaBreakpointCallbackFunction(m_lua_state, stop_frame_sp, 116 bp_loc_sp, extra_args_impl); 117 } 118 119 llvm::Error Lua::RegisterWatchpointCallback(void *baton, const char *body) { 120 lua_pushlightuserdata(m_lua_state, baton); 121 const char *fmt_str = "return function(frame, wp, ...) {0} end"; 122 std::string func_str = llvm::formatv(fmt_str, body).str(); 123 if (luaL_dostring(m_lua_state, func_str.c_str()) != LUA_OK) { 124 llvm::Error e = llvm::make_error<llvm::StringError>( 125 llvm::formatv("{0}", lua_tostring(m_lua_state, -1)), 126 llvm::inconvertibleErrorCode()); 127 // Pop error message from the stack. 128 lua_pop(m_lua_state, 2); 129 return e; 130 } 131 lua_settable(m_lua_state, LUA_REGISTRYINDEX); 132 return llvm::Error::success(); 133 } 134 135 llvm::Expected<bool> 136 Lua::CallWatchpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp, 137 lldb::WatchpointSP wp_sp) { 138 139 lua_pushlightuserdata(m_lua_state, baton); 140 lua_gettable(m_lua_state, LUA_REGISTRYINDEX); 141 return LLDBSwigLuaWatchpointCallbackFunction(m_lua_state, stop_frame_sp, 142 wp_sp); 143 } 144 145 llvm::Error Lua::CheckSyntax(llvm::StringRef buffer) { 146 int error = 147 luaL_loadbuffer(m_lua_state, buffer.data(), buffer.size(), "buffer"); 148 if (error == LUA_OK) { 149 // Pop buffer 150 lua_pop(m_lua_state, 1); 151 return llvm::Error::success(); 152 } 153 154 llvm::Error e = llvm::make_error<llvm::StringError>( 155 llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)), 156 llvm::inconvertibleErrorCode()); 157 // Pop error message from the stack. 158 lua_pop(m_lua_state, 1); 159 return e; 160 } 161 162 llvm::Error Lua::LoadModule(llvm::StringRef filename) { 163 FileSpec file(filename); 164 if (!FileSystem::Instance().Exists(file)) { 165 return llvm::make_error<llvm::StringError>("invalid path", 166 llvm::inconvertibleErrorCode()); 167 } 168 169 ConstString module_extension = file.GetFileNameExtension(); 170 if (module_extension != ".lua") { 171 return llvm::make_error<llvm::StringError>("invalid extension", 172 llvm::inconvertibleErrorCode()); 173 } 174 175 int error = luaL_loadfile(m_lua_state, filename.data()) || 176 lua_pcall(m_lua_state, 0, 1, 0); 177 if (error != LUA_OK) { 178 llvm::Error e = llvm::make_error<llvm::StringError>( 179 llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)), 180 llvm::inconvertibleErrorCode()); 181 // Pop error message from the stack. 182 lua_pop(m_lua_state, 1); 183 return e; 184 } 185 186 ConstString module_name = file.GetFileNameStrippingExtension(); 187 lua_setglobal(m_lua_state, module_name.GetCString()); 188 return llvm::Error::success(); 189 } 190 191 llvm::Error Lua::ChangeIO(FILE *out, FILE *err) { 192 assert(out != nullptr); 193 assert(err != nullptr); 194 195 lua_getglobal(m_lua_state, "io"); 196 197 lua_getfield(m_lua_state, -1, "stdout"); 198 if (luaL_Stream *s = static_cast<luaL_Stream *>( 199 luaL_testudata(m_lua_state, -1, LUA_FILEHANDLE))) { 200 s->f = out; 201 lua_pop(m_lua_state, 1); 202 } else { 203 lua_pop(m_lua_state, 2); 204 return llvm::make_error<llvm::StringError>("could not get stdout", 205 llvm::inconvertibleErrorCode()); 206 } 207 208 lua_getfield(m_lua_state, -1, "stderr"); 209 if (luaL_Stream *s = static_cast<luaL_Stream *>( 210 luaL_testudata(m_lua_state, -1, LUA_FILEHANDLE))) { 211 s->f = out; 212 lua_pop(m_lua_state, 1); 213 } else { 214 lua_pop(m_lua_state, 2); 215 return llvm::make_error<llvm::StringError>("could not get stderr", 216 llvm::inconvertibleErrorCode()); 217 } 218 219 lua_pop(m_lua_state, 1); 220 return llvm::Error::success(); 221 } 222