diff --git a/lib/MAST/Ops.nqp b/lib/MAST/Ops.nqp index 1a32aa13e4..f720d145aa 100644 --- a/lib/MAST/Ops.nqp +++ b/lib/MAST/Ops.nqp @@ -547,7 +547,8 @@ BEGIN { 1291, 1293, 1298, - 1299); + 1299, + 1304); MAST::Ops.WHO<@counts> := nqp::list_i(0, 2, 2, @@ -1092,7 +1093,8 @@ BEGIN { 2, 5, 1, - 5); + 5, + 2); MAST::Ops.WHO<@values> := nqp::list_i(10, 8, 18, @@ -2396,7 +2398,9 @@ BEGIN { 57, 57, 65, - 57); + 57, + 66, + 65); MAST::Ops.WHO<%codes> := nqp::hash('no_op', 0, 'const_i8', 1, 'const_i16', 2, @@ -2941,7 +2945,8 @@ BEGIN { 'write_fhb', 541, 'replace', 542, 'newexception', 543, - 'openpipe', 544); + 'openpipe', 544, + 'backtrace', 545); MAST::Ops.WHO<@names> := nqp::list('no_op', 'const_i8', 'const_i16', @@ -3486,5 +3491,6 @@ BEGIN { 'write_fhb', 'replace', 'newexception', - 'openpipe'); + 'openpipe', + 'backtrace'); } diff --git a/src/core/exceptions.c b/src/core/exceptions.c index 326b8b2ed8..e7ba14ddeb 100644 --- a/src/core/exceptions.c +++ b/src/core/exceptions.c @@ -236,6 +236,72 @@ char * MVM_exception_backtrace_line(MVMThreadContext *tc, MVMFrame *cur_frame, M return o; } +/* Returns a list of hashes containing file, line, sub and annotations. */ +MVMObject * MVM_exception_backtrace(MVMThreadContext *tc, MVMObject *ex_obj) { + MVMException *ex; + MVMFrame *cur_frame; + MVMObject *arr, *annotations, *row, *value; + MVMuint32 count = 0; + MVMString *k_file, *k_line, *k_sub, *k_anno; + + if (IS_CONCRETE(ex_obj) && REPR(ex_obj)->ID == MVM_REPR_ID_MVMException) + ex = (MVMException *)ex_obj; + else + MVM_exception_throw_adhoc(tc, "Op 'backtrace' needs an exception object"); + + MVM_gc_root_temp_push(tc, (MVMCollectable **)&arr); + MVM_gc_root_temp_push(tc, (MVMCollectable **)&annotations); + MVM_gc_root_temp_push(tc, (MVMCollectable **)&row); + MVM_gc_root_temp_push(tc, (MVMCollectable **)&value); + MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_file); + MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_line); + MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_sub); + MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_anno); + + k_file = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "file"); + k_line = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "line"); + k_sub = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "sub"); + k_anno = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "annotations"); + + cur_frame = ex->body.origin; + arr = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray); + + while (cur_frame != NULL) { + MVMuint8 *cur_op = count ? cur_frame->return_address : cur_frame->throw_address; + MVMuint32 offset = cur_op - cur_frame->static_info->body.bytecode; + MVMBytecodeAnnotation *annot = MVM_bytecode_resolve_annotation(tc, &cur_frame->static_info->body, offset); + char *line_number = malloc(16); + snprintf(line_number, 16, "%d", annot ? annot->line_number + 1 : 1); + + /* annotations hash will contain "file" and "line" */ + annotations = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTHash); + + /* file */ + value = MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type, + cur_frame->static_info->body.cu->body.filename); + MVM_repr_bind_key_o(tc, annotations, k_file, value); + + /* line */ + value = (MVMObject *)MVM_string_ascii_decode_nt(tc, tc->instance->VMString, line_number); + value = MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type, (MVMString *)value); + MVM_repr_bind_key_o(tc, annotations, k_line, value); + free(line_number); + + /* row will contain "sub" and "annotations" */ + row = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTHash); + MVM_repr_bind_key_o(tc, row, k_sub, cur_frame->code_ref); + MVM_repr_bind_key_o(tc, row, k_anno, annotations); + + MVM_repr_push_o(tc, arr, row); + cur_frame = cur_frame->caller; + count++; + } + + MVM_gc_root_temp_pop_n(tc, 8); + + return arr; +} + /* Returns the lines (backtrace) of an exception-object as an array. */ MVMObject * MVM_exception_backtrace_strings(MVMThreadContext *tc, MVMObject *ex_obj) { MVMException *ex; @@ -245,7 +311,7 @@ MVMObject * MVM_exception_backtrace_strings(MVMThreadContext *tc, MVMObject *ex_ if (IS_CONCRETE(ex_obj) && REPR(ex_obj)->ID == MVM_REPR_ID_MVMException) ex = (MVMException *)ex_obj; else - MVM_exception_throw_adhoc(tc, "Can only throw an exception object"); + MVM_exception_throw_adhoc(tc, "Op 'backtracestrings' needs an exception object"); cur_frame = ex->body.origin; arr = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray); diff --git a/src/core/exceptions.h b/src/core/exceptions.h index e6eb7a0ba2..a078d10196 100644 --- a/src/core/exceptions.h +++ b/src/core/exceptions.h @@ -58,6 +58,7 @@ struct MVMActiveHandler { }; /* Exception related functions. */ +MVMObject * MVM_exception_backtrace(MVMThreadContext *tc, MVMObject *ex_obj); MVMObject * MVM_exception_backtrace_strings(MVMThreadContext *tc, MVMObject *exObj); void MVM_exception_throwcat(MVMThreadContext *tc, MVMuint8 mode, MVMuint32 cat, MVMRegister *resume_result); void MVM_exception_throwobj(MVMThreadContext *tc, MVMuint8 mode, MVMObject *exObj, MVMRegister *resume_result); diff --git a/src/core/interp.c b/src/core/interp.c index 5a180567ef..77f567c90c 100644 --- a/src/core/interp.c +++ b/src/core/interp.c @@ -3780,6 +3780,10 @@ void MVM_interp_run(MVMThreadContext *tc, void (*initial_invoke)(MVMThreadContex goto NEXT; OP(openpipe): MVM_exception_throw_adhoc(tc, "openpipe NYI"); + OP(backtrace): + GET_REG(cur_op, 0).o = MVM_exception_backtrace(tc, GET_REG(cur_op, 2).o); + cur_op += 4; + goto NEXT; #if MVM_CGOTO OP_CALL_EXTOP: { /* Bounds checking? Never heard of that. */ diff --git a/src/core/oplabels.h b/src/core/oplabels.h index 1c764f00b4..2f5ad7fdd2 100644 --- a/src/core/oplabels.h +++ b/src/core/oplabels.h @@ -546,7 +546,7 @@ static const void * const LABELS[] = { &&OP_replace, &&OP_newexception, &&OP_openpipe, - NULL, + &&OP_backtrace, NULL, NULL, NULL, diff --git a/src/core/oplist b/src/core/oplist index 003ff2b8b4..012df7f4ee 100644 --- a/src/core/oplist +++ b/src/core/oplist @@ -574,3 +574,4 @@ write_fhb r(obj) r(obj) replace w(str) r(str) r(int64) r(int64) r(str) newexception w(obj) openpipe w(obj) r(str) r(str) r(obj) r(str) +backtrace w(obj) r(obj) diff --git a/src/core/ops.c b/src/core/ops.c index 843faddb5a..714e212a8a 100644 --- a/src/core/ops.c +++ b/src/core/ops.c @@ -3821,9 +3821,16 @@ static MVMOpInfo MVM_op_infos[] = { 5, { MVM_operand_write_reg | MVM_operand_obj, MVM_operand_read_reg | MVM_operand_str, MVM_operand_read_reg | MVM_operand_str, MVM_operand_read_reg | MVM_operand_obj, MVM_operand_read_reg | MVM_operand_str } }, + { + MVM_OP_backtrace, + "backtrace", + " ", + 2, + { MVM_operand_write_reg | MVM_operand_obj, MVM_operand_read_reg | MVM_operand_obj } + }, }; -static unsigned short MVM_op_counts = 545; +static unsigned short MVM_op_counts = 546; MVMOpInfo * MVM_op_get_op(unsigned short op) { if (op >= MVM_op_counts) diff --git a/src/core/ops.h b/src/core/ops.h index 0bf216a68e..660576ad71 100644 --- a/src/core/ops.h +++ b/src/core/ops.h @@ -546,6 +546,7 @@ #define MVM_OP_replace 542 #define MVM_OP_newexception 543 #define MVM_OP_openpipe 544 +#define MVM_OP_backtrace 545 #define MVM_OP_EXT_BASE 1024 #define MVM_OP_EXT_CU_LIMIT 1024