diff --git a/docs/source/api/environment.rst b/docs/source/api/environment.rst index 7cfac4b9..17149ba3 100644 --- a/docs/source/api/environment.rst +++ b/docs/source/api/environment.rst @@ -10,6 +10,8 @@ encapsulation table for script sandboxing template void set_environment( const environment& env, const T& target ); + template + basic_environment get_environment( const T& target ); This type is passed to :ref:`sol::state(_view)::script/do_x` to provide an environment where local variables that are set and get retrieve. It is just a plain table, and all the same operations :doc:`from table still apply`. This is important because it allows you to do things like set the table's metatable (using :doc:`sol::metatable_key` for instance) and having its ``__index`` entry point to the global table, meaning you can get -- but not set -- variables from a Global environment. @@ -32,6 +34,15 @@ free functions See :ref:`environment::set_on`. +.. code-block:: cpp + :caption: function: get_environment + + template + basic_environment get_environment( const T& target ); + +This function retrieves the environment from the target object. If it does not have a valid environment, then the environment's valid function will return false after creation. Every function (regular Lua function, executable script, and similar) has an environment, as well as userdata in certain versions of the Lua runtime. + + members ------- @@ -39,8 +50,14 @@ members :caption: constructor: environment environment(lua_State* L, sol::new_table nt, const sol::reference& fallback); + environment(sol::env_t, const sol::reference& object_that_has_environment); + environment(sol::env_t, const sol::stack_reference& object_that_has_environment); -The ones from table are used here (of particular note is the ability to use ``sol::environment(my_lua_state, sol::create);`` to make a fresh, unnamed environment), plus the one unique constructor shown above. It is generally used as ``sol::environment my_env(my_lua_state, sol::create, my_fallback_table);``. The fallback table serves as the backup to lookup attempts on the environment table being created. It is achieved by simply creating a metatable for the ``sol::environment`` being created, and then doing ``env_metatable["__index"] = fallback;``. You can achieve fancier effects by changing the metatable of the environment to your liking, by creating it in some fashion and then setting the metatable explicitly and populating it with data, particularly with :doc:`sol::metatable_key`. +The ones from table are used here (of particular note is the ability to use ``sol::environment(my_lua_state, sol::create);`` to make a fresh, unnamed environment), plus the three unique constructors shown above. + +The first constructor is generally used as ``sol::environment my_env(my_lua_state, sol::create, my_fallback_table);``. The fallback table serves as the backup to lookup attempts on the environment table being created. It is achieved by simply creating a metatable for the ``sol::environment`` being created, and then doing ``env_metatable["__index"] = fallback;``. You can achieve fancier effects by changing the metatable of the environment to your liking, by creating it in some fashion and then setting the metatable explicitly and populating it with data, particularly with :doc:`sol::metatable_key`. + +The second and third unique constructors take a special empty type that serves as a key to trigger this constructor and serves no other purpose, ``sol::env_t``. The shortcut value so you don't have to create one is called ``sol::env_key``. It is used like ``sol::environment my_env(sol::env_key, some_object);``. It will extract the environment out of whatever the second argument is that may or may not have an environment. If it does not have an environment, the constructor will complete but the object will have ``env.valid() == false``, since it will reference Lua's ``nil``. .. code-block:: cpp diff --git a/examples/environments_on_functions.cpp b/examples/environments_on_functions.cpp index 4e9270b5..f33f5b4b 100644 --- a/examples/environments_on_functions.cpp +++ b/examples/environments_on_functions.cpp @@ -14,14 +14,15 @@ int main(int, char**) { lua.script("f = function() return test end"); sol::function f = lua["f"]; - sol::environment env(lua, sol::create); - env["test"] = 5; - sol::set_environment(env, f); + sol::environment env_f(lua, sol::create); + env_f["test"] = 31; + sol::set_environment(env_f, f); // the function returns the value from the environment table int result = f(); - assert(result == 5); + assert(result == 31); + // You can also protect from variables // being set without the 'local' specifier lua.script("g = function() test = 5 end"); @@ -40,5 +41,41 @@ int main(int, char**) { sol::object global_test = lua["test"]; assert(!global_test.valid()); + + // You can retrieve environments in C++ + // and check the environment of functions + // gotten from Lua + + // get the environment from any sol::reference-styled type, + // including sol::object, sol::function, sol::table, sol::userdata ... + lua.set_function("check_f_env", + // capture necessary variable in C++ lambda + [&env_f]( sol::object target ) { + // pull out the environment from func using + // sol::env_key constructor + sol::environment target_env(sol::env_key, target); + int test_env_f = env_f["test"]; + int test_target_env = target_env["test"]; + // the environment for f the one gotten from `target` + // are the same + assert(test_env_f == test_target_env); + assert(test_env_f == 31); + assert(env_f == target_env); + } + ); + lua.set_function("check_g_env", + [&env_g](sol::function target) { + // equivalent: + sol::environment target_env = sol::get_environment(target); + int test_env_g = env_g["test"]; + int test_target_env = target_env["test"]; + assert(test_env_g == test_target_env); + assert(test_env_g == 5); + assert(env_g == target_env); + } + ); + + lua.script("check_f_env(f)"); + lua.script("check_g_env(g)"); return 0; } diff --git a/single/sol/sol.hpp b/single/sol/sol.hpp index d114e8b1..17a1d4a9 100644 --- a/single/sol/sol.hpp +++ b/single/sol/sol.hpp @@ -20,8 +20,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // This file was generated with a script. -// Generated 2017-04-09 23:37:25.660210 UTC -// This header was generated with sol v2.17.0 (revision a6e03ac) +// Generated 2017-04-19 00:21:43.811456 UTC +// This header was generated with sol v2.17.0 (revision b02f54c) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP @@ -3098,8 +3098,11 @@ namespace sol { const nil_t nil{}; #endif - struct metatable_key_t {}; - const metatable_key_t metatable_key = {}; + struct metatable_t {}; + const metatable_t metatable_key = {}; + + struct env_t {}; + const env_t env_key = {}; struct no_metatable_t {}; const no_metatable_t no_metatable = {}; @@ -3683,6 +3686,12 @@ namespace sol { template struct lua_type_of> : std::integral_constant { }; + template <> + struct lua_type_of : std::integral_constant { }; + + template <> + struct lua_type_of : std::integral_constant { }; + template <> struct lua_type_of : std::integral_constant { }; @@ -4079,6 +4088,12 @@ namespace sol { push_popper push_pop(T&& x) { return push_popper(std::forward(x)); } + template + push_popper_at push_pop_at(T&& x) { + int c = x.push(); + lua_State* L = x.lua_state(); + return push_popper_at(L, lua_absindex(L, -c), c); + } template push_popper_n pop_n(lua_State* L, int x) { return push_popper_n(L, x); @@ -5241,6 +5256,50 @@ namespace sol { } }; + template + struct checker { + template + static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { + tracking.use(1); + if (lua_getmetatable(L, index) == 0) { + return true; + } + type t = type_of(L, -1); + if (t == type::table || t == type::none || t == type::nil) { + lua_pop(L, 1); + return true; + } + if (t != type::userdata) { + lua_pop(L, 1); + handler(L, index, type::table, t); + return false; + } + return true; + } + }; + + template + struct checker { + template + static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { + tracking.use(1); + if (lua_getmetatable(L, index) == 0) { + return true; + } + type t = type_of(L, -1); + if (t == type::table || t == type::none || t == type::nil) { + lua_pop(L, 1); + return true; + } + if (t != type::userdata) { + lua_pop(L, 1); + handler(L, index, type::table, t); + return false; + } + return true; + } + }; + template struct checker, type::userdata, C> { template @@ -5953,7 +6012,6 @@ namespace sol { return std::pair(L, index)), decltype(stack::get(L, index))>{stack::get(L, index, tracking), stack::get(L, index + tracking.used, tracking)}; } }; - } // stack } // sol @@ -6162,6 +6220,27 @@ namespace sol { namespace sol { namespace stack { + inline int push_environment_of(lua_State* L, int index = -1) { +#if SOL_LUA_VERSION < 502 + // Use lua_setfenv + lua_getfenv(L, index); + return 1; +#else + // Use upvalues as explained in Lua 5.2 and beyond's manual + if (lua_getupvalue(L, index, 1) == nullptr) { + push(L, lua_nil); + return 1; + } +#endif + return 1; + } + + template + int push_environment_of(const T& target) { + target.push(); + return push_environment_of(target.lua_state(), -1) + 1; + } + template struct pusher> { template @@ -6398,8 +6477,8 @@ namespace sol { }; template<> - struct pusher { - static int push(lua_State* L, metatable_key_t) { + struct pusher { + static int push(lua_State* L, metatable_t) { lua_pushlstring(L, "__mt", 4); return 1; } @@ -6488,7 +6567,7 @@ namespace sol { return 1; } - template , no_metatable_t, metatable_key_t>> = meta::enabler> + template , no_metatable_t, metatable_t>> = meta::enabler> static int push(lua_State* L, Arg&& arg, Args&&... args) { const auto name = &usertype_traits>::user_gc_metatable()[0]; return push_with(L, name, std::forward(arg), std::forward(args)...); @@ -6501,7 +6580,7 @@ namespace sol { } template - static int push(lua_State* L, metatable_key_t, Key&& key, Args&&... args) { + static int push(lua_State* L, metatable_t, Key&& key, Args&&... args) { const auto name = &key[0]; return push_with(L, name, std::forward(args)...); } @@ -6876,13 +6955,28 @@ namespace sol { }; template - struct field_getter { - void get(lua_State* L, metatable_key_t, int tableindex = -1) { + struct field_getter { + void get(lua_State* L, metatable_t, int tableindex = -1) { if (lua_getmetatable(L, tableindex) == 0) push(L, lua_nil); } }; + template + struct field_getter { + void get(lua_State* L, env_t, int tableindex = -1) { +#if SOL_LUA_VERSION < 502 + // Use lua_setfenv + lua_getfenv(L, tableindex); +#else + // Use upvalues as explained in Lua 5.2 and beyond's manual + if (lua_getupvalue(L, tableindex, 1) == nullptr) { + push(L, lua_nil); + } +#endif + } + }; + template struct field_getter::value>> { template @@ -6990,9 +7084,9 @@ namespace sol { }; template - struct field_setter { + struct field_setter { template - void set(lua_State* L, metatable_key_t, Value&& value, int tableindex = -2) { + void set(lua_State* L, metatable_t, Value&& value, int tableindex = -2) { push(L, std::forward(value)); lua_setmetatable(L, tableindex); } @@ -12683,6 +12777,18 @@ namespace sol { namespace sol { typedef table_core table; + + namespace stack { + template <> + struct getter { + static table get(lua_State* L, int index = -1) { + if (lua_getmetatable(L, index) == 0) { + return table(L, ref_index(LUA_REFNIL)); + } + return table(L, -1); + } + }; + } // stack } // sol // end of sol/table.hpp @@ -12701,16 +12807,25 @@ namespace sol { basic_environment(basic_environment&&) = default; basic_environment& operator=(const basic_environment&) = default; basic_environment& operator=(basic_environment&&) = default; + + basic_environment(env_t, const stack_reference& extraction_target) : table_t(extraction_target.lua_state(), (stack::push_environment_of(extraction_target), -1)) { + lua_pop(this->lua_state(), 2); + } + basic_environment(env_t, const reference& extraction_target) : table_t(extraction_target.lua_state(), (stack::push_environment_of(extraction_target), -1)) { + lua_pop(this->lua_state(), 2); + } - basic_environment(lua_State* L, sol::new_table t, const sol::reference& fallback) : table_t(L, std::move(t)) { + basic_environment(lua_State* L, new_table t, const reference& fallback) : table_t(L, std::move(t)) { sol::stack_table mt(L, sol::new_table(0, 1)); mt.set(sol::meta_function::index, fallback); this->set(metatable_key, mt); mt.pop(); } + template , basic_environment>>, meta::boolean...>::value)>, + meta::boolean>::value)>, meta::boolean>::value)> > = meta::enabler> basic_environment(T&& arg, Args&&... args) : table_t(std::forward(arg), std::forward(args)...) { } @@ -12718,20 +12833,17 @@ namespace sol { template void set_on(const T& target) const { lua_State* L = target.lua_state(); + auto pp = stack::push_pop(target); #if SOL_LUA_VERSION < 502 // Use lua_setfenv - target.push(); this->push(); lua_setfenv(L, -2); - target.pop(); #else // Use upvalues as explained in Lua 5.2 and beyond's manual - target.push(); this->push(); if (lua_setupvalue(L, -2, 1) == nullptr) { this->pop(); } - target.pop(); #endif } }; @@ -12741,6 +12853,21 @@ namespace sol { env.set_on(target); } + template + basic_environment get_environment(const T& target) { + lua_State* L = target.lua_state(); + auto pp = stack::pop_n(L, stack::push_environment_of(target)); + return basic_environment(L, -1); + } + + namespace stack { + template <> + struct getter { + static environment get(lua_State* L, int index = -1) { + return get_environment(stack_reference(L, raw_index(index))); + } + }; + } // stack } // sol // end of sol/environment.hpp diff --git a/sol/environment.hpp b/sol/environment.hpp index f4b6edca..73b261e8 100644 --- a/sol/environment.hpp +++ b/sol/environment.hpp @@ -36,16 +36,25 @@ namespace sol { basic_environment(basic_environment&&) = default; basic_environment& operator=(const basic_environment&) = default; basic_environment& operator=(basic_environment&&) = default; + + basic_environment(env_t, const stack_reference& extraction_target) : table_t(extraction_target.lua_state(), (stack::push_environment_of(extraction_target), -1)) { + lua_pop(this->lua_state(), 2); + } + basic_environment(env_t, const reference& extraction_target) : table_t(extraction_target.lua_state(), (stack::push_environment_of(extraction_target), -1)) { + lua_pop(this->lua_state(), 2); + } - basic_environment(lua_State* L, sol::new_table t, const sol::reference& fallback) : table_t(L, std::move(t)) { + basic_environment(lua_State* L, new_table t, const reference& fallback) : table_t(L, std::move(t)) { sol::stack_table mt(L, sol::new_table(0, 1)); mt.set(sol::meta_function::index, fallback); this->set(metatable_key, mt); mt.pop(); } + template , basic_environment>>, meta::boolean...>::value)>, + meta::boolean>::value)>, meta::boolean>::value)> > = meta::enabler> basic_environment(T&& arg, Args&&... args) : table_t(std::forward(arg), std::forward(args)...) { } @@ -53,20 +62,17 @@ namespace sol { template void set_on(const T& target) const { lua_State* L = target.lua_state(); + auto pp = stack::push_pop(target); #if SOL_LUA_VERSION < 502 // Use lua_setfenv - target.push(); this->push(); lua_setfenv(L, -2); - target.pop(); #else // Use upvalues as explained in Lua 5.2 and beyond's manual - target.push(); this->push(); if (lua_setupvalue(L, -2, 1) == nullptr) { this->pop(); } - target.pop(); #endif } }; @@ -76,6 +82,21 @@ namespace sol { env.set_on(target); } + template + basic_environment get_environment(const T& target) { + lua_State* L = target.lua_state(); + auto pp = stack::pop_n(L, stack::push_environment_of(target)); + return basic_environment(L, -1); + } + + namespace stack { + template <> + struct getter { + static environment get(lua_State* L, int index = -1) { + return get_environment(stack_reference(L, raw_index(index))); + } + }; + } // stack } // sol #endif // SOL_ENVIRONMENT_HPP diff --git a/sol/reference.hpp b/sol/reference.hpp index b0505902..9fbc008a 100644 --- a/sol/reference.hpp +++ b/sol/reference.hpp @@ -83,6 +83,12 @@ namespace sol { push_popper push_pop(T&& x) { return push_popper(std::forward(x)); } + template + push_popper_at push_pop_at(T&& x) { + int c = x.push(); + lua_State* L = x.lua_state(); + return push_popper_at(L, lua_absindex(L, -c), c); + } template push_popper_n pop_n(lua_State* L, int x) { return push_popper_n(L, x); diff --git a/sol/stack_check.hpp b/sol/stack_check.hpp index 7e64ad61..55c6f384 100644 --- a/sol/stack_check.hpp +++ b/sol/stack_check.hpp @@ -266,6 +266,50 @@ namespace sol { } }; + template + struct checker { + template + static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { + tracking.use(1); + if (lua_getmetatable(L, index) == 0) { + return true; + } + type t = type_of(L, -1); + if (t == type::table || t == type::none || t == type::nil) { + lua_pop(L, 1); + return true; + } + if (t != type::userdata) { + lua_pop(L, 1); + handler(L, index, type::table, t); + return false; + } + return true; + } + }; + + template + struct checker { + template + static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { + tracking.use(1); + if (lua_getmetatable(L, index) == 0) { + return true; + } + type t = type_of(L, -1); + if (t == type::table || t == type::none || t == type::nil) { + lua_pop(L, 1); + return true; + } + if (t != type::userdata) { + lua_pop(L, 1); + handler(L, index, type::table, t); + return false; + } + return true; + } + }; + template struct checker, type::userdata, C> { template diff --git a/sol/stack_field.hpp b/sol/stack_field.hpp index 48ebe992..6e247621 100644 --- a/sol/stack_field.hpp +++ b/sol/stack_field.hpp @@ -48,13 +48,28 @@ namespace sol { }; template - struct field_getter { - void get(lua_State* L, metatable_key_t, int tableindex = -1) { + struct field_getter { + void get(lua_State* L, metatable_t, int tableindex = -1) { if (lua_getmetatable(L, tableindex) == 0) push(L, lua_nil); } }; + template + struct field_getter { + void get(lua_State* L, env_t, int tableindex = -1) { +#if SOL_LUA_VERSION < 502 + // Use lua_setfenv + lua_getfenv(L, tableindex); +#else + // Use upvalues as explained in Lua 5.2 and beyond's manual + if (lua_getupvalue(L, tableindex, 1) == nullptr) { + push(L, lua_nil); + } +#endif + } + }; + template struct field_getter::value>> { template @@ -162,9 +177,9 @@ namespace sol { }; template - struct field_setter { + struct field_setter { template - void set(lua_State* L, metatable_key_t, Value&& value, int tableindex = -2) { + void set(lua_State* L, metatable_t, Value&& value, int tableindex = -2) { push(L, std::forward(value)); lua_setmetatable(L, tableindex); } diff --git a/sol/stack_get.hpp b/sol/stack_get.hpp index 6fd2f28c..89c08685 100644 --- a/sol/stack_get.hpp +++ b/sol/stack_get.hpp @@ -601,7 +601,6 @@ namespace sol { return std::pair(L, index)), decltype(stack::get(L, index))>{stack::get(L, index, tracking), stack::get(L, index + tracking.used, tracking)}; } }; - } // stack } // sol diff --git a/sol/stack_push.hpp b/sol/stack_push.hpp index 34717dd3..29cb20ac 100644 --- a/sol/stack_push.hpp +++ b/sol/stack_push.hpp @@ -33,6 +33,27 @@ namespace sol { namespace stack { + inline int push_environment_of(lua_State* L, int index = -1) { +#if SOL_LUA_VERSION < 502 + // Use lua_setfenv + lua_getfenv(L, index); + return 1; +#else + // Use upvalues as explained in Lua 5.2 and beyond's manual + if (lua_getupvalue(L, index, 1) == nullptr) { + push(L, lua_nil); + return 1; + } +#endif + return 1; + } + + template + int push_environment_of(const T& target) { + target.push(); + return push_environment_of(target.lua_state(), -1) + 1; + } + template struct pusher> { template @@ -269,8 +290,8 @@ namespace sol { }; template<> - struct pusher { - static int push(lua_State* L, metatable_key_t) { + struct pusher { + static int push(lua_State* L, metatable_t) { lua_pushlstring(L, "__mt", 4); return 1; } @@ -359,7 +380,7 @@ namespace sol { return 1; } - template , no_metatable_t, metatable_key_t>> = meta::enabler> + template , no_metatable_t, metatable_t>> = meta::enabler> static int push(lua_State* L, Arg&& arg, Args&&... args) { const auto name = &usertype_traits>::user_gc_metatable()[0]; return push_with(L, name, std::forward(arg), std::forward(args)...); @@ -372,7 +393,7 @@ namespace sol { } template - static int push(lua_State* L, metatable_key_t, Key&& key, Args&&... args) { + static int push(lua_State* L, metatable_t, Key&& key, Args&&... args) { const auto name = &key[0]; return push_with(L, name, std::forward(args)...); } diff --git a/sol/table.hpp b/sol/table.hpp index c5b358c5..b4e47b8b 100644 --- a/sol/table.hpp +++ b/sol/table.hpp @@ -26,6 +26,18 @@ namespace sol { typedef table_core table; + + namespace stack { + template <> + struct getter { + static table get(lua_State* L, int index = -1) { + if (lua_getmetatable(L, index) == 0) { + return table(L, ref_index(LUA_REFNIL)); + } + return table(L, -1); + } + }; + } // stack } // sol #endif // SOL_TABLE_HPP diff --git a/sol/types.hpp b/sol/types.hpp index a44751bd..8cc61171 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -119,8 +119,11 @@ namespace sol { const nil_t nil{}; #endif - struct metatable_key_t {}; - const metatable_key_t metatable_key = {}; + struct metatable_t {}; + const metatable_t metatable_key = {}; + + struct env_t {}; + const env_t env_key = {}; struct no_metatable_t {}; const no_metatable_t no_metatable = {}; @@ -704,6 +707,12 @@ namespace sol { template struct lua_type_of> : std::integral_constant { }; + template <> + struct lua_type_of : std::integral_constant { }; + + template <> + struct lua_type_of : std::integral_constant { }; + template <> struct lua_type_of : std::integral_constant { }; diff --git a/test_environments.cpp b/test_environments.cpp index 1328ba8c..91b824e3 100644 --- a/test_environments.cpp +++ b/test_environments.cpp @@ -5,6 +5,65 @@ #include #include "test_stack_guard.hpp" + +TEST_CASE("environments/get", "Envronments can be taken out of things like Lua functions properly") { + sol::state lua; + sol::stack_guard luasg(lua); + lua.open_libraries(sol::lib::base); + + lua.script("f = function() return test end"); + sol::function f = lua["f"]; + + sol::environment env_f(lua, sol::create); + env_f["test"] = 31; + sol::set_environment(env_f, f); + + int result = f(); + REQUIRE(result == 31); + + lua.script("g = function() test = 5 end"); + sol::function g = lua["g"]; + sol::environment env_g(lua, sol::create); + env_g.set_on(g); + + g(); + + int test = env_g["test"]; + REQUIRE(test == 5); + + + sol::object global_test = lua["test"]; + REQUIRE(!global_test.valid()); + + lua.set_function("check_f_env", + [&lua, &env_f](sol::object target) { + sol::stack_guard sg(lua); + sol::environment target_env(sol::env_key, target); + int test_env_f = env_f["test"]; + int test_target_env = target_env["test"]; + REQUIRE(test_env_f == test_target_env); + REQUIRE(test_env_f == 31); + REQUIRE(env_f == target_env); + } + ); + lua.set_function("check_g_env", + [&lua, &env_g](sol::function target) { + sol::stack_guard sg(lua); + sol::environment target_env = sol::get_environment(target); + int test_env_g = env_g["test"]; + int test_target_env = target_env["test"]; + REQUIRE(test_env_g == test_target_env); + REQUIRE(test_env_g == 5); + REQUIRE(env_g == target_env); + } + ); + + REQUIRE_NOTHROW([&lua]() { + lua.script("check_f_env(f)"); + lua.script("check_g_env(g)"); + }()); +} + TEST_CASE("environments/shadowing", "Environments can properly shadow and fallback on variables") { sol::state lua;