Skip to content

Commit

Permalink
Better exception safety for Lua binding
Browse files Browse the repository at this point in the history
  • Loading branch information
jkl1337 committed Feb 26, 2024
1 parent ae5e4b3 commit 94a8bdc
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 63 deletions.
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ PointerAlignment: Left
ReferenceAlignment: Left # New in v13. int &name ==> int& name
ReflowComments: false
SeparateDefinitionBlocks: Always # New in v14.
SortIncludes: false
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
Expand Down
8 changes: 0 additions & 8 deletions luakiwi/luacompat.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,6 @@ extern "C" {

#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501

#define LUA_OPADD 0
#define LUA_OPSUB 1
#define LUA_OPMUL 2
#define LUA_OPDIV 3
#define LUA_OPMOD 4
#define LUA_OPPOW 5
#define LUA_OPUNM 6

static int lua_absindex(lua_State* L, int i) {
if (i < 0 && i > LUA_REGISTRYINDEX)
i += lua_gettop(L) + 1;
Expand Down
80 changes: 48 additions & 32 deletions luakiwi/luakiwi-int.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,49 +141,57 @@ template<typename F>
inline const KiwiErr* wrap_err(F&& f) {
static const constexpr KiwiErr kKiwiErrUnhandledCxxException {
KiwiErrUnknown,
"An unhandled C++ exception occurred."};
"An unhandled C++ exception occurred."
};

try {
f();
} catch (const UnsatisfiableConstraint&) {
static const constexpr KiwiErr err {
KiwiErrUnsatisfiableConstraint,
"The constraint cannot be satisfied."};
"The constraint cannot be satisfied."
};
return &err;
} catch (const UnknownConstraint&) {
static const constexpr KiwiErr err {
KiwiErrUnknownConstraint,
"The constraint has not been added to the solver."};
"The constraint has not been added to the solver."
};
return &err;

} catch (const DuplicateConstraint&) {
static const constexpr KiwiErr err {
KiwiErrDuplicateConstraint,
"The constraint has already been added to the solver."};
"The constraint has already been added to the solver."
};
return &err;

} catch (const UnknownEditVariable&) {
static const constexpr KiwiErr err {
KiwiErrUnknownEditVariable,
"The edit variable has not been added to the solver."};
"The edit variable has not been added to the solver."
};
return &err;

} catch (const DuplicateEditVariable&) {
static const constexpr KiwiErr err {
KiwiErrDuplicateEditVariable,
"The edit variable has already been added to the solver."};
"The edit variable has already been added to the solver."
};
return &err;

} catch (const BadRequiredStrength&) {
static const constexpr KiwiErr err {
KiwiErrBadRequiredStrength,
"A required strength cannot be used in this context."};
"A required strength cannot be used in this context."
};
return &err;

} catch (const InternalSolverError& ex) {
static const constexpr KiwiErr base {
KiwiErrInternalSolverError,
"An internal solver error occurred."};
"An internal solver error occurred."
};
return new_error(&base, ex);
} catch (std::bad_alloc&) {
static const constexpr KiwiErr err {KiwiErrAlloc, "A memory allocation failed."};
Expand All @@ -208,9 +216,12 @@ inline const KiwiErr* wrap_err(P&& s, R&& ref, F&& f) {

template<typename T, typename... Args>
inline T* make_unmanaged(Args... args) {
auto* o = new T(std::forward<Args>(args)...);
o->m_refcount = 1;
return o;
auto* p = new (std::nothrow) T(std::forward<Args>(args)...);
if (lk_unlikely(!p))
return nullptr;

p->m_refcount = 1;
return p;
}

template<typename T>
Expand Down Expand Up @@ -238,30 +249,35 @@ inline ConstraintData* kiwi_constraint_new(
strength = kiwi::strength::required;
}

std::vector<Term> terms;
terms.reserve(static_cast<std::size_t>(
(lhs && lhs->term_count > 0 ? lhs->term_count : 0)
+ (rhs && rhs->term_count > 0 ? rhs->term_count : 0)
));

if (lhs) {
for (int i = 0; i < lhs->term_count; ++i) {
const auto& t = lhs->terms[i];
terms.emplace_back(Variable(t.var), t.coefficient);
try {
std::vector<Term> terms;

terms.reserve(static_cast<decltype(terms)::size_type>(
(lhs && lhs->term_count > 0 ? lhs->term_count : 0)
+ (rhs && rhs->term_count > 0 ? rhs->term_count : 0)
));

if (lhs) {
for (int i = 0; i < lhs->term_count; ++i) {
const auto& t = lhs->terms[i];
terms.emplace_back(Variable(t.var), t.coefficient);
}
}
}
if (rhs) {
for (int i = 0; i < rhs->term_count; ++i) {
const auto& t = rhs->terms[i];
terms.emplace_back(Variable(t.var), -t.coefficient);
if (rhs) {
for (int i = 0; i < rhs->term_count; ++i) {
const auto& t = rhs->terms[i];
terms.emplace_back(Variable(t.var), -t.coefficient);
}
}
}
return make_unmanaged<ConstraintData>(
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
static_cast<RelationalOperator>(op),
strength
);

return make_unmanaged<ConstraintData>(
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
static_cast<RelationalOperator>(op),
strength
);
} catch (...) {
return nullptr;
}
}

inline const KiwiErr* kiwi_solver_add_constraint(Solver& s, ConstraintData* constraint) {
Expand Down
64 changes: 42 additions & 22 deletions luakiwi/luakiwi.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
#include "ljkiwi.hpp"
#include <math.h>
#include <stdlib.h>
#include <string.h>

#include "luacompat.h"
#include "luakiwi-int.h"

namespace {
Expand All @@ -13,9 +11,7 @@ namespace {

enum TypeId { NOTYPE, VAR = 1, TERM, EXPR, CONSTRAINT, SOLVER, ERROR, NUMBER };

const int ERR_KIND_TAB = NUMBER + 1;
const int VAR_SUB_FN = ERR_KIND_TAB + 1;
const int CONTEXT_TAB_MAX = VAR_SUB_FN + 1;
enum { ERR_KIND_TAB = NUMBER + 1, VAR_SUB_FN, MEM_ERR_MSG, CONTEXT_TAB_MAX };

constexpr const char* const lkiwi_error_kinds[] = {
"KiwiErrNone",
Expand Down Expand Up @@ -218,13 +214,19 @@ inline KiwiSolver* get_solver(lua_State* L, int idx) {
return static_cast<KiwiSolver*>(check_arg(L, idx, SOLVER));
}

// note this expects the 2nd upvalue to have the variable weak table
VariableData* var_new(lua_State* L, VariableData* var) {
*static_cast<VariableData**>(lua_newuserdata(L, sizeof(VariableData*))) = var;

VariableData** var_new(lua_State* L) {
auto** varp = static_cast<VariableData**>(lua_newuserdata(L, sizeof(VariableData*)));
push_type(L, VAR);
lua_setmetatable(L, -2);
return varp;
}

// note this expects the 2nd upvalue to have the variable weak table
VariableData* var_register(lua_State* L, VariableData* var) {
if (lk_unlikely(!var)) {
lua_rawgeti(L, lua_upvalueindex(1), MEM_ERR_MSG);
lua_error(L);
}
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
// a true compatibility shim has performance implications here
lua_pushlightuserdata(L, var);
Expand Down Expand Up @@ -260,10 +262,13 @@ inline ConstraintData* constraint_new(
double strength
) {
auto** c = static_cast<ConstraintData**>(lua_newuserdata(L, sizeof(ConstraintData*)));
*c = kiwi_constraint_new(lhs, rhs, op, strength);

push_type(L, CONSTRAINT);
lua_setmetatable(L, -2);

if (lk_unlikely(!(*c = kiwi_constraint_new(lhs, rhs, op, strength)))) {
lua_rawgeti(L, lua_upvalueindex(1), MEM_ERR_MSG);
lua_error(L);
}
return *c;
}

Expand Down Expand Up @@ -540,11 +545,15 @@ constexpr const struct luaL_Reg kiwi_var_m[] = {
{"eq", lkiwi_eq},
{"le", lkiwi_le},
{"ge", lkiwi_ge},
{0, 0}};
{0, 0}
};

int lkiwi_var_new(lua_State* L) {
const char* name = luaL_optstring(L, 1, "");
var_new(L, make_unmanaged<VariableData>(name));

auto* varp = var_new(L);
var_register(L, *varp = make_unmanaged<VariableData>(name));

return 1;
}

Expand Down Expand Up @@ -662,8 +671,10 @@ int lkiwi_term_m_index(lua_State* L) {
#else
lua_rawgetp(L, lua_upvalueindex(2), term->var);
#endif
if (lua_isnil(L, -1))
var_new(L, term->var);
if (lua_isnil(L, -1)) {
auto* varp = var_new(L);
var_register(L, *varp = retain_unmanaged(term->var));
}
return 1;
} else if (len == 11 && memcmp("coefficient", k, len) == 0) {
lua_pushnumber(L, term->coefficient);
Expand Down Expand Up @@ -692,7 +703,8 @@ constexpr const struct luaL_Reg kiwi_term_m[] = {
{"eq", lkiwi_eq},
{"le", lkiwi_le},
{"ge", lkiwi_ge},
{0, 0}};
{0, 0}
};

int lkiwi_term_new(lua_State* L) {
auto* var = get_var(L, 1);
Expand Down Expand Up @@ -908,7 +920,8 @@ constexpr const struct luaL_Reg kiwi_expr_m[] = {
{"eq", lkiwi_eq},
{"le", lkiwi_le},
{"ge", lkiwi_ge},
{0, 0}};
{0, 0}
};

int lkiwi_expr_new(lua_State* L) {
int nterms = lua_gettop(L) - 1;
Expand Down Expand Up @@ -1060,7 +1073,8 @@ constexpr const struct luaL_Reg kiwi_constraint_m[] = {
{"expression", lkiwi_constraint_expression},
{"add_to", lkiwi_constraint_add_to},
{"remove_from", lkiwi_constraint_remove_from},
{0, 0}};
{0, 0}
};

int lkiwi_constraint_new(lua_State* L) {
const auto* lhs = get_expr_opt(L, 1);
Expand Down Expand Up @@ -1133,7 +1147,8 @@ constexpr const struct luaL_Reg lkiwi_constraints[] = {
{"pair_ratio", lkiwi_constraints_pair_ratio},
{"pair", lkiwi_constraints_pair},
{"single", lkiwi_constraints_single},
{0, 0}};
{0, 0}
};

void lkiwi_mod_constraints_new(lua_State* L, int ctx_i) {
luaL_newlibtable(L, lkiwi_constraints);
Expand Down Expand Up @@ -1192,7 +1207,8 @@ int lkiwi_error_m_tostring(lua_State* L) {

constexpr const struct luaL_Reg lkiwi_error_m[] = {
{"__tostring", lkiwi_error_m_tostring},
{0, 0}};
{0, 0}
};

int lkiwi_error_mask(lua_State* L) {
int invert = lua_toboolean(L, 2);
Expand Down Expand Up @@ -1444,7 +1460,8 @@ constexpr const struct luaL_Reg kiwi_solver_m[] = {
{"set_error_mask", lkiwi_solver_set_error_mask},
{"__tostring", lkiwi_solver_m_tostring},
{"__gc", lkiwi_solver_m_gc},
{0, 0}};
{0, 0}
};

int lkiwi_solver_new(lua_State* L) {
lua_Integer error_mask;
Expand Down Expand Up @@ -1541,7 +1558,8 @@ constexpr const struct luaL_Reg lkiwi[] = {
{"eq", lkiwi_eq},
{"le", lkiwi_le},
{"ge", lkiwi_ge},
{0, 0}};
{0, 0}
};

int no_member_mt_index(lua_State* L) {
luaL_error(L, "attempt to access non-existent member '%s'", lua_tostring(L, 2));
Expand Down Expand Up @@ -1619,6 +1637,8 @@ extern "C" LJKIWI_EXPORT int luaopen_ljkiwi(lua_State* L) {
int ctx_i = lua_gettop(L);

compat_init(L, ctx_i);
lua_pushliteral(L, "kiwi library memory allocation error");
lua_rawseti(L, ctx_i, MEM_ERR_MSG);

no_member_mt_new(L);
register_type(L, "kiwi.Var", ctx_i, VAR, kiwi_var_m);
Expand Down

0 comments on commit 94a8bdc

Please sign in to comment.