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