Lib
QOLを高める
script.cpp
Go to the documentation of this file.
1 #include "stdafx.h"
2 #include "include/script.h"
3 #include "include/exceptions.h"
4 #include "include/debug.h"
5 #include "include/file.h"
6 
7 namespace yappy {
8 namespace lua {
9 
10 using error::throwTrace;
11 
12 LuaError::LuaError(const std::string &msg, lua_State *L) :
13  runtime_error("")
14 {
15  const char *str = lua_tostring(L, -1);
16  if (str == nullptr) {
17  m_what = msg;
18  }
19  else {
20  m_what = msg + ": " + str;
21  }
22  lua_pop(L, 1);
23 }
24 
25 namespace {
26 
27 // Error happens outside protected env
28 // This is fatal and exit application
29 int atpanic(lua_State *L)
30 {
31  error::throwTrace<std::runtime_error>("Lua panic");
32  // Don't return
33  return 0;
34 }
35 
37 // Copied from linit.c
39 const luaL_Reg loadedlibs[] = {
40  { "_G", luaopen_base },
41 // { LUA_LOADLIBNAME, luaopen_package },
42  { LUA_COLIBNAME, luaopen_coroutine },
43  { LUA_TABLIBNAME, luaopen_table },
44 // { LUA_IOLIBNAME, luaopen_io },
45 // { LUA_OSLIBNAME, luaopen_os },
46  { LUA_STRLIBNAME, luaopen_string },
47  { LUA_MATHLIBNAME, luaopen_math },
48  { LUA_UTF8LIBNAME, luaopen_utf8 },
49 // { LUA_DBLIBNAME, luaopen_debug },
50  { NULL, NULL }
51 };
52 
53 LUALIB_API void my_luaL_openlibs(lua_State *L) {
54  const luaL_Reg *lib;
55  /* "require" functions from 'loadedlibs' and set results to global table */
56  for (lib = loadedlibs; lib->func; lib++) {
57  luaL_requiref(L, lib->name, lib->func, 1);
58  lua_pop(L, 1); /* remove lib */
59  }
60 }
62 // Copied from linit.c END
64 
65 } // namespace
66 
67 void Lua::LuaDeleter::operator()(lua_State *lua)
68 {
69  ::lua_close(lua);
70 }
71 
72 void *Lua::luaAlloc(void *ud, void *ptr, size_t osize, size_t nsize)
73 {
74  auto *lua = reinterpret_cast<Lua *>(ud);
75  if (nsize == 0) {
76  // free(ptr);
77  //debug::writef(L"luaAlloc(free) %p, %08zx, %08zx", ptr, osize, nsize);
78  if (ptr != nullptr) {
79  BOOL ret = ::HeapFree(lua->m_heap.get(), 0, ptr);
80  if (!ret) {
81  debug::writeLine(L"[warning] HeapFree() failed");
82  }
83  }
84  return nullptr;
85  }
86  else {
87  // realloc(ptr, nsize);
88  if (nsize >= 0x7FFF8) {
89  debug::writef(L"[warning] Attempt to allocate %zu bytes", nsize);
90  }
91  if (ptr == nullptr) {
92  //debug::writef(L"luaAlloc(alloc) %p, %08zx, %08zx", ptr, osize, nsize);
93  return ::HeapAlloc(lua->m_heap.get(), 0, nsize);
94  }
95  else {
96  //debug::writef(L"luaAlloc(realloc) %p, %08zx, %08zx", ptr, osize, nsize);
97  return ::HeapReAlloc(lua->m_heap.get(), 0, ptr, nsize);
98  }
99  }
100 }
101 
102 Lua::Lua(bool debugEnable, size_t maxHeapSize, size_t initHeapSize,
103  int instLimit) :
104  m_debugEnable(debugEnable)
105 {
106  debug::writeLine("Initializing lua...");
107 
108  HANDLE tmpHeap = ::HeapCreate(HeapOption, initHeapSize, maxHeapSize);
109  error::checkWin32Result(tmpHeap != nullptr, "HeapCreate() failed");
110  m_heap.reset(tmpHeap);
111 
112  lua_State *tmpLua = lua_newstate(luaAlloc, this);
113  if (tmpLua == nullptr) {
114  throw std::bad_alloc();
115  }
116  m_lua.reset(tmpLua);
117 
118  m_dbg = std::make_unique<debugger::LuaDebugger>(
119  m_lua.get(), debugEnable, instLimit, maxHeapSize);
120 
121  debug::writeLine("Initializing lua OK");
122 
123  ::lua_atpanic(m_lua.get(), atpanic);
124  my_luaL_openlibs(m_lua.get());
125 
126  lua_State *L = m_lua.get();
127  // delete print function
128  lua_pushnil(L);
129  lua_setglobal(L, "print");
130  // delete load function
131  lua_pushnil(L);
132  lua_setglobal(L, "dofile");
133  lua_pushnil(L);
134  lua_setglobal(L, "loadfile");
135  lua_pushnil(L);
136  lua_setglobal(L, "load");
137 }
138 
139 lua_State *Lua::getLuaState() const
140 {
141  return m_lua.get();
142 }
143 
145 {
146  debug::writeLine("Finalize lua");
147 }
148 
150 {
151  lua_State *L = m_lua.get();
152  luaL_newlib(L, export::trace_RegList);
153  lua_setglobal(L, "trace");
154 
155  // print <- trace.write
156  lua_pushcfunction(L, export::trace::write);
157  lua_setglobal(L, "print");
158 }
159 
161 {
162  lua_State *L = m_lua.get();
163  luaL_newlibtable(L, export::trace_RegList);
164  // upvalue[1]: Lua *
165  lua_pushlightuserdata(L, this);
166  luaL_setfuncs(L, export::sys_RegList, 1);
167  lua_setglobal(L, "sys");
168 }
169 
171 {
172  lua_State *L = m_lua.get();
173  luaL_newlib(L, export::rand_RegList);
174  lua_setglobal(L, "rand");
175 }
176 
178 {
179  lua_State *L = m_lua.get();
180  luaL_newlibtable(L, export::resource_RegList);
181  // upvalue[1]: Application *
182  lua_pushlightuserdata(L, app);
183  luaL_setfuncs(L, export::resource_RegList, 1);
184  lua_setglobal(L, "resource");
185 }
186 
188 {
189  lua_State *L = m_lua.get();
190  luaL_newlibtable(L, export::graph_RegList);
191  // upvalue[1]: Application *
192  lua_pushlightuserdata(L, app);
193  luaL_setfuncs(L, export::graph_RegList, 1);
194  lua_setglobal(L, "graph");
195 }
196 
198 {
199  lua_State *L = m_lua.get();
200  luaL_newlibtable(L, export::sound_RegList);
201  // upvalue[1]: Application *
202  lua_pushlightuserdata(L, app);
203  luaL_setfuncs(L, export::sound_RegList, 1);
204  lua_setglobal(L, "sound");
205 }
206 
207 void Lua::loadFile(const wchar_t *fileName, bool autoBreak, bool prot)
208 {
209  lua_State *L = m_lua.get();
210 
211  file::Bytes buf = file::loadFile(fileName);
212 
213  // push chunk function
214  std::string chunkName("@");
215  chunkName += util::wc2utf8(fileName).get();
216  int ret = ::luaL_loadbufferx(L,
217  reinterpret_cast<const char *>(buf.data()), buf.size(),
218  chunkName.c_str(), "t");
219  if (ret != LUA_OK) {
220  throwTrace<LuaError>("Load script failed", L);
221  }
222  // prepare debug info
223  m_dbg->loadDebugInfo(chunkName.c_str(),
224  reinterpret_cast<const char *>(buf.data()), buf.size());
225  // call it
226  if (prot) {
227  pcallInternal(0, 0, autoBreak);
228  }
229  else {
230  lua_call(L, 0, 0);
231  }
232 }
233 
234 void Lua::pcallInternal(int narg, int nret, bool autoBreak)
235 {
236  m_dbg->pcall(narg, nret, autoBreak);
237 }
238 
239 
240 namespace {
241 
242 std::vector<std::string> luaValueToStrListInternal(
243  lua_State *L, int ind, int maxDepth,
244  int depth, int indent, char kind, int kindIndent,
245  std::vector<const void *> visitedTable)
246 {
247  std::vector<std::string> result;
248 
249  int tind = lua_absindex(L, ind);
250 
251  int type = lua_type(L, ind);
252  if (type == LUA_TNONE) {
253  throwTrace<std::logic_error>("invalid index: " + std::to_string(ind));
254  }
255  const char *typestr = lua_typename(L, type);
256  // copy and tostring it
257  lua_pushvalue(L, ind);
258  const char *valstr = lua_tostring(L, -1);
259  valstr = (valstr == NULL) ? "" : valstr;
260  // pop copy
261  lua_pop(L, 1);
262  // boolean cannot be tostring
263  if (type == LUA_TBOOLEAN) {
264  valstr = lua_toboolean(L, ind) ? "true" : "false";
265  }
266 
267  {
268  std::string str;
269  for (int i = 0; i < indent; i++) {
270  if (i == kindIndent) {
271  str += kind;
272  }
273  else {
274  str += ' ';
275  }
276  }
277  str += "(";
278  str += typestr;
279  str += ") ";
280  str += valstr;
281  result.emplace_back(std::move(str));
282  }
283  // table recursive call
284  if (type == LUA_TTABLE && depth < maxDepth) {
285  // call pairs(table)
286  // __pairs(table) or
287  // next, table, nil
288  lua_getglobal(L, "pairs");
289  lua_pushvalue(L, tind);
290  lua_call(L, 1, 3);
291 
292  // initially: next, table, key=nil
293  // save next and table
294  int nextInd = lua_absindex(L, -3);
295  int tableInd = lua_absindex(L, -2);
296  // stack top is key=nil
297  while (1) {
298  // call next(table, key)
299  lua_pushvalue(L, nextInd);
300  lua_pushvalue(L, tableInd);
301  lua_pushvalue(L, -3);
302  // next, table, key, [next, table, key]
303  lua_call(L, 2, 2);
304  // next, table, key, [newkey, val]
305 
306  // end
307  if (lua_isnil(L, -1)) {
308  lua_pop(L, 2);
309  break;
310  }
311 
312  auto visited = [&visitedTable, L](int ind) -> bool {
313  return std::find(visitedTable.cbegin(), visitedTable.cend(),
314  lua_topointer(L, -2)) != visitedTable.cend();
315  };
316  // key:-2, value:-1
317  if (!visited(-2)) {
318  visitedTable.emplace_back(lua_topointer(L, -2));
319  auto key = luaValueToStrListInternal(
320  L, -2, maxDepth, depth + 1, indent + 4, 'K', indent, visitedTable);
321  result.insert(result.end(), key.begin(), key.end());
322  visitedTable.pop_back();
323  }
324  if (!visited(-1)) {
325  visitedTable.emplace_back(lua_topointer(L, -1));
326  auto val = luaValueToStrListInternal(
327  L, -1, maxDepth, depth + 1, indent + 4, 'V', indent, visitedTable);
328  result.insert(result.end(), val.begin(), val.end());
329  visitedTable.pop_back();
330  }
331  // pop value, keep key stack top
332  // next, table, key, newkey
333  lua_pop(L, 1);
334  // replace old key with new key
335  // next, table, newkey
336  lua_replace(L, -2);
337  }
338  // next, table, newkey => none
339  lua_pop(L, 3);
340  }
341  return result;
342 }
343 
344 } // namespace
345 
346 std::vector<std::string> luaValueToStrList(
347  lua_State *L, int ind, int maxDepth)
348 {
349  return luaValueToStrListInternal(L, ind, maxDepth, 0, 0, '\0', 0,
350  std::vector<const void *>());
351 }
352 
353 } // namespace lua
354 } // namespace yappy
std::unique_ptr< char[]> wc2utf8(const wchar_t *in)
Wide char to UTF-8.
Definition: util.h:105
const char * str
Definition: input.cpp:197
void loadFile(const wchar_t *fileName, bool autoBreak, bool prot=true)
Load script file and eval it.
Definition: script.cpp:207
Debug utilities.
LuaError(const std::string &msg, lua_State *L)
Definition: script.cpp:12
const luaL_Reg graph_RegList[]
void writef(const wchar_t *fmt,...) noexcept
Write debug message using format string like printf.
Definition: debug.cpp:103
std::vector< uint8_t > loadFile(const wchar_t *fileName)
Load file from abstract file system.
Definition: file.cpp:103
lua_State * getLuaState() const
Returns lua_State which this object has.
Definition: script.cpp:139
const luaL_Reg sys_RegList[]
Definition: script_export.h:52
const luaL_Reg rand_RegList[]
Definition: script_export.h:72
void loadSysLib()
Definition: script.cpp:160
void loadSoundLib(framework::Application *app)
Definition: script.cpp:197
Definition: config.cpp:6
void loadTraceLib()
Definition: script.cpp:149
void writeLine(const wchar_t *str=L"") noexcept
Write debug string and new line.
Definition: debug.h:64
Lua(bool debugEnable, size_t maxHeapSize, size_t initHeapSize=1024 *1024, int instLimit=10 *10000)
Create new lua_State and open standard libs.
Definition: script.cpp:102
void loadGraphLib(framework::Application *app)
Definition: script.cpp:187
void loadRandLib()
Definition: script.cpp:170
const luaL_Reg resource_RegList[]
Definition: script_export.h:97
std::vector< uint8_t > Bytes
File byte sequence. Vector of uint8_t.
Definition: file.h:25
char msg[LINE_DATA_SIZE-sizeof(LARGE_INTEGER)-sizeof(uint32_t)]
Definition: debug.cpp:159
const luaL_Reg trace_RegList[]
Definition: script_export.h:34
~Lua()
Destruct lua_State.
Definition: script.cpp:144
void checkWin32Result(bool cond, const std::string &msg)
Definition: exceptions.h:50
static int write(lua_State *L)
デバッグ出力する。
const luaL_Reg sound_RegList[]
Lua state manager.
Definition: script.h:33
void loadResourceLib(framework::Application *app)
Definition: script.cpp:177
User application base, which manages a window and DirectX objects.
Definition: framework.h:300
std::vector< std::string > luaValueToStrList(lua_State *L, int ind, int maxDepth)
Definition: script.cpp:346