Remove as many of the std::string error handlers as possible to avoid memory leaks in C mode

Add some checks for container usertypes that masquerade as convertible from tables
Make sure shared_ptr-like types that are copyable are returned directly (but do not do this for move-only types)
This commit is contained in:
ThePhD 2019-11-09 04:53:48 +00:00
parent 88355fcdf8
commit ce32549bc6
No known key found for this signature in database
GPG Key ID: 1509DB1C0F702BFA
9 changed files with 328 additions and 260 deletions

View File

@ -147,7 +147,8 @@ namespace sol {
typedef meta::tuple_types<typename traits::return_type> return_types; typedef meta::tuple_types<typename traits::return_type> return_types;
typedef typename traits::free_args_list args_list; typedef typename traits::free_args_list args_list;
// compile-time eliminate any functions that we know ahead of time are of improper arity // compile-time eliminate any functions that we know ahead of time are of improper arity
if constexpr (!traits::runtime_variadics_t::value && meta::find_in_pack_v<meta::index_value<traits::free_arity>, meta::index_value<M>...>::value) { if constexpr (!traits::runtime_variadics_t::value
&& meta::find_in_pack_v<meta::index_value<traits::free_arity>, meta::index_value<M>...>::value) {
return overload_match_arity(types<Fxs...>(), return overload_match_arity(types<Fxs...>(),
std::index_sequence<In...>(), std::index_sequence<In...>(),
std::index_sequence<M...>(), std::index_sequence<M...>(),
@ -237,7 +238,8 @@ namespace sol {
typedef meta::tuple_types<typename traits::return_type> return_types; typedef meta::tuple_types<typename traits::return_type> return_types;
typedef typename traits::free_args_list args_list; typedef typename traits::free_args_list args_list;
// compile-time eliminate any functions that we know ahead of time are of improper arity // compile-time eliminate any functions that we know ahead of time are of improper arity
if constexpr (!traits::runtime_variadics_t::value && meta::find_in_pack_v<meta::index_value<traits::free_arity>, meta::index_value<M>...>::value) { if constexpr (!traits::runtime_variadics_t::value
&& meta::find_in_pack_v<meta::index_value<traits::free_arity>, meta::index_value<M>...>::value) {
return overload_match_arity(types<Fx1, Fxs...>(), return overload_match_arity(types<Fx1, Fxs...>(),
std::index_sequence<I1, In...>(), std::index_sequence<I1, In...>(),
std::index_sequence<M...>(), std::index_sequence<M...>(),
@ -373,6 +375,7 @@ namespace sol {
} }
else { else {
if constexpr (std::is_const_v<meta::unwrapped_t<T>>) { if constexpr (std::is_const_v<meta::unwrapped_t<T>>) {
(void)f;
return luaL_error(L, "sol: cannot write to a readonly (const) variable"); return luaL_error(L, "sol: cannot write to a readonly (const) variable");
} }
else { else {
@ -502,8 +505,13 @@ namespace sol {
else { else {
using returns_list = typename wrap::returns_list; using returns_list = typename wrap::returns_list;
using caller = typename wrap::caller; using caller = typename wrap::caller;
return stack::call_into_lua<checked, clean_stack>( return stack::call_into_lua<checked, clean_stack>(returns_list(),
returns_list(), types<>(), L, boost + (is_variable ? 3 : 2), caller(), std::forward<Fx>(fx), std::forward<Args>(args)...); types<>(),
L,
boost + (is_variable ? 3 : 2),
caller(),
std::forward<Fx>(fx),
std::forward<Args>(args)...);
} }
} }
else { else {
@ -727,8 +735,7 @@ namespace sol {
template <typename F, typename... Args> template <typename F, typename... Args>
static int call(lua_State* L, F&& f, Args&&... args) { static int call(lua_State* L, F&& f, Args&&... args) {
constexpr bool is_specialized = meta::any< constexpr bool is_specialized = meta::any<std::is_same<U, detail::no_prop>,
std::is_same<U, detail::no_prop>,
meta::is_specialization_of<U, var_wrapper>, meta::is_specialization_of<U, var_wrapper>,
meta::is_specialization_of<U, constructor_wrapper>, meta::is_specialization_of<U, constructor_wrapper>,
meta::is_specialization_of<U, constructor_list>, meta::is_specialization_of<U, constructor_list>,
@ -861,7 +868,8 @@ namespace sol {
} }
} }
template <typename T, bool is_index, bool is_variable, typename F, int start = 1, bool checked = detail::default_safe_function_calls, bool clean_stack = true> template <typename T, bool is_index, bool is_variable, typename F, int start = 1, bool checked = detail::default_safe_function_calls,
bool clean_stack = true>
inline int call_user(lua_State* L) { inline int call_user(lua_State* L) {
auto& fx = stack::unqualified_get<user<F>>(L, upvalue_index(start)); auto& fx = stack::unqualified_get<user<F>>(L, upvalue_index(start));
return call_wrapped<T, is_index, is_variable, 0, checked, clean_stack>(L, fx); return call_wrapped<T, is_index, is_variable, 0, checked, clean_stack>(L, fx);

View File

@ -27,6 +27,8 @@
#include "types.hpp" #include "types.hpp"
#include "demangle.hpp" #include "demangle.hpp"
#include <cstdio>
namespace sol { namespace sol {
namespace detail { namespace detail {
@ -46,21 +48,20 @@ namespace sol {
constexpr const char* not_enough_stack_space_environment = "not enough space left on Lua stack to retrieve environment"; constexpr const char* not_enough_stack_space_environment = "not enough space left on Lua stack to retrieve environment";
constexpr const char* protected_function_error = "caught (...) unknown error during protected_function call"; constexpr const char* protected_function_error = "caught (...) unknown error during protected_function call";
inline void accumulate_and_mark(const std::string& n, std::string& addendum, int& marker) { inline void accumulate_and_mark(const std::string& n, std::string& aux_message, int& marker) {
if (marker > 0) { if (marker > 0) {
addendum += ", "; aux_message += ", ";
} }
addendum += n; aux_message += n;
++marker; ++marker;
} }
} } // namespace detail
inline std::string associated_type_name(lua_State* L, int index, type t) { inline std::string associated_type_name(lua_State* L, int index, type t) {
switch (t) { switch (t) {
case type::poly: case type::poly:
return "anything"; return "anything";
case type::userdata: case type::userdata: {
{
#if defined(SOL_SAFE_STACK_CHECK) && SOL_SAFE_STACK_CHECK #if defined(SOL_SAFE_STACK_CHECK) && SOL_SAFE_STACK_CHECK
luaL_checkstack(L, 2, "not enough space to push get the type name"); luaL_checkstack(L, 2, "not enough space to push get the type name");
#endif // make sure stack doesn't overflow #endif // make sure stack doesn't overflow
@ -81,63 +82,67 @@ namespace sol {
return lua_typename(L, static_cast<int>(t)); return lua_typename(L, static_cast<int>(t));
} }
inline int type_panic_string(lua_State* L, int index, type expected, type actual, const std::string& message = "") noexcept(false) { inline int push_type_panic_string(lua_State* L, int index, type expected, type actual, string_view message, string_view aux_message) noexcept {
const char* err = message.empty() ? "stack index %d, expected %s, received %s" : "stack index %d, expected %s, received %s: %s"; const char* err = message.size() == 0
std::string actualname = associated_type_name(L, index, actual); ? (aux_message.size() == 0 ? "stack index %d, expected %s, received %s" : "stack index %d, expected %s, received %s: %s")
return luaL_error(L, err, index, : "stack index %d, expected %s, received %s: %s %s";
expected == type::poly ? "anything" : lua_typename(L, static_cast<int>(expected)), const char* type_name = expected == type::poly ? "anything" : lua_typename(L, static_cast<int>(expected));
actualname.c_str(), {
message.c_str()); std::string actual_name = associated_type_name(L, index, actual);
lua_pushfstring(L, err, index, type_name, actual_name.c_str(), message.data(), aux_message.data());
}
return 1;
}
inline int type_panic_string(lua_State* L, int index, type expected, type actual, string_view message = "") noexcept(false) {
push_type_panic_string(L, index, expected, actual, message, "");
return lua_error(L);
} }
inline int type_panic_c_str(lua_State* L, int index, type expected, type actual, const char* message = nullptr) noexcept(false) { inline int type_panic_c_str(lua_State* L, int index, type expected, type actual, const char* message = nullptr) noexcept(false) {
const char* err = message == nullptr || (std::char_traits<char>::length(message) == 0) ? "stack index %d, expected %s, received %s" : "stack index %d, expected %s, received %s: %s"; push_type_panic_string(L, index, expected, actual, message == nullptr ? "" : message, "");
std::string actualname = associated_type_name(L, index, actual); return lua_error(L);
return luaL_error(L, err, index,
expected == type::poly ? "anything" : lua_typename(L, static_cast<int>(expected)),
actualname.c_str(),
message);
} }
struct type_panic_t { struct type_panic_t {
int operator()(lua_State* L, int index, type expected, type actual) const noexcept(false) { int operator()(lua_State* L, int index, type expected, type actual) const noexcept(false) {
return type_panic_c_str(L, index, expected, actual, nullptr); return type_panic_c_str(L, index, expected, actual, nullptr);
} }
int operator()(lua_State* L, int index, type expected, type actual, const char* message) const noexcept(false) { int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
return type_panic_c_str(L, index, expected, actual, message); return type_panic_c_str(L, index, expected, actual, message.data());
}
int operator()(lua_State* L, int index, type expected, type actual, const std::string& message) const noexcept(false) {
return type_panic_string(L, index, expected, actual, message);
} }
}; };
const type_panic_t type_panic = {}; const type_panic_t type_panic = {};
struct constructor_handler { struct constructor_handler {
int operator()(lua_State* L, int index, type expected, type actual, const std::string& message) const noexcept(false) { int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
std::string str = "(type check failed in constructor)"; push_type_panic_string(L, index, expected, actual, message, "(type check failed in constructor)");
return type_panic_string(L, index, expected, actual, message.empty() ? str : message + " " + str); return lua_error(L);
} }
}; };
template <typename F = void> template <typename F = void>
struct argument_handler { struct argument_handler {
int operator()(lua_State* L, int index, type expected, type actual, const std::string& message) const noexcept(false) { int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
std::string str = "(bad argument to variable or function call)"; push_type_panic_string(L, index, expected, actual, message, "(bad argument to variable or function call)");
return type_panic_string(L, index, expected, actual, message.empty() ? str : message + " " + str ); return lua_error(L);
} }
}; };
template <typename R, typename... Args> template <typename R, typename... Args>
struct argument_handler<types<R, Args...>> { struct argument_handler<types<R, Args...>> {
int operator()(lua_State* L, int index, type expected, type actual, const std::string& message) const noexcept(false) { int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
std::string addendum = "(bad argument into '"; {
addendum += detail::demangle<R>(); std::string aux_message = "(bad argument into '";
addendum += "("; aux_message += detail::demangle<R>();
aux_message += "(";
int marker = 0; int marker = 0;
(void)detail::swallow{int(), (detail::accumulate_and_mark(detail::demangle<Args>(), addendum, marker), int())...}; (void)detail::swallow{ int(), (detail::accumulate_and_mark(detail::demangle<Args>(), aux_message, marker), int())... };
addendum += ")')"; aux_message += ")')";
return type_panic_string(L, index, expected, actual, message.empty() ? addendum : message + " " + addendum); push_type_panic_string(L, index, expected, actual, message, aux_message);
}
return lua_error(L);
} }
}; };

View File

@ -71,9 +71,9 @@ namespace sol {
} }
template <typename T> template <typename T>
inline decltype(auto) deref_non_pointer(T&& item) { inline decltype(auto) deref_move_only(T&& item) {
using Tu = meta::unqualified_t<T>; using Tu = meta::unqualified_t<T>;
if constexpr (meta::is_pointer_like_v<Tu> && !std::is_pointer_v<Tu>) { if constexpr (meta::is_pointer_like_v<Tu> && !std::is_pointer_v<Tu> && !std::is_copy_constructible_v<Tu>) {
return *std::forward<T>(item); return *std::forward<T>(item);
} }
else { else {

View File

@ -491,7 +491,7 @@ namespace sol {
captured_type; captured_type;
typedef typename meta::iterator_tag<iterator>::type iterator_category; typedef typename meta::iterator_tag<iterator>::type iterator_category;
typedef std::is_same<iterator_category, std::input_iterator_tag> is_input_iterator; typedef std::is_same<iterator_category, std::input_iterator_tag> is_input_iterator;
typedef meta::conditional_t<is_input_iterator::value, V, decltype(detail::deref_non_pointer(std::declval<captured_type>()))> push_type; typedef meta::conditional_t<is_input_iterator::value, V, decltype(detail::deref_move_only(std::declval<captured_type>()))> push_type;
typedef std::is_copy_assignable<V> is_copyable; typedef std::is_copy_assignable<V> is_copyable;
typedef meta::neg<meta::any<std::is_const<V>, std::is_const<std::remove_reference_t<iterator_return>>, meta::neg<is_copyable>>> is_writable; typedef meta::neg<meta::any<std::is_const<V>, std::is_const<std::remove_reference_t<iterator_return>>, meta::neg<is_copyable>>> is_writable;
typedef meta::unqualified_t<decltype(get_key(is_associative(), std::declval<std::add_lvalue_reference_t<value_type>>()))> key_type; typedef meta::unqualified_t<decltype(get_key(is_associative(), std::declval<std::add_lvalue_reference_t<value_type>>()))> key_type;
@ -564,12 +564,12 @@ namespace sol {
template <typename Iter> template <typename Iter>
static detail::error_result get_associative(std::true_type, lua_State* L, Iter& it) { static detail::error_result get_associative(std::true_type, lua_State* L, Iter& it) {
decltype(auto) v = *it; decltype(auto) v = *it;
return stack::stack_detail::push_reference<push_type>(L, detail::deref_non_pointer(v.second)); return stack::stack_detail::push_reference<push_type>(L, detail::deref_move_only(v.second));
} }
template <typename Iter> template <typename Iter>
static detail::error_result get_associative(std::false_type, lua_State* L, Iter& it) { static detail::error_result get_associative(std::false_type, lua_State* L, Iter& it) {
return stack::stack_detail::push_reference<push_type>(L, detail::deref_non_pointer(*it)); return stack::stack_detail::push_reference<push_type>(L, detail::deref_move_only(*it));
} }
static detail::error_result get_category(std::input_iterator_tag, lua_State* L, T& self, K& key) { static detail::error_result get_category(std::input_iterator_tag, lua_State* L, T& self, K& key) {
@ -1144,7 +1144,7 @@ namespace sol {
else { else {
p = stack::push_reference(L, it->first); p = stack::push_reference(L, it->first);
} }
p += stack::stack_detail::push_reference<push_type>(L, detail::deref_non_pointer(it->second)); p += stack::stack_detail::push_reference<push_type>(L, detail::deref_move_only(it->second));
std::advance(it, 1); std::advance(it, 1);
return p; return p;
} }
@ -1165,7 +1165,7 @@ namespace sol {
else { else {
p = stack::stack_detail::push_reference(L, k + 1); p = stack::stack_detail::push_reference(L, k + 1);
} }
p += stack::stack_detail::push_reference<push_type>(L, detail::deref_non_pointer(*it)); p += stack::stack_detail::push_reference<push_type>(L, detail::deref_move_only(*it));
std::advance(it, 1); std::advance(it, 1);
return p; return p;
} }
@ -1391,7 +1391,7 @@ namespace sol {
} }
int p; int p;
p = stack::push(L, k + 1); p = stack::push(L, k + 1);
p += stack::push_reference(L, detail::deref_non_pointer(*it)); p += stack::push_reference(L, detail::deref_move_only(*it));
std::advance(it, 1); std::advance(it, 1);
return p; return p;
} }
@ -1424,7 +1424,7 @@ namespace sol {
if (idx >= static_cast<std::ptrdiff_t>(std::extent<T>::value) || idx < 0) { if (idx >= static_cast<std::ptrdiff_t>(std::extent<T>::value) || idx < 0) {
return stack::push(L, lua_nil); return stack::push(L, lua_nil);
} }
return stack::push_reference(L, detail::deref_non_pointer(self[idx])); return stack::push_reference(L, detail::deref_move_only(self[idx]));
} }
static int index_get(lua_State* L) { static int index_get(lua_State* L) {

View File

@ -175,10 +175,7 @@ namespace sol { namespace u_detail {
index_call_storage* p_ics = nullptr; index_call_storage* p_ics = nullptr;
usertype_storage_base* p_usb = nullptr; usertype_storage_base* p_usb = nullptr;
void* p_derived_usb = nullptr; void* p_derived_usb = nullptr;
lua_CFunction idx_call = nullptr, lua_CFunction idx_call = nullptr, new_idx_call = nullptr, meta_idx_call = nullptr, meta_new_idx_call = nullptr;
new_idx_call = nullptr,
meta_idx_call = nullptr,
meta_new_idx_call = nullptr;
change_indexing_mem_func change_indexing; change_indexing_mem_func change_indexing;
void operator()(lua_State* L, submetatable_type smt, reference& fast_index_table) { void operator()(lua_State* L, submetatable_type smt, reference& fast_index_table) {
@ -196,14 +193,7 @@ namespace sol { namespace u_detail {
int fast_index_table_push = fast_index_table.push(); int fast_index_table_push = fast_index_table.push();
stack_reference t(L, -fast_index_table_push); stack_reference t(L, -fast_index_table_push);
if (poison_indexing) { if (poison_indexing) {
(usb.*change_indexing)(L, (usb.*change_indexing)(L, smt, p_derived_usb, t, idx_call, new_idx_call, meta_idx_call, meta_new_idx_call);
smt,
p_derived_usb,
t,
idx_call,
new_idx_call,
meta_idx_call,
meta_new_idx_call);
} }
if (is_destruction if (is_destruction
&& (smt == submetatable_type::reference || smt == submetatable_type::const_reference || smt == submetatable_type::named && (smt == submetatable_type::reference || smt == submetatable_type::const_reference || smt == submetatable_type::named
@ -264,14 +254,7 @@ namespace sol { namespace u_detail {
stack::set_field(L, detail::base_class_check_key(), reinterpret_cast<void*>(base_class_check_func), t.stack_index()); stack::set_field(L, detail::base_class_check_key(), reinterpret_cast<void*>(base_class_check_func), t.stack_index());
stack::set_field(L, detail::base_class_cast_key(), reinterpret_cast<void*>(base_class_cast_func), t.stack_index()); stack::set_field(L, detail::base_class_cast_key(), reinterpret_cast<void*>(base_class_cast_func), t.stack_index());
// change indexing, forcefully // change indexing, forcefully
(p_usb->*change_indexing)(L, (p_usb->*change_indexing)(L, smt, p_derived_usb, t, idx_call, new_idx_call, meta_idx_call, meta_new_idx_call);
smt,
p_derived_usb,
t,
idx_call,
new_idx_call,
meta_idx_call,
meta_new_idx_call);
t.pop(); t.pop();
} }
}; };
@ -279,7 +262,8 @@ namespace sol { namespace u_detail {
struct binding_data_equals { struct binding_data_equals {
void* binding_data; void* binding_data;
binding_data_equals(void* b) : binding_data(b) {} binding_data_equals(void* b) : binding_data(b) {
}
bool operator()(const std::unique_ptr<binding_base>& ptr) const { bool operator()(const std::unique_ptr<binding_base>& ptr) const {
return binding_data == ptr->data(); return binding_data == ptr->data();
@ -644,6 +628,7 @@ namespace sol { namespace u_detail {
using Binding = binding<KeyU, ValueU, T>; using Binding = binding<KeyU, ValueU, T>;
using is_var_bind = is_variable_binding<ValueU>; using is_var_bind = is_variable_binding<ValueU>;
if constexpr (std::is_same_v<KeyU, call_construction>) { if constexpr (std::is_same_v<KeyU, call_construction>) {
(void)key;
std::unique_ptr<Binding> p_binding = std::make_unique<Binding>(std::forward<Value>(value)); std::unique_ptr<Binding> p_binding = std::make_unique<Binding>(std::forward<Value>(value));
Binding& b = *p_binding; Binding& b = *p_binding;
this->storage.push_back(std::move(p_binding)); this->storage.push_back(std::move(p_binding));
@ -689,9 +674,10 @@ namespace sol { namespace u_detail {
void* derived_this = static_cast<void*>(static_cast<usertype_storage<T>*>(this)); void* derived_this = static_cast<void*>(static_cast<usertype_storage<T>*>(this));
index_call_storage ics; index_call_storage ics;
ics.binding_data = b.data(); ics.binding_data = b.data();
ics.index = is_index || is_static_index ? &Binding::template call_with_<true, is_var_bind::value> : &Binding::template index_call_with_<true, is_var_bind::value>; ics.index = is_index || is_static_index ? &Binding::template call_with_<true, is_var_bind::value>
ics.new_index : &Binding::template index_call_with_<true, is_var_bind::value>;
= is_new_index || is_static_new_index ? &Binding::template call_with_<false, is_var_bind::value> : &Binding::template index_call_with_<false, is_var_bind::value>; ics.new_index = is_new_index || is_static_new_index ? &Binding::template call_with_<false, is_var_bind::value>
: &Binding::template index_call_with_<false, is_var_bind::value>;
string_for_each_metatable_func for_each_fx; string_for_each_metatable_func for_each_fx;
for_each_fx.is_destruction = is_destruction; for_each_fx.is_destruction = is_destruction;

View File

@ -20,8 +20,8 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// This file was generated with a script. // This file was generated with a script.
// Generated 2019-10-02 08:43:39.434050 UTC // Generated 2019-11-09 04:52:35.824332 UTC
// This header was generated with sol v3.0.3 (revision 908074e) // This header was generated with sol v3.0.3 (revision 88355fc)
// https://github.com/ThePhD/sol2 // https://github.com/ThePhD/sol2
#ifndef SOL_SINGLE_INCLUDE_FORWARD_HPP #ifndef SOL_SINGLE_INCLUDE_FORWARD_HPP

View File

@ -20,8 +20,8 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// This file was generated with a script. // This file was generated with a script.
// Generated 2019-10-02 08:43:38.869924 UTC // Generated 2019-11-09 04:52:35.317324 UTC
// This header was generated with sol v3.0.3 (revision 908074e) // This header was generated with sol v3.0.3 (revision 88355fc)
// https://github.com/ThePhD/sol2 // https://github.com/ThePhD/sol2
#ifndef SOL_SINGLE_INCLUDE_HPP #ifndef SOL_SINGLE_INCLUDE_HPP
@ -1228,9 +1228,9 @@ namespace sol {
} }
template <typename T> template <typename T>
inline decltype(auto) deref_non_pointer(T&& item) { inline decltype(auto) deref_move_only(T&& item) {
using Tu = meta::unqualified_t<T>; using Tu = meta::unqualified_t<T>;
if constexpr (meta::is_pointer_like_v<Tu> && !std::is_pointer_v<Tu>) { if constexpr (meta::is_pointer_like_v<Tu> && !std::is_pointer_v<Tu> && !std::is_copy_constructible_v<Tu>) {
return *std::forward<T>(item); return *std::forward<T>(item);
} }
else { else {
@ -8266,6 +8266,8 @@ namespace sol {
// beginning of sol/error_handler.hpp // beginning of sol/error_handler.hpp
#include <cstdio>
namespace sol { namespace sol {
namespace detail { namespace detail {
@ -8285,21 +8287,20 @@ namespace sol {
constexpr const char* not_enough_stack_space_environment = "not enough space left on Lua stack to retrieve environment"; constexpr const char* not_enough_stack_space_environment = "not enough space left on Lua stack to retrieve environment";
constexpr const char* protected_function_error = "caught (...) unknown error during protected_function call"; constexpr const char* protected_function_error = "caught (...) unknown error during protected_function call";
inline void accumulate_and_mark(const std::string& n, std::string& addendum, int& marker) { inline void accumulate_and_mark(const std::string& n, std::string& aux_message, int& marker) {
if (marker > 0) { if (marker > 0) {
addendum += ", "; aux_message += ", ";
} }
addendum += n; aux_message += n;
++marker; ++marker;
} }
} } // namespace detail
inline std::string associated_type_name(lua_State* L, int index, type t) { inline std::string associated_type_name(lua_State* L, int index, type t) {
switch (t) { switch (t) {
case type::poly: case type::poly:
return "anything"; return "anything";
case type::userdata: case type::userdata: {
{
#if defined(SOL_SAFE_STACK_CHECK) && SOL_SAFE_STACK_CHECK #if defined(SOL_SAFE_STACK_CHECK) && SOL_SAFE_STACK_CHECK
luaL_checkstack(L, 2, "not enough space to push get the type name"); luaL_checkstack(L, 2, "not enough space to push get the type name");
#endif // make sure stack doesn't overflow #endif // make sure stack doesn't overflow
@ -8320,63 +8321,67 @@ namespace sol {
return lua_typename(L, static_cast<int>(t)); return lua_typename(L, static_cast<int>(t));
} }
inline int type_panic_string(lua_State* L, int index, type expected, type actual, const std::string& message = "") noexcept(false) { inline int push_type_panic_string(lua_State* L, int index, type expected, type actual, string_view message, string_view aux_message) noexcept {
const char* err = message.empty() ? "stack index %d, expected %s, received %s" : "stack index %d, expected %s, received %s: %s"; const char* err = message.size() == 0
std::string actualname = associated_type_name(L, index, actual); ? (aux_message.size() == 0 ? "stack index %d, expected %s, received %s" : "stack index %d, expected %s, received %s: %s")
return luaL_error(L, err, index, : "stack index %d, expected %s, received %s: %s %s";
expected == type::poly ? "anything" : lua_typename(L, static_cast<int>(expected)), const char* type_name = expected == type::poly ? "anything" : lua_typename(L, static_cast<int>(expected));
actualname.c_str(), {
message.c_str()); std::string actual_name = associated_type_name(L, index, actual);
lua_pushfstring(L, err, index, type_name, actual_name.c_str(), message.data(), aux_message.data());
}
return 1;
}
inline int type_panic_string(lua_State* L, int index, type expected, type actual, string_view message = "") noexcept(false) {
push_type_panic_string(L, index, expected, actual, message, "");
return lua_error(L);
} }
inline int type_panic_c_str(lua_State* L, int index, type expected, type actual, const char* message = nullptr) noexcept(false) { inline int type_panic_c_str(lua_State* L, int index, type expected, type actual, const char* message = nullptr) noexcept(false) {
const char* err = message == nullptr || (std::char_traits<char>::length(message) == 0) ? "stack index %d, expected %s, received %s" : "stack index %d, expected %s, received %s: %s"; push_type_panic_string(L, index, expected, actual, message == nullptr ? "" : message, "");
std::string actualname = associated_type_name(L, index, actual); return lua_error(L);
return luaL_error(L, err, index,
expected == type::poly ? "anything" : lua_typename(L, static_cast<int>(expected)),
actualname.c_str(),
message);
} }
struct type_panic_t { struct type_panic_t {
int operator()(lua_State* L, int index, type expected, type actual) const noexcept(false) { int operator()(lua_State* L, int index, type expected, type actual) const noexcept(false) {
return type_panic_c_str(L, index, expected, actual, nullptr); return type_panic_c_str(L, index, expected, actual, nullptr);
} }
int operator()(lua_State* L, int index, type expected, type actual, const char* message) const noexcept(false) { int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
return type_panic_c_str(L, index, expected, actual, message); return type_panic_c_str(L, index, expected, actual, message.data());
}
int operator()(lua_State* L, int index, type expected, type actual, const std::string& message) const noexcept(false) {
return type_panic_string(L, index, expected, actual, message);
} }
}; };
const type_panic_t type_panic = {}; const type_panic_t type_panic = {};
struct constructor_handler { struct constructor_handler {
int operator()(lua_State* L, int index, type expected, type actual, const std::string& message) const noexcept(false) { int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
std::string str = "(type check failed in constructor)"; push_type_panic_string(L, index, expected, actual, message, "(type check failed in constructor)");
return type_panic_string(L, index, expected, actual, message.empty() ? str : message + " " + str); return lua_error(L);
} }
}; };
template <typename F = void> template <typename F = void>
struct argument_handler { struct argument_handler {
int operator()(lua_State* L, int index, type expected, type actual, const std::string& message) const noexcept(false) { int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
std::string str = "(bad argument to variable or function call)"; push_type_panic_string(L, index, expected, actual, message, "(bad argument to variable or function call)");
return type_panic_string(L, index, expected, actual, message.empty() ? str : message + " " + str ); return lua_error(L);
} }
}; };
template <typename R, typename... Args> template <typename R, typename... Args>
struct argument_handler<types<R, Args...>> { struct argument_handler<types<R, Args...>> {
int operator()(lua_State* L, int index, type expected, type actual, const std::string& message) const noexcept(false) { int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
std::string addendum = "(bad argument into '"; {
addendum += detail::demangle<R>(); std::string aux_message = "(bad argument into '";
addendum += "("; aux_message += detail::demangle<R>();
aux_message += "(";
int marker = 0; int marker = 0;
(void)detail::swallow{int(), (detail::accumulate_and_mark(detail::demangle<Args>(), addendum, marker), int())...}; (void)detail::swallow{ int(), (detail::accumulate_and_mark(detail::demangle<Args>(), aux_message, marker), int())... };
addendum += ")')"; aux_message += ")')";
return type_panic_string(L, index, expected, actual, message.empty() ? addendum : message + " " + addendum); push_type_panic_string(L, index, expected, actual, message, aux_message);
}
return lua_error(L);
} }
}; };
@ -16241,7 +16246,8 @@ namespace sol {
typedef meta::tuple_types<typename traits::return_type> return_types; typedef meta::tuple_types<typename traits::return_type> return_types;
typedef typename traits::free_args_list args_list; typedef typename traits::free_args_list args_list;
// compile-time eliminate any functions that we know ahead of time are of improper arity // compile-time eliminate any functions that we know ahead of time are of improper arity
if constexpr (!traits::runtime_variadics_t::value && meta::find_in_pack_v<meta::index_value<traits::free_arity>, meta::index_value<M>...>::value) { if constexpr (!traits::runtime_variadics_t::value
&& meta::find_in_pack_v<meta::index_value<traits::free_arity>, meta::index_value<M>...>::value) {
return overload_match_arity(types<Fxs...>(), return overload_match_arity(types<Fxs...>(),
std::index_sequence<In...>(), std::index_sequence<In...>(),
std::index_sequence<M...>(), std::index_sequence<M...>(),
@ -16331,7 +16337,8 @@ namespace sol {
typedef meta::tuple_types<typename traits::return_type> return_types; typedef meta::tuple_types<typename traits::return_type> return_types;
typedef typename traits::free_args_list args_list; typedef typename traits::free_args_list args_list;
// compile-time eliminate any functions that we know ahead of time are of improper arity // compile-time eliminate any functions that we know ahead of time are of improper arity
if constexpr (!traits::runtime_variadics_t::value && meta::find_in_pack_v<meta::index_value<traits::free_arity>, meta::index_value<M>...>::value) { if constexpr (!traits::runtime_variadics_t::value
&& meta::find_in_pack_v<meta::index_value<traits::free_arity>, meta::index_value<M>...>::value) {
return overload_match_arity(types<Fx1, Fxs...>(), return overload_match_arity(types<Fx1, Fxs...>(),
std::index_sequence<I1, In...>(), std::index_sequence<I1, In...>(),
std::index_sequence<M...>(), std::index_sequence<M...>(),
@ -16467,6 +16474,7 @@ namespace sol {
} }
else { else {
if constexpr (std::is_const_v<meta::unwrapped_t<T>>) { if constexpr (std::is_const_v<meta::unwrapped_t<T>>) {
(void)f;
return luaL_error(L, "sol: cannot write to a readonly (const) variable"); return luaL_error(L, "sol: cannot write to a readonly (const) variable");
} }
else { else {
@ -16596,8 +16604,13 @@ namespace sol {
else { else {
using returns_list = typename wrap::returns_list; using returns_list = typename wrap::returns_list;
using caller = typename wrap::caller; using caller = typename wrap::caller;
return stack::call_into_lua<checked, clean_stack>( return stack::call_into_lua<checked, clean_stack>(returns_list(),
returns_list(), types<>(), L, boost + (is_variable ? 3 : 2), caller(), std::forward<Fx>(fx), std::forward<Args>(args)...); types<>(),
L,
boost + (is_variable ? 3 : 2),
caller(),
std::forward<Fx>(fx),
std::forward<Args>(args)...);
} }
} }
else { else {
@ -16821,8 +16834,7 @@ namespace sol {
template <typename F, typename... Args> template <typename F, typename... Args>
static int call(lua_State* L, F&& f, Args&&... args) { static int call(lua_State* L, F&& f, Args&&... args) {
constexpr bool is_specialized = meta::any< constexpr bool is_specialized = meta::any<std::is_same<U, detail::no_prop>,
std::is_same<U, detail::no_prop>,
meta::is_specialization_of<U, var_wrapper>, meta::is_specialization_of<U, var_wrapper>,
meta::is_specialization_of<U, constructor_wrapper>, meta::is_specialization_of<U, constructor_wrapper>,
meta::is_specialization_of<U, constructor_list>, meta::is_specialization_of<U, constructor_list>,
@ -16955,7 +16967,8 @@ namespace sol {
} }
} }
template <typename T, bool is_index, bool is_variable, typename F, int start = 1, bool checked = detail::default_safe_function_calls, bool clean_stack = true> template <typename T, bool is_index, bool is_variable, typename F, int start = 1, bool checked = detail::default_safe_function_calls,
bool clean_stack = true>
inline int call_user(lua_State* L) { inline int call_user(lua_State* L) {
auto& fx = stack::unqualified_get<user<F>>(L, upvalue_index(start)); auto& fx = stack::unqualified_get<user<F>>(L, upvalue_index(start));
return call_wrapped<T, is_index, is_variable, 0, checked, clean_stack>(L, fx); return call_wrapped<T, is_index, is_variable, 0, checked, clean_stack>(L, fx);
@ -19522,7 +19535,7 @@ namespace sol {
captured_type; captured_type;
typedef typename meta::iterator_tag<iterator>::type iterator_category; typedef typename meta::iterator_tag<iterator>::type iterator_category;
typedef std::is_same<iterator_category, std::input_iterator_tag> is_input_iterator; typedef std::is_same<iterator_category, std::input_iterator_tag> is_input_iterator;
typedef meta::conditional_t<is_input_iterator::value, V, decltype(detail::deref_non_pointer(std::declval<captured_type>()))> push_type; typedef meta::conditional_t<is_input_iterator::value, V, decltype(detail::deref_move_only(std::declval<captured_type>()))> push_type;
typedef std::is_copy_assignable<V> is_copyable; typedef std::is_copy_assignable<V> is_copyable;
typedef meta::neg<meta::any<std::is_const<V>, std::is_const<std::remove_reference_t<iterator_return>>, meta::neg<is_copyable>>> is_writable; typedef meta::neg<meta::any<std::is_const<V>, std::is_const<std::remove_reference_t<iterator_return>>, meta::neg<is_copyable>>> is_writable;
typedef meta::unqualified_t<decltype(get_key(is_associative(), std::declval<std::add_lvalue_reference_t<value_type>>()))> key_type; typedef meta::unqualified_t<decltype(get_key(is_associative(), std::declval<std::add_lvalue_reference_t<value_type>>()))> key_type;
@ -19595,12 +19608,12 @@ namespace sol {
template <typename Iter> template <typename Iter>
static detail::error_result get_associative(std::true_type, lua_State* L, Iter& it) { static detail::error_result get_associative(std::true_type, lua_State* L, Iter& it) {
decltype(auto) v = *it; decltype(auto) v = *it;
return stack::stack_detail::push_reference<push_type>(L, detail::deref_non_pointer(v.second)); return stack::stack_detail::push_reference<push_type>(L, detail::deref_move_only(v.second));
} }
template <typename Iter> template <typename Iter>
static detail::error_result get_associative(std::false_type, lua_State* L, Iter& it) { static detail::error_result get_associative(std::false_type, lua_State* L, Iter& it) {
return stack::stack_detail::push_reference<push_type>(L, detail::deref_non_pointer(*it)); return stack::stack_detail::push_reference<push_type>(L, detail::deref_move_only(*it));
} }
static detail::error_result get_category(std::input_iterator_tag, lua_State* L, T& self, K& key) { static detail::error_result get_category(std::input_iterator_tag, lua_State* L, T& self, K& key) {
@ -20175,7 +20188,7 @@ namespace sol {
else { else {
p = stack::push_reference(L, it->first); p = stack::push_reference(L, it->first);
} }
p += stack::stack_detail::push_reference<push_type>(L, detail::deref_non_pointer(it->second)); p += stack::stack_detail::push_reference<push_type>(L, detail::deref_move_only(it->second));
std::advance(it, 1); std::advance(it, 1);
return p; return p;
} }
@ -20196,7 +20209,7 @@ namespace sol {
else { else {
p = stack::stack_detail::push_reference(L, k + 1); p = stack::stack_detail::push_reference(L, k + 1);
} }
p += stack::stack_detail::push_reference<push_type>(L, detail::deref_non_pointer(*it)); p += stack::stack_detail::push_reference<push_type>(L, detail::deref_move_only(*it));
std::advance(it, 1); std::advance(it, 1);
return p; return p;
} }
@ -20422,7 +20435,7 @@ namespace sol {
} }
int p; int p;
p = stack::push(L, k + 1); p = stack::push(L, k + 1);
p += stack::push_reference(L, detail::deref_non_pointer(*it)); p += stack::push_reference(L, detail::deref_move_only(*it));
std::advance(it, 1); std::advance(it, 1);
return p; return p;
} }
@ -20455,7 +20468,7 @@ namespace sol {
if (idx >= static_cast<std::ptrdiff_t>(std::extent<T>::value) || idx < 0) { if (idx >= static_cast<std::ptrdiff_t>(std::extent<T>::value) || idx < 0) {
return stack::push(L, lua_nil); return stack::push(L, lua_nil);
} }
return stack::push_reference(L, detail::deref_non_pointer(self[idx])); return stack::push_reference(L, detail::deref_move_only(self[idx]));
} }
static int index_get(lua_State* L) { static int index_get(lua_State* L) {
@ -21254,10 +21267,7 @@ namespace sol { namespace u_detail {
index_call_storage* p_ics = nullptr; index_call_storage* p_ics = nullptr;
usertype_storage_base* p_usb = nullptr; usertype_storage_base* p_usb = nullptr;
void* p_derived_usb = nullptr; void* p_derived_usb = nullptr;
lua_CFunction idx_call = nullptr, lua_CFunction idx_call = nullptr, new_idx_call = nullptr, meta_idx_call = nullptr, meta_new_idx_call = nullptr;
new_idx_call = nullptr,
meta_idx_call = nullptr,
meta_new_idx_call = nullptr;
change_indexing_mem_func change_indexing; change_indexing_mem_func change_indexing;
void operator()(lua_State* L, submetatable_type smt, reference& fast_index_table) { void operator()(lua_State* L, submetatable_type smt, reference& fast_index_table) {
@ -21275,14 +21285,7 @@ namespace sol { namespace u_detail {
int fast_index_table_push = fast_index_table.push(); int fast_index_table_push = fast_index_table.push();
stack_reference t(L, -fast_index_table_push); stack_reference t(L, -fast_index_table_push);
if (poison_indexing) { if (poison_indexing) {
(usb.*change_indexing)(L, (usb.*change_indexing)(L, smt, p_derived_usb, t, idx_call, new_idx_call, meta_idx_call, meta_new_idx_call);
smt,
p_derived_usb,
t,
idx_call,
new_idx_call,
meta_idx_call,
meta_new_idx_call);
} }
if (is_destruction if (is_destruction
&& (smt == submetatable_type::reference || smt == submetatable_type::const_reference || smt == submetatable_type::named && (smt == submetatable_type::reference || smt == submetatable_type::const_reference || smt == submetatable_type::named
@ -21343,14 +21346,7 @@ namespace sol { namespace u_detail {
stack::set_field(L, detail::base_class_check_key(), reinterpret_cast<void*>(base_class_check_func), t.stack_index()); stack::set_field(L, detail::base_class_check_key(), reinterpret_cast<void*>(base_class_check_func), t.stack_index());
stack::set_field(L, detail::base_class_cast_key(), reinterpret_cast<void*>(base_class_cast_func), t.stack_index()); stack::set_field(L, detail::base_class_cast_key(), reinterpret_cast<void*>(base_class_cast_func), t.stack_index());
// change indexing, forcefully // change indexing, forcefully
(p_usb->*change_indexing)(L, (p_usb->*change_indexing)(L, smt, p_derived_usb, t, idx_call, new_idx_call, meta_idx_call, meta_new_idx_call);
smt,
p_derived_usb,
t,
idx_call,
new_idx_call,
meta_idx_call,
meta_new_idx_call);
t.pop(); t.pop();
} }
}; };
@ -21358,7 +21354,8 @@ namespace sol { namespace u_detail {
struct binding_data_equals { struct binding_data_equals {
void* binding_data; void* binding_data;
binding_data_equals(void* b) : binding_data(b) {} binding_data_equals(void* b) : binding_data(b) {
}
bool operator()(const std::unique_ptr<binding_base>& ptr) const { bool operator()(const std::unique_ptr<binding_base>& ptr) const {
return binding_data == ptr->data(); return binding_data == ptr->data();
@ -21723,6 +21720,7 @@ namespace sol { namespace u_detail {
using Binding = binding<KeyU, ValueU, T>; using Binding = binding<KeyU, ValueU, T>;
using is_var_bind = is_variable_binding<ValueU>; using is_var_bind = is_variable_binding<ValueU>;
if constexpr (std::is_same_v<KeyU, call_construction>) { if constexpr (std::is_same_v<KeyU, call_construction>) {
(void)key;
std::unique_ptr<Binding> p_binding = std::make_unique<Binding>(std::forward<Value>(value)); std::unique_ptr<Binding> p_binding = std::make_unique<Binding>(std::forward<Value>(value));
Binding& b = *p_binding; Binding& b = *p_binding;
this->storage.push_back(std::move(p_binding)); this->storage.push_back(std::move(p_binding));
@ -21768,9 +21766,10 @@ namespace sol { namespace u_detail {
void* derived_this = static_cast<void*>(static_cast<usertype_storage<T>*>(this)); void* derived_this = static_cast<void*>(static_cast<usertype_storage<T>*>(this));
index_call_storage ics; index_call_storage ics;
ics.binding_data = b.data(); ics.binding_data = b.data();
ics.index = is_index || is_static_index ? &Binding::template call_with_<true, is_var_bind::value> : &Binding::template index_call_with_<true, is_var_bind::value>; ics.index = is_index || is_static_index ? &Binding::template call_with_<true, is_var_bind::value>
ics.new_index : &Binding::template index_call_with_<true, is_var_bind::value>;
= is_new_index || is_static_new_index ? &Binding::template call_with_<false, is_var_bind::value> : &Binding::template index_call_with_<false, is_var_bind::value>; ics.new_index = is_new_index || is_static_new_index ? &Binding::template call_with_<false, is_var_bind::value>
: &Binding::template index_call_with_<false, is_var_bind::value>;
string_for_each_metatable_func for_each_fx; string_for_each_metatable_func for_each_fx;
for_each_fx.is_destruction = is_destruction; for_each_fx.is_destruction = is_destruction;

View File

@ -0,0 +1,47 @@
// sol3
// The MIT License (MIT)
// Copyright (c) 2013-2019 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.
#include "sol_test.hpp"
#include <catch.hpp>
struct user_container_type : public std::vector<int> {
typedef std::vector<int> base_t;
using base_t::base_t;
};
TEST_CASE("usertype/container checking fake container", "A container should not respond yes in .is<> to every table in Lua") {
sol::state lua;
sol::stack_guard luasg(lua);
sol::optional<sol::error> err0 = lua.safe_script("a = {}");
REQUIRE_FALSE(err0.has_value());
sol::object a = lua["a"];
REQUIRE(a.is<user_container_type>());
REQUIRE_FALSE(a.is<user_container_type&>());
REQUIRE_FALSE(a.is<user_container_type&&>());
REQUIRE_FALSE(a.is<const user_container_type&>());
REQUIRE_FALSE(a.is<const user_container_type&&>());
}

View File

@ -27,6 +27,17 @@
#include <catch.hpp> #include <catch.hpp>
struct unique_user_Display {
int value = 5;
};
std::vector<std::shared_ptr<unique_user_Display>> unique_user_foo() {
return { std::make_shared<unique_user_Display>(), std::make_shared<unique_user_Display>(), std::make_shared<unique_user_Display>() };
}
int unique_user_bar(std::shared_ptr<unique_user_Display> item) {
return item->value;
}
struct factory_test { struct factory_test {
private: private:
@ -171,3 +182,15 @@ TEST_CASE("usertype/unique void pointers", "can compile shared_ptr<void> types a
auto result = lua.safe_script("f(s)", sol::script_pass_on_error); auto result = lua.safe_script("f(s)", sol::script_pass_on_error);
REQUIRE(result.valid()); REQUIRE(result.valid());
} }
TEST_CASE("usertype/unique containers", "copyable unique usertypes in containers are just fine and do not deref/decay") {
sol::state lua;
lua.open_libraries();
lua["foo"] = unique_user_foo;
lua["bar"] = unique_user_bar;
sol::optional<sol::error> err0 = lua.safe_script("v3 = foo()");
REQUIRE_FALSE(err0.has_value());
sol::optional<sol::error> err1 = lua.safe_script("assert(bar(v3[1]) == 5)");
REQUIRE_FALSE(err1.has_value());
}