#include static VALUE cContext; /* "Step", "Next" and "Finish" do their work by saving information about where * to stop next. reset_stepping_stop_points removes/resets this information. */ extern void reset_stepping_stop_points(debug_context_t *context) { context->dest_frame = -1; context->lines = -1; context->steps = -1; context->stop_frame = -1; } static inline VALUE Context_stack_size(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return INT2FIX(context->stack_size); } static inline VALUE Context_dead(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_DEAD) ? Qtrue : Qfalse; } static void context_mark(void *data) { debug_context_t *context = (debug_context_t *)data; rb_gc_mark(context->backtrace); } static void context_free(void *data) { } extern VALUE context_create() { debug_context_t *context = ALLOC(debug_context_t); context->last_file = Qnil; context->last_line = Qnil; context->flags = 0; context->stack_size = 0; reset_stepping_stop_points(context); context->stop_reason = CTX_STOP_NONE; context->backtrace = Qnil; return Data_Wrap_Struct(cContext, context_mark, context_free, context); } extern VALUE context_dup(debug_context_t *context) { debug_context_t *new_context = ALLOC(debug_context_t); memcpy(new_context, context, sizeof(debug_context_t)); reset_stepping_stop_points(new_context); new_context->backtrace = context->backtrace; CTX_FL_SET(new_context, CTX_FL_DEAD); return Data_Wrap_Struct(cContext, context_mark, context_free, new_context); } static VALUE dc_backtrace(const debug_context_t *context) { if (NIL_P(context->backtrace)) rb_raise(rb_eRuntimeError, "Backtrace information is not available"); return context->backtrace; } static VALUE dc_frame_get(const debug_context_t *context, int frame_index, enum frame_component type) { if (frame_index >= RARRAY_LEN(dc_backtrace(context))) rb_raise(rb_eRuntimeError, "That frame doesn't exist!"); VALUE frame = rb_ary_entry(dc_backtrace(context), frame_index); return rb_ary_entry(frame, type); } static VALUE dc_frame_location(const debug_context_t *context, int frame_index) { return dc_frame_get(context, frame_index, LOCATION); } static VALUE dc_frame_self(const debug_context_t *context, int frame_index) { return dc_frame_get(context, frame_index, SELF); } static VALUE dc_frame_class(const debug_context_t *context, int frame_index) { return dc_frame_get(context, frame_index, CLASS); } static VALUE dc_frame_binding(const debug_context_t *context, int frame_index) { return dc_frame_get(context, frame_index, BINDING); } static VALUE load_backtrace(const rb_debug_inspector_t *inspector) { VALUE backtrace = rb_ary_new(); VALUE locs = rb_debug_inspector_backtrace_locations(inspector); int i; for (i=0; ibacktrace = load_backtrace(inspector); return Qnil; } static VALUE open_debug_inspector_i(const rb_debug_inspector_t *inspector, void *data) { struct call_with_inspection_data *cwi = (struct call_with_inspection_data *)data; cwi->dc->backtrace = load_backtrace(inspector); return rb_funcall2(cwi->context_obj, cwi->id, cwi->argc, cwi->argv); } static VALUE open_debug_inspector(struct call_with_inspection_data *cwi) { return rb_debug_inspector_open(open_debug_inspector_i, cwi); } static VALUE close_debug_inspector(struct call_with_inspection_data *cwi) { cwi->dc->backtrace = Qnil; return Qnil; } extern VALUE call_with_debug_inspector(struct call_with_inspection_data *data) { return rb_ensure(open_debug_inspector, (VALUE)data, close_debug_inspector, (VALUE)data); } #define FRAME_SETUP \ debug_context_t *context; \ VALUE frame_no; \ int frame_n; \ Data_Get_Struct(self, debug_context_t, context); \ if (!rb_scan_args(argc, argv, "01", &frame_no)) \ frame_n = 0; \ else \ frame_n = FIX2INT(frame_no); \ if (frame_n < 0 || frame_n >= context->stack_size) \ { \ rb_raise(rb_eArgError, "Invalid frame number %d, stack (0...%d)", \ frame_n, context->stack_size-1); \ } \ static VALUE Context_frame_file(int argc, VALUE *argv, VALUE self) { FRAME_SETUP; VALUE loc; loc = dc_frame_location(context, frame_n); return rb_funcall(loc, rb_intern("path"), 0); } static VALUE Context_frame_line(int argc, VALUE *argv, VALUE self) { FRAME_SETUP; VALUE loc; loc = dc_frame_location(context, frame_n); return rb_funcall(loc, rb_intern("lineno"), 0); } static VALUE Context_frame_method(int argc, VALUE *argv, VALUE self) { FRAME_SETUP; VALUE loc; loc = dc_frame_location(context, frame_n); return rb_str_intern(rb_funcall(loc, rb_intern("label"), 0)); } static VALUE Context_frame_binding(int argc, VALUE *argv, VALUE self) { FRAME_SETUP; return dc_frame_binding(context, frame_n); } static VALUE Context_frame_self(int argc, VALUE *argv, VALUE self) { FRAME_SETUP; return dc_frame_self(context, frame_n); } static VALUE Context_frame_class(int argc, VALUE *argv, VALUE self) { FRAME_SETUP; return dc_frame_class(context, frame_n); } static VALUE Context_tracing(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_TRACING) ? Qtrue : Qfalse; } static VALUE Context_set_tracing(VALUE self, VALUE value) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (RTEST(value)) CTX_FL_SET(context, CTX_FL_TRACING); else CTX_FL_UNSET(context, CTX_FL_TRACING); return value; } static VALUE Context_stop_reason(VALUE self) { debug_context_t *context; const char *symbol; Data_Get_Struct(self, debug_context_t, context); if (CTX_FL_TEST(context, CTX_FL_DEAD)) symbol = "post-mortem"; else switch (context->stop_reason) { case CTX_STOP_STEP: symbol = "step"; break; case CTX_STOP_BREAKPOINT: symbol = "breakpoint"; break; case CTX_STOP_CATCHPOINT: symbol = "catchpoint"; break; case CTX_STOP_NONE: default: symbol = "none"; } return ID2SYM(rb_intern(symbol)); } #if 0 static VALUE Context_jump(VALUE self, VALUE line, VALUE file) { debug_context_t *context; debug_frame_t *frame; int i, lineno; Data_Get_Struct(self, debug_context_t, context); frame = context->stack; lineno = FIX2INT(line); for (i = 0; i < context->stack_size; i++) { if (strcmp(frame->file, RSTRING_PTR(file))) { /* And now? */ } frame = frame->prev; } } #endif static VALUE Context_step_into(int argc, VALUE *argv, VALUE self) { VALUE steps; VALUE force; debug_context_t *context; rb_scan_args(argc, argv, "11", &steps, &force); if (FIX2INT(steps) < 0) rb_raise(rb_eRuntimeError, "Steps argument can't be negative."); Data_Get_Struct(self, debug_context_t, context); context->steps = FIX2INT(steps); if (RTEST(force)) CTX_FL_SET(context, CTX_FL_FORCE_MOVE); else CTX_FL_UNSET(context, CTX_FL_FORCE_MOVE); return steps; } static VALUE Context_step_over(int argc, VALUE *argv, VALUE self) { VALUE lines, frame, force; debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (context->stack_size == 0) rb_raise(rb_eRuntimeError, "No frames collected."); rb_scan_args(argc, argv, "12", &lines, &frame, &force); context->lines = FIX2INT(lines); if (FIX2INT(frame) < 0 && FIX2INT(frame) >= context->stack_size) rb_raise(rb_eRuntimeError, "Destination frame is out of range."); context->dest_frame = context->stack_size - FIX2INT(frame); if (RTEST(force)) CTX_FL_SET(context, CTX_FL_FORCE_MOVE); else CTX_FL_UNSET(context, CTX_FL_FORCE_MOVE); return Qnil; } static VALUE Context_step_out(VALUE self, VALUE frame) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (FIX2INT(frame) < 0 && FIX2INT(frame) >= context->stack_size) rb_raise(rb_eRuntimeError, "Stop frame is out of range."); context->stop_frame = context->stack_size - FIX2INT(frame); return frame; } /* * Document-class: Context * * == Summary * * Byebug keeps a single instance of this class. */ VALUE Init_context(VALUE mByebug) { cContext = rb_define_class_under(mByebug, "Context", rb_cObject); rb_define_method(cContext, "stack_size", Context_stack_size, 0); rb_define_method(cContext, "dead?", Context_dead, 0); rb_define_method(cContext, "stop_reason", Context_stop_reason, 0); rb_define_method(cContext, "tracing", Context_tracing, 0); rb_define_method(cContext, "tracing=", Context_set_tracing, 1); rb_define_method(cContext, "frame_file", Context_frame_file, -1); rb_define_method(cContext, "frame_line", Context_frame_line, -1); rb_define_method(cContext, "frame_method", Context_frame_method, -1); rb_define_method(cContext, "frame_binding", Context_frame_binding, -1); rb_define_method(cContext, "frame_self", Context_frame_self, -1); rb_define_method(cContext, "frame_class", Context_frame_class, -1); rb_define_method(cContext, "step_into", Context_step_into, -1); rb_define_method(cContext, "step_over", Context_step_over, -1); rb_define_method(cContext, "step_out", Context_step_out, 1); return cContext; }