diff --git a/include/cubescript/cubescript.hh b/include/cubescript/cubescript.hh index b06a5493..21a62ce8 100644 --- a/include/cubescript/cubescript.hh +++ b/include/cubescript/cubescript.hh @@ -315,8 +315,31 @@ enum { CsLibAll = 0b111 }; +struct CsStackStateNode { + CsStackStateNode const *next; + CsIdent const *id; + int index; +}; + +struct CsStackState { + CsStackState(CsStackStateNode *nd = nullptr, bool gap = false); + CsStackState(CsStackState const &) = delete; + CsStackState(CsStackState &&st); + ~CsStackState(); + + CsStackState &operator=(CsStackState const &) = delete; + CsStackState &operator=(CsStackState &&); + + CsStackStateNode const *get() const; + bool gap() const; + +private: + CsStackStateNode *p_node; + bool p_gap; +}; + using CsHookCb = ostd::Function; -using CsPanicCb = ostd::Function; +using CsPanicCb = ostd::Function; struct OSTD_EXPORT CsState { CsMap idents; @@ -353,10 +376,13 @@ struct OSTD_EXPORT CsState { CsPanicCb &get_panic_func(); template - bool pcall(F func, ostd::String *error) { + bool pcall( + F func, ostd::String *error = nullptr, + CsStackState *stack = nullptr + ) { return ipcall([](void *data) { (*static_cast(data))(); - }, error, &func); + }, error, stack, &func); } void error(CsString msg); @@ -476,7 +502,10 @@ struct OSTD_EXPORT CsState { private: CsIdent *add_ident(CsIdent *id); - bool ipcall(void (*f)(void *data), ostd::String *error, void *data); + bool ipcall( + void (*f)(void *data), ostd::String *error, + CsStackState *stack, void *data + ); CsHookCb p_callhook; CsPanicCb p_panicfunc; @@ -655,6 +684,27 @@ namespace util { } return ret; } + + template + inline ostd::Size print_stack(R &&writer, CsStackState const &st) { + ostd::Size ret = 0; + auto nd = st.get(); + while (nd) { + auto rt = ostd::format( + writer, + ((nd->index == 1) && st.gap()) + ? " ..%d) %s\n" : " %d) %s\n", + nd->index, nd->id->get_name() + ); + if (rt > 0) { + ret += ostd::Size(rt); + } else { + return ret; + } + nd = nd->next; + } + return ret; + } } /* namespace util */ } /* namespace cscript */ diff --git a/src/cs_vm.cc b/src/cs_vm.cc index e42c79a2..18b53f04 100644 --- a/src/cs_vm.cc +++ b/src/cs_vm.cc @@ -69,6 +69,69 @@ ostd::ConstCharRange cs_debug_line( return fmt; } +CsStackState::CsStackState(CsStackStateNode *nd, bool gap): + p_node(nd), p_gap(gap) +{} +CsStackState::CsStackState(CsStackState &&st): + p_node(st.p_node), p_gap(st.p_gap) +{ + st.p_node = nullptr; + st.p_gap = false; +} + +CsStackState::~CsStackState() { + delete[] p_node; +} + +CsStackState &CsStackState::operator=(CsStackState &&st) { + p_node = st.p_node; + p_gap = st.p_gap; + st.p_node = nullptr; + st.p_gap = false; + return *this; +} + +CsStackStateNode const *CsStackState::get() const { + return p_node; +} + +bool CsStackState::gap() const { + return p_gap; +} + +CsStackState cs_save_stack(CsState &cs) { + CsIvar *dalias = static_cast(cs.identmap[DbgaliasIdx]); + if (!dalias->get_value()) { + return CsStackState(nullptr, true); + } + int total = 0, depth = 0; + for (CsIdentLink *l = cs.p_callstack; l != &cs.noalias; l = l->next) { + total++; + } + CsStackStateNode *st = + new CsStackStateNode[ostd::min(total, dalias->get_value())]; + CsStackStateNode *ret = st, *nd = st; + ++st; + for (CsIdentLink *l = cs.p_callstack; l != &cs.noalias; l = l->next) { + ++depth; + if (depth < dalias->get_value()) { + nd->id = l->id; + nd->index = total - depth + 1; + if (l->next == &cs.noalias) { + nd->next = nullptr; + } else { + nd->next = st; + } + nd = st++; + } else if (l->next == &cs.noalias) { + nd->id = l->id; + nd->index = 1; + nd->next = NULL; + } + } + return CsStackState(ret, total != dalias->get_value()); +} + void cs_debug_alias(CsState &cs) { CsIvar *dalias = static_cast(cs.identmap[DbgaliasIdx]); if (!dalias->get_value()) { diff --git a/src/cs_vm.hh b/src/cs_vm.hh index 4e538187..c8c7c5e5 100644 --- a/src/cs_vm.hh +++ b/src/cs_vm.hh @@ -88,10 +88,15 @@ constexpr ostd::Size CsTypeStorageSize = struct CsErrorException { CsString errmsg; + CsStackState stack; CsErrorException() = delete; CsErrorException(CsErrorException const &) = delete; - CsErrorException(CsErrorException &&v): errmsg(ostd::move(v.errmsg)) {} - CsErrorException(CsString &&v): errmsg(ostd::move(v)) {} + CsErrorException(CsErrorException &&v): + errmsg(ostd::move(v.errmsg)), stack(ostd::move(v.stack)) + {} + CsErrorException(CsString &&v, CsStackState &&st): + errmsg(ostd::move(v)), stack(ostd::move(st)) + {} }; ostd::ConstCharRange cs_debug_line( @@ -100,6 +105,8 @@ ostd::ConstCharRange cs_debug_line( void cs_debug_alias(CsState &cs); +CsStackState cs_save_stack(CsState &cs); + template void cs_debug_code(CsState &cs, ostd::ConstCharRange fmt, A &&...args) { if (cs.nodebug) { diff --git a/src/cubescript.cc b/src/cubescript.cc index b15ec1ee..81e39a46 100644 --- a/src/cubescript.cc +++ b/src/cubescript.cc @@ -261,7 +261,7 @@ CsState::CsState(): noalias.argstack = nullptr; /* default panic func */ - p_panicfunc = [this](CsString v) { + p_panicfunc = [this](CsString v, CsStackState) { get_err().writefln( "PANIC: unprotected error in call to CubeScript (%s)", v ); @@ -418,15 +418,21 @@ CsPanicCb &CsState::get_panic_func() { return p_panicfunc; } -bool CsState::ipcall(void (*f)(void *data), ostd::String *error, void *data) { +bool CsState::ipcall( + void (*f)(void *data), ostd::String *error, + CsStackState *stack, void *data +) { ++protect; try { f(data); - } catch (CsErrorException const &v) { + } catch (CsErrorException &v) { --protect; if (error) { *error = ostd::move(v.errmsg); } + if (stack) { + *stack = ostd::move(v.stack); + } return false; } catch (...) { --protect; @@ -438,10 +444,10 @@ bool CsState::ipcall(void (*f)(void *data), ostd::String *error, void *data) { void CsState::error(CsString msg) { if (protect) { - throw CsErrorException(ostd::move(msg)); + throw CsErrorException(ostd::move(msg), cs_save_stack(*this)); } else { if (p_panicfunc) { - p_panicfunc(ostd::move(msg)); + p_panicfunc(ostd::move(msg), cs_save_stack(*this)); } exit(EXIT_FAILURE); } diff --git a/tools/repl.cc b/tools/repl.cc index 59d8fee3..c4b15d68 100644 --- a/tools/repl.cc +++ b/tools/repl.cc @@ -197,6 +197,7 @@ static void do_call(CsState &cs, ostd::ConstCharRange line, bool file = false) { CsValue ret; signal(SIGINT, do_sigint); ostd::String err; + cscript::CsStackState st; if (!cs.pcall([&]() { if (file) { if (!cs.run_file(line, ret)) { @@ -205,9 +206,10 @@ static void do_call(CsState &cs, ostd::ConstCharRange line, bool file = false) { } else { cs.run(line, ret); } - }, &err)) { + }, &err, &st)) { signal(SIGINT, SIG_DFL); - ostd::writeln("error: ", err); + cs.get_out().writeln("error: ", err); + cscript::util::print_stack(cs.get_out().iter(), st); return; } signal(SIGINT, SIG_DFL);