Lua: Memory usage limit
[lsnes.git] / src / lua / lua.cpp
blob4440460e120f1c9e2a249d7bd3d75cb49705eb41
1 #include "cmdhelp/lua.hpp"
2 #include "core/command.hpp"
3 #include "library/globalwrap.hpp"
4 #include "library/keyboard.hpp"
5 #include "lua/internal.hpp"
6 #include "lua/lua.hpp"
7 #include "lua/unsaferewind.hpp"
8 #include "core/instance.hpp"
9 #include "core/mainloop.hpp"
10 #include "core/messages.hpp"
11 #include "core/memorymanip.hpp"
12 #include "core/moviedata.hpp"
13 #include "core/misc.hpp"
15 #include <map>
16 #include <cstring>
17 #include <string>
18 #include <iostream>
19 extern "C" {
20 #include <lualib.h>
23 extern const char* lua_sysrc_script;
25 lua::function_group lua_func_bit;
26 lua::function_group lua_func_misc;
27 lua::function_group lua_func_load;
28 lua::function_group lua_func_zip;
30 lua::class_group lua_class_callback;
31 lua::class_group lua_class_gui;
32 lua::class_group lua_class_bind;
33 lua::class_group lua_class_pure;
34 lua::class_group lua_class_movie;
35 lua::class_group lua_class_memory;
36 lua::class_group lua_class_fileio;
38 namespace
40 typedef settingvar::model_int<32,1024> mb_model;
41 settingvar::supervariable<mb_model> SET_lua_maxmem(lsnes_setgrp, "lua-maxmem",
42 "Lua‣Maximum memory use (MB)", 128);
44 void pushpair(lua::state& L, std::string key, double value)
46 L.pushstring(key.c_str());
47 L.pushnumber(value);
48 L.settable(-3);
51 void pushpair(lua::state& L, std::string key, std::string value)
53 L.pushstring(key.c_str());
54 L.pushstring(value.c_str());
55 L.settable(-3);
58 std::string get_mode_str(int mode)
60 if(mode < 0)
61 return "disabled";
62 else if(mode > 0)
63 return "axis";
64 return "pressure0+";
68 void push_keygroup_parameters(lua::state& L, keyboard::key& p)
70 keyboard::mouse_calibration p2;
71 int mode;
72 L.newtable();
73 switch(p.get_type()) {
74 case keyboard::KBD_KEYTYPE_KEY:
75 pushpair(L, "value", p.get_state());
76 pushpair(L, "type", "key");
77 break;
78 case keyboard::KBD_KEYTYPE_HAT:
79 pushpair(L, "value", p.get_state());
80 pushpair(L, "type", "hat");
81 break;
82 case keyboard::KBD_KEYTYPE_MOUSE:
83 p2 = p.cast_mouse()->get_calibration();
84 pushpair(L, "value", p.get_state());
85 pushpair(L, "type", "mouse");
86 break;
87 case keyboard::KBD_KEYTYPE_AXIS:
88 mode = p.cast_axis()->get_mode();
89 pushpair(L, "value", p.get_state());
90 pushpair(L, "type", get_mode_str(mode));
91 break;
96 namespace
98 void soft_oom(int status)
100 if(status == 0)
101 messages << "Lua: Memory limit exceeded, attempting to free memory..." << std::endl;
102 if(status < 0)
103 messages << "Lua: Memory allocation still failed." << std::endl;
104 if(status > 0)
105 messages << "Lua: Allocation successful after freeing some memory." << std::endl;
108 int push_keygroup_parameters2(lua::state& L, keyboard::key* p)
110 push_keygroup_parameters(L, *p);
111 return 1;
115 const char* read_lua_fragment(lua_State* unused, void* fragment, size_t* size)
117 const char*& luareader_fragment = *reinterpret_cast<const char**>(fragment);
118 if(luareader_fragment) {
119 const char* ret = luareader_fragment;
120 *size = strlen(luareader_fragment);
121 luareader_fragment = NULL;
122 return ret;
123 } else {
124 *size = 0;
125 return NULL;
129 #define TEMPORARY "LUAINTERP_INTERNAL_COMMAND_TEMPORARY"
131 const char* CONST_eval_sysrc_lua = "local fn, err = " LUA_LOAD_CMD "(" TEMPORARY ", \"<built-in>\"); "
132 "if fn then fn(); else print2(\"Parse error in sysrc.lua script: \"..err); end;";
133 const char* CONST_eval_lua_lua = "local fn, err = " LUA_LOAD_CMD "(" TEMPORARY "); if fn then fn(); else "
134 " print(\"Parse error in Lua statement: \"..err); end;";
135 const char* CONST_run_lua_lua = "dofile(" TEMPORARY ");";
137 int system_write_error(lua::state& L)
139 throw std::runtime_error("_SYSTEM is write-protected");
142 void copy_system_tables(lua::state& L)
144 L.pushglobals();
145 L.newtable();
146 L.pushnil();
147 while(L.next(-3)) {
148 //Stack: _SYSTEM, KEY, VALUE
149 L.pushvalue(-2);
150 L.pushvalue(-2);
151 //Stack: _SYSTEM, KEY, VALUE, KEY, VALUE
152 L.rawset(-5);
153 //Stack: _SYSTEM, KEY, VALUE
154 L.pop(1);
155 //Stack: _SYSTEM, KEY
157 L.newtable();
158 L.push_trampoline(system_write_error, 0);
159 L.setfield(-2, "__newindex");
160 L.setmetatable(-2);
161 L.setglobal("_SYSTEM");
165 void lua_state::_listener::on_setting_change(settingvar::group& grp, const settingvar::base& val)
167 if(val.get_iname() == "lua-maxmem")
168 obj.set_memory_limit(dynamic_cast<const settingvar::variable<mb_model>*>(&val)->get());
171 void lua_state::set_memory_limit(size_t limit_mb)
173 L.set_memory_limit(limit_mb << 20);
176 lua_state::lua_state(lua::state& _L, command::group& _command, settingvar::group& settings)
177 : L(_L), command(_command),
178 resetcmd(command, CLUA::reset, [this]() { this->do_reset(); }),
179 evalcmd(command, CLUA::eval, [this](const std::string& a) { this->do_eval_lua(a); }),
180 evalcmd2(command, CLUA::eval2, [this](const std::string& a) { this->do_eval_lua(a); }),
181 runcmd(command, CLUA::run, [this](command::arg_filename a) { this->do_run_lua(a); }),
182 listener(settings, *this)
184 requests_repaint = false;
185 requests_subframe_paint = false;
186 render_ctx = NULL;
187 input_controllerdata = NULL;
188 //We can't read the value of lua maxmem setting here (it crashes), so just set default, it will be changed
189 //if needed.
190 L.set_memory_limit(1 << 27);
192 idle_hook_time = 0x7EFFFFFFFFFFFFFFULL;
193 timer_hook_time = 0x7EFFFFFFFFFFFFFFULL;
194 veto_flag = NULL;
195 kill_frame = NULL;
196 hscl = NULL;
197 vscl = NULL;
198 synchronous_paint_ctx = NULL;
199 recursive_flag = false;
200 luareader_fragment = NULL;
202 renderq_saved = NULL;
203 renderq_last = NULL;
204 renderq_redirect = false;
206 on_paint = new lua::state::callback_list(L, "paint", "on_paint");
207 on_video = new lua::state::callback_list(L, "video", "on_video");
208 on_reset = new lua::state::callback_list(L, "reset", "on_reset");
209 on_frame = new lua::state::callback_list(L, "frame", "on_frame");
210 on_rewind = new lua::state::callback_list(L, "rewind", "on_rewind");
211 on_idle = new lua::state::callback_list(L, "idle", "on_idle");
212 on_timer = new lua::state::callback_list(L, "timer", "on_timer");
213 on_frame_emulated = new lua::state::callback_list(L, "frame_emulated", "on_frame_emulated");
214 on_readwrite = new lua::state::callback_list(L, "readwrite", "on_readwrite");
215 on_startup = new lua::state::callback_list(L, "startup", "on_startup");
216 on_pre_load = new lua::state::callback_list(L, "pre_load", "on_pre_load");
217 on_post_load = new lua::state::callback_list(L, "post_load", "on_post_load");
218 on_err_load = new lua::state::callback_list(L, "err_load", "on_err_load");
219 on_pre_save = new lua::state::callback_list(L, "pre_save", "on_pre_save");
220 on_post_save = new lua::state::callback_list(L, "post_save", "on_post_save");
221 on_err_save = new lua::state::callback_list(L, "err_save", "on_err_save");
222 on_input = new lua::state::callback_list(L, "input", "on_input");
223 on_snoop = new lua::state::callback_list(L, "snoop", "on_snoop");
224 on_snoop2 = new lua::state::callback_list(L, "snoop2", "on_snoop2");
225 on_button = new lua::state::callback_list(L, "button", "on_button");
226 on_quit = new lua::state::callback_list(L, "quit", "on_quit");
227 on_keyhook = new lua::state::callback_list(L, "keyhook", "on_keyhook");
228 on_movie_lost = new lua::state::callback_list(L, "movie_lost", "on_movie_lost");
229 on_pre_rewind = new lua::state::callback_list(L, "pre_rewind", "on_pre_rewind");
230 on_post_rewind = new lua::state::callback_list(L, "post_rewind", "on_post_rewind");
231 on_set_rewind = new lua::state::callback_list(L, "set_rewind", "on_set_rewind");
232 on_latch = new lua::state::callback_list(L, "latch", "on_latch");
235 lua_state::~lua_state()
237 delete on_paint;
238 delete on_video;
239 delete on_reset;
240 delete on_frame;
241 delete on_rewind;
242 delete on_idle;
243 delete on_timer;
244 delete on_frame_emulated;
245 delete on_readwrite;
246 delete on_startup;
247 delete on_pre_load;
248 delete on_post_load;
249 delete on_err_load;
250 delete on_pre_save;
251 delete on_post_save;
252 delete on_err_save;
253 delete on_input;
254 delete on_snoop;
255 delete on_snoop2;
256 delete on_button;
257 delete on_quit;
258 delete on_keyhook;
259 delete on_movie_lost;
260 delete on_pre_rewind;
261 delete on_post_rewind;
262 delete on_set_rewind;
263 delete on_latch;
266 void lua_state::callback_do_paint(struct lua::render_context* ctx, bool non_synthetic) throw()
268 run_synchronous_paint(ctx);
269 run_callback(*on_paint, lua::state::store_tag(render_ctx, ctx), lua::state::boolean_tag(non_synthetic));
272 void lua_state::callback_do_video(struct lua::render_context* ctx, bool& _kill_frame, uint32_t& _hscl,
273 uint32_t& _vscl) throw()
275 run_callback(*on_video, lua::state::store_tag(render_ctx, ctx), lua::state::store_tag(kill_frame,
276 &_kill_frame), lua::state::store_tag(hscl, &_hscl), lua::state::store_tag(vscl, &_vscl));
279 void lua_state::callback_do_reset() throw()
281 run_callback(*on_reset);
284 void lua_state::callback_do_frame() throw()
286 run_callback(*on_frame);
289 void lua_state::callback_do_rewind() throw()
291 run_callback(*on_rewind);
294 void lua_state::callback_do_idle() throw()
296 idle_hook_time = 0x7EFFFFFFFFFFFFFFULL;
297 run_callback(*on_idle);
300 void lua_state::callback_do_timer() throw()
302 timer_hook_time = 0x7EFFFFFFFFFFFFFFULL;
303 run_callback(*on_timer);
306 void lua_state::callback_do_frame_emulated() throw()
308 run_callback(*on_frame_emulated);
311 void lua_state::callback_do_readwrite() throw()
313 run_callback(*on_readwrite);
316 void lua_state::callback_pre_load(const std::string& name) throw()
318 run_callback(*on_pre_load, lua::state::string_tag(name));
321 void lua_state::callback_err_load(const std::string& name) throw()
323 run_callback(*on_err_load, lua::state::string_tag(name));
326 void lua_state::callback_post_load(const std::string& name, bool was_state) throw()
328 run_callback(*on_post_load, lua::state::string_tag(name), lua::state::boolean_tag(was_state));
331 void lua_state::callback_pre_save(const std::string& name, bool is_state) throw()
333 run_callback(*on_pre_save, lua::state::string_tag(name), lua::state::boolean_tag(is_state));
336 void lua_state::callback_err_save(const std::string& name) throw()
338 run_callback(*on_err_save, lua::state::string_tag(name));
341 void lua_state::callback_post_save(const std::string& name, bool is_state) throw()
343 run_callback(*on_post_save, lua::state::string_tag(name), lua::state::boolean_tag(is_state));
346 void lua_state::callback_do_input(portctrl::frame& data, bool subframe) throw()
348 run_callback(*on_input, lua::state::store_tag(input_controllerdata, &data),
349 lua::state::boolean_tag(subframe));
352 void lua_state::callback_snoop_input(uint32_t port, uint32_t controller, uint32_t index, short value) throw()
354 if(run_callback(*on_snoop2, lua::state::numeric_tag(port), lua::state::numeric_tag(controller),
355 lua::state::numeric_tag(index), lua::state::numeric_tag(value)))
356 return;
357 run_callback(*on_snoop, lua::state::numeric_tag(port), lua::state::numeric_tag(controller),
358 lua::state::numeric_tag(index), lua::state::numeric_tag(value));
361 bool lua_state::callback_do_button(uint32_t port, uint32_t controller, uint32_t index, const char* type)
363 bool flag = false;
364 run_callback(*on_button, lua::state::store_tag(veto_flag, &flag), lua::state::numeric_tag(port),
365 lua::state::numeric_tag(controller), lua::state::numeric_tag(index), lua::state::string_tag(type));
366 return flag;
369 namespace
371 lua::_class<lua_unsaferewind> LUA_class_unsaferewind(lua_class_movie, "UNSAFEREWIND", {}, {
372 }, &lua_unsaferewind::print);
375 void lua_state::do_reset()
377 L.reset();
378 luaL_openlibs(L.handle());
380 run_sysrc_lua(true);
381 copy_system_tables(L);
382 messages << "Lua VM reset" << std::endl;
385 void lua_state::do_evaluate(const std::string& a)
387 if(a == "")
388 throw std::runtime_error("Expected expression to evaluate");
389 do_eval_lua(a);
392 void lua_state::callback_quit() throw()
394 run_callback(*on_quit);
397 void lua_state::callback_keyhook(const std::string& key, keyboard::key& p) throw()
399 run_callback(*on_keyhook, lua::state::string_tag(key), lua::state::fnptr_tag(push_keygroup_parameters2, &p));
402 void init_lua() throw()
404 auto& core = lsnes_instance;
405 core.lua->set_oom_handler(OOM_panic);
406 core.lua->set_soft_oom_handler(soft_oom);
407 try {
408 core.lua->reset();
409 core.lua->add_function_group(lua_func_bit);
410 core.lua->add_function_group(lua_func_load);
411 core.lua->add_function_group(lua_func_misc);
412 core.lua->add_function_group(lua_func_zip);
413 core.lua->add_class_group(lua_class_callback);
414 core.lua->add_class_group(lua_class_gui);
415 core.lua->add_class_group(lua_class_bind);
416 core.lua->add_class_group(lua_class_pure);
417 core.lua->add_class_group(lua_class_movie);
418 core.lua->add_class_group(lua_class_memory);
419 core.lua->add_class_group(lua_class_fileio);
420 } catch(std::exception& e) {
421 messages << "Can't initialize Lua." << std::endl;
422 fatal_error();
424 luaL_openlibs(core.lua->handle());
425 core.lua2->run_sysrc_lua(false);
426 copy_system_tables(*core.lua);
429 void quit_lua() throw()
431 lsnes_instance.lua->deinit();
435 #define LUA_TIMED_HOOK_IDLE 0
436 #define LUA_TIMED_HOOK_TIMER 1
438 uint64_t lua_state::timed_hook(int timer) throw()
440 switch(timer) {
441 case LUA_TIMED_HOOK_IDLE:
442 return idle_hook_time;
443 case LUA_TIMED_HOOK_TIMER:
444 return timer_hook_time;
446 return 0;
449 void lua_state::callback_do_unsafe_rewind(movie& mov, void* u)
451 auto& core = CORE();
452 if(u) {
453 lua_unsaferewind* u2 = reinterpret_cast<lua::objpin<lua_unsaferewind>*>(u)->object();
454 //Load.
455 try {
456 run_callback(*on_pre_rewind);
457 run_callback(*on_movie_lost, "unsaferewind");
458 mainloop_restore_state(u2->console_state);
459 mov.fast_load(u2->console_state.save_frame, u2->ptr, u2->console_state.lagged_frames,
460 u2->console_state.pollcounters);
461 core.mlogic->get_mfile().dyn = u2->console_state;
462 run_callback(*on_post_rewind);
463 delete reinterpret_cast<lua::objpin<lua_unsaferewind>*>(u);
464 } catch(std::bad_alloc& e) {
465 OOM_panic();
466 } catch(...) {
467 return;
469 } else {
470 //Save
471 run_callback(*on_set_rewind, lua::state::fn_tag([&core, &mov](lua::state& L) ->
472 int {
473 lua_unsaferewind* u2 = lua::_class<lua_unsaferewind>::create(*core.lua);
474 u2->console_state = core.mlogic->get_mfile().dyn;
475 mov.fast_save(u2->console_state.save_frame, u2->ptr, u2->console_state.lagged_frames,
476 u2->console_state.pollcounters);
477 return 1;
478 }));
482 void lua_state::callback_movie_lost(const char* what)
484 run_callback(*on_movie_lost, std::string(what));
487 void lua_state::callback_do_latch(std::list<std::string>& args)
489 run_callback(*on_latch, lua::state::vararg_tag(args));
492 lua_unsaferewind::lua_unsaferewind(lua::state& L)
496 void lua_state::run_startup_scripts()
498 for(auto i : startup_scripts) {
499 messages << "Trying to run Lua script: " << i << std::endl;
500 do_run_lua(i);
504 void lua_state::add_startup_script(const std::string& file)
506 startup_scripts.push_back(file);
509 const std::map<std::string, std::u32string>& lua_state::get_watch_vars()
511 return watch_vars;
514 bool lua_state::run_lua_fragment() throw(std::bad_alloc)
516 bool result = true;
517 if(recursive_flag)
518 return false;
519 int t = L.load(read_lua_fragment, &luareader_fragment, "run_lua_fragment", "t");
520 if(t == LUA_ERRSYNTAX) {
521 messages << "Can't run Lua: Internal syntax error: " << L.tostring(-1)
522 << std::endl;
523 L.pop(1);
524 return false;
526 if(t == LUA_ERRMEM) {
527 messages << "Can't run Lua: Out of memory" << std::endl;
528 L.pop(1);
529 return false;
531 recursive_flag = true;
532 int r = L.pcall(0, 0, 0);
533 recursive_flag = false;
534 if(r == LUA_ERRRUN) {
535 messages << "Error running Lua hunk: " << L.tostring(-1) << std::endl;
536 L.pop(1);
537 result = false;
539 if(r == LUA_ERRMEM) {
540 messages << "Error running Lua hunk: Out of memory" << std::endl;
541 L.pop(1);
542 result = false;
544 if(r == LUA_ERRERR) {
545 messages << "Error running Lua hunk: Double Fault???" << std::endl;
546 L.pop(1);
547 result = false;
549 #ifdef LUA_ERRGCMM
550 if(r == LUA_ERRGCMM) {
551 messages << "Error running Lua hunk: Fault in garbage collector" << std::endl;
552 L.pop(1);
553 result = false;
555 #endif
556 render_ctx = NULL;
557 if(requests_repaint) {
558 requests_repaint = false;
559 command.invoke("repaint");
561 return result;
564 void lua_state::do_eval_lua(const std::string& c) throw(std::bad_alloc)
566 L.pushlstring(c.c_str(), c.length());
567 L.setglobal(TEMPORARY);
568 luareader_fragment = CONST_eval_lua_lua;
569 run_lua_fragment();
572 void lua_state::do_run_lua(const std::string& c) throw(std::bad_alloc)
574 L.pushlstring(c.c_str(), c.length());
575 L.setglobal(TEMPORARY);
576 luareader_fragment = CONST_run_lua_lua;
577 run_lua_fragment();
580 template<typename... T> bool lua_state::run_callback(lua::state::callback_list& list, T... args)
582 if(recursive_flag)
583 return true;
584 recursive_flag = true;
585 try {
586 if(!list.callback(args...)) {
587 recursive_flag = false;
588 return false;
590 } catch(std::exception& e) {
591 messages << e.what() << std::endl;
593 recursive_flag = false;
594 render_ctx = NULL;
595 if(requests_repaint) {
596 requests_repaint = false;
597 command.invoke("repaint");
599 return true;
602 void lua_state::run_sysrc_lua(bool rerun)
604 L.pushstring(lua_sysrc_script);
605 L.setglobal(TEMPORARY);
606 luareader_fragment = CONST_eval_sysrc_lua;
607 if(!run_lua_fragment() && !rerun) {
608 //run_lua_fragment shows error.
609 //messages << "Failed to run sysrc lua script" << std::endl;
610 fatal_error();
614 void lua_state::run_synchronous_paint(struct lua::render_context* ctx)
616 if(!synchronous_paint_ctx)
617 return;
618 lua_renderq_run(ctx, synchronous_paint_ctx);