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/FormatVariadic.h"
13 
14 using namespace lldb_private;
15 using namespace lldb;
16 
17 static int lldb_print(lua_State *L) {
18   int n = lua_gettop(L);
19   lua_getglobal(L, "io");
20   lua_getfield(L, -1, "stdout");
21   lua_getfield(L, -1, "write");
22   for (int i = 1; i <= n; i++) {
23     lua_pushvalue(L, -1); // write()
24     lua_pushvalue(L, -3); // io.stdout
25     luaL_tolstring(L, i, nullptr);
26     lua_pushstring(L, i != n ? "\t" : "\n");
27     lua_call(L, 3, 0);
28   }
29   return 0;
30 }
31 
32 Lua::Lua() : m_lua_state(luaL_newstate()) {
33   assert(m_lua_state);
34   luaL_openlibs(m_lua_state);
35   luaopen_lldb(m_lua_state);
36   lua_pushcfunction(m_lua_state, lldb_print);
37   lua_setglobal(m_lua_state, "print");
38 }
39 
40 Lua::~Lua() {
41   assert(m_lua_state);
42   lua_close(m_lua_state);
43 }
44 
45 llvm::Error Lua::Run(llvm::StringRef buffer) {
46   int error =
47       luaL_loadbuffer(m_lua_state, buffer.data(), buffer.size(), "buffer") ||
48       lua_pcall(m_lua_state, 0, 0, 0);
49   if (error == LUA_OK)
50     return llvm::Error::success();
51 
52   llvm::Error e = llvm::make_error<llvm::StringError>(
53       llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)),
54       llvm::inconvertibleErrorCode());
55   // Pop error message from the stack.
56   lua_pop(m_lua_state, 1);
57   return e;
58 }
59 
60 llvm::Error Lua::LoadModule(llvm::StringRef filename) {
61   FileSpec file(filename);
62   if (!FileSystem::Instance().Exists(file)) {
63     return llvm::make_error<llvm::StringError>("invalid path",
64                                                llvm::inconvertibleErrorCode());
65   }
66 
67   ConstString module_extension = file.GetFileNameExtension();
68   if (module_extension != ".lua") {
69     return llvm::make_error<llvm::StringError>("invalid extension",
70                                                llvm::inconvertibleErrorCode());
71   }
72 
73   int error = luaL_loadfile(m_lua_state, filename.data()) ||
74               lua_pcall(m_lua_state, 0, 1, 0);
75   if (error != LUA_OK) {
76     llvm::Error e = llvm::make_error<llvm::StringError>(
77         llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)),
78         llvm::inconvertibleErrorCode());
79     // Pop error message from the stack.
80     lua_pop(m_lua_state, 1);
81     return e;
82   }
83 
84   ConstString module_name = file.GetFileNameStrippingExtension();
85   lua_setglobal(m_lua_state, module_name.GetCString());
86   return llvm::Error::success();
87 }
88 
89 llvm::Error Lua::ChangeIO(FILE *out, FILE *err) {
90   assert(out != nullptr);
91   assert(err != nullptr);
92 
93   lua_getglobal(m_lua_state, "io");
94 
95   lua_getfield(m_lua_state, -1, "stdout");
96   if (luaL_Stream *s = static_cast<luaL_Stream *>(
97           luaL_testudata(m_lua_state, -1, LUA_FILEHANDLE))) {
98     s->f = out;
99     lua_pop(m_lua_state, 1);
100   } else {
101     lua_pop(m_lua_state, 2);
102     return llvm::make_error<llvm::StringError>("could not get stdout",
103                                                llvm::inconvertibleErrorCode());
104   }
105 
106   lua_getfield(m_lua_state, -1, "stderr");
107   if (luaL_Stream *s = static_cast<luaL_Stream *>(
108           luaL_testudata(m_lua_state, -1, LUA_FILEHANDLE))) {
109     s->f = out;
110     lua_pop(m_lua_state, 1);
111   } else {
112     lua_pop(m_lua_state, 2);
113     return llvm::make_error<llvm::StringError>("could not get stderr",
114                                                llvm::inconvertibleErrorCode());
115   }
116 
117   lua_pop(m_lua_state, 1);
118   return llvm::Error::success();
119 }
120