xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp (revision fe6060f10f634930ff71b7c50291ddc610da2475)
1480093f4SDimitry Andric //===-- Lua.cpp -----------------------------------------------------------===//
2480093f4SDimitry Andric //
3480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6480093f4SDimitry Andric //
7480093f4SDimitry Andric //===----------------------------------------------------------------------===//
8480093f4SDimitry Andric 
9480093f4SDimitry Andric #include "Lua.h"
10480093f4SDimitry Andric #include "lldb/Host/FileSystem.h"
11480093f4SDimitry Andric #include "lldb/Utility/FileSpec.h"
12e8d8bef9SDimitry Andric #include "llvm/Support/Error.h"
13480093f4SDimitry Andric #include "llvm/Support/FormatVariadic.h"
14480093f4SDimitry Andric 
15480093f4SDimitry Andric using namespace lldb_private;
16480093f4SDimitry Andric using namespace lldb;
17480093f4SDimitry Andric 
18e8d8bef9SDimitry Andric #pragma clang diagnostic push
19e8d8bef9SDimitry Andric #pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
20e8d8bef9SDimitry Andric 
21e8d8bef9SDimitry Andric // Disable warning C4190: 'LLDBSwigPythonBreakpointCallbackFunction' has
22e8d8bef9SDimitry Andric // C-linkage specified, but returns UDT 'llvm::Expected<bool>' which is
23e8d8bef9SDimitry Andric // incompatible with C
24e8d8bef9SDimitry Andric #if _MSC_VER
25e8d8bef9SDimitry Andric #pragma warning (push)
26e8d8bef9SDimitry Andric #pragma warning (disable : 4190)
27e8d8bef9SDimitry Andric #endif
28e8d8bef9SDimitry Andric 
29e8d8bef9SDimitry Andric extern "C" llvm::Expected<bool> LLDBSwigLuaBreakpointCallbackFunction(
30e8d8bef9SDimitry Andric     lua_State *L, lldb::StackFrameSP stop_frame_sp,
31e8d8bef9SDimitry Andric     lldb::BreakpointLocationSP bp_loc_sp, StructuredDataImpl *extra_args_impl);
32e8d8bef9SDimitry Andric 
33*fe6060f1SDimitry Andric extern "C" llvm::Expected<bool> LLDBSwigLuaWatchpointCallbackFunction(
34*fe6060f1SDimitry Andric     lua_State *L, lldb::StackFrameSP stop_frame_sp, lldb::WatchpointSP wp_sp);
35*fe6060f1SDimitry Andric 
36e8d8bef9SDimitry Andric #if _MSC_VER
37e8d8bef9SDimitry Andric #pragma warning (pop)
38e8d8bef9SDimitry Andric #endif
39e8d8bef9SDimitry Andric 
40e8d8bef9SDimitry Andric #pragma clang diagnostic pop
41e8d8bef9SDimitry Andric 
42e8d8bef9SDimitry Andric static int lldb_print(lua_State *L) {
43e8d8bef9SDimitry Andric   int n = lua_gettop(L);
44e8d8bef9SDimitry Andric   lua_getglobal(L, "io");
45e8d8bef9SDimitry Andric   lua_getfield(L, -1, "stdout");
46e8d8bef9SDimitry Andric   lua_getfield(L, -1, "write");
47e8d8bef9SDimitry Andric   for (int i = 1; i <= n; i++) {
48e8d8bef9SDimitry Andric     lua_pushvalue(L, -1); // write()
49e8d8bef9SDimitry Andric     lua_pushvalue(L, -3); // io.stdout
50e8d8bef9SDimitry Andric     luaL_tolstring(L, i, nullptr);
51e8d8bef9SDimitry Andric     lua_pushstring(L, i != n ? "\t" : "\n");
52e8d8bef9SDimitry Andric     lua_call(L, 3, 0);
53e8d8bef9SDimitry Andric   }
54e8d8bef9SDimitry Andric   return 0;
55e8d8bef9SDimitry Andric }
56e8d8bef9SDimitry Andric 
57e8d8bef9SDimitry Andric Lua::Lua() : m_lua_state(luaL_newstate()) {
58e8d8bef9SDimitry Andric   assert(m_lua_state);
59e8d8bef9SDimitry Andric   luaL_openlibs(m_lua_state);
60e8d8bef9SDimitry Andric   luaopen_lldb(m_lua_state);
61e8d8bef9SDimitry Andric   lua_pushcfunction(m_lua_state, lldb_print);
62e8d8bef9SDimitry Andric   lua_setglobal(m_lua_state, "print");
63e8d8bef9SDimitry Andric }
64e8d8bef9SDimitry Andric 
65e8d8bef9SDimitry Andric Lua::~Lua() {
66e8d8bef9SDimitry Andric   assert(m_lua_state);
67e8d8bef9SDimitry Andric   lua_close(m_lua_state);
68e8d8bef9SDimitry Andric }
69e8d8bef9SDimitry Andric 
70480093f4SDimitry Andric llvm::Error Lua::Run(llvm::StringRef buffer) {
71480093f4SDimitry Andric   int error =
72480093f4SDimitry Andric       luaL_loadbuffer(m_lua_state, buffer.data(), buffer.size(), "buffer") ||
73480093f4SDimitry Andric       lua_pcall(m_lua_state, 0, 0, 0);
74e8d8bef9SDimitry Andric   if (error == LUA_OK)
75480093f4SDimitry Andric     return llvm::Error::success();
76480093f4SDimitry Andric 
77480093f4SDimitry Andric   llvm::Error e = llvm::make_error<llvm::StringError>(
78480093f4SDimitry Andric       llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)),
79480093f4SDimitry Andric       llvm::inconvertibleErrorCode());
80480093f4SDimitry Andric   // Pop error message from the stack.
81480093f4SDimitry Andric   lua_pop(m_lua_state, 1);
82480093f4SDimitry Andric   return e;
83480093f4SDimitry Andric }
84480093f4SDimitry Andric 
85e8d8bef9SDimitry Andric llvm::Error Lua::RegisterBreakpointCallback(void *baton, const char *body) {
86e8d8bef9SDimitry Andric   lua_pushlightuserdata(m_lua_state, baton);
87e8d8bef9SDimitry Andric   const char *fmt_str = "return function(frame, bp_loc, ...) {0} end";
88e8d8bef9SDimitry Andric   std::string func_str = llvm::formatv(fmt_str, body).str();
89e8d8bef9SDimitry Andric   if (luaL_dostring(m_lua_state, func_str.c_str()) != LUA_OK) {
90e8d8bef9SDimitry Andric     llvm::Error e = llvm::make_error<llvm::StringError>(
91e8d8bef9SDimitry Andric         llvm::formatv("{0}", lua_tostring(m_lua_state, -1)),
92e8d8bef9SDimitry Andric         llvm::inconvertibleErrorCode());
93e8d8bef9SDimitry Andric     // Pop error message from the stack.
94e8d8bef9SDimitry Andric     lua_pop(m_lua_state, 2);
95e8d8bef9SDimitry Andric     return e;
96e8d8bef9SDimitry Andric   }
97e8d8bef9SDimitry Andric   lua_settable(m_lua_state, LUA_REGISTRYINDEX);
98e8d8bef9SDimitry Andric   return llvm::Error::success();
99e8d8bef9SDimitry Andric }
100e8d8bef9SDimitry Andric 
101e8d8bef9SDimitry Andric llvm::Expected<bool>
102e8d8bef9SDimitry Andric Lua::CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp,
103e8d8bef9SDimitry Andric                             lldb::BreakpointLocationSP bp_loc_sp,
104e8d8bef9SDimitry Andric                             StructuredData::ObjectSP extra_args_sp) {
105e8d8bef9SDimitry Andric 
106e8d8bef9SDimitry Andric   lua_pushlightuserdata(m_lua_state, baton);
107e8d8bef9SDimitry Andric   lua_gettable(m_lua_state, LUA_REGISTRYINDEX);
108e8d8bef9SDimitry Andric   auto *extra_args_impl = [&]() -> StructuredDataImpl * {
109e8d8bef9SDimitry Andric     if (extra_args_sp == nullptr)
110e8d8bef9SDimitry Andric       return nullptr;
111e8d8bef9SDimitry Andric     auto *extra_args_impl = new StructuredDataImpl();
112e8d8bef9SDimitry Andric     extra_args_impl->SetObjectSP(extra_args_sp);
113e8d8bef9SDimitry Andric     return extra_args_impl;
114e8d8bef9SDimitry Andric   }();
115e8d8bef9SDimitry Andric   return LLDBSwigLuaBreakpointCallbackFunction(m_lua_state, stop_frame_sp,
116e8d8bef9SDimitry Andric                                                bp_loc_sp, extra_args_impl);
117e8d8bef9SDimitry Andric }
118e8d8bef9SDimitry Andric 
119*fe6060f1SDimitry Andric llvm::Error Lua::RegisterWatchpointCallback(void *baton, const char *body) {
120*fe6060f1SDimitry Andric   lua_pushlightuserdata(m_lua_state, baton);
121*fe6060f1SDimitry Andric   const char *fmt_str = "return function(frame, wp, ...) {0} end";
122*fe6060f1SDimitry Andric   std::string func_str = llvm::formatv(fmt_str, body).str();
123*fe6060f1SDimitry Andric   if (luaL_dostring(m_lua_state, func_str.c_str()) != LUA_OK) {
124*fe6060f1SDimitry Andric     llvm::Error e = llvm::make_error<llvm::StringError>(
125*fe6060f1SDimitry Andric         llvm::formatv("{0}", lua_tostring(m_lua_state, -1)),
126*fe6060f1SDimitry Andric         llvm::inconvertibleErrorCode());
127*fe6060f1SDimitry Andric     // Pop error message from the stack.
128*fe6060f1SDimitry Andric     lua_pop(m_lua_state, 2);
129*fe6060f1SDimitry Andric     return e;
130*fe6060f1SDimitry Andric   }
131*fe6060f1SDimitry Andric   lua_settable(m_lua_state, LUA_REGISTRYINDEX);
132*fe6060f1SDimitry Andric   return llvm::Error::success();
133*fe6060f1SDimitry Andric }
134*fe6060f1SDimitry Andric 
135*fe6060f1SDimitry Andric llvm::Expected<bool>
136*fe6060f1SDimitry Andric Lua::CallWatchpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp,
137*fe6060f1SDimitry Andric                             lldb::WatchpointSP wp_sp) {
138*fe6060f1SDimitry Andric 
139*fe6060f1SDimitry Andric   lua_pushlightuserdata(m_lua_state, baton);
140*fe6060f1SDimitry Andric   lua_gettable(m_lua_state, LUA_REGISTRYINDEX);
141*fe6060f1SDimitry Andric   return LLDBSwigLuaWatchpointCallbackFunction(m_lua_state, stop_frame_sp,
142*fe6060f1SDimitry Andric                                                wp_sp);
143*fe6060f1SDimitry Andric }
144*fe6060f1SDimitry Andric 
145e8d8bef9SDimitry Andric llvm::Error Lua::CheckSyntax(llvm::StringRef buffer) {
146e8d8bef9SDimitry Andric   int error =
147e8d8bef9SDimitry Andric       luaL_loadbuffer(m_lua_state, buffer.data(), buffer.size(), "buffer");
148e8d8bef9SDimitry Andric   if (error == LUA_OK) {
149e8d8bef9SDimitry Andric     // Pop buffer
150e8d8bef9SDimitry Andric     lua_pop(m_lua_state, 1);
151e8d8bef9SDimitry Andric     return llvm::Error::success();
152e8d8bef9SDimitry Andric   }
153e8d8bef9SDimitry Andric 
154e8d8bef9SDimitry Andric   llvm::Error e = llvm::make_error<llvm::StringError>(
155e8d8bef9SDimitry Andric       llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)),
156e8d8bef9SDimitry Andric       llvm::inconvertibleErrorCode());
157e8d8bef9SDimitry Andric   // Pop error message from the stack.
158e8d8bef9SDimitry Andric   lua_pop(m_lua_state, 1);
159e8d8bef9SDimitry Andric   return e;
160e8d8bef9SDimitry Andric }
161e8d8bef9SDimitry Andric 
162480093f4SDimitry Andric llvm::Error Lua::LoadModule(llvm::StringRef filename) {
163480093f4SDimitry Andric   FileSpec file(filename);
164480093f4SDimitry Andric   if (!FileSystem::Instance().Exists(file)) {
165480093f4SDimitry Andric     return llvm::make_error<llvm::StringError>("invalid path",
166480093f4SDimitry Andric                                                llvm::inconvertibleErrorCode());
167480093f4SDimitry Andric   }
168480093f4SDimitry Andric 
169480093f4SDimitry Andric   ConstString module_extension = file.GetFileNameExtension();
170480093f4SDimitry Andric   if (module_extension != ".lua") {
171480093f4SDimitry Andric     return llvm::make_error<llvm::StringError>("invalid extension",
172480093f4SDimitry Andric                                                llvm::inconvertibleErrorCode());
173480093f4SDimitry Andric   }
174480093f4SDimitry Andric 
175480093f4SDimitry Andric   int error = luaL_loadfile(m_lua_state, filename.data()) ||
176480093f4SDimitry Andric               lua_pcall(m_lua_state, 0, 1, 0);
177e8d8bef9SDimitry Andric   if (error != LUA_OK) {
178480093f4SDimitry Andric     llvm::Error e = llvm::make_error<llvm::StringError>(
179480093f4SDimitry Andric         llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)),
180480093f4SDimitry Andric         llvm::inconvertibleErrorCode());
181480093f4SDimitry Andric     // Pop error message from the stack.
182480093f4SDimitry Andric     lua_pop(m_lua_state, 1);
183480093f4SDimitry Andric     return e;
184480093f4SDimitry Andric   }
185480093f4SDimitry Andric 
186480093f4SDimitry Andric   ConstString module_name = file.GetFileNameStrippingExtension();
187480093f4SDimitry Andric   lua_setglobal(m_lua_state, module_name.GetCString());
188480093f4SDimitry Andric   return llvm::Error::success();
189480093f4SDimitry Andric }
1905ffd83dbSDimitry Andric 
1915ffd83dbSDimitry Andric llvm::Error Lua::ChangeIO(FILE *out, FILE *err) {
1925ffd83dbSDimitry Andric   assert(out != nullptr);
1935ffd83dbSDimitry Andric   assert(err != nullptr);
1945ffd83dbSDimitry Andric 
1955ffd83dbSDimitry Andric   lua_getglobal(m_lua_state, "io");
1965ffd83dbSDimitry Andric 
1975ffd83dbSDimitry Andric   lua_getfield(m_lua_state, -1, "stdout");
1985ffd83dbSDimitry Andric   if (luaL_Stream *s = static_cast<luaL_Stream *>(
1995ffd83dbSDimitry Andric           luaL_testudata(m_lua_state, -1, LUA_FILEHANDLE))) {
2005ffd83dbSDimitry Andric     s->f = out;
2015ffd83dbSDimitry Andric     lua_pop(m_lua_state, 1);
2025ffd83dbSDimitry Andric   } else {
2035ffd83dbSDimitry Andric     lua_pop(m_lua_state, 2);
2045ffd83dbSDimitry Andric     return llvm::make_error<llvm::StringError>("could not get stdout",
2055ffd83dbSDimitry Andric                                                llvm::inconvertibleErrorCode());
2065ffd83dbSDimitry Andric   }
2075ffd83dbSDimitry Andric 
2085ffd83dbSDimitry Andric   lua_getfield(m_lua_state, -1, "stderr");
2095ffd83dbSDimitry Andric   if (luaL_Stream *s = static_cast<luaL_Stream *>(
2105ffd83dbSDimitry Andric           luaL_testudata(m_lua_state, -1, LUA_FILEHANDLE))) {
2115ffd83dbSDimitry Andric     s->f = out;
2125ffd83dbSDimitry Andric     lua_pop(m_lua_state, 1);
2135ffd83dbSDimitry Andric   } else {
2145ffd83dbSDimitry Andric     lua_pop(m_lua_state, 2);
2155ffd83dbSDimitry Andric     return llvm::make_error<llvm::StringError>("could not get stderr",
2165ffd83dbSDimitry Andric                                                llvm::inconvertibleErrorCode());
2175ffd83dbSDimitry Andric   }
2185ffd83dbSDimitry Andric 
2195ffd83dbSDimitry Andric   lua_pop(m_lua_state, 1);
2205ffd83dbSDimitry Andric   return llvm::Error::success();
2215ffd83dbSDimitry Andric }
222