From 10b1bb04a957895c962a12e5c32d7e6a35df33bd Mon Sep 17 00:00:00 2001 From: ThePhD Date: Tue, 7 Nov 2017 20:19:36 -0500 Subject: [PATCH] formalize `unsafe_function_result` add index-based getter to function_result add safety checks for double deleters and other things of that nature --- docs/source/api/function.rst | 16 ++-- docs/source/api/protected_function.rst | 2 +- docs/source/api/proxy.rst | 26 +++--- docs/source/api/state.rst | 2 +- docs/source/safety.rst | 2 +- examples/indirect_function_calls.cpp | 74 ++++++++++++++++ sol/forward.hpp | 10 ++- sol/function.hpp | 8 +- sol/function_result.hpp | 91 +------------------ sol/protected_function_result.hpp | 38 ++++---- sol/simple_usertype_metatable.hpp | 52 +++++++++-- sol/stack.hpp | 2 +- sol/stack_proxy.hpp | 8 +- sol/state_view.hpp | 22 ++--- sol/types.hpp | 2 +- sol/unsafe_function.hpp | 6 +- sol/unsafe_function_result.hpp | 115 +++++++++++++++++++++++++ sol/usertype_metatable.hpp | 26 ++++-- tests/test_state.cpp | 2 +- 19 files changed, 337 insertions(+), 167 deletions(-) create mode 100644 examples/indirect_function_calls.cpp create mode 100644 sol/unsafe_function_result.hpp diff --git a/docs/source/api/function.rst b/docs/source/api/function.rst index 1eb2a648..0464caa1 100644 --- a/docs/source/api/function.rst +++ b/docs/source/api/function.rst @@ -5,7 +5,7 @@ function .. note:: - This abstraction assumes the function runs safely. If you expect your code to have errors (e.g., you don't always have explicit control over it or are trying to debug errors), please use :doc:`sol::protected_function` explicitly. + This abstraction assumes the function runs safely. If you expect your code to have errors (e.g., you don't always have explicit control over it or are trying to debug errors), please use :doc:`sol::protected_function` explicitly. You can also make ``sol::function`` default to ``sol::protected_function`` by turning on :ref:`the safety features`. .. code-block:: cpp @@ -15,12 +15,12 @@ function Function is a correct-assuming version of :doc:`protected_function`, omitting the need for typechecks and error handling (thus marginally increasing speed in some cases). It is the default function type of Sol. Grab a function directly off the stack using the constructor: .. code-block:: cpp - :caption: constructor: function + :caption: constructor: unsafe_function - function(lua_State* L, int index = -1); + unsafe_function(lua_State* L, int index = -1); -When called without the return types being specified by either a ``sol::types<...>`` list or a ``call( ... )`` template type list, it generates a :ref:`function_result` class that gets implicitly converted to the requested return type. For example: +Calls the constructor and creates this type, straight from the stack. For example: .. code-block:: lua :caption: func_barks.lua @@ -44,7 +44,7 @@ The following C++ code will call this function from this file and retrieve the r sol::function woof = lua["woof"]; double numwoof = woof(20); -The call ``woof(20)`` generates a :ref:`function_result`, which is then implicitly converted to an ``double`` after being called. The intermediate temporary ``function_result`` is then destructed, popping the Lua function call results off the Lua stack. +The call ``woof(20)`` generates a :ref:`unsafe_function_result`, which is then implicitly converted to an ``double`` after being called. The intermediate temporary ``function_result`` is then destructed, popping the Lua function call results off the Lua stack. You can also return multiple values by using ``std::tuple``, or if you need to bind them to pre-existing variables use ``sol::tie``: @@ -67,13 +67,13 @@ This makes it much easier to work with multiple return values. Using ``std::tie` .. warning:: - Do NOT save the return type of a :ref:`function_result` with ``auto``, as in ``auto numwoof = woof(20);``, and do NOT store it anywhere. Unlike its counterpart :ref:`protected_function_result`, ``function_result`` is NOT safe to store as it assumes that its return types are still at the top of the stack and when its destructor is called will pop the number of results the function was supposed to return off the top of the stack. If you mess with the Lua stack between saving ``function_result`` and it being destructed, you will be subject to an incredible number of surprising and hard-to-track bugs. Don't do it. + Do NOT save the return type of a :ref:`unsafe_function_result` (``function_result`` when :ref:`safety configurations are not turned on`) with ``auto``, as in ``auto numwoof = woof(20);``, and do NOT store it anywhere. Unlike its counterpart :ref:`protected_function_result`, ``function_result`` is NOT safe to store as it assumes that its return types are still at the top of the stack and when its destructor is called will pop the number of results the function was supposed to return off the top of the stack. If you mess with the Lua stack between saving ``function_result`` and it being destructed, you will be subject to an incredible number of surprising and hard-to-track bugs. Don't do it. .. code-block:: cpp :caption: function: call operator / function call template - function_result operator()( Args&&... args ); + unsafe_function_result operator()( Args&&... args ); template decltype(auto) call( Args&&... args ); @@ -81,7 +81,7 @@ This makes it much easier to work with multiple return values. Using ``std::tie` template decltype(auto) operator()( types, Args&&... args ); -Calls the function. The second ``operator()`` lets you specify the templated return types using the ``my_func(sol::types, ...)`` syntax. Function assumes there are no runtime errors, and thusly will call the ``atpanic`` function if an error does occur. +Calls the function. The second ``operator()`` lets you specify the templated return types using the ``my_func(sol::types, ...)`` syntax. Function assumes there are no runtime errors, and thusly will call the ``atpanic`` function if a detectable error does occur, and otherwise can return garbage / bogus values if the user is not careful. To know more about how function arguments are handled, see :ref:`this note` diff --git a/docs/source/api/protected_function.rst b/docs/source/api/protected_function.rst index 47d49062..dcb68402 100644 --- a/docs/source/api/protected_function.rst +++ b/docs/source/api/protected_function.rst @@ -95,7 +95,7 @@ Alternatively, with a bad or good function call, you can use ``sol::optional`` t // No value! } -That makes the code a bit more concise and easy to reason about if you don't want to bother with reading the error. Thankfully, unlike ``sol::function_result``, you can save ``sol::protected_function_result`` in a variable and push/pop things above it on the stack where its returned values are. This makes it a bit more flexible than the rigid, performant ``sol::function_result`` type that comes from calling :doc:`sol::function`. +That makes the code a bit more concise and easy to reason about if you don't want to bother with reading the error. Thankfully, unlike ``sol::unsafe_function_result``, you can save ``sol::protected_function_result`` in a variable and push/pop things above it on the stack where its returned values are. This makes it a bit more flexible than the rigid, performant ``sol::unsafe_function_result`` type that comes from calling :doc:`sol::unsafe_function`. If you're confident the result succeeded, you can also just put the type you want (like ``double`` or ``std::string`` right there and it will get it. But, if it doesn't work out, sol can throw and/or panic if you have the :doc:`safety<../safety>` features turned on: diff --git a/docs/source/api/proxy.rst b/docs/source/api/proxy.rst index 0f778d33..8a812ca0 100644 --- a/docs/source/api/proxy.rst +++ b/docs/source/api/proxy.rst @@ -1,5 +1,5 @@ -proxy, (protected\_)function_result - proxy_base derivatives -============================================================ +proxy, (protected\unsafe)_function_result - proxy_base derivatives +================================================================== *``table[x]`` and ``function(...)`` conversion struct* @@ -13,7 +13,7 @@ proxy, (protected\_)function_result - proxy_base derivatives struct stack_proxy: proxy_base<...>; - struct function_result : proxy_base<...>; + struct unsafe_function_result : proxy_base<...>; struct protected_function_result: proxy_base<...>; @@ -133,7 +133,7 @@ Returns whether this proxy actually refers to a valid object. It uses :ref:`sol: template proxy& operator=( Fx&& function ); -Sets the value associated with the keys the proxy was generated with to ``value``. If this is a function, calls ``set_function``. If it is not, just calls ``set``. Does not exist on :ref:`function_result` or :ref:`protected_function_result`. See :ref:`note` for caveats. +Sets the value associated with the keys the proxy was generated with to ``value``. If this is a function, calls ``set_function``. If it is not, just calls ``set``. Does not exist on :ref:`unsage_function_result` or :ref:`protected_function_result`. See :ref:`note` for caveats. .. code-block:: c++ :caption: function: set a callable @@ -142,7 +142,7 @@ Sets the value associated with the keys the proxy was generated with to ``value` template proxy& set_function( Fx&& fx ); -Sets the value associated with the keys the proxy was generated with to a function ``fx``. Does not exist on :ref:`function_result` or :ref:`protected_function_result`. +Sets the value associated with the keys the proxy was generated with to a function ``fx``. Does not exist on :ref:`unsafe_function_result` or :ref:`protected_function_result`. .. code-block:: c++ @@ -152,29 +152,33 @@ Sets the value associated with the keys the proxy was generated with to a functi template proxy& set( T&& value ); -Sets the value associated with the keys the proxy was generated with to ``value``. Does not exist on :ref:`function_result` or :ref:`protected_function_result`. +Sets the value associated with the keys the proxy was generated with to ``value``. Does not exist on :ref:`unsafe_function_result` or :ref:`protected_function_result`. stack_proxy ----------- ``sol::stack_proxy`` is what gets returned by :doc:`sol::variadic_args` and other parts of the framework. It is similar to proxy, but is meant to alias a stack index and not a named variable. -.. _function-result: +.. _unsafe-function-result: -function_result ---------------- +unsafe_function_result +---------------------- -``function_result`` is a temporary-only, intermediate-only implicit conversion worker for when :doc:`function` is called. It is *NOT* meant to be stored or captured with ``auto``. It provides fast access to the desired underlying value. It does not implement ``set`` / ``set_function`` / templated ``operator=``, as is present on :ref:`proxy`. +``unsafe_function_result`` is a temporary-only, intermediate-only implicit conversion worker for when :doc:`function` is called. It is *NOT* meant to be stored or captured with ``auto``. It provides fast access to the desired underlying value. It does not implement ``set`` / ``set_function`` / templated ``operator=``, as is present on :ref:`proxy`. +This type does, however, allow access to multiple underlying values. Use ``result.get(index_offset)`` to retrieve an object of ``Type`` at an offset of ``index_offset`` in the results. Offset is 0 based. Not specifying an argument defaults the value to 0. + .. _protected-function-result: protected_function_result ------------------------- -``protected_function_result`` is a nicer version of ``function_result`` that can be used to detect errors. Its gives safe access to the desired underlying value. It does not implement ``set`` / ``set_function`` / templated ``operator=`` as is present on :ref:`proxy`. +``protected_function_result`` is a nicer version of ``unsafe_function_result`` that can be used to detect errors. Its gives safe access to the desired underlying value. It does not implement ``set`` / ``set_function`` / templated ``operator=`` as is present on :ref:`proxy`. +This type does, however, allow access to multiple underlying values. Use ``result.get(index_offset)`` to retrieve an object of ``Type`` at an offset of ``index_offset`` in the results. Offset is 0 based. Not specifying an argument defaults the value to 0. + .. _note 1: on function objects and proxies diff --git a/docs/source/api/state.rst b/docs/source/api/state.rst index 84c3995e..c67f8603 100644 --- a/docs/source/api/state.rst +++ b/docs/source/api/state.rst @@ -88,7 +88,7 @@ If you need safety, please use the version of these functions with ``safe`` (suc These functions run the desired blob of either code that is in a string, or code that comes from a filename, on the ``lua_State*``. It will not run isolated: any scripts or code run will affect code in the ``lua_State*`` the object uses as well (unless ``local`` is applied to a variable declaration, as specified by the Lua language). Code ran in this fashion is not isolated. If you need isolation, consider creating a new state or traditional Lua sandboxing techniques. -If your script returns a value, you can capture it from the returned :ref:`sol::function_result`/:ref:`sol::protected_function_result`. Note that the plain versions that do not take an environment or a callback function assume that the contents internally not only loaded properly but ran to completion without errors, for the sake of simplicity and performance. +If your script returns a value, you can capture it from the returned :ref:`sol::unsafe_function_result`/:ref:`sol::protected_function_result`. Note that the plain versions that do not take an environment or a callback function assume that the contents internally not only loaded properly but ran to completion without errors, for the sake of simplicity and performance. To handle errors when using the second overload, provide a callable function/object that takes a ``lua_State*`` as its first argument and a ``sol::protected_function_result`` as its second argument. ``sol::script_default_on_error`` and ``sol::script_pass_on_error`` are 2 functions provided by sol that will either generate a traceback error to return / throw (if throwing is allowed); or, pass the error on through and return it to the user (respectively). An example of having your: diff --git a/docs/source/safety.rst b/docs/source/safety.rst index ebffbc57..39c66a15 100644 --- a/docs/source/safety.rst +++ b/docs/source/safety.rst @@ -91,4 +91,4 @@ As a side note, binding functions with default parameters does not magically bin .. warning:: - Do **NOT** save the return type of a :ref:`function_result` with ``auto``, as in ``auto numwoof = woof(20);``, and do NOT store it anywhere unless you are exactly aware of the consequences of messing with the stack. See :ref:`here` for more information. + Do **NOT** save the return type of a :ref:`unsafe_function_result` with ``auto``, as in ``auto numwoof = woof(20);``, and do NOT store it anywhere unless you are exactly aware of the consequences of messing with the stack. See :ref:`here` for more information. diff --git a/examples/indirect_function_calls.cpp b/examples/indirect_function_calls.cpp new file mode 100644 index 00000000..129fc70e --- /dev/null +++ b/examples/indirect_function_calls.cpp @@ -0,0 +1,74 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include + +sol::variadic_results call_it(sol::object function_name, sol::variadic_args args, sol::this_environment env, sol::this_state L) { + sol::state_view lua = L; + // default to global table as environment + sol::environment function_environment = lua.globals(); + if (env) { + // if we have an environment, use that instead + function_environment = env; + } + + // get and call the function + sol::protected_function pf = function_environment[function_name]; + sol::protected_function_result res = pf(args); + + // + sol::variadic_results results; + if (!res.valid()) { + // something went wrong: log/crash/whatever + return results; + } + int returncount = res.return_count(); + for (int i = 0; i < returncount; i++) { + // pass offset to get the object that was returned + sol::object obj = res.get(i); + results.push_back(obj); + } + // return the results + return results; +} + +int main() { + std::cout << "=== indirect function calls example ===" << std::endl; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua["call_it"] = call_it; + + // some functions to call + lua.script(R"( +function add (a, b) + return a + b; +end + +function subtract (a, b) + return a - b; +end + +function log (x) + print(x) +end +)"); + + // call the functions indirectly, using a name + lua.script(R"( + call_it("log", "hiyo") + call_it("log", 24) + subtract_result = call_it("subtract", 5, 1) + add_result = call_it("add", 5, 1) + )"); + + int subtract_result = lua["subtract_result"]; + int add_result = lua["add_result"]; + + assert(add_result == 6); + assert(subtract_result == 4); + + std::cout << std::endl; + return 0; +} diff --git a/sol/forward.hpp b/sol/forward.hpp index f8ce3576..173a73e3 100644 --- a/sol/forward.hpp +++ b/sol/forward.hpp @@ -79,7 +79,7 @@ namespace sol { using main_protected_function = main_safe_function; using stack_protected_function = stack_safe_function; using stack_aligned_protected_function = stack_aligned_safe_function; -#ifdef SOL_SAFE_FUNCTIONS +#ifdef SOL_SAFE_FUNCTION using function = protected_function; using main_function = main_protected_function; using stack_function = stack_protected_function; @@ -92,10 +92,14 @@ namespace sol { #endif using stack_aligned_stack_handler_function = basic_protected_function; - struct function_result; + struct unsafe_function_result; struct protected_function_result; using safe_function_result = protected_function_result; - using unsafe_function_result = function_result; +#ifdef SOL_SAFE_FUNCTION + using function_result = safe_function_result; +#else + using function_result = unsafe_function_result; +#endif template class basic_object; diff --git a/sol/function.hpp b/sol/function.hpp index d5143467..db346710 100644 --- a/sol/function.hpp +++ b/sol/function.hpp @@ -29,7 +29,7 @@ namespace sol { - inline protected_function_result::protected_function_result(function_result&& o) noexcept + inline protected_function_result::protected_function_result(unsafe_function_result&& o) noexcept : L(o.lua_state()), index(o.stack_index()), returncount(o.return_count()), popcount(o.return_count()), err(o.status()) { // Must be manual, otherwise destructor will screw us // return count being 0 is enough to keep things clean @@ -37,7 +37,7 @@ namespace sol { o.abandon(); } - inline protected_function_result& protected_function_result::operator=(function_result&& o) noexcept { + inline protected_function_result& protected_function_result::operator=(unsafe_function_result&& o) noexcept { L = o.lua_state(); index = o.stack_index(); returncount = o.return_count(); @@ -50,14 +50,14 @@ namespace sol { return *this; } - inline function_result::function_result(protected_function_result&& o) noexcept + inline unsafe_function_result::unsafe_function_result(protected_function_result&& o) noexcept : L(o.lua_state()), index(o.stack_index()), returncount(o.return_count()) { // Must be manual, otherwise destructor will screw us // return count being 0 is enough to keep things clean // but we will be thorough o.abandon(); } - inline function_result& function_result::operator=(protected_function_result&& o) noexcept { + inline unsafe_function_result& unsafe_function_result::operator=(protected_function_result&& o) noexcept { L = o.lua_state(); index = o.stack_index(); returncount = o.return_count(); diff --git a/sol/function_result.hpp b/sol/function_result.hpp index 2c939c66..ccbf33e6 100644 --- a/sol/function_result.hpp +++ b/sol/function_result.hpp @@ -22,94 +22,7 @@ #ifndef SOL_FUNCTION_RESULT_HPP #define SOL_FUNCTION_RESULT_HPP -#include "reference.hpp" -#include "tuple.hpp" -#include "stack.hpp" -#include "proxy_base.hpp" -#include - -namespace sol { - struct function_result : public proxy_base { - private: - lua_State* L; - int index; - int returncount; - - public: - function_result() = default; - function_result(lua_State* Ls, int idx = -1, int retnum = 0) - : L(Ls), index(idx), returncount(retnum) { - } - function_result(const function_result&) = default; - function_result& operator=(const function_result&) = default; - function_result(function_result&& o) - : L(o.L), index(o.index), returncount(o.returncount) { - // Must be manual, otherwise destructor will screw us - // return count being 0 is enough to keep things clean - // but will be thorough - o.abandon(); - } - function_result& operator=(function_result&& o) { - L = o.L; - index = o.index; - returncount = o.returncount; - // Must be manual, otherwise destructor will screw us - // return count being 0 is enough to keep things clean - // but will be thorough - o.abandon(); - return *this; - } - - function_result(const protected_function_result& o) = delete; - function_result& operator=(const protected_function_result& o) = delete; - function_result(protected_function_result&& o) noexcept; - function_result& operator=(protected_function_result&& o) noexcept; - - template - decltype(auto) get() const { - return stack::get(L, index); - } - - call_status status() const noexcept { - return call_status::ok; - } - - bool valid() const noexcept { - return status() == call_status::ok || status() == call_status::yielded; - } - - lua_State* lua_state() const { - return L; - }; - int stack_index() const { - return index; - }; - int return_count() const { - return returncount; - }; - void abandon() noexcept { - //L = nullptr; - index = 0; - returncount = 0; - } - ~function_result() { - lua_pop(L, returncount); - } - }; - - namespace stack { - template <> - struct pusher { - static int push(lua_State* L, const function_result& fr) { - int p = 0; - for (int i = 0; i < fr.return_count(); ++i) { - lua_pushvalue(L, i + fr.stack_index()); - ++p; - } - return p; - } - }; - } // namespace stack -} // namespace sol +#include "protected_function_result.hpp" +#include "unsafe_function_result.hpp" #endif // SOL_FUNCTION_RESULT_HPP diff --git a/sol/protected_function_result.hpp b/sol/protected_function_result.hpp index 648851e4..dc315ac8 100644 --- a/sol/protected_function_result.hpp +++ b/sol/protected_function_result.hpp @@ -38,37 +38,43 @@ namespace sol { call_status err; template - decltype(auto) tagged_get(types>) const { + decltype(auto) tagged_get(types>, int index_offset) const { + int target = index + index_offset; if (!valid()) { return optional(nullopt); } - return stack::get>(L, index); + return stack::get>(L, target); } template - decltype(auto) tagged_get(types) const { + decltype(auto) tagged_get(types, int index_offset) const { + int target = index + index_offset; #ifdef SOL_CHECK_ARGUMENTS if (!valid()) { - type_panic_c_str(L, index, type_of(L, index), type::none, "bad get from protected_function_result (is not an error)"); + type t = type_of(L, target); + type_panic_c_str(L, target, t, type::none, "bad get from protected_function_result (is not an error)"); } #endif // Check Argument Safety - return stack::get(L, index); + return stack::get(L, target); } - optional tagged_get(types>) const { + optional tagged_get(types>, int index_offset) const { + int target = index + index_offset; if (valid()) { return nullopt; } - return error(detail::direct_error, stack::get(L, index)); + return error(detail::direct_error, stack::get(L, target)); } - error tagged_get(types) const { + error tagged_get(types, int index_offset) const { + int target = index + index_offset; #ifdef SOL_CHECK_ARGUMENTS if (valid()) { - type_panic_c_str(L, index, type_of(L, index), type::none, "bad get from protected_function_result (is an error)"); + type t = type_of(L, target); + type_panic_c_str(L, target, t, type::none, "bad get from protected_function_result (is an error)"); } #endif // Check Argument Safety - return error(detail::direct_error, stack::get(L, index)); + return error(detail::direct_error, stack::get(L, target)); } public: @@ -98,10 +104,10 @@ namespace sol { return *this; } - protected_function_result(const function_result& o) = delete; - protected_function_result& operator=(const function_result& o) = delete; - protected_function_result(function_result&& o) noexcept; - protected_function_result& operator=(function_result&& o) noexcept; + protected_function_result(const unsafe_function_result& o) = delete; + protected_function_result& operator=(const unsafe_function_result& o) = delete; + protected_function_result(unsafe_function_result&& o) noexcept; + protected_function_result& operator=(unsafe_function_result&& o) noexcept; call_status status() const noexcept { return err; @@ -112,8 +118,8 @@ namespace sol { } template - decltype(auto) get() const { - return tagged_get(types>()); + decltype(auto) get(int index_offset = 0) const { + return tagged_get(types>(), index_offset); } lua_State* lua_state() const noexcept { diff --git a/sol/simple_usertype_metatable.hpp b/sol/simple_usertype_metatable.hpp index ca6d8a7d..1a738249 100644 --- a/sol/simple_usertype_metatable.hpp +++ b/sol/simple_usertype_metatable.hpp @@ -422,14 +422,55 @@ namespace sol { stack::set_field(L, "name", detail::demangle(), type_table.stack_index()); stack::set_field(L, "is", &usertype_detail::is_check, type_table.stack_index()); - auto register_kvp = [&](std::size_t meta_index, stack_reference& t, const std::string& first, object& second) { - meta_function mf = meta_function::construct; - for (std::size_t j = 1; j < properties.size(); ++j) { - mf = static_cast(j); + auto safety_check = [&](const std::string& first) { + for (std::size_t j = 0; j < properties.size(); ++j) { + meta_function mf = static_cast(j); const std::string& mfname = to_string(mf); + bool& prop = properties[j]; + if (mfname != first) + continue; + switch (mf) { + case meta_function::construct: + if (prop) { +#ifndef SOL_NO_EXCEPTIONS + throw error( +#else + assert(false && +#endif + "sol: 2 separate constructor (new) functions were set on this type. Please specify only 1 sol::meta_function::construct/'new' type AND wrap the function in a sol::factories/initializers call, as shown by the documentation and examples, otherwise you may create problems"); + } + break; + case meta_function::garbage_collect: + if (prop) { +#ifndef SOL_NO_EXCEPTIONS + throw error( +#else + assert(false && +#endif + "sol: 2 separate garbage_collect functions were set on this type. Please specify only 1 sol::meta_function::gc type AND wrap the function in a sol::destruct call, as shown by the documentation and examples"); + } + return; + default: + break; + } + prop = true; + break; + } + }; + + for (auto& kvp : varmap.functions) { + auto& first = std::get<0>(kvp); + safety_check(first); + } + + auto register_kvp = [&](std::size_t meta_index, stack_reference& t, const std::string& first, object& second) { + meta_function mf = meta_function::construct; + for (std::size_t j = 0; j < properties.size(); ++j) { + mf = static_cast(j); + const std::string& mfname = to_string(mf); + bool& prop = properties[j]; if (mfname != first) continue; - properties[j] = true; switch (mf) { case meta_function::index: umx.indexfunc = second; @@ -440,6 +481,7 @@ namespace sol { default: break; } + prop = true; break; } switch (meta_index) { diff --git a/sol/stack.hpp b/sol/stack.hpp index 5ec25601..7e0db553 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -232,7 +232,7 @@ namespace sol { } inline void luajit_exception_handler(lua_State* L, int (*handler)(lua_State*, lua_CFunction) = detail::c_trampoline) { -#ifdef SOL_LUAJIT +#if defined(SOL_LUAJIT) && !defined(SOL_EXCEPTIONS_SAFE_PROPAGATION) if (L == nullptr) { return; } diff --git a/sol/stack_proxy.hpp b/sol/stack_proxy.hpp index 0e2b4964..914e2adc 100644 --- a/sol/stack_proxy.hpp +++ b/sol/stack_proxy.hpp @@ -105,7 +105,7 @@ namespace sol { namespace detail { template <> - struct is_speshul : std::true_type {}; + struct is_speshul : std::true_type {}; template <> struct is_speshul : std::true_type {}; @@ -121,15 +121,15 @@ namespace sol { } // namespace detail template <> - struct tie_size : std::integral_constant {}; + struct tie_size : std::integral_constant {}; template - stack_proxy get(const function_result& fr) { + stack_proxy get(const unsafe_function_result& fr) { return stack_proxy(fr.lua_state(), static_cast(fr.stack_index() + I)); } template - stack_proxy get(types t, const function_result& fr) { + stack_proxy get(types t, const unsafe_function_result& fr) { return detail::get(t, index_value(), index_value<0>(), fr); } diff --git a/sol/state_view.hpp b/sol/state_view.hpp index 0c399fb6..aaf38c75 100644 --- a/sol/state_view.hpp +++ b/sol/state_view.hpp @@ -377,7 +377,7 @@ namespace sol { } template - function_result unsafe_script(const string_view& code, const basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + unsafe_function_result unsafe_script(const string_view& code, const basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { detail::typical_chunk_name_t basechunkname = {}; const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); int index = lua_gettop(L); @@ -390,19 +390,19 @@ namespace sol { } int postindex = lua_gettop(L); int returns = postindex - index; - return function_result(L, (std::max)(postindex - (returns - 1), 1), returns); + return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns); } - function_result unsafe_script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + unsafe_function_result unsafe_script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { int index = lua_gettop(L); stack::script(L, code, chunkname, mode); int postindex = lua_gettop(L); int returns = postindex - index; - return function_result(L, (std::max)(postindex - (returns - 1), 1), returns); + return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns); } template - function_result unsafe_script_file(const std::string& filename, const basic_environment& env, load_mode mode = load_mode::any) { + unsafe_function_result unsafe_script_file(const std::string& filename, const basic_environment& env, load_mode mode = load_mode::any) { int index = lua_gettop(L); if (luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())) { lua_error(L); @@ -413,15 +413,15 @@ namespace sol { } int postindex = lua_gettop(L); int returns = postindex - index; - return function_result(L, (std::max)(postindex - (returns - 1), 1), returns); + return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns); } - function_result unsafe_script_file(const std::string& filename, load_mode mode = load_mode::any) { + unsafe_function_result unsafe_script_file(const std::string& filename, load_mode mode = load_mode::any) { int index = lua_gettop(L); stack::script_file(L, filename, mode); int postindex = lua_gettop(L); int returns = postindex - index; - return function_result(L, (std::max)(postindex - (returns - 1), 1), returns); + return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns); } template >> = meta::enabler> @@ -452,7 +452,7 @@ namespace sol { return safe_script_file(filename, env, script_default_on_error, mode); } -#ifdef SOL_SAFE_FUNCTIONS +#ifdef SOL_SAFE_FUNCTION protected_function_result script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return safe_script(code, chunkname, mode); } @@ -461,11 +461,11 @@ namespace sol { return safe_script_file(filename, mode); } #else - function_result script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + unsafe_function_result script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return unsafe_script(code, chunkname, mode); } - function_result script_file(const std::string& filename, load_mode mode = load_mode::any) { + unsafe_function_result script_file(const std::string& filename, load_mode mode = load_mode::any) { return unsafe_script_file(filename, mode); } #endif diff --git a/sol/types.hpp b/sol/types.hpp index b9c70235..1f3f0f37 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -1085,7 +1085,7 @@ namespace sol { template struct is_lua_primitive : std::true_type {}; template <> - struct is_lua_primitive : std::true_type {}; + struct is_lua_primitive : std::true_type {}; template <> struct is_lua_primitive : std::true_type {}; template diff --git a/sol/unsafe_function.hpp b/sol/unsafe_function.hpp index 3aa553a2..a81e038b 100644 --- a/sol/unsafe_function.hpp +++ b/sol/unsafe_function.hpp @@ -53,13 +53,13 @@ namespace sol { luacall(n, 0); } - function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n) const { + unsafe_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n) const { int stacksize = lua_gettop(lua_state()); int firstreturn = (std::max)(1, stacksize - static_cast(n)); luacall(n, LUA_MULTRET); int poststacksize = lua_gettop(lua_state()); int returncount = poststacksize - (firstreturn - 1); - return function_result(lua_state(), firstreturn, returncount); + return unsafe_function_result(lua_state(), firstreturn, returncount); } public: @@ -113,7 +113,7 @@ namespace sol { } template - function_result operator()(Args&&... args) const { + unsafe_function_result operator()(Args&&... args) const { return call<>(std::forward(args)...); } diff --git a/sol/unsafe_function_result.hpp b/sol/unsafe_function_result.hpp new file mode 100644 index 00000000..acd86913 --- /dev/null +++ b/sol/unsafe_function_result.hpp @@ -0,0 +1,115 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2017 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SOL_UNSAFE_FUNCTION_RESULT_HPP +#define SOL_UNSAFE_FUNCTION_RESULT_HPP + +#include "reference.hpp" +#include "tuple.hpp" +#include "stack.hpp" +#include "proxy_base.hpp" +#include + +namespace sol { + struct unsafe_function_result : public proxy_base { + private: + lua_State* L; + int index; + int returncount; + + public: + unsafe_function_result() = default; + unsafe_function_result(lua_State* Ls, int idx = -1, int retnum = 0) + : L(Ls), index(idx), returncount(retnum) { + } + unsafe_function_result(const unsafe_function_result&) = default; + unsafe_function_result& operator=(const unsafe_function_result&) = default; + unsafe_function_result(unsafe_function_result&& o) + : L(o.L), index(o.index), returncount(o.returncount) { + // Must be manual, otherwise destructor will screw us + // return count being 0 is enough to keep things clean + // but will be thorough + o.abandon(); + } + unsafe_function_result& operator=(unsafe_function_result&& o) { + L = o.L; + index = o.index; + returncount = o.returncount; + // Must be manual, otherwise destructor will screw us + // return count being 0 is enough to keep things clean + // but will be thorough + o.abandon(); + return *this; + } + + unsafe_function_result(const protected_function_result& o) = delete; + unsafe_function_result& operator=(const protected_function_result& o) = delete; + unsafe_function_result(protected_function_result&& o) noexcept; + unsafe_function_result& operator=(protected_function_result&& o) noexcept; + + template + decltype(auto) get(int index_offset = 0) const { + return stack::get(L, index + index_offset); + } + + call_status status() const noexcept { + return call_status::ok; + } + + bool valid() const noexcept { + return status() == call_status::ok || status() == call_status::yielded; + } + + lua_State* lua_state() const { + return L; + }; + int stack_index() const { + return index; + }; + int return_count() const { + return returncount; + }; + void abandon() noexcept { + //L = nullptr; + index = 0; + returncount = 0; + } + ~unsafe_function_result() { + lua_pop(L, returncount); + } + }; + + namespace stack { + template <> + struct pusher { + static int push(lua_State* L, const unsafe_function_result& fr) { + int p = 0; + for (int i = 0; i < fr.return_count(); ++i) { + lua_pushvalue(L, i + fr.stack_index()); + ++p; + } + return p; + } + }; + } // namespace stack +} // namespace sol + +#endif // SOL_UNSAFE_FUNCTION_RESULT_HPP diff --git a/sol/usertype_metatable.hpp b/sol/usertype_metatable.hpp index edb7cfe8..8a74285a 100644 --- a/sol/usertype_metatable.hpp +++ b/sol/usertype_metatable.hpp @@ -460,35 +460,47 @@ namespace sol { return; } luaL_Reg reg = usertype_detail::make_reg(std::forward(n), make_func()); - for (std::size_t i = 1; i < properties.size(); ++i) { + for (std::size_t i = 0; i < properties.size(); ++i) { meta_function mf = static_cast(i); + bool& prop = properties[i]; const std::string& mfname = to_string(mf); if (mfname == reg.name) { switch (mf) { + case meta_function::construct: + if (prop) { +#ifndef SOL_NO_EXCEPTIONS + throw error( +#else + assert(false && +#endif + "sol: 2 separate constructor (new) functions were set on this type. Please specify only 1 sol::meta_function::construct/'new' type AND wrap the function in a sol::factories/initializers call, as shown by the documentation and examples, otherwise you may create problems"); + } + break; case meta_function::garbage_collect: if (destructfunc != nullptr) { -#ifdef SOL_NO_EXCEPTIONS - throw error("sol: 2 separate garbage_collect functions were set on this type. Please specify only 1 sol::meta_function::gc type AND wrap the function in a sol::destruct call, as shown by the documentation and examples"); +#ifndef SOL_NO_EXCEPTIONS + throw error( #else - assert(false && "sol: 2 separate garbage_collect functions were set on this type. Please specify only 1 sol::meta_function::gc type AND wrap the function in a sol::destruct call, as shown by the documentation and examples"); + assert(false && #endif + "sol: 2 separate garbage_collect functions were set on this type. Please specify only 1 sol::meta_function::gc type AND wrap the function in a sol::destruct call, as shown by the documentation and examples"); } destructfunc = reg.func; return; case meta_function::index: indexfunc = reg.func; mustindex = true; - properties[i] = true; + prop = true; return; case meta_function::new_index: newindexfunc = reg.func; mustindex = true; - properties[i] = true; + prop = true; return; default: break; } - properties[i] = true; + prop = true; break; } } diff --git a/tests/test_state.cpp b/tests/test_state.cpp index d9246570..0f410310 100644 --- a/tests/test_state.cpp +++ b/tests/test_state.cpp @@ -653,7 +653,7 @@ TEST_CASE("state/script return converts", "make sure that script return values a sol::state lua; sol::protected_function_result r1 = lua.unsafe_script("return 2"); - sol::function_result r2 = lua.safe_script("return 3"); + sol::unsafe_function_result r2 = lua.safe_script("return 3"); int v1 = r1; int v2 = r2;