feat(lua): add safe_function template that catches Lean and C++ exceptions and convert them into Lua errors
I'm using the approach described at: http://stackoverflow.com/questions/4615890/how-to-handle-c-exceptions-when-calling-functions-from-lua BTW, in some Lua versions, the C++ exceptions are correctly propagated. I think we should not rely on features of particular implementations. For example, LuaJIT does not propagate C++ exceptions. Whenever an exception is thrown from C++ code invoked from LuaJit, LuaJit interrupts the execution and converts it to an error "C++ exception". On the other hand, Lua 5.2 PUC-Rio interpreter (for Ubuntu) seem to propagate the C++ exceptions. The template safe_function solves the issue. It will also produce a Lua error whenever the function being wrapped throws an exception. The error message is based on the "what()" method. Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
1a734979b4
commit
fd7e85f0bb
5 changed files with 53 additions and 17 deletions
|
@ -17,7 +17,11 @@ static int name_eq(lua_State * L);
|
||||||
static int name_lt(lua_State * L);
|
static int name_lt(lua_State * L);
|
||||||
|
|
||||||
static const struct luaL_Reg name_m[] = {
|
static const struct luaL_Reg name_m[] = {
|
||||||
{"__gc", name_gc}, {"__tostring", name_tostring}, {"__eq", name_eq}, {"__lt", name_lt}, {0, 0}
|
{"__gc", name_gc}, // never throws
|
||||||
|
{"__tostring", safe_function<name_tostring>},
|
||||||
|
{"__eq", safe_function<name_eq>},
|
||||||
|
{"__lt", safe_function<name_lt>},
|
||||||
|
{0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static int mk_name(lua_State * L) {
|
static int mk_name(lua_State * L) {
|
||||||
|
@ -84,7 +88,7 @@ void init_name(lua_State * L) {
|
||||||
luaL_newmetatable(L, "name.mt");
|
luaL_newmetatable(L, "name.mt");
|
||||||
setfuncs(L, name_m, 0);
|
setfuncs(L, name_m, 0);
|
||||||
|
|
||||||
lua_pushcfunction(L, mk_name);
|
lua_pushcfunction(L, safe_function<mk_name>);
|
||||||
lua_setglobal(L, "name");
|
lua_setglobal(L, "name");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,17 +102,23 @@ public:
|
||||||
luaL_newmetatable(L, M);
|
luaL_newmetatable(L, M);
|
||||||
setfuncs(L, m, 0);
|
setfuncs(L, m, 0);
|
||||||
|
|
||||||
lua_pushcfunction(L, mk);
|
lua_pushcfunction(L, safe_function<mk>);
|
||||||
lua_setglobal(L, N);
|
lua_setglobal(L, N);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, char const * N, char const * M>
|
template<typename T, char const * N, char const * M>
|
||||||
const struct luaL_Reg num_bindings<T, N, M>::m[] = {
|
const struct luaL_Reg num_bindings<T, N, M>::m[] = {
|
||||||
{"__gc", num_bindings<T, N, M>::gc}, {"__tostring", num_bindings<T, N, M>::tostring}, {"__eq", num_bindings<T, N, M>::eq},
|
{"__gc", num_bindings<T, N, M>::gc}, // never throws
|
||||||
{"__lt", num_bindings<T, N, M>::lt}, {"__add", num_bindings<T, N, M>::add}, {"__add", num_bindings<T, N, M>::sub},
|
{"__tostring", safe_function<num_bindings<T, N, M>::tostring>},
|
||||||
{"__mul", num_bindings<T, N, M>::mul}, {"__div", num_bindings<T, N, M>::div}, {"__pow", num_bindings<T, N, M>::power},
|
{"__eq", safe_function<num_bindings<T, N, M>::eq>},
|
||||||
{"__unm", num_bindings<T, N, M>::umn},
|
{"__lt", safe_function<num_bindings<T, N, M>::lt>},
|
||||||
|
{"__add", safe_function<num_bindings<T, N, M>::add>},
|
||||||
|
{"__add", safe_function<num_bindings<T, N, M>::sub>},
|
||||||
|
{"__mul", safe_function<num_bindings<T, N, M>::mul>},
|
||||||
|
{"__div", safe_function<num_bindings<T, N, M>::div>},
|
||||||
|
{"__pow", safe_function<num_bindings<T, N, M>::power>},
|
||||||
|
{"__unm", safe_function<num_bindings<T, N, M>::umn>},
|
||||||
{0, 0}
|
{0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -121,6 +127,7 @@ constexpr char const mpz_metatable[] = "mpz.mt";
|
||||||
void init_mpz(lua_State * L) {
|
void init_mpz(lua_State * L) {
|
||||||
num_bindings<mpz, mpz_name, mpz_metatable>::init(L);
|
num_bindings<mpz, mpz_name, mpz_metatable>::init(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr char const mpq_name[] = "mpq";
|
constexpr char const mpq_name[] = "mpq";
|
||||||
constexpr char const mpq_metatable[] = "mpq.mt";
|
constexpr char const mpq_metatable[] = "mpq.mt";
|
||||||
void init_mpq(lua_State * L) {
|
void init_mpq(lua_State * L) {
|
||||||
|
|
|
@ -6,6 +6,9 @@ Author: Leonardo de Moura
|
||||||
*/
|
*/
|
||||||
#ifdef LEAN_USE_LUA
|
#ifdef LEAN_USE_LUA
|
||||||
#include <lua.hpp>
|
#include <lua.hpp>
|
||||||
|
#include <exception>
|
||||||
|
#include "util/exception.h"
|
||||||
|
|
||||||
namespace lean {
|
namespace lean {
|
||||||
/**
|
/**
|
||||||
\brief luaL_setfuncs replacement. The function luaL_setfuncs is only available in Lua 5.2.
|
\brief luaL_setfuncs replacement. The function luaL_setfuncs is only available in Lua 5.2.
|
||||||
|
@ -21,5 +24,24 @@ void setfuncs(lua_State * L, luaL_Reg const * l, int nup) {
|
||||||
}
|
}
|
||||||
lua_pop(L, nup); // remove upvalues
|
lua_pop(L, nup); // remove upvalues
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int safe_function_wrapper(lua_State * L, lua_CFunction f){
|
||||||
|
static thread_local std::string _error_msg;
|
||||||
|
char const * error_msg;
|
||||||
|
try {
|
||||||
|
return f(L);
|
||||||
|
} catch (exception & e) {
|
||||||
|
_error_msg = e.what();
|
||||||
|
error_msg = _error_msg.c_str();
|
||||||
|
} catch (std::bad_alloc &) {
|
||||||
|
error_msg = "out of memory";
|
||||||
|
} catch (std::exception & e) {
|
||||||
|
_error_msg = e.what();
|
||||||
|
error_msg = _error_msg.c_str();
|
||||||
|
} catch(...) {
|
||||||
|
error_msg = "unknown error";
|
||||||
|
}
|
||||||
|
return luaL_error(L, error_msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -8,5 +8,13 @@ Author: Leonardo de Moura
|
||||||
#include <lua.hpp>
|
#include <lua.hpp>
|
||||||
namespace lean {
|
namespace lean {
|
||||||
void setfuncs(lua_State * L, luaL_Reg const * l, int nup);
|
void setfuncs(lua_State * L, luaL_Reg const * l, int nup);
|
||||||
|
/**
|
||||||
|
\brief Wrapper for invoking function f, and catching Lean exceptions.
|
||||||
|
*/
|
||||||
|
int safe_function_wrapper(lua_State * L, lua_CFunction f);
|
||||||
|
template<lua_CFunction F>
|
||||||
|
int safe_function(lua_State * L) {
|
||||||
|
return safe_function_wrapper(L, F);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -9,14 +9,13 @@ Author: Leonardo de Moura
|
||||||
|
|
||||||
#ifdef LEAN_USE_LUA
|
#ifdef LEAN_USE_LUA
|
||||||
#include <lua.hpp>
|
#include <lua.hpp>
|
||||||
#include "util/exception.h"
|
|
||||||
#include "bindings/lua/name.h"
|
#include "bindings/lua/name.h"
|
||||||
#include "bindings/lua/numerics.h"
|
#include "bindings/lua/numerics.h"
|
||||||
|
|
||||||
int main(int argc, char ** argv) {
|
int main(int argc, char ** argv) {
|
||||||
int status, result;
|
int status, result;
|
||||||
lua_State *L;
|
lua_State *L;
|
||||||
|
int exitcode = 0;
|
||||||
L = luaL_newstate();
|
L = luaL_newstate();
|
||||||
luaL_openlibs(L);
|
luaL_openlibs(L);
|
||||||
lean::init_name(L);
|
lean::init_name(L);
|
||||||
|
@ -27,21 +26,17 @@ int main(int argc, char ** argv) {
|
||||||
status = luaL_loadfile(L, argv[i]);
|
status = luaL_loadfile(L, argv[i]);
|
||||||
if (status) {
|
if (status) {
|
||||||
std::cerr << "Couldn't load file: " << lua_tostring(L, -1) << "\n";
|
std::cerr << "Couldn't load file: " << lua_tostring(L, -1) << "\n";
|
||||||
return 1;
|
exitcode = 1;
|
||||||
}
|
} else {
|
||||||
try {
|
|
||||||
result = lua_pcall(L, 0, LUA_MULTRET, 0);
|
result = lua_pcall(L, 0, LUA_MULTRET, 0);
|
||||||
if (result) {
|
if (result) {
|
||||||
std::cerr << "Failed to run script: " << lua_tostring(L, -1) << "\n";
|
std::cerr << "Failed to run script: " << lua_tostring(L, -1) << "\n";
|
||||||
return 1;
|
exitcode = 1;
|
||||||
}
|
}
|
||||||
} catch (lean::exception & ex) {
|
|
||||||
std::cerr << "Lean exception when running: " << argv[i] << "\n";
|
|
||||||
std::cerr << ex.what() << "\n";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lua_close(L);
|
lua_close(L);
|
||||||
return 0;
|
return exitcode;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
int main() {
|
int main() {
|
||||||
|
|
Loading…
Reference in a new issue