diff --git a/.gitignore b/.gitignore index 4fdc74bc..44284ee2 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,6 @@ main.ilk main.pdb lua-5.3.4-cxx/ lua-5.3.4/ +temp.bad_runtime.lua +temp.bad_syntax.lua +temp.good.lua diff --git a/docs/source/api/api-top.rst b/docs/source/api/api-top.rst index 407f292e..938acce0 100644 --- a/docs/source/api/api-top.rst +++ b/docs/source/api/api-top.rst @@ -10,7 +10,12 @@ Browse the various function and classes :doc:`Sol<../index>` utilizes to make yo :maxdepth: 2 state + reference + stack_reference + make_reference table + userdata + environment proxy containers nested @@ -25,11 +30,7 @@ Browse the various function and classes :doc:`Sol<../index>` utilizes to make yo coroutine error object - userdata - reference thread - stack_reference - make_reference optional this_state variadic_args diff --git a/docs/source/api/environment.rst b/docs/source/api/environment.rst new file mode 100644 index 00000000..110cc1dd --- /dev/null +++ b/docs/source/api/environment.rst @@ -0,0 +1,28 @@ +environment +=========== +encapsulation table for script sandboxing +----------------------------------------- + +.. code-block:: cpp + :caption: environment + + class environment : public table; + + void set_environment( const reference& target, const environment& env); + + +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. + +Please see `an example demonstrating the above and more`_ for further information. + +members +------- + +.. code-block:: cpp + :caption: constructor: environment + + environment(lua_State* L, sol::new_table nt, const sol::reference& fallback); + +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::environmeny 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`. + +.. an example demonstrating the above and more: diff --git a/docs/source/api/table.rst b/docs/source/api/table.rst index 25197b12..148fd290 100644 --- a/docs/source/api/table.rst +++ b/docs/source/api/table.rst @@ -25,8 +25,9 @@ members :caption: constructor: table table(lua_State* L, int index = -1); + table(lua_State* L, sol::new_table nt) -Takes a table from the Lua stack at the specified index and allows a person to use all of the abstractions therein. +The first takes a table from the Lua stack at the specified index and allows a person to use all of the abstractions therein. The second creates a new table using the capacity hints specified in ``sol::new_table``'s structure (``sequence_hint`` and ``map_hint``). If you don't care exactly about the capacity, create a table using ``sol::table my_table(my_lua_state, sol::create);``. .. code-block:: cpp :caption: function: get / traversing get diff --git a/docs/source/api/userdata.rst b/docs/source/api/userdata.rst index 6bc64791..ebf220d7 100644 --- a/docs/source/api/userdata.rst +++ b/docs/source/api/userdata.rst @@ -6,8 +6,8 @@ reference to a userdata .. code-block:: cpp :caption: (light\_)userdata reference - class userdata : public reference; + class userdata : public table; - class light_userdata : public reference; + class light_userdata : public table; -These type is meant to hold a reference to a (light) userdata from Lua and make it easy to push an existing userdata onto the stack. It is essentially identical to :doc:`reference` in every way, just with a definitive C++ type. +These types are meant to hold a reference to a (light) userdata from Lua and make it easy to push an existing userdata onto the stack. It is essentially identical to :doc:`table
` in every way, just with a definitive C++ type that ensures the type is some form of userdata (helpful for trapping type errors with :doc:`safety features turned on<../safety>`). You can also use its ``.is()`` and ``.as()`` methods to check if its of a specific type and retrieve that type, respectively. diff --git a/docs/source/features.rst b/docs/source/features.rst index c3370343..4c2324e4 100644 --- a/docs/source/features.rst +++ b/docs/source/features.rst @@ -87,6 +87,7 @@ Explanations for a few categories are below (rest are self-explanatory). * overloading: the ability to call overloaded functions, matched based on arity or type (``foo( 1 )`` from lua calls a different function then ``foo( "bark" )``). * Lua thread: basic wrapping of the lua thread API; ties in with coroutine. * coroutines: allowing a function to be called multiple times, resuming the execution of a Lua coroutine each time +* environments: an abstraction for getting, setting and manipulating an environment, using table techniques, functions or otherwise. Typically for the purposes of sandboxing +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ | | plain C | luawrapper | lua-intf | luabind | Selene | Sol2 | oolua | lua-api-pp | kaguya | SLB3 | SWIG | luacppinterface | luwra | @@ -124,6 +125,8 @@ Explanations for a few categories are below (rest are self-explanatory). +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ | Lua thread | ~ | ✗ | ~ | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | ✗ | ✗ | ✔ | ✗ | +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ +| environments | ✗ | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ++---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ | coroutines | ~ | ✗ | ~ | ✔ | ✔ | ✔ | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ | ✗ | +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ | no-rtti support | ✔ | ✗ | ✔ | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | ✔ | ~ | ✔ | ✔ | diff --git a/examples/any_return.cpp b/examples/any_return.cpp index f286071b..f433a0e1 100644 --- a/examples/any_return.cpp +++ b/examples/any_return.cpp @@ -1,3 +1,4 @@ +#define SOL_CHECK_ARGUMENTS #include #include diff --git a/examples/basic.cpp b/examples/basic.cpp index e9971a65..ab86abf7 100644 --- a/examples/basic.cpp +++ b/examples/basic.cpp @@ -1,3 +1,4 @@ +#define SOL_CHECK_ARGUMENTS #include #include @@ -57,4 +58,6 @@ int main() { } std::cout << std::endl; + + return 0; } \ No newline at end of file diff --git a/examples/config.cpp b/examples/config.cpp index be55bab3..9df5f5ff 100644 --- a/examples/config.cpp +++ b/examples/config.cpp @@ -1,4 +1,6 @@ +#define SOL_CHECK_ARGUMENTS #include + #include #include diff --git a/examples/coroutine.cpp b/examples/coroutine.cpp index 75b4f817..533a2a01 100644 --- a/examples/coroutine.cpp +++ b/examples/coroutine.cpp @@ -1,6 +1,7 @@ -#include +#define SOL_CHECK_ARGUMENTS #include "sol.hpp" +#include #include int main() { diff --git a/examples/customization.cpp b/examples/customization.cpp index f9228951..189708eb 100644 --- a/examples/customization.cpp +++ b/examples/customization.cpp @@ -1,3 +1,4 @@ +#define SOL_CHECK_ARGUMENTS #include #include @@ -34,8 +35,8 @@ namespace sol { // its absolute position using the lua_absindex function int absolute_index = lua_absindex(L, index); // Check first and second second index for being the proper types - bool success = stack::check(L, absolute_index + 1, handler) - && stack::check(L, absolute_index, handler); + bool success = stack::check(L, absolute_index, handler) + && stack::check(L, absolute_index + 1, handler); tracking.use(2); return success; } diff --git a/examples/environments.cpp b/examples/environments.cpp new file mode 100644 index 00000000..0161c231 --- /dev/null +++ b/examples/environments.cpp @@ -0,0 +1,53 @@ +#define SOL_CHECK_ARGUMENTS +#include + +#include +#include +#include +#include + +void test_environment(std::string key, const sol::environment& env, const sol::state_view& lua) { + sol::optional maybe_env_a = env[key]; + sol::optional maybe_global_a = lua[key]; + if (maybe_global_a) { + std::cout << "\t'" << key << "' is " << maybe_global_a.value() << " in the global environment" << std::endl; + } + else { + std::cout << "\t'" << key << "' does not exist in the global environment" << std::endl; + } + if (maybe_env_a) { + std::cout << "\t'" << key << "' is " << maybe_env_a.value() << " in target environment" << std::endl; + } + else { + std::cout << "\t'" << key << "' does not exist in target environment" << std::endl; + } +} + +int main(int, char**) { + std::cout << "=== environments example ===" << std::endl; + + sol::state lua; + // A global variable to see if we can "fallback" into it + lua["b"] = 2142; + + // Create a new environment + sol::environment plain_env(lua, sol::create); + // Use it + lua.script("a = 24", plain_env); + std::cout << "-- target: plain_env" << std::endl; + test_environment("a", plain_env, lua); + test_environment("b", plain_env, lua); + std::cout << std::endl; + + // Create an environment with a fallback + sol::environment env_with_fallback(lua, sol::create, lua.globals()); + // Use it + lua.script("a = 56", env_with_fallback); + std::cout << "-- target: env_with_fallback (fallback is global table)" << std::endl; + test_environment("a", env_with_fallback, lua); + test_environment("b", env_with_fallback, lua); + std::cout << std::endl; + + + return 0; +} diff --git a/examples/functions.cpp b/examples/functions.cpp index f91ae5a5..8d16d8b0 100644 --- a/examples/functions.cpp +++ b/examples/functions.cpp @@ -1,4 +1,6 @@ +#define SOL_CHECK_ARGUMENTS #include + #include #include diff --git a/examples/namespacing.cpp b/examples/namespacing.cpp index 8d32744e..d968df97 100644 --- a/examples/namespacing.cpp +++ b/examples/namespacing.cpp @@ -1,5 +1,6 @@ #define SOL_CHECK_ARGUMENTS #include + #include int main() { diff --git a/examples/optional_with_iteration.cpp b/examples/optional_with_iteration.cpp index b4e571c8..2c6fc335 100644 --- a/examples/optional_with_iteration.cpp +++ b/examples/optional_with_iteration.cpp @@ -1,6 +1,6 @@ -#define SOL_CHECK_ARGUMENTS 1 - +#define SOL_CHECK_ARGUMENTS #include + #include #include #include diff --git a/examples/protected_functions.cpp b/examples/protected_functions.cpp index 340f0134..1e7aa2dc 100644 --- a/examples/protected_functions.cpp +++ b/examples/protected_functions.cpp @@ -1,3 +1,4 @@ +#define SOL_CHECK_ARGUMENTS #include #include diff --git a/examples/require.cpp b/examples/require.cpp index a94765b9..3ccc0405 100644 --- a/examples/require.cpp +++ b/examples/require.cpp @@ -1,7 +1,7 @@ #define SOL_CHECK_ARGUMENTS #include -#include +#include #include struct some_class { diff --git a/examples/self_call.cpp b/examples/self_call.cpp index c64ded34..5f727c5a 100644 --- a/examples/self_call.cpp +++ b/examples/self_call.cpp @@ -1,5 +1,6 @@ #define SOL_CHECK_ARGUMENTS #include + #include #include diff --git a/examples/static_variables.cpp b/examples/static_variables.cpp index c101e824..d27f7d1b 100644 --- a/examples/static_variables.cpp +++ b/examples/static_variables.cpp @@ -1,3 +1,4 @@ +#define SOL_CHECK_ARGUMENTS #include #include diff --git a/examples/tables.cpp b/examples/tables.cpp index 41798d3b..4b8d8abe 100644 --- a/examples/tables.cpp +++ b/examples/tables.cpp @@ -1,4 +1,6 @@ +#define SOL_CHECK_ARGUMENTS #include + #include #include diff --git a/examples/usertype.cpp b/examples/usertype.cpp index bdb07015..5b2e934c 100644 --- a/examples/usertype.cpp +++ b/examples/usertype.cpp @@ -1,4 +1,6 @@ +#define SOL_CHECK_ARGUMENTS #include + #include #include #include diff --git a/examples/usertype_advanced.cpp b/examples/usertype_advanced.cpp index cd6de9b5..2307016a 100644 --- a/examples/usertype_advanced.cpp +++ b/examples/usertype_advanced.cpp @@ -1,3 +1,4 @@ +#define SOL_CHECK_ARGUMENTS #include #include diff --git a/examples/usertype_initializers.cpp b/examples/usertype_initializers.cpp index 10f1f68c..c0828b07 100644 --- a/examples/usertype_initializers.cpp +++ b/examples/usertype_initializers.cpp @@ -1,5 +1,6 @@ #define SOL_CHECK_ARGUMENTS #include + #include #include #include diff --git a/examples/usertype_simple.cpp b/examples/usertype_simple.cpp index bfb15b30..f851f0e5 100644 --- a/examples/usertype_simple.cpp +++ b/examples/usertype_simple.cpp @@ -1,5 +1,6 @@ #define SOL_CHECK_ARGUMENTS #include + #include #include #include diff --git a/examples/usertype_special_functions.cpp b/examples/usertype_special_functions.cpp index 44ff93e0..0166d4df 100644 --- a/examples/usertype_special_functions.cpp +++ b/examples/usertype_special_functions.cpp @@ -1,5 +1,6 @@ #define SOL_CHECK_ARGUMENTS #include + #include #include #include diff --git a/examples/usertype_var.cpp b/examples/usertype_var.cpp index 9f56490a..ec6d1acc 100644 --- a/examples/usertype_var.cpp +++ b/examples/usertype_var.cpp @@ -1,5 +1,6 @@ #define SOL_CHECK_ARGUMENTS #include + #include struct test { diff --git a/examples/variables.cpp b/examples/variables.cpp index ec20921b..f8c6f336 100644 --- a/examples/variables.cpp +++ b/examples/variables.cpp @@ -1,4 +1,6 @@ +#define SOL_CHECK_ARGUMENTS #include + #include int main() { diff --git a/examples/variadic_args.cpp b/examples/variadic_args.cpp index 918e33ec..6fe1f402 100644 --- a/examples/variadic_args.cpp +++ b/examples/variadic_args.cpp @@ -1,3 +1,4 @@ +#define SOL_CHECK_ARGUMENTS #include #include diff --git a/sol/compatibility.hpp b/sol/compatibility.hpp index 81e7742d..1a9d4e42 100644 --- a/sol/compatibility.hpp +++ b/sol/compatibility.hpp @@ -31,6 +31,15 @@ #ifndef SOL_NO_COMPAT +#ifdef SOL_USING_CXX_LUA + +#include "compatibility/5.1.0.h" +#include "compatibility/5.0.0.h" +#include "compatibility/5.x.x.h" +#include "compatibility/5.x.x.inl" + +#else + #ifdef __cplusplus extern "C" { #endif @@ -42,6 +51,8 @@ extern "C" { } #endif +#endif // C++ Mangling for Lua + #endif // SOL_NO_COMPAT #endif // SOL_COMPATIBILITY_HPP diff --git a/sol/compatibility/version.hpp b/sol/compatibility/version.hpp index 5397cbdb..9595516c 100644 --- a/sol/compatibility/version.hpp +++ b/sol/compatibility/version.hpp @@ -28,7 +28,7 @@ #include #else #include -#endif // C++-compiler Lua +#endif // C++ Mangming for Lua #if defined(_WIN32) || defined(_MSC_VER) #ifndef SOL_CODECVT_SUPPORT diff --git a/sol/environment.hpp b/sol/environment.hpp new file mode 100644 index 00000000..99679952 --- /dev/null +++ b/sol/environment.hpp @@ -0,0 +1,73 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2016 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_ENVIRONMENT_HPP +#define SOL_ENVIRONMENT_HPP + +#include "table.hpp" + +namespace sol { + + template + struct basic_environment : basic_table { + private: + typedef basic_table table_t; + public: + basic_environment() noexcept = default; + basic_environment(const basic_environment&) = default; + basic_environment(basic_environment&&) = default; + basic_environment& operator=(const basic_environment&) = default; + basic_environment& operator=(basic_environment&&) = default; + + basic_environment(lua_State* L, sol::new_table t, const sol::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::enabler> + basic_environment(T&& arg, Args&&... args) : table_t(std::forward(arg), std::forward(args)...) { } + }; + + template + void set_environment(const reference& target, const basic_environment& env) { + lua_State* L = target.lua_state(); +#if SOL_LUA_VERSION < 502 + // Use lua_setfenv + target.push(); + env.push(); + lua_setfenv(L, -2); + env.pop(); + target.pop(); +#else + // Use upvalues as explained in Lua 5.2 and beyond's manual + target.push(); + env.push(); + if (lua_setupvalue(L, -2, 1) == nullptr) { + env.pop(); + } + target.pop(); +#endif + } + +} // sol + +#endif // SOL_ENVIRONMENT_HPP diff --git a/sol/object.hpp b/sol/object.hpp index 932fc6d5..cd8be7d2 100644 --- a/sol/object.hpp +++ b/sol/object.hpp @@ -24,6 +24,7 @@ #include "reference.hpp" #include "stack.hpp" +#include "object_base.hpp" #include "userdata.hpp" #include "as_args.hpp" #include "variadic_args.hpp" @@ -51,35 +52,10 @@ namespace sol { return r; } - template - class basic_object : public base_t { + template + class basic_object : public basic_object_base { private: - template - decltype(auto) as_stack(std::true_type) const { - return stack::get(base_t::lua_state(), base_t::stack_index()); - } - - template - decltype(auto) as_stack(std::false_type) const { - base_t::push(); - return stack::pop(base_t::lua_state()); - } - - template - bool is_stack(std::true_type) const { - return stack::check(base_t::lua_state(), base_t::stack_index(), no_panic); - } - - template - bool is_stack(std::false_type) const { - int r = base_t::registry_index(); - if (r == LUA_REFNIL) - return meta::any_same, lua_nil_t, nullopt_t, std::nullptr_t>::value ? true : false; - if (r == LUA_NOREF) - return false; - auto pp = stack::push_pop(*this); - return stack::check(base_t::lua_state(), -1, no_panic); - } + typedef basic_object_base base_t; template basic_object(std::integral_constant, lua_State* L, int index = -1) noexcept : base_t(L, index) { @@ -90,7 +66,7 @@ namespace sol { public: basic_object() noexcept = default; - template , basic_object>>, meta::neg>, std::is_base_of>> = meta::enabler> + template , basic_object>>, meta::neg>, std::is_base_of>> = meta::enabler> basic_object(T&& r) : base_t(std::forward(r)) {} basic_object(lua_nil_t r) : base_t(r) {} basic_object(const basic_object&) = default; @@ -109,22 +85,12 @@ namespace sol { basic_object(lua_State* L, in_place_t, T&& arg, Args&&... args) noexcept : basic_object(L, in_place, std::forward(arg), std::forward(args)...) {} basic_object& operator=(const basic_object&) = default; basic_object& operator=(basic_object&&) = default; - basic_object& operator=(const base_t& b) { base_t::operator=(b); return *this; } - basic_object& operator=(base_t&& b) { base_t::operator=(std::move(b)); return *this; } + basic_object& operator=(const base_type& b) { base_t::operator=(b); return *this; } + basic_object& operator=(base_type&& b) { base_t::operator=(std::move(b)); return *this; } template basic_object& operator=(const proxy_base& r) { this->operator=(r.operator basic_object()); return *this; } template basic_object& operator=(proxy_base&& r) { this->operator=(r.operator basic_object()); return *this; } - - template - decltype(auto) as() const { - return as_stack(std::is_same()); - } - - template - bool is() const { - return is_stack(std::is_same()); - } }; template @@ -136,22 +102,6 @@ namespace sol { object make_object(lua_State* L, Args&&... args) { return make_reference(L, std::forward(args)...); } - - inline bool operator==(const object& lhs, const lua_nil_t&) { - return !lhs.valid(); - } - - inline bool operator==(const lua_nil_t&, const object& rhs) { - return !rhs.valid(); - } - - inline bool operator!=(const object& lhs, const lua_nil_t&) { - return lhs.valid(); - } - - inline bool operator!=(const lua_nil_t&, const object& rhs) { - return rhs.valid(); - } } // sol #endif // SOL_OBJECT_HPP diff --git a/sol/object_base.hpp b/sol/object_base.hpp new file mode 100644 index 00000000..76330bc1 --- /dev/null +++ b/sol/object_base.hpp @@ -0,0 +1,81 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2016 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_OBJECT_BASE_HPP +#define SOL_OBJECT_BASE_HPP + +#include "reference.hpp" +#include "stack.hpp" + +namespace sol { + + template + class basic_object_base : public base_t { + private: + template + decltype(auto) as_stack(std::true_type) const { + return stack::get(base_t::lua_state(), base_t::stack_index()); + } + + template + decltype(auto) as_stack(std::false_type) const { + base_t::push(); + return stack::pop(base_t::lua_state()); + } + + template + bool is_stack(std::true_type) const { + return stack::check(base_t::lua_state(), base_t::stack_index(), no_panic); + } + + template + bool is_stack(std::false_type) const { + int r = base_t::registry_index(); + if (r == LUA_REFNIL) + return meta::any_same, lua_nil_t, nullopt_t, std::nullptr_t>::value ? true : false; + if (r == LUA_NOREF) + return false; + auto pp = stack::push_pop(*this); + return stack::check(base_t::lua_state(), -1, no_panic); + } + + public: + basic_object_base() noexcept = default; + basic_object_base(const basic_object_base&) = default; + basic_object_base(basic_object_base&&) = default; + basic_object_base& operator=(const basic_object_base&) = default; + basic_object_base& operator=(basic_object_base&&) = default; + template , basic_object_base>>> = meta::enabler> + basic_object_base(T&& arg, Args&&... args) : base_t(std::forward(arg), std::forward(args)...) { } + + template + decltype(auto) as() const { + return as_stack(std::is_same()); + } + + template + bool is() const { + return is_stack(std::is_same()); + } + }; +} // sol + +#endif // SOL_OBJECT_BASE_HPP diff --git a/sol/reference.hpp b/sol/reference.hpp index d097636b..9d679b73 100644 --- a/sol/reference.hpp +++ b/sol/reference.hpp @@ -189,6 +189,22 @@ namespace sol { inline bool operator!= (const reference& l, const reference& r) { return !operator==(l, r); } + + inline bool operator==(const reference& lhs, const lua_nil_t&) { + return !lhs.valid(); + } + + inline bool operator==(const lua_nil_t&, const reference& rhs) { + return !rhs.valid(); + } + + inline bool operator!=(const reference& lhs, const lua_nil_t&) { + return lhs.valid(); + } + + inline bool operator!=(const lua_nil_t&, const reference& rhs) { + return rhs.valid(); + } } // sol #endif // SOL_REFERENCE_HPP diff --git a/sol/stack_core.hpp b/sol/stack_core.hpp index 2675e02e..7ae7c495 100644 --- a/sol/stack_core.hpp +++ b/sol/stack_core.hpp @@ -25,7 +25,6 @@ #include "types.hpp" #include "reference.hpp" #include "stack_reference.hpp" -#include "userdata.hpp" #include "tuple.hpp" #include "traits.hpp" #include "tie.hpp" diff --git a/sol/stack_reference.hpp b/sol/stack_reference.hpp index 56169066..ccbc50a8 100644 --- a/sol/stack_reference.hpp +++ b/sol/stack_reference.hpp @@ -22,6 +22,8 @@ #ifndef SOL_STACK_REFERENCE_HPP #define SOL_STACK_REFERENCE_HPP +#include "types.hpp" + namespace sol { class stack_reference { private: @@ -91,6 +93,22 @@ namespace sol { inline bool operator!= (const stack_reference& l, const stack_reference& r) { return !operator==(l, r); } + + inline bool operator==(const stack_reference& lhs, const lua_nil_t&) { + return !lhs.valid(); + } + + inline bool operator==(const lua_nil_t&, const stack_reference& rhs) { + return !rhs.valid(); + } + + inline bool operator!=(const stack_reference& lhs, const lua_nil_t&) { + return lhs.valid(); + } + + inline bool operator!=(const lua_nil_t&, const stack_reference& rhs) { + return rhs.valid(); + } } // sol #endif // SOL_STACK_REFERENCE_HPP diff --git a/sol/state_view.hpp b/sol/state_view.hpp index 1aa39993..620e70f4 100644 --- a/sol/state_view.hpp +++ b/sol/state_view.hpp @@ -24,6 +24,7 @@ #include "error.hpp" #include "table.hpp" +#include "environment.hpp" #include "load_result.hpp" #include @@ -256,6 +257,28 @@ namespace sol { return require_core(key, [this, &filename]() {stack::script_file(L, filename); }, create_global); } + template + protected_function_result do_string(const std::string& code, const basic_environment& env) { + load_status x = static_cast(luaL_loadstring(L, code.c_str())); + if (x != load_status::ok) { + return protected_function_result(L, -1, 0, 1, static_cast(x)); + } + protected_function pf(L, -1); + set_environment(pf, env); + return pf(); + } + + template + protected_function_result do_file(const std::string& filename, const basic_environment& env) { + load_status x = static_cast(luaL_loadfile(L, filename.c_str())); + if (x != load_status::ok) { + return protected_function_result(L, -1, 0, 1, static_cast(x)); + } + protected_function pf(L, -1); + set_environment(pf, env); + return pf(); + } + protected_function_result do_string(const std::string& code) { load_status x = static_cast(luaL_loadstring(L, code.c_str())); if (x != load_status::ok) { @@ -274,7 +297,15 @@ namespace sol { return pf(); } - template + protected_function_result script(const std::string& code, const environment& env) { + return script(code, env, sol::default_on_error); + } + + protected_function_result script_file(const std::string& filename, const environment& env) { + return script_file(filename, env, sol::default_on_error); + } + + template >> = meta::enabler> protected_function_result script(const std::string& code, Fx&& on_error) { protected_function_result pfr = do_string(code); if (!pfr.valid()) { @@ -283,7 +314,7 @@ namespace sol { return pfr; } - template + template >> = meta::enabler> protected_function_result script_file(const std::string& filename, Fx&& on_error) { protected_function_result pfr = do_file(filename); if (!pfr.valid()) { @@ -292,6 +323,24 @@ namespace sol { return pfr; } + template + protected_function_result script(const std::string& code, const basic_environment& env, Fx&& on_error) { + protected_function_result pfr = do_string(code, env); + if (!pfr.valid()) { + return on_error(L, std::move(pfr)); + } + return pfr; + } + + template + protected_function_result script_file(const std::string& filename, const basic_environment& env, Fx&& on_error) { + protected_function_result pfr = do_file(filename, env); + if (!pfr.valid()) { + return on_error(L, std::move(pfr)); + } + return pfr; + } + function_result script(const std::string& code) { int index = lua_gettop(L); stack::script(L, code); diff --git a/sol/table_core.hpp b/sol/table_core.hpp index 8004ebd2..d02aa37e 100644 --- a/sol/table_core.hpp +++ b/sol/table_core.hpp @@ -38,8 +38,24 @@ namespace sol { } } - template - class basic_table_core : public base_t { + struct new_table { + int sequence_hint = 0; + int map_hint = 0; + + new_table() = default; + new_table(const new_table&) = default; + new_table(new_table&&) = default; + new_table& operator=(const new_table&) = default; + new_table& operator=(new_table&&) = default; + + new_table(int sequence_hint, int map_hint = 0) : sequence_hint(sequence_hint), map_hint(map_hint) {} + }; + + const new_table create = new_table{}; + + template + class basic_table_core : public basic_object_base { + typedef basic_object_base base_t; friend class state; friend class state_view; @@ -152,30 +168,26 @@ namespace sol { traverse_set_deep(std::forward(keys)...); } - basic_table_core(lua_State* L, detail::global_tag t) noexcept : reference(L, t) { } - + basic_table_core(lua_State* L, detail::global_tag t) noexcept : base_t(L, t) { } + public: - typedef basic_table_iterator iterator; + typedef basic_table_iterator iterator; typedef iterator const_iterator; - basic_table_core() noexcept : base_t() { } - template , basic_table_core>>, meta::neg>, std::is_base_of>> = meta::enabler> - basic_table_core(T&& r) noexcept : base_t(std::forward(r)) { -#ifdef SOL_CHECK_ARGUMENTS - if (!is_table>::value) { - auto pp = stack::push_pop(*this); - stack::check(base_t::lua_state(), -1, type_panic); - } -#endif // Safety - } + basic_table_core() noexcept = default; basic_table_core(const basic_table_core&) = default; basic_table_core(basic_table_core&&) = default; basic_table_core& operator=(const basic_table_core&) = default; basic_table_core& operator=(basic_table_core&&) = default; basic_table_core(const stack_reference& r) : basic_table_core(r.lua_state(), r.stack_index()) {} basic_table_core(stack_reference&& r) : basic_table_core(r.lua_state(), r.stack_index()) {} - template >>, meta::neg>> = meta::enabler> + template >>, meta::neg, ref_index>>> = meta::enabler> basic_table_core(lua_State* L, T&& r) : basic_table_core(L, sol::ref_index(r.registry_index())) {} + basic_table_core(lua_State* L, new_table nt) : base_t(L, (lua_createtable(L, nt.sequence_hint, nt.map_hint), -1)) { + if (!std::is_base_of::value) { + lua_pop(L, 1); + } + } basic_table_core(lua_State* L, int index = -1) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS stack::check(L, index, type_panic); @@ -187,6 +199,15 @@ namespace sol { stack::check(L, -1, type_panic); #endif // Safety } + template , basic_table_core>>, meta::neg>, std::is_base_of>> = meta::enabler> + basic_table_core(T&& r) noexcept : base_t(std::forward(r)) { +#ifdef SOL_CHECK_ARGUMENTS + if (!is_table>::value) { + auto pp = stack::push_pop(*this); + stack::check(base_t::lua_state(), -1, type_panic); + } +#endif // Safety + } iterator begin() const { return iterator(*this); diff --git a/sol/types.hpp b/sol/types.hpp index 354e7e94..ed901155 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -104,6 +104,9 @@ namespace sol { return std::addressof(item); } }; + + struct unchecked_t {}; + const unchecked_t unchecked = unchecked_t{}; } // detail struct lua_nil_t {}; @@ -571,10 +574,16 @@ namespace sol { using table_core = basic_table_core; template using stack_table_core = basic_table_core; + template + using basic_table = basic_table_core; typedef table_core table; typedef table_core global_table; typedef stack_table_core stack_table; typedef stack_table_core stack_global_table; + template + struct basic_environment; + using environment = basic_environment; + using stack_environment = basic_environment; template class basic_function; template diff --git a/sol/userdata.hpp b/sol/userdata.hpp index 382f89b1..9e1802b0 100644 --- a/sol/userdata.hpp +++ b/sol/userdata.hpp @@ -22,14 +22,16 @@ #ifndef SOL_USERDATA_HPP #define SOL_USERDATA_HPP -#include "reference.hpp" +#include "object_base.hpp" +#include "table.hpp" namespace sol { - template - class basic_userdata : public base_t { + template + class basic_userdata : public basic_table { + typedef basic_table base_t; public: basic_userdata() noexcept = default; - template , basic_userdata>>, meta::neg>, std::is_base_of>> = meta::enabler> + template , basic_userdata>>, meta::neg>, std::is_base_of>> = meta::enabler> basic_userdata(T&& r) noexcept : base_t(std::forward(r)) { #ifdef SOL_CHECK_ARGUMENTS if (!is_userdata>::value) { @@ -44,7 +46,7 @@ namespace sol { basic_userdata& operator=(basic_userdata&&) = default; basic_userdata(const stack_reference& r) : basic_userdata(r.lua_state(), r.stack_index()) {} basic_userdata(stack_reference&& r) : basic_userdata(r.lua_state(), r.stack_index()) {} - template >>, meta::neg>> = meta::enabler> + template >>, meta::neg, ref_index>>> = meta::enabler> basic_userdata(lua_State* L, T&& r) : basic_userdata(L, sol::ref_index(r.registry_index())) {} basic_userdata(lua_State* L, int index = -1) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS @@ -59,14 +61,15 @@ namespace sol { } }; - template - class basic_lightuserdata : public base_t { + template + class basic_lightuserdata : public basic_object_base< base_type > { + typedef basic_object_base base_t; public: basic_lightuserdata() noexcept = default; - template , basic_lightuserdata>>, meta::neg>, std::is_base_of>> = meta::enabler> + template , basic_lightuserdata>>, meta::neg>, std::is_base_of>> = meta::enabler> basic_lightuserdata(T&& r) noexcept : base_t(std::forward(r)) { #ifdef SOL_CHECK_ARGUMENTS - if (!is_userdata>::value) { + if (!is_lightuserdata>::value) { auto pp = stack::push_pop(*this); type_assert(base_t::lua_state(), -1, type::lightuserdata); } @@ -78,7 +81,7 @@ namespace sol { basic_lightuserdata& operator=(basic_lightuserdata&&) = default; basic_lightuserdata(const stack_reference& r) : basic_lightuserdata(r.lua_state(), r.stack_index()) {} basic_lightuserdata(stack_reference&& r) : basic_lightuserdata(r.lua_state(), r.stack_index()) {} - template >>, meta::neg>> = meta::enabler> + template >>, meta::neg, ref_index>>> = meta::enabler> basic_lightuserdata(lua_State* L, T&& r) : basic_lightuserdata(L, sol::ref_index(r.registry_index())) {} basic_lightuserdata(lua_State* L, int index = -1) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS diff --git a/test_environments.cpp b/test_environments.cpp new file mode 100644 index 00000000..718f252d --- /dev/null +++ b/test_environments.cpp @@ -0,0 +1,46 @@ +#define SOL_CHECK_ARGUMENTS + +#include +#include +#include +#include "test_stack_guard.hpp" + +TEST_CASE("environments/shadowing", "Environments can properly shadow and fallback on variables") { + + sol::state lua; + lua["b"] = 2142; + + SECTION("no fallback") { + sol::environment plain_env(lua, sol::create); + lua.script("a = 24", plain_env); + sol::optional maybe_env_a = plain_env["a"]; + sol::optional maybe_global_a = lua["a"]; + sol::optional maybe_env_b = plain_env["b"]; + sol::optional maybe_global_b = lua["b"]; + + REQUIRE(maybe_env_a != sol::nullopt); + REQUIRE(maybe_env_a.value() == 24); + REQUIRE(maybe_env_b == sol::nullopt); + + REQUIRE(maybe_global_a == sol::nullopt); + REQUIRE(maybe_global_b != sol::nullopt); + REQUIRE(maybe_global_b.value() == 2142); + } + SECTION("fallback") { + sol::environment env_with_fallback(lua, sol::create, lua.globals()); + lua.script("a = 56", env_with_fallback, sol::default_on_error); + sol::optional maybe_env_a = env_with_fallback["a"]; + sol::optional maybe_global_a = lua["a"]; + sol::optional maybe_env_b = env_with_fallback["b"]; + sol::optional maybe_global_b = lua["b"]; + + REQUIRE(maybe_env_a != sol::nullopt); + REQUIRE(maybe_env_a.value() == 56); + REQUIRE(maybe_env_b != sol::nullopt); + REQUIRE(maybe_env_b.value() == 2142); + + REQUIRE(maybe_global_a == sol::nullopt); + REQUIRE(maybe_global_b != sol::nullopt); + REQUIRE(maybe_global_b.value() == 2142); + } +} diff --git a/test_state.cpp b/test_state.cpp new file mode 100644 index 00000000..3cefdf61 --- /dev/null +++ b/test_state.cpp @@ -0,0 +1,424 @@ +#define SOL_CHECK_ARGUMENTS + +#include +#include +#include +#include +#include "test_stack_guard.hpp" + +TEST_CASE("state/require_file", "opening files as 'requires'") { + static const char FILE_NAME[] = "./tmp_thingy.lua"; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + SECTION("with usertypes") + { + struct foo { + foo(int bar) : bar(bar) {} + + const int bar; + }; + + lua.new_usertype("foo", + sol::constructors>{}, + "bar", &foo::bar + ); + + std::fstream file(FILE_NAME, std::ios::out); + file << "return { modfunc = function () return foo.new(221) end }" << std::endl; + file.close(); + + const sol::table thingy1 = lua.require_file("thingy", FILE_NAME); + + CHECK(thingy1.valid()); + + const foo foo_v = thingy1["modfunc"](); + + int val1 = foo_v.bar; + + CHECK(val1 == 221); + } + + SECTION("simple") + { + std::fstream file(FILE_NAME, std::ios::out); + file << "return { modfunc = function () return 221 end }" << std::endl; + file.close(); + + const sol::table thingy1 = lua.require_file("thingy", FILE_NAME); + const sol::table thingy2 = lua.require_file("thingy", FILE_NAME); + + CHECK(thingy1.valid()); + CHECK(thingy2.valid()); + + int val1 = thingy1["modfunc"](); + int val2 = thingy2["modfunc"](); + + CHECK(val1 == 221); + CHECK(val2 == 221); + // must have loaded the same table + CHECK(thingy1 == thingy2); + } + + std::remove(FILE_NAME); +} + +TEST_CASE("state/require_script", "opening strings as 'requires' clauses") { + std::string code = "return { modfunc = function () return 221 end }"; + + sol::state lua; + sol::table thingy1 = lua.require_script("thingy", code); + sol::table thingy2 = lua.require_script("thingy", code); + + int val1 = thingy1["modfunc"](); + int val2 = thingy2["modfunc"](); + REQUIRE(val1 == 221); + REQUIRE(val2 == 221); + // must have loaded the same table + REQUIRE(thingy1 == thingy2); +} + +TEST_CASE("state/require", "opening using a file") { + struct open { + static int open_func(lua_State* L) { + sol::state_view lua = L; + return sol::stack::push(L, lua.create_table_with("modfunc", sol::as_function([]() { return 221; }))); + } + }; + + sol::state lua; + sol::table thingy1 = lua.require("thingy", open::open_func); + sol::table thingy2 = lua.require("thingy", open::open_func); + + int val1 = thingy1["modfunc"](); + int val2 = thingy2["modfunc"](); + REQUIRE(val1 == 221); + REQUIRE(val2 == 221); + // THIS IS ONLY REQUIRED IN LUA 5.3, FOR SOME REASON + // must have loaded the same table + // REQUIRE(thingy1 == thingy2); +} + +TEST_CASE("state/multi-require", "make sure that requires transfers across hand-rolled script implementation and standard requiref") { + struct open { + static int open_func(lua_State* L) { + sol::state_view lua = L; + return sol::stack::push(L, lua.create_table_with("modfunc", sol::as_function([]() { return 221; }))); + } + }; + + std::string code = "return { modfunc = function () return 221 end }"; + sol::state lua; + sol::table thingy1 = lua.require("thingy", open::open_func); + sol::table thingy2 = lua.require("thingy", open::open_func); + sol::table thingy3 = lua.require_script("thingy", code); + + int val1 = thingy1["modfunc"](); + int val2 = thingy2["modfunc"](); + int val3 = thingy3["modfunc"](); + REQUIRE(val1 == 221); + REQUIRE(val2 == 221); + REQUIRE(val3 == 221); + // must have loaded the same table + // Lua is not obliged to give a shit. Thanks, Lua + //REQUIRE(thingy1 == thingy2); + // But we care, thankfully + //REQUIRE(thingy1 == thingy3); + REQUIRE(thingy2 == thingy3); +} + +TEST_CASE("state/require-safety", "make sure unrelated modules aren't harmed in using requires") { + sol::state lua; + lua.open_libraries(); + std::string t1 = lua.script(R"(require 'io' +return 'test1')"); + sol::object ot2 = lua.require_script("test2", R"(require 'io' +return 'test2')"); + std::string t2 = ot2.as(); + std::string t3 = lua.script(R"(require 'io' +return 'test3')"); + REQUIRE(t1 == "test1"); + REQUIRE(t2 == "test2"); + REQUIRE(t3 == "test3"); +} + +TEST_CASE("state/leak-check", "make sure there are no humongous memory leaks in iteration") { +#if 0 + sol::state lua; + lua.script(R"( +record = {} +for i=1,256 do + record[i] = i +end +function run() + for i=1,25000 do + fun(record) + end +end +function run2() + for i=1,50000 do + fun(record) + end +end +)"); + + lua["fun"] = [](const sol::table &t) { + //removing the for loop fixes the memory leak + auto b = t.begin(); + auto e = t.end(); + for (; b != e; ++b) { + + } + }; + + size_t beforewarmup = lua.memory_used(); + lua["run"](); + + size_t beforerun = lua.memory_used(); + lua["run"](); + size_t afterrun = lua.memory_used(); + lua["run2"](); + size_t afterrun2 = lua.memory_used(); + + // Less memory used before the warmup + REQUIRE(beforewarmup <= beforerun); + // Iteration size and such does not bloat or affect memory + // (these are weak checks but they'll warn us nonetheless if something goes wrong) + REQUIRE(beforerun == afterrun); + REQUIRE(afterrun == afterrun2); +#else + REQUIRE(true); +#endif +} + +TEST_CASE("state/script-returns", "make sure script returns are done properly") { + std::string script = + R"( +local example = +{ + str = "this is a string", + num = 1234, + + func = function(self) + print(self.str) + return "fstr" + end +} + +return example; +)"; + + auto bar = [&script](sol::this_state l) { + sol::state_view lua = l; + sol::table data = lua.script(script); + + std::string str = data["str"]; + int num = data["num"]; + std::string fstr = data["func"](data); + REQUIRE(str == "this is a string"); + REQUIRE(fstr == "fstr"); + REQUIRE(num == 1234); + }; + + auto foo = [&script](int, sol::this_state l) { + sol::state_view lua = l; + sol::table data = lua.script(script); + + std::string str = data["str"]; + int num = data["num"]; + std::string fstr = data["func"](data); + REQUIRE(str == "this is a string"); + REQUIRE(fstr == "fstr"); + REQUIRE(num == 1234); + }; + + auto bar2 = [&script](sol::this_state l) { + sol::state_view lua = l; + sol::table data = lua.do_string(script); + + std::string str = data["str"]; + int num = data["num"]; + std::string fstr = data["func"](data); + REQUIRE(str == "this is a string"); + REQUIRE(fstr == "fstr"); + REQUIRE(num == 1234); + }; + + auto foo2 = [&script](int, sol::this_state l) { + sol::state_view lua = l; + sol::table data = lua.do_string(script); + + std::string str = data["str"]; + int num = data["num"]; + std::string fstr = data["func"](data); + REQUIRE(str == "this is a string"); + REQUIRE(fstr == "fstr"); + REQUIRE(num == 1234); + }; + + sol::state lua; + lua.open_libraries(); + + lua.set_function("foo", foo); + lua.set_function("foo2", foo2); + lua.set_function("bar", bar); + lua.set_function("bar2", bar2); + + lua.script("bar() bar2() foo(1) foo2(1)"); +} + +TEST_CASE("state/copy-move", "ensure state can be properly copied and moved") { + sol::state lua; + lua["a"] = 1; + + sol::state lua2(std::move(lua)); + int a2 = lua2["a"]; + REQUIRE(a2 == 1); + lua = std::move(lua2); + int a = lua["a"]; + REQUIRE(a == 1); +} + +TEST_CASE("state/requires-reload", "ensure that reloading semantics do not cause a crash") { + sol::state lua; + lua.open_libraries(); + lua.script("require 'io'\nreturn 'test1'"); + lua.require_script("test2", "require 'io'\nreturn 'test2'"); + lua.script("require 'io'\nreturn 'test3'"); +} + +TEST_CASE("state/script-do-load", "test success and failure cases for loading and running scripts") { + const static std::string bad_syntax = "weird\n%$@symb\nols"; + static const char file_bad_syntax[] = "./temp.bad_syntax.lua"; + const static std::string bad_runtime = "bad.code = 20"; + static const char file_bad_runtime[] = "./temp.bad_runtime.lua"; + const static std::string good = "a = 21\nreturn a"; + static const char file_good[] = "./temp.good.lua"; + + SECTION("script") { + sol::state lua; + int ar = lua.script(good); + int a = lua["a"]; + REQUIRE(a == 21); + REQUIRE(ar == 21); + } + SECTION("script-handler") { + sol::state lua; + auto errbs = lua.script(bad_syntax, sol::simple_on_error); + REQUIRE(!errbs.valid()); + + auto errbr = lua.script(bad_runtime, sol::simple_on_error); + REQUIRE(!errbr.valid()); + + auto result = lua.script(good, sol::simple_on_error); + int a = lua["a"]; + int ar = result; + REQUIRE(result.valid()); + REQUIRE(a == 21); + REQUIRE(ar == 21); + } + SECTION("do_string") { + sol::state lua; + auto errbs = lua.do_string(bad_syntax); + REQUIRE(!errbs.valid()); + + auto errbr = lua.do_string(bad_runtime); + REQUIRE(!errbr.valid()); + + auto result = lua.do_string(good); + int a = lua["a"]; + int ar = result; + REQUIRE(result.valid()); + REQUIRE(a == 21); + REQUIRE(ar == 21); + } + SECTION("load_string") { + sol::state lua; + auto errbsload = lua.load(bad_syntax); + REQUIRE(!errbsload.valid()); + + sol::load_result errbrload = lua.load(bad_runtime); + REQUIRE(errbrload.valid()); + sol::protected_function errbrpf = errbrload; + auto errbr = errbrpf(); + REQUIRE(!errbr.valid()); + + sol::load_result resultload = lua.load(good); + REQUIRE(resultload.valid()); + sol::protected_function resultpf = resultload; + auto result = resultpf(); + int a = lua["a"]; + int ar = result; + REQUIRE(result.valid()); + REQUIRE(a == 21); + REQUIRE(ar == 21); + } + { + std::ofstream fbs(file_bad_syntax, std::ios::out); + fbs << bad_syntax; + std::ofstream fbr(file_bad_runtime, std::ios::out); + fbr << bad_runtime; + std::ofstream fg(file_good, std::ios::out); + fg << good; + } + SECTION("script_file") { + sol::state lua; + int ar = lua.script_file(file_good); + int a = lua["a"]; + REQUIRE(a == 21); + REQUIRE(ar == 21); + } + SECTION("script_file-handler") { + sol::state lua; + auto errbs = lua.script_file(file_bad_syntax, sol::simple_on_error); + REQUIRE(!errbs.valid()); + + auto errbr = lua.script_file(file_bad_runtime, sol::simple_on_error); + REQUIRE(!errbr.valid()); + + auto result = lua.script_file(file_good, sol::simple_on_error); + int a = lua["a"]; + int ar = result; + REQUIRE(result.valid()); + REQUIRE(a == 21); + REQUIRE(ar == 21); + } + SECTION("do_file") { + sol::state lua; + auto errbs = lua.do_file(file_bad_syntax); + REQUIRE(!errbs.valid()); + + auto errbr = lua.do_file(file_bad_runtime); + REQUIRE(!errbr.valid()); + + auto result = lua.do_file(file_good); + int a = lua["a"]; + int ar = result; + REQUIRE(result.valid()); + REQUIRE(a == 21); + REQUIRE(ar == 21); + } + SECTION("load_file") { + sol::state lua; + auto errbsload = lua.load_file(file_bad_syntax); + REQUIRE(!errbsload.valid()); + + sol::load_result errbrload = lua.load_file(file_bad_runtime); + REQUIRE(errbrload.valid()); + sol::protected_function errbrpf = errbrload; + auto errbr = errbrpf(); + REQUIRE(!errbr.valid()); + + sol::load_result resultload = lua.load_file(file_good); + REQUIRE(resultload.valid()); + sol::protected_function resultpf = resultload; + auto result = resultpf(); + int a = lua["a"]; + int ar = result; + REQUIRE(result.valid()); + REQUIRE(a == 21); + REQUIRE(ar == 21); + } + +} diff --git a/tests.cpp b/tests.cpp index 9ed6c0f0..a2d80f4f 100644 --- a/tests.cpp +++ b/tests.cpp @@ -286,19 +286,23 @@ TEST_CASE("object/conversions", "make sure all basic reference types can be made lua["l"] = static_cast(nullptr); sol::table t = lua.create_table(); + sol::table t2(lua, sol::create); sol::thread th = sol::thread::create(lua); sol::function f = lua["f"]; sol::protected_function pf = lua["f"]; sol::userdata ud = lua["d"]; sol::lightuserdata lud = lua["l"]; + sol::environment env(lua, sol::create); sol::object ot(t); - sol::object ot2 = ot; + sol::object ot2(t2); + sol::object oteq = ot; sol::object oth(th); sol::object of(f); sol::object opf(pf); sol::object od(ud); sol::object ol(lud); + sol::object oenv(env); auto oni = sol::make_object(lua, 50); auto ond = sol::make_object(lua, 50.0); @@ -311,6 +315,7 @@ TEST_CASE("object/conversions", "make sure all basic reference types can be made REQUIRE(ot.get_type() == sol::type::table); REQUIRE(ot2.get_type() == sol::type::table); + REQUIRE(oteq.get_type() == sol::type::table); REQUIRE(oth.get_type() == sol::type::thread); REQUIRE(of.get_type() == sol::type::function); REQUIRE(opf.get_type() == sol::type::function); @@ -321,143 +326,7 @@ TEST_CASE("object/conversions", "make sure all basic reference types can be made REQUIRE(osl.get_type() == sol::type::string); REQUIRE(os.get_type() == sol::type::string); REQUIRE(omn.get_type() == sol::type::nil); -} - -TEST_CASE("state/require_file", "opening files as 'requires'") { - static const char FILE_NAME[] = "./tmp_thingy.lua"; - - sol::state lua; - lua.open_libraries(sol::lib::base); - - SECTION("with usertypes") - { - struct foo { - foo(int bar) : bar(bar) {} - - const int bar; - }; - - lua.new_usertype("foo", - sol::constructors>{}, - "bar", &foo::bar - ); - - std::fstream file(FILE_NAME, std::ios::out); - file << "return { modfunc = function () return foo.new(221) end }" << std::endl; - file.close(); - - const sol::table thingy1 = lua.require_file("thingy", FILE_NAME); - - CHECK(thingy1.valid()); - - const foo foo_v = thingy1["modfunc"](); - - int val1 = foo_v.bar; - - CHECK(val1 == 221); - } - - SECTION("simple") - { - std::fstream file(FILE_NAME, std::ios::out); - file << "return { modfunc = function () return 221 end }" << std::endl; - file.close(); - - const sol::table thingy1 = lua.require_file("thingy", FILE_NAME); - const sol::table thingy2 = lua.require_file("thingy", FILE_NAME); - - CHECK(thingy1.valid()); - CHECK(thingy2.valid()); - - int val1 = thingy1["modfunc"](); - int val2 = thingy2["modfunc"](); - - CHECK(val1 == 221); - CHECK(val2 == 221); - // must have loaded the same table - CHECK(thingy1 == thingy2); - } - - std::remove(FILE_NAME); -} - -TEST_CASE("state/require_script", "opening strings as 'requires' clauses") { - std::string code = "return { modfunc = function () return 221 end }"; - - sol::state lua; - sol::table thingy1 = lua.require_script("thingy", code); - sol::table thingy2 = lua.require_script("thingy", code); - - int val1 = thingy1["modfunc"](); - int val2 = thingy2["modfunc"](); - REQUIRE(val1 == 221); - REQUIRE(val2 == 221); - // must have loaded the same table - REQUIRE(thingy1 == thingy2); -} - -TEST_CASE("state/require", "opening using a file") { - struct open { - static int open_func(lua_State* L) { - sol::state_view lua = L; - return sol::stack::push(L, lua.create_table_with("modfunc", sol::as_function([]() { return 221; }))); - } - }; - - sol::state lua; - sol::table thingy1 = lua.require("thingy", open::open_func); - sol::table thingy2 = lua.require("thingy", open::open_func); - - int val1 = thingy1["modfunc"](); - int val2 = thingy2["modfunc"](); - REQUIRE(val1 == 221); - REQUIRE(val2 == 221); - // THIS IS ONLY REQUIRED IN LUA 5.3, FOR SOME REASON - // must have loaded the same table - // REQUIRE(thingy1 == thingy2); -} - -TEST_CASE("state/multi-require", "make sure that requires transfers across hand-rolled script implementation and standard requiref") { - struct open { - static int open_func(lua_State* L) { - sol::state_view lua = L; - return sol::stack::push(L, lua.create_table_with("modfunc", sol::as_function([]() { return 221; }))); - } - }; - - std::string code = "return { modfunc = function () return 221 end }"; - sol::state lua; - sol::table thingy1 = lua.require("thingy", open::open_func); - sol::table thingy2 = lua.require("thingy", open::open_func); - sol::table thingy3 = lua.require_script("thingy", code); - - int val1 = thingy1["modfunc"](); - int val2 = thingy2["modfunc"](); - int val3 = thingy3["modfunc"](); - REQUIRE(val1 == 221); - REQUIRE(val2 == 221); - REQUIRE(val3 == 221); - // must have loaded the same table - // Lua is not obliged to give a shit. Thanks, Lua - //REQUIRE(thingy1 == thingy2); - // But we care, thankfully - //REQUIRE(thingy1 == thingy3); - REQUIRE(thingy2 == thingy3); -} - -TEST_CASE("state/require-safety", "make sure unrelated modules aren't harmed in using requires") { - sol::state lua; - lua.open_libraries(); - std::string t1 = lua.script(R"(require 'io' -return 'test1')"); - sol::object ot2 = lua.require_script("test2", R"(require 'io' -return 'test2')"); - std::string t2 = ot2.as(); - std::string t3 = lua.script(R"(require 'io' -return 'test3')"); - REQUIRE(t1 == "test1"); - REQUIRE(t2 == "test2"); - REQUIRE(t3 == "test3"); + REQUIRE(oenv.get_type() == sol::type::table); } TEST_CASE("feature/indexing-overrides", "make sure index functions can be overridden on types") { @@ -732,151 +601,6 @@ TEST_CASE("numbers/integers", "make sure integers are detectable on most platfor REQUIRE(b_is_double); } -TEST_CASE("state/leak-check", "make sure there are no humongous memory leaks in iteration") { -#if 0 - sol::state lua; - lua.script(R"( -record = {} -for i=1,256 do - record[i] = i -end -function run() - for i=1,25000 do - fun(record) - end -end -function run2() - for i=1,50000 do - fun(record) - end -end -)"); - - lua["fun"] = [](const sol::table &t) { - //removing the for loop fixes the memory leak - auto b = t.begin(); - auto e = t.end(); - for (; b != e; ++b) { - - } - }; - - size_t beforewarmup = lua.memory_used(); - lua["run"](); - - size_t beforerun = lua.memory_used(); - lua["run"](); - size_t afterrun = lua.memory_used(); - lua["run2"](); - size_t afterrun2 = lua.memory_used(); - - // Less memory used before the warmup - REQUIRE(beforewarmup <= beforerun); - // Iteration size and such does not bloat or affect memory - // (these are weak checks but they'll warn us nonetheless if something goes wrong) - REQUIRE(beforerun == afterrun); - REQUIRE(afterrun == afterrun2); -#else - REQUIRE(true); -#endif -} - -TEST_CASE("state/script-returns", "make sure script returns are done properly") { - std::string script = - R"( -local example = -{ - str = "this is a string", - num = 1234, - - func = function(self) - print(self.str) - return "fstr" - end -} - -return example; -)"; - - auto bar = [&script](sol::this_state l) { - sol::state_view lua = l; - sol::table data = lua.script(script); - - std::string str = data["str"]; - int num = data["num"]; - std::string fstr = data["func"](data); - REQUIRE(str == "this is a string"); - REQUIRE(fstr == "fstr"); - REQUIRE(num == 1234); - }; - - auto foo = [&script](int, sol::this_state l) { - sol::state_view lua = l; - sol::table data = lua.script(script); - - std::string str = data["str"]; - int num = data["num"]; - std::string fstr = data["func"](data); - REQUIRE(str == "this is a string"); - REQUIRE(fstr == "fstr"); - REQUIRE(num == 1234); - }; - - auto bar2 = [&script](sol::this_state l) { - sol::state_view lua = l; - sol::table data = lua.do_string(script); - - std::string str = data["str"]; - int num = data["num"]; - std::string fstr = data["func"](data); - REQUIRE(str == "this is a string"); - REQUIRE(fstr == "fstr"); - REQUIRE(num == 1234); - }; - - auto foo2 = [&script](int, sol::this_state l) { - sol::state_view lua = l; - sol::table data = lua.do_string(script); - - std::string str = data["str"]; - int num = data["num"]; - std::string fstr = data["func"](data); - REQUIRE(str == "this is a string"); - REQUIRE(fstr == "fstr"); - REQUIRE(num == 1234); - }; - - sol::state lua; - lua.open_libraries(); - - lua.set_function("foo", foo); - lua.set_function("foo2", foo2); - lua.set_function("bar", bar); - lua.set_function("bar2", bar2); - - lua.script("bar() bar2() foo(1) foo2(1)"); -} - -TEST_CASE("state/copy-move", "ensure state can be properly copied and moved") { - sol::state lua; - lua["a"] = 1; - - sol::state lua2(std::move(lua)); - int a2 = lua2["a"]; - REQUIRE(a2 == 1); - lua = std::move(lua2); - int a = lua["a"]; - REQUIRE(a == 1); -} - -TEST_CASE("requires/reload", "ensure that reloading semantics do not cause a crash") { - sol::state lua; - lua.open_libraries(); - lua.script("require 'io'\nreturn 'test1'"); - lua.require_script("test2", "require 'io'\nreturn 'test2'"); - lua.script("require 'io'\nreturn 'test3'"); -} - TEST_CASE("object/is-method", "test whether or not the is abstraction works properly for a user-defined type") { struct thing {};