You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

251 lines
6.3KB

  1. #include "ScriptEngine.hpp"
  2. #include <luajit-2.0/lua.hpp>
  3. struct LuaJITEngine : ScriptEngine {
  4. lua_State* L = NULL;
  5. // This is a mirror of ProcessBlock that we are going to use
  6. // to provide 1-based indices within the Lua VM
  7. struct LuaProcessBlock {
  8. float sampleRate;
  9. float sampleTime;
  10. int bufferSize;
  11. float* inputs[NUM_ROWS + 1];
  12. float* outputs[NUM_ROWS + 1];
  13. float* knobs;
  14. bool* switches;
  15. float* lights[NUM_ROWS + 1];
  16. float* switchLights[NUM_ROWS + 1];
  17. };
  18. LuaProcessBlock luaBlock;
  19. ~LuaJITEngine() {
  20. if (L)
  21. lua_close(L);
  22. }
  23. std::string getEngineName() override {
  24. return "Lua";
  25. }
  26. int run(const std::string& path, const std::string& script) override {
  27. ProcessBlock* block = getProcessBlock();
  28. // Initialize all the pointers with an offset of -1
  29. #pragma GCC diagnostic push
  30. #pragma GCC diagnostic ignored "-Warray-bounds"
  31. luaBlock.knobs = &block->knobs[-1];
  32. luaBlock.switches = &block->switches[-1];
  33. for (int i = 0; i < NUM_ROWS; i++) {
  34. luaBlock.inputs[i + 1] = &block->inputs[i][-1];
  35. luaBlock.outputs[i + 1] = &block->outputs[i][-1];
  36. luaBlock.lights[i + 1] = &block->lights[i][-1];
  37. luaBlock.switchLights[i + 1] = &block->switchLights[i][-1];
  38. }
  39. #pragma GCC diagnostic pop
  40. L = luaL_newstate();
  41. if (!L) {
  42. display("Could not create LuaJIT context");
  43. return -1;
  44. }
  45. // Import a subset of the standard library
  46. static const luaL_Reg lj_lib_load[] = {
  47. {"", luaopen_base},
  48. {LUA_LOADLIBNAME, luaopen_package},
  49. {LUA_TABLIBNAME, luaopen_table},
  50. {LUA_STRLIBNAME, luaopen_string},
  51. {LUA_MATHLIBNAME, luaopen_math},
  52. {LUA_BITLIBNAME, luaopen_bit},
  53. {LUA_JITLIBNAME, luaopen_jit},
  54. {LUA_FFILIBNAME, luaopen_ffi},
  55. {NULL, NULL}
  56. };
  57. for (const luaL_Reg* lib = lj_lib_load; lib->func; lib++) {
  58. lua_pushcfunction(L, lib->func);
  59. lua_pushstring(L, lib->name);
  60. lua_call(L, 1, 0);
  61. }
  62. // Set user pointer
  63. lua_pushlightuserdata(L, this);
  64. lua_setglobal(L, "_engine");
  65. // Set global functions
  66. // lua_pushcfunction(L, native_print);
  67. // lua_setglobal(L, "print");
  68. lua_pushcfunction(L, native_display);
  69. lua_setglobal(L, "display");
  70. // Set config
  71. lua_newtable(L);
  72. {
  73. // frameDivider
  74. lua_pushinteger(L, 32);
  75. lua_setfield(L, -2, "frameDivider");
  76. // bufferSize
  77. lua_pushinteger(L, 1);
  78. lua_setfield(L, -2, "bufferSize");
  79. }
  80. lua_setglobal(L, "config");
  81. // Load the FFI auxiliary functions.
  82. std::stringstream ffi_stream;
  83. ffi_stream
  84. << "local ffi = require('ffi')" << std::endl
  85. // Describe the struct `LuaProcessBlock` so that LuaJIT knows how to access the data
  86. << "ffi.cdef[[" << std::endl
  87. << "struct LuaProcessBlock {" << std::endl
  88. << "float sampleRate;" << std::endl
  89. << "float sampleTime;" << std::endl
  90. << "int bufferSize;" << std::endl
  91. << "float *inputs[" << NUM_ROWS + 1 << "];" << std::endl
  92. << "float *outputs[" << NUM_ROWS + 1 << "];" << std::endl
  93. << "float *knobs;" << std::endl
  94. << "bool *switches;" << std::endl
  95. << "float *lights[" << NUM_ROWS + 1 << "];" << std::endl
  96. << "float *switchLights[" << NUM_ROWS + 1 << "];" << std::endl
  97. << "};]]" << std::endl
  98. // Declare the function `_castBlock` used to transform `luaBlock` pointer into a LuaJIT cdata
  99. << "_ffi_cast = ffi.cast" << std::endl
  100. << "function _castBlock(b) return _ffi_cast('struct LuaProcessBlock*', b) end" << std::endl
  101. // Remove global functions that could be abused
  102. << "jit = nil; require = nil; ffi = nil; load = nil; loadfile = nil; loadstring = nil; dofile = nil;" << std::endl;
  103. std::string ffi_script = ffi_stream.str();
  104. // Compile the ffi script
  105. if (luaL_loadbuffer(L, ffi_script.c_str(), ffi_script.size(), "ffi_script.lua")) {
  106. const char* s = lua_tostring(L, -1);
  107. WARN("LuaJIT: %s", s);
  108. display(s);
  109. lua_pop(L, 1);
  110. return -1;
  111. }
  112. // Run the ffi script
  113. if (lua_pcall(L, 0, 0, 0)) {
  114. const char* s = lua_tostring(L, -1);
  115. WARN("LuaJIT: %s", s);
  116. display(s);
  117. lua_pop(L, 1);
  118. return -1;
  119. }
  120. // Compile user script
  121. if (luaL_loadbuffer(L, script.c_str(), script.size(), path.c_str())) {
  122. const char* s = lua_tostring(L, -1);
  123. WARN("LuaJIT: %s", s);
  124. display(s);
  125. lua_pop(L, 1);
  126. return -1;
  127. }
  128. // Run script
  129. if (lua_pcall(L, 0, 0, 0)) {
  130. const char* s = lua_tostring(L, -1);
  131. WARN("LuaJIT: %s", s);
  132. display(s);
  133. lua_pop(L, 1);
  134. return -1;
  135. }
  136. // Get config
  137. lua_getglobal(L, "config");
  138. {
  139. // frameDivider
  140. lua_getfield(L, -1, "frameDivider");
  141. int frameDivider = lua_tointeger(L, -1);
  142. setFrameDivider(frameDivider);
  143. lua_pop(L, 1);
  144. // bufferSize
  145. lua_getfield(L, -1, "bufferSize");
  146. int bufferSize = lua_tointeger(L, -1);
  147. setBufferSize(bufferSize);
  148. lua_pop(L, 1);
  149. }
  150. lua_pop(L, 1);
  151. // Get process function
  152. lua_getglobal(L, "process");
  153. if (!lua_isfunction(L, -1)) {
  154. display("No process() function");
  155. return -1;
  156. }
  157. // Create block object
  158. lua_getglobal(L, "_castBlock");
  159. lua_pushlightuserdata(L, (void*) &luaBlock);
  160. if (lua_pcall(L, 1, 1, 0)) {
  161. const char* s = lua_tostring(L, -1);
  162. WARN("LuaJIT: Error casting block: %s", s);
  163. display(s);
  164. lua_pop(L, 1);
  165. return -1;
  166. }
  167. return 0;
  168. }
  169. int process() override {
  170. ProcessBlock* block = getProcessBlock();
  171. // Update the values of the block.
  172. // The pointer values do not change.
  173. luaBlock.sampleRate = block->sampleRate;
  174. luaBlock.sampleTime = block->sampleTime;
  175. luaBlock.bufferSize = block->bufferSize;
  176. // Duplicate process function
  177. lua_pushvalue(L, -2);
  178. // Duplicate block
  179. lua_pushvalue(L, -2);
  180. // Call process function
  181. if (lua_pcall(L, 1, 0, 0)) {
  182. const char* err = lua_tostring(L, -1);
  183. WARN("LuaJIT: %s", err);
  184. display(err);
  185. return -1;
  186. }
  187. return 0;
  188. }
  189. static LuaJITEngine* getEngine(lua_State* L) {
  190. lua_getglobal(L, "_engine");
  191. LuaJITEngine* engine = (LuaJITEngine*) lua_touserdata(L, -1);
  192. lua_pop(L, 1);
  193. return engine;
  194. }
  195. // static int native_print(lua_State* L) {
  196. // lua_getglobal(L, "tostring");
  197. // lua_pushvalue(L, 1);
  198. // lua_call(L, 1, 1);
  199. // const char* s = lua_tostring(L, 1);
  200. // INFO("LuaJIT: %s", s);
  201. // return 0;
  202. // }
  203. static int native_display(lua_State* L) {
  204. lua_getglobal(L, "tostring");
  205. lua_pushvalue(L, 1);
  206. lua_call(L, 1, 1);
  207. const char* s = lua_tostring(L, -1);
  208. if (!s)
  209. s = "(null)";
  210. getEngine(L)->display(s);
  211. return 0;
  212. }
  213. };
  214. __attribute__((constructor(1000)))
  215. static void constructor() {
  216. addScriptEngine<LuaJITEngine>("lua");
  217. }