memory safe error stack state

master
Daniel Kolesa 2021-05-15 23:27:34 +02:00
parent 3189d87ac9
commit 8086c23a77
4 changed files with 68 additions and 41 deletions

View File

@ -40,9 +40,12 @@ struct LIBCUBESCRIPT_EXPORT error {
* index 1 and the one above it greater than 2). The gap is controlled * index 1 and the one above it greater than 2). The gap is controlled
* by the value of the `dbgalias` cubescript variable at the time of * by the value of the `dbgalias` cubescript variable at the time of
* creation of the error (the stack list will contain at most N nodes). * creation of the error (the stack list will contain at most N nodes).
*
* When getting the stack state, it will be represented as a span with
* the first element being the topmost node and the last element being
* the bottommost (index 1).
*/ */
struct stack_node { struct stack_node {
stack_node const *next; /**< @brief Next level. */
struct ident const &id; /**< @brief The ident of this level. */ struct ident const &id; /**< @brief The ident of this level. */
std::size_t index; /**< @brief The level index. */ std::size_t index; /**< @brief The level index. */
}; };
@ -51,10 +54,12 @@ struct LIBCUBESCRIPT_EXPORT error {
error(error const &) = delete; error(error const &) = delete;
/** @brief Errors are move constructible. */ /** @brief Errors are move constructible. */
error(error &&v): error(error &&v);
p_errbeg{v.p_errbeg}, p_errend{v.p_errend},
p_stack{std::move(v.p_stack)}, p_state{v.p_state} error &operator=(error const &) = delete;
{}
/** @brief Errors are move assignable. */
error &operator=(error &&v);
/** @brief Construct an error using a string. */ /** @brief Construct an error using a string. */
error(state &cs, std::string_view msg); error(state &cs, std::string_view msg);
@ -63,26 +68,18 @@ struct LIBCUBESCRIPT_EXPORT error {
~error(); ~error();
/** @brief Get a view of the error message. */ /** @brief Get a view of the error message. */
std::string_view what() const { std::string_view what() const;
return std::string_view{p_errbeg, std::size_t(p_errend - p_errbeg)};
}
/** @brief Get a reference to the call stack state. */ /** @brief Get the call stack state at the point of error. */
stack_node *stack() { span_type<stack_node const> stack() const;
return p_stack;
}
/** @brief Get a reference to the call stack state. */
stack_node const *stack() const {
return p_stack;
}
private: private:
friend struct error_p; friend struct error_p;
error(state &cs, char const *errbeg, char const *errend); error(state &cs, char const *errbeg, char const *errend);
char const *p_errbeg, *p_errend; char const *p_errbeg, *p_errend;
stack_node *p_stack; stack_node *p_sbeg, *p_send;
state *p_state; state *p_state;
}; };

View File

@ -2,28 +2,35 @@
#include <cstdlib> #include <cstdlib>
#include <algorithm> #include <algorithm>
#include <utility>
#include "cs_thread.hh" #include "cs_thread.hh"
#include "cs_error.hh" #include "cs_error.hh"
namespace cubescript { namespace cubescript {
static typename error::stack_node *save_stack(state &cs) { static void save_stack(
state &cs, typename error::stack_node *&sbeg,
typename error::stack_node *&send
) {
auto &ts = state_p{cs}.ts(); auto &ts = state_p{cs}.ts();
builtin_var *dalias = ts.istate->ivar_dbgalias; builtin_var *dalias = ts.istate->ivar_dbgalias;
auto dval = std::size_t(std::clamp( auto dval = std::size_t(std::clamp(
dalias->value().get_integer(), integer_type(0), integer_type(1000) dalias->value().get_integer(), integer_type(0), integer_type(1000)
)); ));
if (!dval) { if (!dval) {
return nullptr; sbeg = send = nullptr;
return;
} }
std::size_t depth = 0; std::size_t depth = 0;
std::size_t total = ts.callstack.size(); std::size_t total = ts.callstack.size();
if (!total) { if (!total) {
return nullptr; sbeg = send = nullptr;
return;
} }
auto slen = std::min(total, dval);
auto *st = static_cast<typename error::stack_node *>(ts.istate->alloc( auto *st = static_cast<typename error::stack_node *>(ts.istate->alloc(
nullptr, 0, sizeof(typename error::stack_node) * std::min(total, dval) nullptr, 0, sizeof(typename error::stack_node) * slen
)); ));
typename error::stack_node *ret = st, *nd = st; typename error::stack_node *ret = st, *nd = st;
++st; ++st;
@ -32,27 +39,41 @@ static typename error::stack_node *save_stack(state &cs) {
++depth; ++depth;
if (depth < dval) { if (depth < dval) {
new (nd) typename error::stack_node{ new (nd) typename error::stack_node{
nullptr, lev.id, total - depth + 1 lev.id, total - depth + 1
}; };
if (i == 0) { if (i == 0) {
break; break;
} }
nd->next = st;
nd = st++; nd = st++;
} else if (i == 0) { } else if (i == 0) {
new (nd) typename error::stack_node{nullptr, lev.id, 1}; new (nd) typename error::stack_node{lev.id, 1};
break; break;
} }
} }
return ret; sbeg = ret;
send = ret + slen;
}
LIBCUBESCRIPT_EXPORT error::error(error &&v):
p_errbeg{v.p_errbeg}, p_errend{v.p_errend},
p_sbeg{v.p_sbeg}, p_send{v.p_send}, p_state{v.p_state}
{
v.p_sbeg = v.p_send = nullptr;
}
LIBCUBESCRIPT_EXPORT error &error::operator=(error &&v) {
std::swap(p_errbeg, v.p_errbeg);
std::swap(p_errend, v.p_errend);
std::swap(p_sbeg, v.p_sbeg);
std::swap(p_send, v.p_send);
std::swap(p_state, v.p_state);
return *this;
} }
LIBCUBESCRIPT_EXPORT error::~error() { LIBCUBESCRIPT_EXPORT error::~error() {
std::size_t slen = 0; state_p{*p_state}.ts().istate->destroy_array(
for (stack_node const *nd = p_stack; nd; nd = nd->next) { p_sbeg, std::size_t(p_send - p_sbeg)
++slen; );
}
state_p{*p_state}.ts().istate->destroy_array(p_stack, slen);
} }
LIBCUBESCRIPT_EXPORT error::error(state &cs, std::string_view msg): LIBCUBESCRIPT_EXPORT error::error(state &cs, std::string_view msg):
@ -64,13 +85,23 @@ LIBCUBESCRIPT_EXPORT error::error(state &cs, std::string_view msg):
buf[msg.size()] = '\0'; buf[msg.size()] = '\0';
p_errbeg = sp; p_errbeg = sp;
p_errend = buf + msg.size(); p_errend = buf + msg.size();
p_stack = save_stack(cs); save_stack(cs, p_sbeg, p_send);
} }
LIBCUBESCRIPT_EXPORT error::error( LIBCUBESCRIPT_EXPORT error::error(
state &cs, char const *errbeg, char const *errend state &cs, char const *errbeg, char const *errend
): p_errbeg{errbeg}, p_errend{errend}, p_state{&cs} { ): p_errbeg{errbeg}, p_errend{errend}, p_state{&cs} {
p_stack = save_stack(cs); save_stack(cs, p_sbeg, p_send);
}
std::string_view error::what() const {
return std::string_view{p_errbeg, std::size_t(p_errend - p_errbeg)};
}
span_type<typename error::stack_node const> error::stack() const {
return span_type<typename error::stack_node const>{
p_sbeg, std::size_t(p_send - p_sbeg)
};
} }
} /* namespace cubescript */ } /* namespace cubescript */

View File

@ -84,19 +84,18 @@ LIBCUBESCRIPT_EXPORT void std_init_base(state &gcs) {
auto tb = any_value{}; auto tb = any_value{};
val.set_string(e.what(), cs); val.set_string(e.what(), cs);
ts.get_astack(ra).set_alias(ra, ts, val); ts.get_astack(ra).set_alias(ra, ts, val);
if (auto *snd = e.stack(); snd) { if (auto nds = e.stack(); !nds.empty()) {
auto bc = args[4].get_code(); auto bc = args[4].get_code();
if (!bc.empty()) { if (!bc.empty()) {
alias_local ist{cs, args[2].get_ident(cs)}; alias_local ist{cs, args[2].get_ident(cs)};
alias_local vst{cs, args[3].get_ident(cs)}; alias_local vst{cs, args[3].get_ident(cs)};
any_value idv{}; any_value idv{};
while (snd) { for (auto &nd: nds) {
idv.set_integer(integer_type(snd->index)); idv.set_integer(integer_type(nd.index));
ist.set(idv); ist.set(idv);
idv.set_string(snd->id.name().data(), cs); idv.set_string(nd.id.name().data(), cs);
vst.set(idv); vst.set(idv);
bc.call(cs); bc.call(cs);
snd = snd->next;
} }
} }
} }

View File

@ -254,13 +254,13 @@ static bool do_call(cs::state &cs, std::string_view line, bool file = false) {
"%s%s\n", !is_lnum ? "stdin: " : "stdin:", e.what().data() "%s%s\n", !is_lnum ? "stdin: " : "stdin:", e.what().data()
); );
std::size_t pindex = 1; std::size_t pindex = 1;
for (auto *nd = e.stack(); nd; nd = nd->next) { for (auto &nd: e.stack()) {
std::printf(" "); std::printf(" ");
if ((nd->index == 1) && (pindex > 2)) { if ((nd.index == 1) && (pindex > 2)) {
std::printf(".."); std::printf("..");
} }
pindex = nd->index; pindex = nd.index;
std::printf("%zu) %s\n", nd->index, nd->id.name().data()); std::printf("%zu) %s\n", nd.index, nd.id.name().data());
} }
return false; return false;
} }