Add backtraces to errors

This commit is contained in:
Laria 2023-01-28 21:41:48 +01:00
parent 3cc2a83226
commit bd19f689b9
7 changed files with 184 additions and 114 deletions

View file

@ -619,12 +619,9 @@ typedef size_t apfl_slotidx;
enum apfl_result {
APFL_RESULT_OK, // Evaluation succeeded, value is on the stack
// TODO: No further details are yet provided on (fatal) error. Not quite
// sure what to return / how errors/exceptions should work. Maybe a
// value + a backtrace?
APFL_RESULT_ERR,
APFL_RESULT_ERR_ALLOC,
APFL_RESULT_ERR, // Evaluation failed, an error is on the stack
APFL_RESULT_ERRERR, // Evaluation failed and an error occurred during error handling, no error on the stack.
APFL_RESULT_ERR_ALLOC, // Evaluation failed due to memory allocation error, no error on the stack.
};
struct apfl_config {
@ -636,7 +633,7 @@ apfl_ctx apfl_ctx_new(struct apfl_config);
void apfl_ctx_destroy(apfl_ctx);
typedef void (*apfl_panic_callback)(apfl_ctx, bool with_error_on_stack, void *);
typedef void (*apfl_panic_callback)(apfl_ctx, void *, enum apfl_result);
void apfl_ctx_set_panic_callback(apfl_ctx, apfl_panic_callback, void *);
@ -645,8 +642,12 @@ typedef struct apfl_iterative_runner_data *apfl_iterative_runner;
apfl_iterative_runner apfl_iterative_runner_new(apfl_ctx, struct apfl_source_reader);
bool apfl_iterative_runner_next(apfl_iterative_runner);
enum apfl_result apfl_iterative_runner_get_result(apfl_iterative_runner);
bool apfl_iterative_runner_has_error_on_stack(apfl_iterative_runner);
bool apfl_iterative_runner_stopped_because_of_error(apfl_iterative_runner);
bool apfl_iterative_runner_run_repl(
apfl_iterative_runner,
struct apfl_format_writer w_out,
struct apfl_format_writer w_err
);
void apfl_iterative_runner_destroy(apfl_iterative_runner);
typedef void (*apfl_cfunc)(apfl_ctx);
@ -722,7 +723,7 @@ void apfl_push_userdata(apfl_ctx, void *);
void *apfl_get_userdata(apfl_ctx, apfl_stackidx);
void apfl_call(apfl_ctx, apfl_stackidx func, apfl_stackidx args);
enum apfl_result apfl_call_protected(apfl_ctx, apfl_stackidx func, apfl_stackidx args, bool *with_error_on_stack);
enum apfl_result apfl_call_protected(apfl_ctx, apfl_stackidx func, apfl_stackidx args);
enum apfl_call_stack_entry_type {
APFL_CSE_FUNCTION,

View file

@ -19,22 +19,33 @@ static bool current_stack_move_to_top(apfl_ctx, apfl_stackidx);
static struct apfl_string *new_copied_string(struct gc *, struct apfl_string_view);
APFL_NORETURN static void
panic(apfl_ctx ctx, bool with_error_on_stack, enum apfl_result result)
panic(apfl_ctx ctx, enum apfl_result result)
{
(void)ctx;
(void)result;
(void)with_error_on_stack;
fprintf(stderr, "panic!\n");
// TODO: more details
abort();
}
struct protected_errcallback_data {
void *opaque_outer;
void (*errcallback)(apfl_ctx, void *);
};
void
protected_errcallback(apfl_ctx ctx, void *opaque)
{
struct protected_errcallback_data *data = opaque;
data->errcallback(ctx, data->opaque_outer);
}
enum apfl_result
apfl_do_protected(
apfl_ctx ctx,
void (*callback)(apfl_ctx, void *),
void *opaque,
bool *with_error_on_stack
void (*errcallback)(apfl_ctx, void *)
) {
struct error_handler *prev_handler = ctx->error_handler;
@ -52,17 +63,45 @@ apfl_do_protected(
result = (enum apfl_result)(rv - RESULT_OFF_FOR_LONGJMP);
assert(result != APFL_RESULT_OK);
struct apfl_value err;
if ((*with_error_on_stack = handler.with_error_on_stack)) {
if (!apfl_stack_pop(ctx, &err, -1)) {
*with_error_on_stack = false;
// TODO: Indicate error during error handling
bool with_error_on_stack = APFL_RESULT_ERR;
if (with_error_on_stack && errcallback != NULL) {
struct protected_errcallback_data data = {
.opaque_outer = opaque,
.errcallback = errcallback,
};
switch (apfl_do_protected(ctx, protected_errcallback, &data, NULL)) {
case APFL_RESULT_OK:
break;
case APFL_RESULT_ERR:
result = APFL_RESULT_ERRERR;
with_error_on_stack = false;
break;
case APFL_RESULT_ERRERR:
result = APFL_RESULT_ERRERR;
with_error_on_stack = false;
break;
case APFL_RESULT_ERR_ALLOC:
result = APFL_RESULT_ERR_ALLOC;
with_error_on_stack = false;
break;
}
}
apfl_gc_tmproots_restore(&ctx->gc, tmproots);
assert(callstack_len <= ctx->call_stack.cap);
struct apfl_value err;
if (with_error_on_stack && !apfl_stack_pop(ctx, &err, -1)) {
result = APFL_RESULT_ERRERR;
with_error_on_stack = false;
}
apfl_gc_tmproots_restore(&ctx->gc, tmproots);
tmproots = apfl_gc_tmproots_begin(&ctx->gc);
if (with_error_on_stack && !apfl_value_add_as_tmproot(&ctx->gc, err)) {
result = APFL_RESULT_ERRERR;
with_error_on_stack = false;
}
assert(callstack_len <= ctx->call_stack.cap);
for (size_t i = callstack_len; i < ctx->call_stack.len; i++) {
apfl_call_stack_entry_deinit(ctx->gc.allocator, &ctx->call_stack.items[i]);
}
@ -79,12 +118,12 @@ apfl_do_protected(
)
);
if (*with_error_on_stack) {
if (!apfl_stack_push(ctx, err)) {
*with_error_on_stack = false;
// TODO: Indicate error during error handling
}
if (with_error_on_stack && !apfl_stack_push(ctx, err)) {
result = APFL_RESULT_ERRERR;
with_error_on_stack = false;
}
apfl_gc_tmproots_restore(&ctx->gc, tmproots);
}
ctx->error_handler = prev_handler;
@ -105,49 +144,53 @@ call_protected_cb(apfl_ctx ctx, void *opaque)
}
enum apfl_result
apfl_call_protected(apfl_ctx ctx, apfl_stackidx func, apfl_stackidx args, bool *with_error_on_stack)
apfl_call_protected(apfl_ctx ctx, apfl_stackidx func, apfl_stackidx args)
{
struct call_protected_data data = {
.func = func,
.args = args,
};
return apfl_do_protected(ctx, call_protected_cb, &data, with_error_on_stack);
return apfl_do_protected(ctx, call_protected_cb, &data, NULL);
}
APFL_NORETURN static void
raise_error(apfl_ctx ctx, bool with_error_on_stack, enum apfl_result type)
raise_error(apfl_ctx ctx, enum apfl_result type)
{
assert(type != APFL_RESULT_OK);
if (ctx->error_handler != NULL) {
ctx->error_handler->with_error_on_stack = with_error_on_stack;
longjmp(ctx->error_handler->jump, (int)type + RESULT_OFF_FOR_LONGJMP);
}
if (ctx->panic_callback != NULL) {
ctx->panic_callback(ctx, with_error_on_stack, ctx->panic_callback_data);
ctx->panic_callback(ctx, ctx->panic_callback_data, type);
}
panic(ctx, with_error_on_stack, type);
panic(ctx, type);
}
APFL_NORETURN void
apfl_raise_error(apfl_ctx ctx, apfl_stackidx idx)
{
bool ok = current_stack_move_to_top(ctx, idx);
raise_error(ctx, ok, APFL_RESULT_ERR);
if (!current_stack_move_to_top(ctx, idx)) {
raise_error(ctx, APFL_RESULT_ERRERR);
}
raise_error(ctx, APFL_RESULT_ERR);
}
APFL_NORETURN void
apfl_raise_const_error(apfl_ctx ctx, const char *message)
{
raise_error(ctx, try_push_const_string(ctx, message), APFL_RESULT_ERR);
if (!try_push_const_string(ctx, message)) {
raise_error(ctx, APFL_RESULT_ERRERR);
}
raise_error(ctx, APFL_RESULT_ERR);
}
APFL_NORETURN void
apfl_raise_alloc_error(apfl_ctx ctx)
{
raise_error(ctx, false, APFL_RESULT_ERR_ALLOC);
raise_error(ctx, APFL_RESULT_ERR_ALLOC);
}
APFL_NORETURN void

View file

@ -129,7 +129,6 @@ struct call_stack {
struct error_handler {
jmp_buf jump;
bool with_error_on_stack;
};
struct iterative_runners_list {
@ -202,7 +201,7 @@ enum apfl_result apfl_do_protected(
apfl_ctx ctx,
void (*callback)(apfl_ctx, void *),
void *opaque,
bool *with_error_on_stack
void (*errcallback)(apfl_ctx, void *)
);
bool apfl_ctx_register_iterative_runner(apfl_ctx, apfl_iterative_runner);

View file

@ -1353,7 +1353,6 @@ struct apfl_iterative_runner_data {
apfl_tokenizer_ptr tokenizer;
apfl_parser_ptr parser;
enum apfl_result result;
bool with_error_on_stack;
enum interative_runner_state state;
struct scope *scope;
};
@ -1494,6 +1493,54 @@ iterative_runner_next_protected(apfl_ctx ctx, void *opaque)
assert(false);
}
#define DECORATE_TRY_FMT(ctx, x) do { if (!(x)) { apfl_raise_alloc_error(ctx); } } while (0)
static void
iterative_runner_decorate_error(apfl_ctx ctx, void *opaque)
{
(void)opaque;
struct apfl_string_builder sb = apfl_string_builder_init(ctx->gc.allocator);
struct apfl_format_writer w = apfl_format_string_writer(&sb);
apfl_tostring(ctx, -1);
if (!(
apfl_format_put_string(w, apfl_get_string(ctx, -1))
&& apfl_format_put_string(w, "\n\nBacktrace:")
)) {
goto fail;
}
size_t depth = apfl_call_stack_depth(ctx);
for (size_t i = 0; i < depth; i++) {
if (!(
apfl_format_put_string(w, "\n")
&& apfl_format_put_string(w, "#")
&& apfl_format_put_int(w, (int)i+1)
&& apfl_format_put_string(w, ": ")
&& apfl_call_stack_entry_info_format(
w,
apfl_call_stack_inspect(ctx, i)
)
)) {
goto fail;
}
}
struct apfl_string string = apfl_string_builder_move_string(&sb);
apfl_string_builder_deinit(&sb);
if (!apfl_move_string_onto_stack(ctx, string)) {
apfl_raise_alloc_error(ctx);
}
return;
fail:
apfl_string_builder_deinit(&sb);
apfl_raise_alloc_error(ctx);
}
bool
apfl_iterative_runner_next(apfl_iterative_runner runner)
{
@ -1503,7 +1550,12 @@ apfl_iterative_runner_next(apfl_iterative_runner runner)
apfl_stack_clear(runner->ctx);
runner->result = apfl_do_protected(runner->ctx, iterative_runner_next_protected, runner, &runner->with_error_on_stack);
runner->result = apfl_do_protected(
runner->ctx,
iterative_runner_next_protected,
runner,
iterative_runner_decorate_error
);
return runner->state == IRUNNER_OK;
}
@ -1514,18 +1566,54 @@ apfl_iterative_runner_get_result(apfl_iterative_runner runner)
return runner->result;
}
bool
apfl_iterative_runner_has_error_on_stack(apfl_iterative_runner runner)
{
return runner->with_error_on_stack;
}
bool
apfl_iterative_runner_stopped_because_of_error(apfl_iterative_runner runner)
{
return runner->state == IRUNNER_ERR;
}
bool
apfl_iterative_runner_run_repl(
apfl_iterative_runner runner,
struct apfl_format_writer w_out,
struct apfl_format_writer w_err
) {
apfl_ctx ctx = runner->ctx;
while (apfl_iterative_runner_next(runner)) {
switch (apfl_iterative_runner_get_result(runner)) {
case APFL_RESULT_OK :
if (apfl_get_type(ctx, -1) == APFL_VALUE_NIL) {
apfl_drop(ctx, -1);
} else {
FMT_TRY(apfl_debug_print_val(ctx, -1, w_out));
}
break;
case APFL_RESULT_ERR:
FMT_TRY(apfl_format_put_string(w_err, "Error occurred during evaluation:\n"));
if (apfl_get_type(ctx, -1) == APFL_VALUE_STRING) {
FMT_TRY(apfl_format_put_string(w_err, apfl_get_string(ctx, -1)));
} else {
FMT_TRY(apfl_debug_print_val(ctx, -1, w_err));
}
FMT_TRY(apfl_format_put_char(w_err, '\n'));
break;
case APFL_RESULT_ERRERR:
FMT_TRY(apfl_format_put_string(w_err, "Error occurred during error handling.\n"));
break;
case APFL_RESULT_ERR_ALLOC:
FMT_TRY(apfl_format_put_string(w_err, "Fatal: Could not allocate memory.\n"));
return false;
}
}
if (apfl_iterative_runner_stopped_because_of_error(runner)) {
return false;
}
return true;
}
void
apfl_iterative_runner_destroy(apfl_iterative_runner runner)
{

View file

@ -120,7 +120,7 @@ parse_test(const struct apfl_string str, struct test_parts *parts)
static void
dump_stack_error(apfl_ctx ctx, apfl_iterative_runner runner)
{
if (apfl_iterative_runner_has_error_on_stack(runner)) {
if (apfl_iterative_runner_get_result(runner) == APFL_RESULT_ERR) {
assert(apfl_debug_print_val(ctx, -1, apfl_format_file_writer(stderr)));
}
}
@ -175,6 +175,9 @@ runtest(const char *filename)
fprintf(stderr, "Error occurred during evaluation.\n");
dump_stack_error(ctx, runner);
return T_ERR;
case APFL_RESULT_ERRERR:
fprintf(stderr, "Error while handling error.\n");
return T_ERR;
case APFL_RESULT_ERR_ALLOC:
fprintf(stderr, "Fatal: Could not allocate memory.\n");
return T_FATAL;

View file

@ -138,22 +138,15 @@ exit:
return rv;
}
static void
dump_stack_error(apfl_ctx ctx, apfl_iterative_runner runner)
{
if (apfl_iterative_runner_has_error_on_stack(runner)) {
assert(apfl_debug_print_val(ctx, -1, apfl_format_file_writer(stderr)));
}
}
static int
repl_eval(void)
{
int rv = 0;
struct apfl_format_writer w_out = apfl_format_file_writer(stdout);
struct apfl_format_writer w_err = apfl_format_file_writer(stderr);
apfl_ctx ctx = apfl_ctx_new((struct apfl_config) {
.allocator = apfl_allocator_default(),
.output_writer = apfl_format_file_writer(stdout),
.output_writer = w_out,
});
if (ctx == NULL) {
fprintf(stderr, "Failed to init the context\n");
@ -167,31 +160,8 @@ repl_eval(void)
return 1;
}
while (apfl_iterative_runner_next(runner)) {
switch (apfl_iterative_runner_get_result(runner)) {
case APFL_RESULT_OK :
if (apfl_get_type(ctx, -1) == APFL_VALUE_NIL) {
apfl_drop(ctx, -1);
} else {
assert(apfl_debug_print_val(ctx, -1, apfl_format_file_writer(stdout)));
}
break;
case APFL_RESULT_ERR:
dump_stack_error(ctx, runner);
fprintf(stderr, "Error occurred during evaluation.\n");
break;
case APFL_RESULT_ERR_ALLOC:
fprintf(stderr, "Fatal: Could not allocate memory.\n");
rv = 1;
goto exit;
}
}
int rv = apfl_iterative_runner_run_repl(runner, w_out, w_err) ? 0 : 1;
if (apfl_iterative_runner_stopped_because_of_error(runner)) {
rv = 1;
}
exit:
apfl_iterative_runner_destroy(runner);
apfl_ctx_destroy(ctx);
return rv;

View file

@ -62,19 +62,9 @@ playground_source_reader_cb(void *context, char *buf, size_t *len, bool need)
return true;
}
static void
dump_stack_error(apfl_ctx ctx, apfl_iterative_runner runner, struct apfl_format_writer w_err)
{
if (apfl_iterative_runner_has_error_on_stack(runner)) {
assert(apfl_debug_print_val(ctx, -1, w_err));
}
}
int
main(void)
{
int rv = 0;
struct apfl_format_writer w_ok = {.write = web_fmt_write, .opaque = (void *)0};
struct apfl_format_writer w_err = {.write = web_fmt_write, .opaque = (void *)1};
@ -104,32 +94,8 @@ main(void)
return 1;
}
while (apfl_iterative_runner_next(runner)) {
switch (apfl_iterative_runner_get_result(runner)) {
case APFL_RESULT_OK :
if (apfl_get_type(ctx, -1) == APFL_VALUE_NIL) {
apfl_drop(ctx, -1);
} else {
assert(apfl_debug_print_val(ctx, -1, w_ok));
}
break;
case APFL_RESULT_ERR:
dump_stack_error(ctx, runner, w_err);
assert(apfl_format_put_string(w_err, "Error occurred during evaluation.\n"));
break;
case APFL_RESULT_ERR_ALLOC:
assert(apfl_format_put_string(w_err, "Fatal: Could not allocate memory.\n"));
rv = 1;
goto exit;
}
}
int rv = apfl_iterative_runner_run_repl(runner, w_ok, w_err) ? 0 : 1;
if (apfl_iterative_runner_stopped_because_of_error(runner)) {
assert(apfl_format_put_string(w_err, "Runner stopped due to error.\n"));
rv = 1;
}
exit:
free(source_reader_data.str);
apfl_iterative_runner_destroy(runner);
apfl_ctx_destroy(ctx);