improve docs

refactor default state additions to let user do the same thing with less effort
prepare for sol::upvalues tinkering
This commit is contained in:
ThePhD 2018-03-05 22:54:31 -05:00
parent 2f220b6eb2
commit 68738cdc83
10 changed files with 214 additions and 169 deletions

View File

@ -17,13 +17,18 @@ The majority of the members between ``state_view`` and :doc:`sol::table<table>`
.. _state-automatic-handlers:
One last thing you should understand: constructing a ``sol::state`` does a few things behind-the-scenes for you, mostly to ensure compatibility. They are as follows:
``sol::state`` automatic handlers
---------------------------------
One last thing you should understand: constructing a ``sol::state`` does a few things behind-the-scenes for you, mostly to ensure compatibility and good error handler/error handling. The function it uses to do this is ``set_default_state``. They are as follows:
* set a default panic handler with ``state_view::set_panic``
* set a default ``sol::protected_function`` handler with ``sol::protected_function::set_default_handler``, using a ``sol::reference`` to ``&sol::detail::default_traceback_error_handler`` as the default handler function
* register the state as the main thread (only does something for Lua 5.1, which does not have a way to get the main thread) using ``sol::stack::register_main_thread(L)``
* register the LuaJIT C function exception handler with ``stack::luajit_exception_handler(L)``
sol::state_view does none of these things for you. If you want to make sure your self-created or self-managed state has the same properties, please apply this function once to the state. Please note that it will override your panic handler and, if using LuaJIT, your LuaJIT C function handler.
.. warning::
It is your responsibility to make sure ``sol::state_view`` goes out of scope before you call ``lua_close`` on a pre-existing state, or before ``sol::state`` goes out of scope and its destructor gets called. Failure to do so can result in intermittent crashes because the ``sol::state_view`` has outstanding references to an already-dead ``lua_State*``, and thusly will try to decrement the reference counts for the Lua Registry and the Global Table on a dead state. Please use ``{`` and ``}`` to create a new scope, or other lifetime techniques, when you know you are going to call ``lua_close`` so that you have a chance to specifically control the lifetime of a ``sol::state_view`` object.

View File

@ -69,7 +69,7 @@ Please make sure to use the ``SOL_EXCEPTIONS_SAFE_PROPAGATION`` define before in
Catch and CRASH!
----------------
By default, Sol will add a ``default_at_panic`` handler. If exceptions are not turned off, this handler will throw to allow the user a chance to recover. However, in almost all cases, when Lua calls ``lua_atpanic`` and hits this function, it means that something *irreversibly wrong* occured in your code or the Lua code and the VM is in an unpredictable or dead state. Catching an error thrown from the default handler and then proceeding as if things are cleaned up or okay is NOT the best idea. Unexpected bugs in optimized and release mode builds can result, among other serious issues.
By default, Sol will add a ``default_at_panic`` handler to states opened by Sol (see :ref:`sol::state automatic handlers<state-automatic-handlers>` for more details). If exceptions are not turned off, this handler will throw to allow the user a chance to recover. However, in almost all cases, when Lua calls ``lua_atpanic`` and hits this function, it means that something *irreversibly wrong* occured in your code or the Lua code and the VM is in an unpredictable or dead state. Catching an error thrown from the default handler and then proceeding as if things are cleaned up or okay is NOT the best idea. Unexpected bugs in optimized and release mode builds can result, among other serious issues.
It is preferred if you catch an error that you log what happened, terminate the Lua VM as soon as possible, and then crash if your application cannot handle spinning up a new Lua state. Catching can be done, but you should understand the risks of what you're doing when you do it. For more information about catching exceptions, the potentials, not turning off exceptions and other tricks and caveats, read about :doc:`exceptions in Sol here<exceptions>`.

View File

@ -5,7 +5,7 @@ since somebody is going to ask about it...
Yes, you can turn off exceptions in Sol with ``#define SOL_NO_EXCEPTIONS`` before including or by passing the command line argument that defines ``SOL_NO_EXCEPTIONS``. We don't recommend it unless you're playing with a Lua distro that also doesn't play nice with exceptions (like non-x64 versions of :ref:`LuaJIT<LuaJIT and exceptions>` ).
If you turn this off, the default `at_panic`_ function :doc:`state<api/state>` set for you will not throw. Instead, the default Lua behavior of aborting will take place (and give you no chance of escape unless you implement your own at_panic function and decide to try ``longjmp`` out).
If you turn this off, the default `at_panic`_ function :doc:`state<api/state>` set for you will not throw (see :ref:`sol::state automatic handlers<state-automatic-handlers>` for more details). Instead, the default Lua behavior of aborting will take place (and give you no chance of escape unless you implement your own at_panic function and decide to try ``longjmp`` out).
To make this not be the case, you can set a panic function directly with ``lua_atpanic( lua, my_panic_function );`` or when you create the ``sol::state`` with ``sol::state lua(my_panic_function);``. Here's an example ``my_panic_function`` you can have that prints out its errors:

View File

@ -10,6 +10,8 @@ config
Note that you can obtain safety with regards to functions you bind by using the :doc:`protect<api/protect>` wrapper around function/variable bindings you set into Lua. Additionally, you can have basic boolean checks when using the API by just converting to a :doc:`sol::optional\<T><api/optional>` when necessary for getting things out of Lua and for function arguments.
Also note that you can have your own states use sol2's safety panics and similar to protect your code from crashes. See :ref:`sol::state automatic handlers<state-automatic-handlers>` for more details.
.. _config-safety:
Safety Config
@ -44,6 +46,13 @@ Safety Config
* Affects nearly the entire library for safety (with some blind spots covered by the other definitions)
* **Not** turned on by default under any settings: *this MUST be turned on manually*
``SOL_DEFAULT_PASS_ON_ERROR`` triggers the following changes:
* The default error handler for ``sol::state_view::script_`` functions is ``sol::script_pass_on_error`` rather than ``sol::script_throw_on_error``
* Passes errors on through: **very dangerous** as you can ignore or never be warned about errors if you don't catch the return value of specific functions
* **Not** turned on by default: *this MUST be turned on manually*
* Don't turn this on unless you have an extremely good reason
* *DON'T TURN THIS ON UNLESS YOU HAVE AN EXTREMELY GOOD REASON*
``SOL_CHECK_ARGUMENTS`` triggers the following changes:
* If ``SOL_SAFE_USERTYPE``, ``SOL_SAFE_REFERENCES``, ``SOL_SAFE_FUNCTION``, ``SOL_SAFE_NUMERICS``, ``SOL_SAFE_GETTER``, and ``SOL_SAFE_FUNCTION_CALLS`` are not defined, they get defined and the effects described above kick in
* **Not** turned on by default under any settings: *this MUST be turned on manually*

View File

@ -271,7 +271,7 @@ Some more things you can do/read about:
* :doc:`stack references<../api/stack_reference>` to have zero-overhead Sol abstractions while not copying to the Lua registry.
* :doc:`resolve<../api/resolve>` overloads in case you have overloaded functions; a cleaner casting utility. You must use this to emulate default parameters.
.. _a basic example: https://github.com/ThePhD/sol2/blob/develop/examples/usertype.cpp
.. _special functions: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_special_functions.cpp
.. _initializers: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_initializers.cpp
.. _basic example: https://github.com/ThePhD/sol2/blob/develop/examples/usertype.cpp
.. _special functions example: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_special_functions.cpp
.. _initializers example: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_initializers.cpp
.. _sol2 tutorial examples: https://github.com/ThePhD/sol2/tree/develop/examples/tutorials/quick_n_dirty

View File

@ -29,63 +29,19 @@
namespace sol {
namespace detail {
inline int default_at_panic(lua_State* L) {
#ifdef SOL_NO_EXCEPTIONS
(void)L;
return -1;
#else
size_t messagesize;
const char* message = lua_tolstring(L, -1, &messagesize);
if (message) {
std::string err(message, messagesize);
lua_settop(L, 0);
throw error(err);
}
lua_settop(L, 0);
throw error(std::string("An unexpected error occurred and forced the lua state to call atpanic"));
#endif
}
inline int default_traceback_error_handler(lua_State* L) {
using namespace sol;
std::string msg = "An unknown error has triggered the default error handler";
optional<string_view> maybetopmsg = stack::check_get<string_view>(L, 1);
if (maybetopmsg) {
const string_view& topmsg = maybetopmsg.value();
msg.assign(topmsg.data(), topmsg.size());
}
luaL_traceback(L, L, msg.c_str(), 1);
optional<string_view> maybetraceback = stack::check_get<string_view>(L, -1);
if (maybetraceback) {
const string_view& traceback = maybetraceback.value();
msg.assign(traceback.data(), traceback.size());
}
return stack::push(L, msg);
}
} // namespace detail
class state : private std::unique_ptr<lua_State, detail::state_deleter>, public state_view {
private:
typedef std::unique_ptr<lua_State, detail::state_deleter> unique_base;
public:
state(lua_CFunction panic = detail::default_at_panic)
state(lua_CFunction panic = default_at_panic)
: unique_base(luaL_newstate()), state_view(unique_base::get()) {
set_panic(panic);
lua_CFunction f = c_call<decltype(&detail::default_traceback_error_handler), &detail::default_traceback_error_handler>;
protected_function::set_default_handler(object(lua_state(), in_place, f));
stack::register_main_thread(unique_base::get());
stack::luajit_exception_handler(unique_base::get());
set_default_state(unique_base::get(), panic);
}
state(lua_CFunction panic, lua_Alloc alfunc, void* alpointer = nullptr)
: unique_base(lua_newstate(alfunc, alpointer)), state_view(unique_base::get()) {
set_panic(panic);
lua_CFunction f = c_call<decltype(&detail::default_traceback_error_handler), &detail::default_traceback_error_handler>;
protected_function::set_default_handler(object(lua_state(), in_place, f));
stack::register_main_thread(unique_base::get());
stack::luajit_exception_handler(unique_base::get());
set_default_state(unique_base::get(), panic);
}
state(const state&) = delete;

159
sol/state_handling.hpp Normal file
View File

@ -0,0 +1,159 @@
// sol2
// The MIT License (MIT)
// Copyright (c) 2013-2018 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_STATE_DEFAULT_HPP
#define SOL_STATE_DEFAULT_HPP
#include "stack.hpp"
#include "function.hpp"
#include "object.hpp"
namespace sol {
inline void register_main_thread(lua_State* L) {
#if SOL_LUA_VERSION < 502
if (L == nullptr) {
lua_pushnil(L);
lua_setglobal(L, detail::default_main_thread_name());
return;
}
lua_pushthread(L);
lua_setglobal(L, detail::default_main_thread_name());
#else
(void)L;
#endif
}
inline int default_at_panic(lua_State* L) {
#ifdef SOL_NO_EXCEPTIONS
(void)L;
return -1;
#else
size_t messagesize;
const char* message = lua_tolstring(L, -1, &messagesize);
if (message) {
std::string err(message, messagesize);
lua_settop(L, 0);
throw error(err);
}
lua_settop(L, 0);
throw error(std::string("An unexpected error occurred and forced the lua state to call atpanic"));
#endif
}
inline int default_traceback_error_handler(lua_State* L) {
std::string msg = "An unknown error has triggered the default error handler";
sol::optional<sol::string_view> maybetopmsg = stack::check_get<string_view>(L, 1);
if (maybetopmsg) {
const string_view& topmsg = maybetopmsg.value();
msg.assign(topmsg.data(), topmsg.size());
}
luaL_traceback(L, L, msg.c_str(), 1);
optional<sol::string_view> maybetraceback = stack::check_get<string_view>(L, -1);
if (maybetraceback) {
const sol::string_view& traceback = maybetraceback.value();
msg.assign(traceback.data(), traceback.size());
}
return stack::push(L, msg);
}
inline void set_default_state(lua_State* L, lua_CFunction panic_function = &default_at_panic, lua_CFunction traceback_function = c_call<decltype(&default_traceback_error_handler), &default_traceback_error_handler>) {
lua_atpanic(L, panic_function);
protected_function::set_default_handler(object(L, in_place, traceback_function));
register_main_thread(L);
stack::luajit_exception_handler(L);
}
inline std::size_t total_memory_used(lua_State* L) {
std::size_t kb = lua_gc(L, LUA_GCCOUNT, 0);
kb *= 1024;
kb += lua_gc(L, LUA_GCCOUNTB, 0);
return kb;
}
inline protected_function_result script_pass_on_error(lua_State*, protected_function_result result) {
return result;
}
inline protected_function_result script_throw_on_error(lua_State*L, protected_function_result result) {
type t = type_of(L, result.stack_index());
std::string err = "sol: ";
err += to_string(result.status());
err += " error";
#ifndef SOL_NO_EXCEPTIONS
std::exception_ptr eptr = std::current_exception();
if (eptr) {
err += " with a ";
try {
std::rethrow_exception(eptr);
}
catch (const std::exception& ex) {
err += "std::exception -- ";
err.append(ex.what());
}
catch (const std::string& message) {
err += "thrown message -- ";
err.append(message);
}
catch (const char* message) {
err += "thrown message -- ";
err.append(message);
}
catch (...) {
err.append("thrown but unknown type, cannot serialize into error message");
}
}
#endif // serialize exception information if possible
if (t == type::string) {
err += ": ";
string_view serr = stack::get<string_view>(L, result.stack_index());
err.append(serr.data(), serr.size());
}
#ifdef SOL_NO_EXCEPTIONS
// replacing information of stack error into pfr
int target = result.stack_index();
if (result.pop_count() > 0) {
stack::remove(L, target, result.pop_count());
}
int pushed = stack::push(L, err);
int top = lua_gettop(L);
int towards = top - target;
if (towards != 0) {
lua_rotate(L, top, towards);
}
#else
// just throw our error
throw error(detail::direct_error, err);
#endif
return result;
}
inline protected_function_result script_default_on_error(lua_State* L, protected_function_result pfr) {
#ifdef SOL_DEFAULT_PASS_ON_ERROR
return script_pass_on_error(L, std::move(pfr));
#else
return script_throw_on_error(L, std::move(pfr));
#endif
}
} // namespace sol
#endif // SOL_STATE_DEFAULT_HPP

View File

@ -28,111 +28,10 @@
#include "table.hpp"
#include "environment.hpp"
#include "load_result.hpp"
#include "state_handling.hpp"
#include <memory>
namespace sol {
enum class lib : char {
// print, assert, and other base functions
base,
// require and other package functions
package,
// coroutine functions and utilities
coroutine,
// string library
string,
// functionality from the OS
os,
// all things math
math,
// the table manipulator and observer functions
table,
// the debug library
debug,
// the bit library: different based on which you're using
bit32,
// input/output library
io,
// LuaJIT only
ffi,
// LuaJIT only
jit,
// library for handling utf8: new to Lua
utf8,
// do not use
count
};
inline std::size_t total_memory_used(lua_State* L) {
std::size_t kb = lua_gc(L, LUA_GCCOUNT, 0);
kb *= 1024;
kb += lua_gc(L, LUA_GCCOUNTB, 0);
return kb;
}
inline protected_function_result script_pass_on_error(lua_State*, protected_function_result result) {
return result;
}
inline protected_function_result script_throw_on_error(lua_State*L, protected_function_result result) {
type t = type_of(L, result.stack_index());
std::string err = "sol: ";
err += to_string(result.status());
err += " error";
#ifndef SOL_NO_EXCEPTIONS
std::exception_ptr eptr = std::current_exception();
if (eptr) {
err += " with a ";
try {
std::rethrow_exception(eptr);
}
catch (const std::exception& ex) {
err += "std::exception -- ";
err.append(ex.what());
}
catch (const std::string& message) {
err += "thrown message -- ";
err.append(message);
}
catch (const char* message) {
err += "thrown message -- ";
err.append(message);
}
catch (...) {
err.append("thrown but unknown type, cannot serialize into error message");
}
}
#endif // serialize exception information if possible
if (t == type::string) {
err += ": ";
string_view serr = stack::get<string_view>(L, result.stack_index());
err.append(serr.data(), serr.size());
}
#ifdef SOL_NO_EXCEPTIONS
// replacing information of stack error into pfr
int target = result.stack_index();
if (result.pop_count() > 0) {
stack::remove(L, target, result.pop_count());
}
int pushed = stack::push(L, err);
int top = lua_gettop(L);
int towards = top - target;
if (towards != 0) {
lua_rotate(L, top, towards);
}
#else
// just throw our error
throw error(detail::direct_error, err);
#endif
return result;
}
inline protected_function_result script_default_on_error(lua_State* L, protected_function_result pfr) {
#ifdef SOL_DEFAULT_PASS_ON_ERROR
return script_pass_on_error(L, std::move(pfr));
#else
return script_throw_on_error(L, std::move(pfr));
#endif
}
class state_view {
private:

View File

@ -77,20 +77,6 @@ namespace sol {
return lts;
}
};
inline void register_main_thread(lua_State* L) {
#if SOL_LUA_VERSION < 502
if (L == nullptr) {
lua_pushnil(L);
lua_setglobal(L, detail::default_main_thread_name());
return;
}
lua_pushthread(L);
lua_setglobal(L, detail::default_main_thread_name());
#else
(void)L;
#endif
}
} // namespace stack
template <typename base_t>

View File

@ -609,6 +609,37 @@ namespace sol {
}
};
enum class lib : char {
// print, assert, and other base functions
base,
// require and other package functions
package,
// coroutine functions and utilities
coroutine,
// string library
string,
// functionality from the OS
os,
// all things math
math,
// the table manipulator and observer functions
table,
// the debug library
debug,
// the bit library: different based on which you're using
bit32,
// input/output library
io,
// LuaJIT only
ffi,
// LuaJIT only
jit,
// library for handling utf8: new to Lua
utf8,
// do not use
count
};
enum class call_syntax {
dot = 0,
colon = 1