// The MIT License (MIT) // Copyright (c) 2013-2017 Rapptz, ThePhD and contributors // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef SOL_STATE_VIEW_HPP #define SOL_STATE_VIEW_HPP #include "error.hpp" #include "table.hpp" #include "environment.hpp" #include "load_result.hpp" #include namespace sol { enum class lib : char { // print, assert, and other base functions base, // require and other package functions package, // coroutine functions and utilities coroutine, // string library string, // functionality from the OS os, // all things math math, // the table manipulator and observer functions table, // the debug library debug, // the bit library: different based on which you're using bit32, // input/output library io, // LuaJIT only ffi, // LuaJIT only jit, // library for handling utf8: new to Lua utf8, // do not use count }; inline std::size_t total_memory_used(lua_State* L) { std::size_t kb = lua_gc(L, LUA_GCCOUNT, 0); kb *= 1024; kb += lua_gc(L, LUA_GCCOUNTB, 0); return kb; } inline protected_function_result script_pass_on_error(lua_State*, protected_function_result result) { return result; } inline protected_function_result script_default_on_error(lua_State* L, protected_function_result pfr) { type t = type_of(L, pfr.stack_index()); std::string err = "sol: "; err += to_string(pfr.status()); err += " error:"; if (t == type::string) { err += " "; string_view serr = stack::get(L, pfr.stack_index()); err.append(serr.data(), serr.size()); } #ifdef SOL_NO_EXCEPTIONS // replacing information of stack error into pfr if (t != type::none) { lua_pop(L, 1); } stack::push(L, err); #else // just throw our error throw error(detail::direct_error, err); #endif return pfr; } class state_view { private: lua_State* L; table reg; global_table global; optional is_loaded_package(const std::string& key) { auto loaded = reg.traverse_get>("_LOADED", key); bool is53mod = loaded && !(loaded->is() && !loaded->as()); if (is53mod) return loaded; #if SOL_LUA_VERSION <= 501 auto loaded51 = global.traverse_get>("package", "loaded", key); bool is51mod = loaded51 && !(loaded51->is() && !loaded51->as()); if (is51mod) return loaded51; #endif return nullopt; } template void ensure_package(const std::string& key, T&& sr) { #if SOL_LUA_VERSION <= 501 auto pkg = global["package"]; if (!pkg.valid()) { pkg = create_table_with("loaded", create_table_with(key, sr)); } else { auto ld = pkg["loaded"]; if (!ld.valid()) { ld = create_table_with(key, sr); } else { ld[key] = sr; } } #endif auto loaded = reg["_LOADED"]; if (!loaded.valid()) { loaded = create_table_with(key, sr); } else { loaded[key] = sr; } } template object require_core(const std::string& key, Fx&& action, bool create_global = true) { optional loaded = is_loaded_package(key); if (loaded && loaded->valid()) return std::move(*loaded); action(); stack_reference sr(L, -1); if (create_global) set(key, sr); ensure_package(key, sr); return stack::pop(L); } public: typedef global_table::iterator iterator; typedef global_table::const_iterator const_iterator; state_view(lua_State* Ls) : L(Ls), reg(Ls, LUA_REGISTRYINDEX), global(Ls, detail::global_) { } state_view(this_state Ls) : state_view(Ls.L) { } lua_State* lua_state() const { return L; } template void open_libraries(Args&&... args) { static_assert(meta::all_same::value, "all types must be libraries"); if (sizeof...(args) == 0) { luaL_openlibs(L); return; } lib libraries[1 + sizeof...(args)] = {lib::count, std::forward(args)...}; for (auto&& library : libraries) { switch (library) { #if SOL_LUA_VERSION <= 501 && defined(SOL_LUAJIT) case lib::coroutine: #endif // luajit opens coroutine base stuff case lib::base: luaL_requiref(L, "base", luaopen_base, 1); lua_pop(L, 1); break; case lib::package: luaL_requiref(L, "package", luaopen_package, 1); lua_pop(L, 1); break; #if !defined(SOL_LUAJIT) case lib::coroutine: #if SOL_LUA_VERSION > 501 luaL_requiref(L, "coroutine", luaopen_coroutine, 1); lua_pop(L, 1); #endif // Lua 5.2+ only break; #endif // Not LuaJIT - comes builtin case lib::string: luaL_requiref(L, "string", luaopen_string, 1); lua_pop(L, 1); break; case lib::table: luaL_requiref(L, "table", luaopen_table, 1); lua_pop(L, 1); break; case lib::math: luaL_requiref(L, "math", luaopen_math, 1); lua_pop(L, 1); break; case lib::bit32: #ifdef SOL_LUAJIT luaL_requiref(L, "bit32", luaopen_bit, 1); lua_pop(L, 1); #elif (SOL_LUA_VERSION == 502) || defined(LUA_COMPAT_BITLIB) || defined(LUA_COMPAT_5_2) luaL_requiref(L, "bit32", luaopen_bit32, 1); lua_pop(L, 1); #else #endif // Lua 5.2 only (deprecated in 5.3 (503)) (Can be turned on with Compat flags) break; case lib::io: luaL_requiref(L, "io", luaopen_io, 1); lua_pop(L, 1); break; case lib::os: luaL_requiref(L, "os", luaopen_os, 1); lua_pop(L, 1); break; case lib::debug: luaL_requiref(L, "debug", luaopen_debug, 1); lua_pop(L, 1); break; case lib::utf8: #if SOL_LUA_VERSION > 502 && !defined(SOL_LUAJIT) luaL_requiref(L, "utf8", luaopen_utf8, 1); lua_pop(L, 1); #endif // Lua 5.3+ only break; case lib::ffi: #ifdef SOL_LUAJIT luaL_requiref(L, "ffi", luaopen_ffi, 1); lua_pop(L, 1); #endif // LuaJIT only break; case lib::jit: #ifdef SOL_LUAJIT luaL_requiref(L, "jit", luaopen_jit, 0); lua_pop(L, 1); #endif // LuaJIT Only break; case lib::count: default: break; } } } object require(const std::string& key, lua_CFunction open_function, bool create_global = true) { luaL_requiref(L, key.c_str(), open_function, create_global ? 1 : 0); return stack::pop(L); } object require_script(const std::string& key, const string_view& code, bool create_global = true, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { auto action = [this, &code, &chunkname, &mode]() { stack::script(L, code, chunkname, mode); }; return require_core(key, action, create_global); } object require_file(const std::string& key, const std::string& filename, bool create_global = true, load_mode mode = load_mode::any) { auto action = [this, &filename, &mode]() { stack::script_file(L, filename, mode); }; return require_core(key, action, create_global); } template protected_function_result do_string(const string_view& code, const basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { detail::typical_chunk_name_t basechunkname = {}; const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); load_status x = static_cast(luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str())); if (x != load_status::ok) { return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast(x)); } stack_aligned_protected_function pf(L, -1); set_environment(env, pf); return pf(); } template protected_function_result do_file(const std::string& filename, const basic_environment& env, load_mode mode = load_mode::any) { load_status x = static_cast(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())); if (x != load_status::ok) { return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast(x)); } stack_aligned_protected_function pf(L, -1); set_environment(env, pf); return pf(); } protected_function_result do_string(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { detail::typical_chunk_name_t basechunkname = {}; const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); load_status x = static_cast(luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str())); if (x != load_status::ok) { return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast(x)); } stack_aligned_protected_function pf(L, -1); return pf(); } protected_function_result do_file(const std::string& filename, load_mode mode = load_mode::any) { load_status x = static_cast(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())); if (x != load_status::ok) { return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast(x)); } stack_aligned_protected_function pf(L, -1); return pf(); } template >> = meta::enabler> protected_function_result safe_script(const string_view& code, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { protected_function_result pfr = do_string(code, chunkname, mode); if (!pfr.valid()) { return on_error(L, std::move(pfr)); } return pfr; } template protected_function_result safe_script(const string_view& code, const basic_environment& env, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { protected_function_result pfr = do_string(code, env, chunkname, mode); if (!pfr.valid()) { return on_error(L, std::move(pfr)); } return pfr; } template protected_function_result safe_script(const string_view& code, const basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return safe_script(code, env, script_default_on_error, chunkname, mode); } protected_function_result safe_script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return safe_script(code, script_default_on_error, chunkname, mode); } template >> = meta::enabler> protected_function_result safe_script_file(const std::string& filename, Fx&& on_error, load_mode mode = load_mode::any) { protected_function_result pfr = do_file(filename, mode); if (!pfr.valid()) { return on_error(L, std::move(pfr)); } return pfr; } template protected_function_result safe_script_file(const std::string& filename, const basic_environment& env, Fx&& on_error, load_mode mode = load_mode::any) { protected_function_result pfr = do_file(filename, env, mode); if (!pfr.valid()) { return on_error(L, std::move(pfr)); } return pfr; } template protected_function_result safe_script_file(const std::string& filename, const basic_environment& env, load_mode mode = load_mode::any) { return safe_script_file(filename, env, script_default_on_error, mode); } protected_function_result safe_script_file(const std::string& filename, load_mode mode = load_mode::any) { return safe_script_file(filename, script_default_on_error, mode); } template function_result unsafe_script(const string_view& code, const basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { detail::typical_chunk_name_t basechunkname = {}; const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); int index = lua_gettop(L); if (luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str())) { lua_error(L); } set_environment(env, stack_reference(L, raw_index(index + 1))); if (lua_pcall(L, 0, LUA_MULTRET, 0)) { lua_error(L); } int postindex = lua_gettop(L); int returns = postindex - index; return function_result(L, (std::max)(postindex - (returns - 1), 1), returns); } function_result unsafe_script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { int index = lua_gettop(L); stack::script(L, code, chunkname, mode); int postindex = lua_gettop(L); int returns = postindex - index; return function_result(L, (std::max)(postindex - (returns - 1), 1), returns); } template function_result unsafe_script_file(const std::string& filename, const basic_environment& env, load_mode mode = load_mode::any) { int index = lua_gettop(L); if (luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())) { lua_error(L); } set_environment(env, stack_reference(L, raw_index(index + 1))); if (lua_pcall(L, 0, LUA_MULTRET, 0)) { lua_error(L); } int postindex = lua_gettop(L); int returns = postindex - index; return function_result(L, (std::max)(postindex - (returns - 1), 1), returns); } function_result unsafe_script_file(const std::string& filename, load_mode mode = load_mode::any) { int index = lua_gettop(L); stack::script_file(L, filename, mode); int postindex = lua_gettop(L); int returns = postindex - index; return function_result(L, (std::max)(postindex - (returns - 1), 1), returns); } template >> = meta::enabler> protected_function_result script(const string_view& code, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return safe_script(code, std::forward(on_error), chunkname, mode); } template >> = meta::enabler> protected_function_result script_file(const std::string& filename, Fx&& on_error, load_mode mode = load_mode::any) { return safe_script_file(filename, std::forward(on_error), mode); } template protected_function_result script(const string_view& code, const basic_environment& env, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return safe_script(code, env, std::forward(on_error), chunkname, mode); } template protected_function_result script_file(const std::string& filename, const basic_environment& env, Fx&& on_error, load_mode mode = load_mode::any) { return safe_script_file(filename, env, std::forward(on_error), mode); } protected_function_result script(const string_view& code, const environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return safe_script(code, env, script_default_on_error, chunkname, mode); } protected_function_result script_file(const std::string& filename, const environment& env, load_mode mode = load_mode::any) { return safe_script_file(filename, env, script_default_on_error, mode); } #ifdef SOL_SAFE_FUNCTIONS protected_function_result script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return safe_script(code, chunkname, mode); } protected_function_result script_file(const std::string& filename, load_mode mode = load_mode::any) { return safe_script_file(filename, mode); } #else function_result script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return unsafe_script(code, chunkname, mode); } function_result script_file(const std::string& filename, load_mode mode = load_mode::any) { return unsafe_script_file(filename, mode); } #endif load_result load(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { detail::typical_chunk_name_t basechunkname = {}; const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); load_status x = static_cast(luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str())); return load_result(L, absolute_index(L, -1), 1, 1, x); } load_result load_buffer(const char* buff, size_t size, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return load(string_view(buff, size), chunkname, mode); } load_result load_file(const std::string& filename, load_mode mode = load_mode::any) { load_status x = static_cast(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())); return load_result(L, absolute_index(L, -1), 1, 1, x); } load_result load(lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { detail::typical_chunk_name_t basechunkname = {}; const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname); #if SOL_LUA_VERSION > 501 load_status x = static_cast(lua_load(L, reader, data, chunknametarget, to_string(mode).c_str())); #else (void)mode; load_status x = static_cast(lua_load(L, reader, data, chunknametarget)); #endif return load_result(L, absolute_index(L, -1), 1, 1, x); } iterator begin() const { return global.begin(); } iterator end() const { return global.end(); } const_iterator cbegin() const { return global.cbegin(); } const_iterator cend() const { return global.cend(); } global_table globals() const { return global; } table registry() const { return reg; } std::size_t memory_used() const { return total_memory_used(lua_state()); } int stack_top() const { return stack::top(L); } void collect_garbage() { lua_gc(lua_state(), LUA_GCCOLLECT, 0); } operator lua_State*() const { return lua_state(); } void set_panic(lua_CFunction panic) { lua_atpanic(L, panic); } template decltype(auto) get(Keys&&... keys) const { return global.get(std::forward(keys)...); } template decltype(auto) get_or(Key&& key, T&& otherwise) const { return global.get_or(std::forward(key), std::forward(otherwise)); } template decltype(auto) get_or(Key&& key, D&& otherwise) const { return global.get_or(std::forward(key), std::forward(otherwise)); } template state_view& set(Args&&... args) { global.set(std::forward(args)...); return *this; } template decltype(auto) traverse_get(Keys&&... keys) const { return global.traverse_get(std::forward(keys)...); } template state_view& traverse_set(Args&&... args) { global.traverse_set(std::forward(args)...); return *this; } template state_view& set_usertype(usertype& user) { return set_usertype(usertype_traits::name(), user); } template state_view& set_usertype(Key&& key, usertype& user) { global.set_usertype(std::forward(key), user); return *this; } template state_view& new_usertype(const std::string& name, Args&&... args) { global.new_usertype(name, std::forward(args)...); return *this; } template state_view& new_usertype(const std::string& name, Args&&... args) { global.new_usertype(name, std::forward(args)...); return *this; } template state_view& new_usertype(const std::string& name, constructors ctor, Args&&... args) { global.new_usertype(name, ctor, std::forward(args)...); return *this; } template state_view& new_simple_usertype(const std::string& name, Args&&... args) { global.new_simple_usertype(name, std::forward(args)...); return *this; } template state_view& new_simple_usertype(const std::string& name, Args&&... args) { global.new_simple_usertype(name, std::forward(args)...); return *this; } template state_view& new_simple_usertype(const std::string& name, constructors ctor, Args&&... args) { global.new_simple_usertype(name, ctor, std::forward(args)...); return *this; } template simple_usertype create_simple_usertype(Args&&... args) { return global.create_simple_usertype(std::forward(args)...); } template simple_usertype create_simple_usertype(Args&&... args) { return global.create_simple_usertype(std::forward(args)...); } template simple_usertype create_simple_usertype(constructors ctor, Args&&... args) { return global.create_simple_usertype(ctor, std::forward(args)...); } template state_view& new_enum(const std::string& name, Args&&... args) { global.new_enum(name, std::forward(args)...); return *this; } template state_view& new_enum(const std::string& name, std::initializer_list> items) { global.new_enum(name, std::move(items)); return *this; } template void for_each(Fx&& fx) { global.for_each(std::forward(fx)); } template proxy operator[](T&& key) { return global[std::forward(key)]; } template proxy operator[](T&& key) const { return global[std::forward(key)]; } template state_view& set_function(Key&& key, Args&&... args) { global.set_function(std::forward(key), std::forward(args)...); return *this; } template state_view& set_function(Key&& key, Args&&... args) { global.set_function(std::forward(key), std::forward(args)...); return *this; } template table create_table(Name&& name, int narr = 0, int nrec = 0) { return global.create(std::forward(name), narr, nrec); } template table create_table(Name&& name, int narr, int nrec, Key&& key, Value&& value, Args&&... args) { return global.create(std::forward(name), narr, nrec, std::forward(key), std::forward(value), std::forward(args)...); } template table create_named_table(Name&& name, Args&&... args) { table x = global.create_with(std::forward(args)...); global.set(std::forward(name), x); return x; } table create_table(int narr = 0, int nrec = 0) { return create_table(lua_state(), narr, nrec); } template table create_table(int narr, int nrec, Key&& key, Value&& value, Args&&... args) { return create_table(lua_state(), narr, nrec, std::forward(key), std::forward(value), std::forward(args)...); } template table create_table_with(Args&&... args) { return create_table_with(lua_state(), std::forward(args)...); } static inline table create_table(lua_State* L, int narr = 0, int nrec = 0) { return global_table::create(L, narr, nrec); } template static inline table create_table(lua_State* L, int narr, int nrec, Key&& key, Value&& value, Args&&... args) { return global_table::create(L, narr, nrec, std::forward(key), std::forward(value), std::forward(args)...); } template static inline table create_table_with(lua_State* L, Args&&... args) { return global_table::create_with(L, std::forward(args)...); } }; } // namespace sol #endif // SOL_STATE_VIEW_HPP