get_environment, documentation updates, and tests

This commit is contained in:
ThePhD 2017-04-18 20:23:20 -04:00
parent b02f54c592
commit 2acc8bee14
12 changed files with 406 additions and 39 deletions

View File

@ -10,6 +10,8 @@ encapsulation table for script sandboxing
template <typename T> template <typename T>
void set_environment( const environment& env, const T& target ); void set_environment( const environment& env, const T& target );
template <typename E = reference, typename T>
basic_environment<E> get_environment( const T& target );
This type is passed to :ref:`sol::state(_view)::script/do_x<state-script-function>` 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<table>`. This is important because it allows you to do things like set the table's metatable (using :doc:`sol::metatable_key<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. This type is passed to :ref:`sol::state(_view)::script/do_x<state-script-function>` 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<table>`. This is important because it allows you to do things like set the table's metatable (using :doc:`sol::metatable_key<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<environment-set-on>`. See :ref:`environment::set_on<environment-set-on>`.
.. code-block:: cpp
:caption: function: get_environment
template <typename E = reference, typename T>
basic_environment<E> 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 members
------- -------
@ -39,8 +50,14 @@ members
:caption: constructor: environment :caption: constructor: environment
environment(lua_State* L, sol::new_table nt, const sol::reference& fallback); 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<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<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 .. code-block:: cpp

View File

@ -14,14 +14,15 @@ int main(int, char**) {
lua.script("f = function() return test end"); lua.script("f = function() return test end");
sol::function f = lua["f"]; sol::function f = lua["f"];
sol::environment env(lua, sol::create); sol::environment env_f(lua, sol::create);
env["test"] = 5; env_f["test"] = 31;
sol::set_environment(env, f); sol::set_environment(env_f, f);
// the function returns the value from the environment table // the function returns the value from the environment table
int result = f(); int result = f();
assert(result == 5); assert(result == 31);
// You can also protect from variables // You can also protect from variables
// being set without the 'local' specifier // being set without the 'local' specifier
lua.script("g = function() test = 5 end"); lua.script("g = function() test = 5 end");
@ -40,5 +41,41 @@ int main(int, char**) {
sol::object global_test = lua["test"]; sol::object global_test = lua["test"];
assert(!global_test.valid()); 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; return 0;
} }

View File

@ -20,8 +20,8 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// This file was generated with a script. // This file was generated with a script.
// Generated 2017-04-09 23:37:25.660210 UTC // Generated 2017-04-19 00:21:43.811456 UTC
// This header was generated with sol v2.17.0 (revision a6e03ac) // This header was generated with sol v2.17.0 (revision b02f54c)
// https://github.com/ThePhD/sol2 // https://github.com/ThePhD/sol2
#ifndef SOL_SINGLE_INCLUDE_HPP #ifndef SOL_SINGLE_INCLUDE_HPP
@ -3098,8 +3098,11 @@ namespace sol {
const nil_t nil{}; const nil_t nil{};
#endif #endif
struct metatable_key_t {}; struct metatable_t {};
const metatable_key_t metatable_key = {}; const metatable_t metatable_key = {};
struct env_t {};
const env_t env_key = {};
struct no_metatable_t {}; struct no_metatable_t {};
const no_metatable_t no_metatable = {}; const no_metatable_t no_metatable = {};
@ -3683,6 +3686,12 @@ namespace sol {
template <typename B> template <typename B>
struct lua_type_of<basic_environment<B>> : std::integral_constant<type, type::table> { }; struct lua_type_of<basic_environment<B>> : std::integral_constant<type, type::table> { };
template <>
struct lua_type_of<metatable_t> : std::integral_constant<type, type::table> { };
template <>
struct lua_type_of<env_t> : std::integral_constant<type, type::table> { };
template <> template <>
struct lua_type_of<new_table> : std::integral_constant<type, type::table> { }; struct lua_type_of<new_table> : std::integral_constant<type, type::table> { };
@ -4079,6 +4088,12 @@ namespace sol {
push_popper<top_level, T> push_pop(T&& x) { push_popper<top_level, T> push_pop(T&& x) {
return push_popper<top_level, T>(std::forward<T>(x)); return push_popper<top_level, T>(std::forward<T>(x));
} }
template <typename T>
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 <bool top_level = false> template <bool top_level = false>
push_popper_n<top_level> pop_n(lua_State* L, int x) { push_popper_n<top_level> pop_n(lua_State* L, int x) {
return push_popper_n<top_level>(L, x); return push_popper_n<top_level>(L, x);
@ -5241,6 +5256,50 @@ namespace sol {
} }
}; };
template <type expected, typename C>
struct checker<metatable_t, expected, C> {
template <typename Handler>
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 <type expected, typename C>
struct checker<env_t, expected, C> {
template <typename Handler>
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 <typename T, typename C> template <typename T, typename C>
struct checker<detail::as_value_tag<T>, type::userdata, C> { struct checker<detail::as_value_tag<T>, type::userdata, C> {
template <typename U, typename Handler> template <typename U, typename Handler>
@ -5953,7 +6012,6 @@ namespace sol {
return std::pair<decltype(stack::get<A>(L, index)), decltype(stack::get<B>(L, index))>{stack::get<A>(L, index, tracking), stack::get<B>(L, index + tracking.used, tracking)}; return std::pair<decltype(stack::get<A>(L, index)), decltype(stack::get<B>(L, index))>{stack::get<A>(L, index, tracking), stack::get<B>(L, index + tracking.used, tracking)};
} }
}; };
} // stack } // stack
} // sol } // sol
@ -6162,6 +6220,27 @@ namespace sol {
namespace sol { namespace sol {
namespace stack { 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 <typename T>
int push_environment_of(const T& target) {
target.push();
return push_environment_of(target.lua_state(), -1) + 1;
}
template <typename T> template <typename T>
struct pusher<detail::as_value_tag<T>> { struct pusher<detail::as_value_tag<T>> {
template <typename F, typename... Args> template <typename F, typename... Args>
@ -6398,8 +6477,8 @@ namespace sol {
}; };
template<> template<>
struct pusher<metatable_key_t> { struct pusher<metatable_t> {
static int push(lua_State* L, metatable_key_t) { static int push(lua_State* L, metatable_t) {
lua_pushlstring(L, "__mt", 4); lua_pushlstring(L, "__mt", 4);
return 1; return 1;
} }
@ -6488,7 +6567,7 @@ namespace sol {
return 1; return 1;
} }
template <typename Arg, typename... Args, meta::disable<meta::any_same<meta::unqualified_t<Arg>, no_metatable_t, metatable_key_t>> = meta::enabler> template <typename Arg, typename... Args, meta::disable<meta::any_same<meta::unqualified_t<Arg>, no_metatable_t, metatable_t>> = meta::enabler>
static int push(lua_State* L, Arg&& arg, Args&&... args) { static int push(lua_State* L, Arg&& arg, Args&&... args) {
const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0]; const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0];
return push_with(L, name, std::forward<Arg>(arg), std::forward<Args>(args)...); return push_with(L, name, std::forward<Arg>(arg), std::forward<Args>(args)...);
@ -6501,7 +6580,7 @@ namespace sol {
} }
template <typename Key, typename... Args> template <typename Key, typename... Args>
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]; const auto name = &key[0];
return push_with<true>(L, name, std::forward<Args>(args)...); return push_with<true>(L, name, std::forward<Args>(args)...);
} }
@ -6876,13 +6955,28 @@ namespace sol {
}; };
template <bool b, bool raw, typename C> template <bool b, bool raw, typename C>
struct field_getter<metatable_key_t, b, raw, C> { struct field_getter<metatable_t, b, raw, C> {
void get(lua_State* L, metatable_key_t, int tableindex = -1) { void get(lua_State* L, metatable_t, int tableindex = -1) {
if (lua_getmetatable(L, tableindex) == 0) if (lua_getmetatable(L, tableindex) == 0)
push(L, lua_nil); push(L, lua_nil);
} }
}; };
template <bool b, bool raw, typename C>
struct field_getter<env_t, b, raw, C> {
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 <typename T, bool raw> template <typename T, bool raw>
struct field_getter<T, true, raw, std::enable_if_t<meta::is_c_str<T>::value>> { struct field_getter<T, true, raw, std::enable_if_t<meta::is_c_str<T>::value>> {
template <typename Key> template <typename Key>
@ -6990,9 +7084,9 @@ namespace sol {
}; };
template <bool b, bool raw, typename C> template <bool b, bool raw, typename C>
struct field_setter<metatable_key_t, b, raw, C> { struct field_setter<metatable_t, b, raw, C> {
template <typename Value> template <typename Value>
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>(value)); push(L, std::forward<Value>(value));
lua_setmetatable(L, tableindex); lua_setmetatable(L, tableindex);
} }
@ -12683,6 +12777,18 @@ namespace sol {
namespace sol { namespace sol {
typedef table_core<false> table; typedef table_core<false> table;
namespace stack {
template <>
struct getter<metatable_t> {
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 } // sol
// end of sol/table.hpp // end of sol/table.hpp
@ -12701,16 +12807,25 @@ namespace sol {
basic_environment(basic_environment&&) = default; basic_environment(basic_environment&&) = default;
basic_environment& operator=(const basic_environment&) = default; basic_environment& operator=(const basic_environment&) = default;
basic_environment& operator=(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)); sol::stack_table mt(L, sol::new_table(0, 1));
mt.set(sol::meta_function::index, fallback); mt.set(sol::meta_function::index, fallback);
this->set(metatable_key, mt); this->set(metatable_key, mt);
mt.pop(); mt.pop();
} }
template <typename T, typename... Args, meta::enable< template <typename T, typename... Args, meta::enable<
meta::neg<std::is_same<meta::unqualified_t<T>, basic_environment>>, meta::neg<std::is_same<meta::unqualified_t<T>, basic_environment>>,
meta::boolean<!(sizeof...(Args) == 2 && meta::any_same<new_table, meta::unqualified_t<Args>...>::value)>, meta::boolean<!(sizeof...(Args) == 2 && meta::any_same<new_table, meta::unqualified_t<Args>...>::value)>,
meta::boolean<!(sizeof...(Args) == 1 && std::is_same<env_t, meta::unqualified_t<T>>::value)>,
meta::boolean<!(sizeof...(Args) == 0 && std::is_base_of<proxy_base_tag, meta::unqualified_t<T>>::value)> meta::boolean<!(sizeof...(Args) == 0 && std::is_base_of<proxy_base_tag, meta::unqualified_t<T>>::value)>
> = meta::enabler> > = meta::enabler>
basic_environment(T&& arg, Args&&... args) : table_t(std::forward<T>(arg), std::forward<Args>(args)...) { } basic_environment(T&& arg, Args&&... args) : table_t(std::forward<T>(arg), std::forward<Args>(args)...) { }
@ -12718,20 +12833,17 @@ namespace sol {
template <typename T> template <typename T>
void set_on(const T& target) const { void set_on(const T& target) const {
lua_State* L = target.lua_state(); lua_State* L = target.lua_state();
auto pp = stack::push_pop(target);
#if SOL_LUA_VERSION < 502 #if SOL_LUA_VERSION < 502
// Use lua_setfenv // Use lua_setfenv
target.push();
this->push(); this->push();
lua_setfenv(L, -2); lua_setfenv(L, -2);
target.pop();
#else #else
// Use upvalues as explained in Lua 5.2 and beyond's manual // Use upvalues as explained in Lua 5.2 and beyond's manual
target.push();
this->push(); this->push();
if (lua_setupvalue(L, -2, 1) == nullptr) { if (lua_setupvalue(L, -2, 1) == nullptr) {
this->pop(); this->pop();
} }
target.pop();
#endif #endif
} }
}; };
@ -12741,6 +12853,21 @@ namespace sol {
env.set_on(target); env.set_on(target);
} }
template <typename E = reference, typename T>
basic_environment<E> 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<E>(L, -1);
}
namespace stack {
template <>
struct getter<env_t> {
static environment get(lua_State* L, int index = -1) {
return get_environment(stack_reference(L, raw_index(index)));
}
};
} // stack
} // sol } // sol
// end of sol/environment.hpp // end of sol/environment.hpp

View File

@ -36,16 +36,25 @@ namespace sol {
basic_environment(basic_environment&&) = default; basic_environment(basic_environment&&) = default;
basic_environment& operator=(const basic_environment&) = default; basic_environment& operator=(const basic_environment&) = default;
basic_environment& operator=(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)); sol::stack_table mt(L, sol::new_table(0, 1));
mt.set(sol::meta_function::index, fallback); mt.set(sol::meta_function::index, fallback);
this->set(metatable_key, mt); this->set(metatable_key, mt);
mt.pop(); mt.pop();
} }
template <typename T, typename... Args, meta::enable< template <typename T, typename... Args, meta::enable<
meta::neg<std::is_same<meta::unqualified_t<T>, basic_environment>>, meta::neg<std::is_same<meta::unqualified_t<T>, basic_environment>>,
meta::boolean<!(sizeof...(Args) == 2 && meta::any_same<new_table, meta::unqualified_t<Args>...>::value)>, meta::boolean<!(sizeof...(Args) == 2 && meta::any_same<new_table, meta::unqualified_t<Args>...>::value)>,
meta::boolean<!(sizeof...(Args) == 1 && std::is_same<env_t, meta::unqualified_t<T>>::value)>,
meta::boolean<!(sizeof...(Args) == 0 && std::is_base_of<proxy_base_tag, meta::unqualified_t<T>>::value)> meta::boolean<!(sizeof...(Args) == 0 && std::is_base_of<proxy_base_tag, meta::unqualified_t<T>>::value)>
> = meta::enabler> > = meta::enabler>
basic_environment(T&& arg, Args&&... args) : table_t(std::forward<T>(arg), std::forward<Args>(args)...) { } basic_environment(T&& arg, Args&&... args) : table_t(std::forward<T>(arg), std::forward<Args>(args)...) { }
@ -53,20 +62,17 @@ namespace sol {
template <typename T> template <typename T>
void set_on(const T& target) const { void set_on(const T& target) const {
lua_State* L = target.lua_state(); lua_State* L = target.lua_state();
auto pp = stack::push_pop(target);
#if SOL_LUA_VERSION < 502 #if SOL_LUA_VERSION < 502
// Use lua_setfenv // Use lua_setfenv
target.push();
this->push(); this->push();
lua_setfenv(L, -2); lua_setfenv(L, -2);
target.pop();
#else #else
// Use upvalues as explained in Lua 5.2 and beyond's manual // Use upvalues as explained in Lua 5.2 and beyond's manual
target.push();
this->push(); this->push();
if (lua_setupvalue(L, -2, 1) == nullptr) { if (lua_setupvalue(L, -2, 1) == nullptr) {
this->pop(); this->pop();
} }
target.pop();
#endif #endif
} }
}; };
@ -76,6 +82,21 @@ namespace sol {
env.set_on(target); env.set_on(target);
} }
template <typename E = reference, typename T>
basic_environment<E> 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<E>(L, -1);
}
namespace stack {
template <>
struct getter<env_t> {
static environment get(lua_State* L, int index = -1) {
return get_environment(stack_reference(L, raw_index(index)));
}
};
} // stack
} // sol } // sol
#endif // SOL_ENVIRONMENT_HPP #endif // SOL_ENVIRONMENT_HPP

View File

@ -83,6 +83,12 @@ namespace sol {
push_popper<top_level, T> push_pop(T&& x) { push_popper<top_level, T> push_pop(T&& x) {
return push_popper<top_level, T>(std::forward<T>(x)); return push_popper<top_level, T>(std::forward<T>(x));
} }
template <typename T>
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 <bool top_level = false> template <bool top_level = false>
push_popper_n<top_level> pop_n(lua_State* L, int x) { push_popper_n<top_level> pop_n(lua_State* L, int x) {
return push_popper_n<top_level>(L, x); return push_popper_n<top_level>(L, x);

View File

@ -266,6 +266,50 @@ namespace sol {
} }
}; };
template <type expected, typename C>
struct checker<metatable_t, expected, C> {
template <typename Handler>
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 <type expected, typename C>
struct checker<env_t, expected, C> {
template <typename Handler>
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 <typename T, typename C> template <typename T, typename C>
struct checker<detail::as_value_tag<T>, type::userdata, C> { struct checker<detail::as_value_tag<T>, type::userdata, C> {
template <typename U, typename Handler> template <typename U, typename Handler>

View File

@ -48,13 +48,28 @@ namespace sol {
}; };
template <bool b, bool raw, typename C> template <bool b, bool raw, typename C>
struct field_getter<metatable_key_t, b, raw, C> { struct field_getter<metatable_t, b, raw, C> {
void get(lua_State* L, metatable_key_t, int tableindex = -1) { void get(lua_State* L, metatable_t, int tableindex = -1) {
if (lua_getmetatable(L, tableindex) == 0) if (lua_getmetatable(L, tableindex) == 0)
push(L, lua_nil); push(L, lua_nil);
} }
}; };
template <bool b, bool raw, typename C>
struct field_getter<env_t, b, raw, C> {
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 <typename T, bool raw> template <typename T, bool raw>
struct field_getter<T, true, raw, std::enable_if_t<meta::is_c_str<T>::value>> { struct field_getter<T, true, raw, std::enable_if_t<meta::is_c_str<T>::value>> {
template <typename Key> template <typename Key>
@ -162,9 +177,9 @@ namespace sol {
}; };
template <bool b, bool raw, typename C> template <bool b, bool raw, typename C>
struct field_setter<metatable_key_t, b, raw, C> { struct field_setter<metatable_t, b, raw, C> {
template <typename Value> template <typename Value>
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>(value)); push(L, std::forward<Value>(value));
lua_setmetatable(L, tableindex); lua_setmetatable(L, tableindex);
} }

View File

@ -601,7 +601,6 @@ namespace sol {
return std::pair<decltype(stack::get<A>(L, index)), decltype(stack::get<B>(L, index))>{stack::get<A>(L, index, tracking), stack::get<B>(L, index + tracking.used, tracking)}; return std::pair<decltype(stack::get<A>(L, index)), decltype(stack::get<B>(L, index))>{stack::get<A>(L, index, tracking), stack::get<B>(L, index + tracking.used, tracking)};
} }
}; };
} // stack } // stack
} // sol } // sol

View File

@ -33,6 +33,27 @@
namespace sol { namespace sol {
namespace stack { 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 <typename T>
int push_environment_of(const T& target) {
target.push();
return push_environment_of(target.lua_state(), -1) + 1;
}
template <typename T> template <typename T>
struct pusher<detail::as_value_tag<T>> { struct pusher<detail::as_value_tag<T>> {
template <typename F, typename... Args> template <typename F, typename... Args>
@ -269,8 +290,8 @@ namespace sol {
}; };
template<> template<>
struct pusher<metatable_key_t> { struct pusher<metatable_t> {
static int push(lua_State* L, metatable_key_t) { static int push(lua_State* L, metatable_t) {
lua_pushlstring(L, "__mt", 4); lua_pushlstring(L, "__mt", 4);
return 1; return 1;
} }
@ -359,7 +380,7 @@ namespace sol {
return 1; return 1;
} }
template <typename Arg, typename... Args, meta::disable<meta::any_same<meta::unqualified_t<Arg>, no_metatable_t, metatable_key_t>> = meta::enabler> template <typename Arg, typename... Args, meta::disable<meta::any_same<meta::unqualified_t<Arg>, no_metatable_t, metatable_t>> = meta::enabler>
static int push(lua_State* L, Arg&& arg, Args&&... args) { static int push(lua_State* L, Arg&& arg, Args&&... args) {
const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0]; const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0];
return push_with(L, name, std::forward<Arg>(arg), std::forward<Args>(args)...); return push_with(L, name, std::forward<Arg>(arg), std::forward<Args>(args)...);
@ -372,7 +393,7 @@ namespace sol {
} }
template <typename Key, typename... Args> template <typename Key, typename... Args>
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]; const auto name = &key[0];
return push_with<true>(L, name, std::forward<Args>(args)...); return push_with<true>(L, name, std::forward<Args>(args)...);
} }

View File

@ -26,6 +26,18 @@
namespace sol { namespace sol {
typedef table_core<false> table; typedef table_core<false> table;
namespace stack {
template <>
struct getter<metatable_t> {
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 } // sol
#endif // SOL_TABLE_HPP #endif // SOL_TABLE_HPP

View File

@ -119,8 +119,11 @@ namespace sol {
const nil_t nil{}; const nil_t nil{};
#endif #endif
struct metatable_key_t {}; struct metatable_t {};
const metatable_key_t metatable_key = {}; const metatable_t metatable_key = {};
struct env_t {};
const env_t env_key = {};
struct no_metatable_t {}; struct no_metatable_t {};
const no_metatable_t no_metatable = {}; const no_metatable_t no_metatable = {};
@ -704,6 +707,12 @@ namespace sol {
template <typename B> template <typename B>
struct lua_type_of<basic_environment<B>> : std::integral_constant<type, type::table> { }; struct lua_type_of<basic_environment<B>> : std::integral_constant<type, type::table> { };
template <>
struct lua_type_of<metatable_t> : std::integral_constant<type, type::table> { };
template <>
struct lua_type_of<env_t> : std::integral_constant<type, type::table> { };
template <> template <>
struct lua_type_of<new_table> : std::integral_constant<type, type::table> { }; struct lua_type_of<new_table> : std::integral_constant<type, type::table> { };

View File

@ -5,6 +5,65 @@
#include <iostream> #include <iostream>
#include "test_stack_guard.hpp" #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") { TEST_CASE("environments/shadowing", "Environments can properly shadow and fallback on variables") {
sol::state lua; sol::state lua;