summaryrefslogtreecommitdiffstats
path: root/src/frontend/mame/luaengine.cpp
diff options
context:
space:
mode:
author Vas Crabb <cuavas@users.noreply.github.com>2022-03-23 20:27:30 +1100
committer GitHub <noreply@github.com>2022-03-23 20:27:30 +1100
commite6588480c477a8132676abbbb32bfc42287d8c6d (patch)
treeb483f8b9e049a280f68ae7b0af9c57372676f5df /src/frontend/mame/luaengine.cpp
parentbb3f3e5abd7051ed371fe7fbdd0f49a56cc03f1f (diff)
Lua engine improvements (#9453)
Made auto-boot script errors and plugin bootstrap errors fatal. Run auto-boot scripts in a sandbox. Globals can be accessed, but not set. The sandbox is cleared on hard reset, but not on soft reset. Added (hopefully) useful to string metafunctions to device_t and address space that show short names and tags. Fixed issues in plugins that surface when strict type checking is enabled, as this means numbers and nil are not automatically converted to strings. Plugins should be tested with debug builds to check for this. Made save item read_block raise an error on invalid arguments rather than returning an empty string, and made it use luaL_buffer directly rather than using the helper wrapper. Changed some more function bindings to use set_function to avoid issues related to ThePhD/sol2#608, and got rid of some unnecessary lambda captures.
Diffstat (limited to 'src/frontend/mame/luaengine.cpp')
-rw-r--r--src/frontend/mame/luaengine.cpp275
1 files changed, 143 insertions, 132 deletions
diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp
index 6981ebfe41b..db35b227faa 100644
--- a/src/frontend/mame/luaengine.cpp
+++ b/src/frontend/mame/luaengine.cpp
@@ -352,16 +352,18 @@ sol::object lua_engine::call_plugin(const std::string &name, sol::object in)
{
std::string field = "cb_" + name;
sol::object obj = sol().registry()[field];
- if(obj.is<sol::protected_function>())
+ if (obj.is<sol::protected_function>())
{
auto res = invoke(obj.as<sol::protected_function>(), in);
- if(!res.valid())
+ if (!res.valid())
{
sol::error err = res;
osd_printf_error("[LUA ERROR] in call_plugin: %s\n", err.what());
}
else
+ {
return res.get<sol::object>();
+ }
}
return sol::lua_nil;
}
@@ -380,7 +382,7 @@ std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vect
}
else
{
- std::tuple<sol::table, std::optional<long>, std::string> table = res;
+ std::tuple<sol::table, std::optional<long>, std::optional<std::string> > table = res;
for (auto &entry : std::get<0>(table))
{
if (entry.second.is<sol::table>())
@@ -389,7 +391,10 @@ std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vect
menu_list.emplace_back(enttable.get<std::string, std::string, std::string>(1, 2, 3));
}
}
- flags = std::get<2>(table);
+ if (std::get<2>(table))
+ flags = *std::get<2>(table);
+ else
+ flags.clear();
return std::get<1>(table);
}
}
@@ -400,7 +405,7 @@ std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vect
std::pair<bool, std::optional<long> > lua_engine::menu_callback(const std::string &menu, int index, const std::string &event)
{
std::string field = "menu_cb_" + menu;
- std::pair<bool, std::optional<long> > ret(false, std::nullopt);
+ std::pair<std::optional<bool>, std::optional<long> > ret(false, std::nullopt);
sol::object obj = sol().registry()[field];
if (obj.is<sol::protected_function>())
{
@@ -415,7 +420,7 @@ std::pair<bool, std::optional<long> > lua_engine::menu_callback(const std::strin
ret = res;
}
}
- return ret;
+ return std::make_pair(std::get<0>(ret) && *std::get<0>(ret), std::get<1>(ret));
}
void lua_engine::set_machine(running_machine *machine)
@@ -423,9 +428,10 @@ void lua_engine::set_machine(running_machine *machine)
m_machine = machine;
}
-int lua_engine::enumerate_functions(const char *id, std::function<bool(const sol::protected_function &func)> &&callback)
+template <typename T>
+size_t lua_engine::enumerate_functions(const char *id, T &&callback)
{
- int count = 0;
+ size_t count = 0;
sol::object functable = sol().registry()[id];
if (functable.is<sol::table>())
{
@@ -439,23 +445,24 @@ int lua_engine::enumerate_functions(const char *id, std::function<bool(const sol
break;
}
}
- return true;
}
return count;
}
bool lua_engine::execute_function(const char *id)
{
- int count = enumerate_functions(id, [this](const sol::protected_function &func)
- {
- auto ret = invoke(func);
- if(!ret.valid())
- {
- sol::error err = ret;
- osd_printf_error("[LUA ERROR] in execute_function: %s\n", err.what());
- }
- return true;
- });
+ size_t count = enumerate_functions(
+ id,
+ [] (const sol::protected_function &func)
+ {
+ auto ret = invoke(func);
+ if (!ret.valid())
+ {
+ sol::error err = ret;
+ osd_printf_error("[LUA ERROR] in execute_function: %s\n", err.what());
+ }
+ return true;
+ });
return count > 0;
}
@@ -521,21 +528,23 @@ void lua_engine::on_periodic()
bool lua_engine::on_missing_mandatory_image(const std::string &instance_name)
{
bool handled = false;
- enumerate_functions("LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE", [this, &instance_name, &handled](const sol::protected_function &func)
- {
- auto ret = invoke(func, instance_name);
+ enumerate_functions(
+ "LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE",
+ [&instance_name, &handled] (const sol::protected_function &func)
+ {
+ auto ret = invoke(func, instance_name);
- if(!ret.valid())
- {
- sol::error err = ret;
- osd_printf_error("[LUA ERROR] in on_missing_mandatory_image: %s\n", err.what());
- }
- else if (ret.get<bool>())
- {
- handled = true;
- }
- return !handled;
- });
+ if (!ret.valid())
+ {
+ sol::error err = ret;
+ osd_printf_error("[LUA ERROR] in on_missing_mandatory_image: %s\n", err.what());
+ }
+ else if (ret.get<bool>())
+ {
+ handled = true;
+ }
+ return !handled;
+ });
return handled;
}
@@ -829,7 +838,7 @@ void lua_engine::initialize()
auto space = buf.prepare(len);
space.add(file.read(space.get(), len));
buf.push();
- return sol::stack::pop<sol::object>(s);
+ return sol::make_reference(s, sol::stack_reference(s, -1));
});
file_type.set("write", [](emu_file &file, const std::string &data) { return file.write(data.data(), data.size()); });
file_type.set("puts", &emu_file::puts);
@@ -974,12 +983,14 @@ void lua_engine::initialize()
}));
item_type.set("size", sol::readonly(&save_item::size));
item_type.set("count", sol::readonly(&save_item::count));
- item_type.set("read", [this](save_item &item, int offset) -> sol::object {
- if(!item.base || (offset >= item.count))
+ item_type.set("read",
+ [this] (save_item &item, int offset) -> sol::object
+ {
+ if (!item.base || (offset >= item.count))
return sol::lua_nil;
const void *const data = reinterpret_cast<const uint8_t *>(item.base) + (item.stride * (offset / item.valcount));
uint64_t ret = 0;
- switch(item.size)
+ switch (item.size)
{
case 1:
default:
@@ -997,33 +1008,38 @@ void lua_engine::initialize()
}
return sol::make_object(sol(), ret);
});
- item_type.set("read_block", [](save_item &item, sol::this_state s, int offset, size_t len) {
- buffer_helper buf(s);
- auto space = buf.prepare(len);
- if(!item.base || ((offset + len) > (item.size * item.count)))
+ item_type.set("read_block",
+ [] (save_item &item, sol::this_state s, uint32_t offset, size_t len) -> sol::object
+ {
+ if (!item.base)
{
- space.add(0);
+ luaL_error(s, "Invalid save item");
+ return sol::lua_nil;
}
- else
+
+ if ((offset + len) > (item.size * item.count))
{
- const uint32_t blocksize = item.size * item.valcount;
- size_t remaining = len;
- uint8_t *dest = reinterpret_cast<uint8_t *>(space.get());
- while(remaining)
- {
- const uint32_t blockno = offset / blocksize;
- const uint32_t available = blocksize - (offset % blocksize);
- const uint32_t chunk = (available < remaining) ? available : remaining;
- const void *const source = reinterpret_cast<const uint8_t *>(item.base) + (blockno * item.stride) + (offset % blocksize);
- std::memcpy(dest, source, chunk);
- offset += chunk;
- remaining -= chunk;
- dest += chunk;
- }
- space.add(len);
+ luaL_error(s, "Range extends beyond end of save item");
+ return sol::lua_nil;
}
- buf.push();
- return sol::stack::pop<sol::object>(s);
+
+ luaL_Buffer buff;
+ uint8_t *dest = reinterpret_cast<uint8_t *>(luaL_buffinitsize(s, &buff, len));
+ const uint32_t blocksize = item.size * item.valcount;
+ size_t remaining = len;
+ while (remaining)
+ {
+ const uint32_t blockno = offset / blocksize;
+ const uint32_t available = blocksize - (offset % blocksize);
+ const uint32_t chunk = (available < remaining) ? available : remaining;
+ const void *const source = reinterpret_cast<const uint8_t *>(item.base) + (blockno * item.stride) + (offset % blocksize);
+ std::memcpy(dest, source, chunk);
+ offset += chunk;
+ remaining -= chunk;
+ dest += chunk;
+ }
+ luaL_pushresultsize(&buff, len);
+ return sol::make_reference(s, sol::stack_reference(s, -1));
});
item_type.set("write", [](save_item &item, int offset, uint64_t value) {
if(!item.base || (offset >= item.count))
@@ -1187,54 +1203,54 @@ void lua_engine::initialize()
auto machine_type = sol().registry().new_usertype<running_machine>("machine", sol::no_constructor);
- machine_type["exit"] = &running_machine::schedule_exit;
- machine_type["hard_reset"] = &running_machine::schedule_hard_reset;
- machine_type["soft_reset"] = &running_machine::schedule_soft_reset;
- machine_type["save"] = &running_machine::schedule_save; // TODO: some kind of completion notification?
- machine_type["load"] = &running_machine::schedule_load; // TODO: some kind of completion notification?
- machine_type["buffer_save"] =
- [] (running_machine &m, sol::this_state s)
- {
- // FIXME: this needs to schedule saving to a buffer and return asynchronously somehow
- // right now it's broken by anonymous timers, synchronize, etc.
- lua_State *L = s;
- luaL_Buffer buff;
- int size = ram_state::get_size(m.save());
- u8 *ptr = (u8 *)luaL_buffinitsize(L, &buff, size);
- save_error error = m.save().write_buffer(ptr, size);
- if (error == STATERR_NONE)
+ machine_type.set_function("exit", &running_machine::schedule_exit);
+ machine_type.set_function("hard_reset", &running_machine::schedule_hard_reset);
+ machine_type.set_function("soft_reset", &running_machine::schedule_soft_reset);
+ machine_type.set_function("save", &running_machine::schedule_save); // TODO: some kind of completion notification?
+ machine_type.set_function("load", &running_machine::schedule_load); // TODO: some kind of completion notification?
+ machine_type.set_function("buffer_save",
+ [] (running_machine &m, sol::this_state s)
{
- luaL_pushresultsize(&buff, size);
- return sol::make_reference(L, sol::stack_reference(L, -1));
- }
- luaL_error(L, "State save error.");
- return sol::make_reference(L, nullptr);
- };
- machine_type["buffer_load"] =
- [] (running_machine &m, sol::this_state s, std::string str)
- {
- // FIXME: this needs to schedule loading from the buffer and return asynchronously somehow
- // right now it's broken by anonymous timers, synchronize, etc.
- save_error error = m.save().read_buffer((u8 *)str.data(), str.size());
- if (error == STATERR_NONE)
+ // FIXME: this needs to schedule saving to a buffer and return asynchronously somehow
+ // right now it's broken by anonymous timers, synchronize, etc.
+ lua_State *L = s;
+ luaL_Buffer buff;
+ int size = ram_state::get_size(m.save());
+ u8 *ptr = (u8 *)luaL_buffinitsize(L, &buff, size);
+ save_error error = m.save().write_buffer(ptr, size);
+ if (error == STATERR_NONE)
+ {
+ luaL_pushresultsize(&buff, size);
+ return sol::make_reference(L, sol::stack_reference(L, -1));
+ }
+ luaL_error(L, "State save error.");
+ return sol::make_reference(L, nullptr);
+ });
+ machine_type.set_function("buffer_load",
+ [] (running_machine &m, sol::this_state s, std::string str)
{
- return true;
- }
- else
+ // FIXME: this needs to schedule loading from the buffer and return asynchronously somehow
+ // right now it's broken by anonymous timers, synchronize, etc.
+ save_error error = m.save().read_buffer((u8 *)str.data(), str.size());
+ if (error == STATERR_NONE)
+ {
+ return true;
+ }
+ else
+ {
+ luaL_error(s,"State load error.");
+ return false;
+ }
+ });
+ machine_type.set_function("popmessage",
+ [] (running_machine &m, std::optional<const char *> str)
{
- luaL_error(s,"State load error.");
- return false;
- }
- };
- machine_type["popmessage"] =
- [] (running_machine &m, const char *str)
- {
- if (str)
- m.popmessage("%s", str);
- else
- m.popmessage();
- };
- machine_type["logerror"] = [] (running_machine &m, std::string const *str) { m.logerror("[luaengine] %s\n", str); };
+ if (str)
+ m.popmessage("%s", *str);
+ else
+ m.popmessage();
+ });
+ machine_type.set_function("logerror", [] (running_machine &m, char const *str) { m.logerror("[luaengine] %s\n", str); });
machine_type["time"] = sol::property(&running_machine::time);
machine_type["system"] = sol::property(&running_machine::system);
machine_type["parameters"] = sol::property(&running_machine::parameters);
@@ -1337,15 +1353,16 @@ void lua_engine::initialize()
auto device_type = sol().registry().new_usertype<device_t>("device", sol::no_constructor);
- device_type["subtag"] = &device_t::subtag;
- device_type["siblingtag"] = &device_t::siblingtag;
- device_type["memregion"] = &device_t::memregion;
- device_type["memshare"] = &device_t::memshare;
- device_type["membank"] = &device_t::membank;
- device_type["ioport"] = &device_t::ioport;
- device_type["subdevice"] = static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::subdevice);
- device_type["siblingdevice"] = static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::siblingdevice);
- device_type["parameter"] = &device_t::parameter;
+ device_type.set_function(sol::meta_function::to_string, [] (device_t &d) { return util::string_format("%s(%s)", d.shortname(), d.tag()); });
+ device_type.set_function("subtag", &device_t::subtag);
+ device_type.set_function("siblingtag", &device_t::siblingtag);
+ device_type.set_function("memregion", &device_t::memregion);
+ device_type.set_function("memshare", &device_t::memshare);
+ device_type.set_function("membank", &device_t::membank);
+ device_type.set_function("ioport", &device_t::ioport);
+ device_type.set_function("subdevice", static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::subdevice));
+ device_type.set_function("siblingdevice", static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::siblingdevice));
+ device_type.set_function("parameter", &device_t::parameter);
device_type["tag"] = sol::property(&device_t::tag);
device_type["basetag"] = sol::property(&device_t::basetag);
device_type["name"] = sol::property(&device_t::name);
@@ -1914,35 +1931,29 @@ void lua_engine::resume(int nparam)
luaL_unref(m_lua_state, LUA_REGISTRYINDEX, nparam);
}
-void lua_engine::run(sol::load_result res)
+//-------------------------------------------------
+// load_script - load script from file path
+//-------------------------------------------------
+
+sol::load_result lua_engine::load_script(std::string const &filename)
{
- if(res.valid())
- {
- auto ret = invoke(res.get<sol::protected_function>());
- if(!ret.valid())
- {
- sol::error err = ret;
- osd_printf_error("[LUA ERROR] in run: %s\n", err.what());
- }
- }
- else
- osd_printf_error("[LUA ERROR] %d loading Lua script\n", (int)res.status());
+ return sol().load_file(filename);
}
//-------------------------------------------------
-// execute - load and execute script
+// load_string - load script from string
//-------------------------------------------------
-void lua_engine::load_script(const char *filename)
+sol::load_result lua_engine::load_string(std::string const &value)
{
- run(sol().load_file(filename));
+ return sol().load(value);
}
//-------------------------------------------------
-// execute_string - execute script from string
+// make_environment - make a sandbox
//-------------------------------------------------
-void lua_engine::load_string(const char *value)
+sol::environment lua_engine::make_environment()
{
- run(sol().load(value));
+ return sol::environment(sol(), sol::create, sol().globals());
}