diff --git a/Rakefile b/Rakefile index 1eeb930..45dabd0 100644 --- a/Rakefile +++ b/Rakefile @@ -15,6 +15,9 @@ RUBY_DEBUG_VERSION = open("ext/ruby_debug/ruby_debug.c") do |f| f.grep(/^#define DEBUG_VERSION/).first[/"(.+)"/,1] end +RUBY_DEBUG_TEENY = ".0" +RUBY_DEBUG_BASE_TEENY = ".0" + COMMON_FILES = FileList[ 'AUTHORS', 'CHANGES', @@ -106,13 +109,13 @@ base_spec = Gem::Specification.new do |spec| spec.homepage = "http://rubyforge.org/projects/ruby-debug19/" spec.summary = "Fast Ruby debugger - core component" spec.description = <<-EOF -ruby-debug is a fast implementation of the standard Ruby debugger debug.rb. +ruby-debug-base19 is a fast implementation of the standard Ruby debugger debug.rb. It is implemented by utilizing a new Ruby C API hook. The core component provides support that front-ends can build on. It provides breakpoint handling, bindings for stack frames among other things. EOF - spec.version = "0.12.0" + spec.version = RUBY_DEBUG_VERSION + RUBY_DEBUG_BASE_TEENY spec.authors = ["Kent Sibilev", "Mark Moseley"] spec.email = "mark@fast-software.com" @@ -138,12 +141,12 @@ cli_spec = Gem::Specification.new do |spec| spec.name = "ruby-debug19" spec.homepage = "http://rubyforge.org/projects/ruby-debug19/" - spec.summary = "Command line interface (CLI) for ruby-debug-base" + spec.summary = "Command line interface (CLI) for ruby-debug-base19" spec.description = <<-EOF A generic command line interface for ruby-debug. EOF - spec.version = "0.11.7" + spec.version = RUBY_DEBUG_VERSION + RUBY_DEBUG_TEENY spec.authors = ["Kent Sibilev", "Mark Moseley"] spec.email = "mark@fast-software.com" diff --git a/ext/ruby_debug/ruby_debug.c b/ext/ruby_debug/ruby_debug.c index f901a5d..17446e5 100644 --- a/ext/ruby_debug/ruby_debug.c +++ b/ext/ruby_debug/ruby_debug.c @@ -33,7 +33,7 @@ typedef struct { static VALUE hook_off = Qtrue; static VALUE tracing = Qfalse; static VALUE locker = Qnil; -static VALUE debug = Qfalse; +static VALUE debug_flag = Qfalse; static VALUE catchall = Qtrue; static VALUE skip_next_exception= Qfalse; @@ -45,8 +45,8 @@ VALUE rdebug_threads_tbl = Qnil; /* Context for each of the threads */ VALUE mDebugger; /* Ruby Debugger Module object */ static VALUE bin_opt_call_c_function; -static VALUE bin_putnil; -static VALUE bin_leave; +static VALUE bin_getdynamic; +static VALUE bin_throw; static VALUE cThreadsTable; static VALUE cContext; static VALUE cDebugThread; @@ -113,6 +113,13 @@ get_event_name(rb_event_flag_t event) } } +static void +debug_runtime_error(debug_context_t *debug_context, const char *err) +{ + if (debug_context != NULL) CTX_FL_SET(debug_context, CTX_FL_IGNORE); + rb_raise(rb_eRuntimeError, err); +} + inline static void reset_stepping_stop_points(debug_context_t *debug_context) { @@ -175,7 +182,7 @@ id2ref_unprotected(VALUE id) static VALUE id2ref_error() { - if(debug == Qtrue) + if(debug_flag == Qtrue) rb_p(rb_errinfo()); return Qnil; } @@ -328,6 +335,97 @@ check_thread_contexts() st_foreach(threads_table->tbl, threads_table_check_i, 0); } +static struct iseq_catch_table_entry * +create_catch_table(debug_context_t *debug_context, unsigned long cont) +{ + struct iseq_catch_table_entry *catch_table = &debug_context->catch_table.tmp_catch_table; + + GET_THREAD()->parse_in_eval++; + GET_THREAD()->mild_compile_error++; + /* compiling with option Qfalse (no options) prevents debug hook calls during this catch routine + */ + catch_table->iseq = rb_iseq_compile_with_option( + rb_str_new_cstr(""), rb_str_new_cstr("(exception catcher)"), INT2FIX(1), Qfalse); + GET_THREAD()->mild_compile_error--; + GET_THREAD()->parse_in_eval--; + + catch_table->type = CATCH_TYPE_RESCUE; + catch_table->start = 0; + catch_table->end = ULONG_MAX; + catch_table->cont = cont; + catch_table->sp = 0; + + return(catch_table); +} + +static rb_control_frame_t * +FUNC_FASTCALL(do_jump)(rb_thread_t *th, rb_control_frame_t *cfp) +{ + VALUE context; + debug_context_t *debug_context; + rb_control_frame_t *jump_cfp; + VALUE *jump_pc; + + thread_context_lookup(th->self, &context, &debug_context, 0); + if (debug_context == NULL) + debug_runtime_error(NULL, "Lost context in jump"); + cfp->pc[-2] = debug_context->saved_jump_ins[0]; + cfp->pc[-1] = debug_context->saved_jump_ins[1]; + + if ((debug_context->jump_pc < debug_context->jump_cfp->iseq->iseq_encoded) || + (debug_context->jump_pc >= debug_context->jump_cfp->iseq->iseq_encoded + debug_context->jump_cfp->iseq->iseq_size)) + debug_runtime_error(debug_context, "Invalid jump PC target"); + + jump_cfp = debug_context->jump_cfp; + jump_pc = debug_context->jump_pc; + debug_context->jump_pc = NULL; + debug_context->jump_cfp = NULL; + if (!CTX_FL_TEST(debug_context, CTX_FL_EXCEPTION_TEST)) + { + debug_context->last_line = 0; + debug_context->last_file = NULL; + debug_context->stop_next = 1; + } + + if (cfp < jump_cfp) + { + /* save all intermediate-frame catch tables + +1 for target frame + +1 for array terminator + */ + int frames = jump_cfp - cfp + 2; + debug_context->old_iseq_catch = (iseq_catch_t*)malloc(frames * sizeof(iseq_catch_t)); + MEMZERO(debug_context->old_iseq_catch, iseq_catch_t, frames); + frames = 0; + do + { + if (cfp->iseq != NULL) + { + debug_context->old_iseq_catch[frames].iseq = cfp->iseq; + debug_context->old_iseq_catch[frames].catch_table = cfp->iseq->catch_table; + debug_context->old_iseq_catch[frames].catch_table_size = cfp->iseq->catch_table_size; + cfp->iseq->catch_table = NULL; + cfp->iseq->catch_table_size = 0; + + frames++; + } + cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); + } while (cfp <= jump_cfp); + + jump_cfp->iseq->catch_table_size = 1; + jump_cfp->iseq->catch_table = + create_catch_table(debug_context, jump_pc - jump_cfp->iseq->iseq_encoded); + jump_cfp->iseq->catch_table->sp = -1; + + JUMP_TAG(TAG_RAISE); + } + else if (cfp > jump_cfp) + debug_runtime_error(debug_context, "Invalid jump frame target"); + + cfp->pc = jump_pc; + return(cfp); +} + static rb_control_frame_t * FUNC_FASTCALL(do_catchall)(rb_thread_t *th, rb_control_frame_t *cfp) { @@ -337,17 +435,35 @@ FUNC_FASTCALL(do_catchall)(rb_thread_t *th, rb_control_frame_t *cfp) thread_context_lookup(th->self, &context, &debug_context, 0); if (debug_context == NULL) - rb_raise(rb_eRuntimeError, "Lost context in catchall"); + debug_runtime_error(NULL, "Lost context in catchall"); if (debug_context->saved_frames == NULL) return(cfp); /* re-throw exception */ + if (CTX_FL_TEST(debug_context, CTX_FL_RETHROW)) + { + cfp->pc[-2] = debug_context->saved_jump_ins[0]; + cfp->pc[-1] = debug_context->saved_jump_ins[1]; + } + else + { + CTX_FL_SET(debug_context, CTX_FL_CATCHING); + CTX_FL_UNSET(debug_context, CTX_FL_EXCEPTION_TEST); + } + + /* restore the call frame state */ + ZFREE(debug_context->cfp); + debug_context->cfp_count = debug_context->saved_cfp_count; + debug_context->saved_cfp_count = 0; + size = sizeof(rb_control_frame_t*) * debug_context->cfp_count; + debug_context->cfp = (rb_control_frame_t**)malloc(size); + memcpy(debug_context->cfp, debug_context->saved_cfp, size); + ZFREE(debug_context->saved_cfp); + size = sizeof(rb_control_frame_t) * ((debug_context->cfp[debug_context->cfp_count-1] - debug_context->cfp[0]) + 1); memcpy(debug_context->cfp[0], debug_context->saved_frames, size); ZFREE(debug_context->saved_frames); - CTX_FL_SET(debug_context, CTX_FL_CATCHING); - CTX_FL_UNSET(debug_context, CTX_FL_IN_EXCEPTION); th->cfp = debug_context->cfp[0]; th->cfp->pc = th->cfp->iseq->iseq_encoded + find_prev_line_start(th->cfp); return(th->cfp); @@ -485,13 +601,14 @@ debug_context_create(VALUE thread) debug_context->top_cfp = NULL; debug_context->catch_cfp = NULL; debug_context->saved_frames = NULL; + debug_context->saved_cfp = NULL; + debug_context->saved_cfp_count = 0; debug_context->cfp = NULL; if(rb_obj_class(thread) == cDebugThread) CTX_FL_SET(debug_context, CTX_FL_IGNORE); return Data_Wrap_Struct(cContext, debug_context_mark, debug_context_free, debug_context); } - static void thread_context_lookup(VALUE thread, VALUE *context, debug_context_t **debug_context, int create) { @@ -666,29 +783,6 @@ call_at_line_check(VALUE self, debug_context_t *debug_context, VALUE breakpoint, call_at_line(context, debug_context, rb_str_new2(file), INT2FIX(line)); } -static struct iseq_catch_table_entry * -create_catch_table(debug_context_t *debug_context, unsigned long cont) -{ - struct iseq_catch_table_entry *catch_table = &debug_context->catch_table.tmp_catch_table; - - GET_THREAD()->parse_in_eval++; - GET_THREAD()->mild_compile_error++; - /* compiling with option Qfalse (no options) prevents debug hook calls during this catch routine - */ - catch_table->iseq = rb_iseq_compile_with_option( - rb_str_new_cstr(""), rb_str_new_cstr("(exception catcher)"), INT2FIX(1), Qfalse); - GET_THREAD()->mild_compile_error--; - GET_THREAD()->parse_in_eval--; - - catch_table->type = CATCH_TYPE_RESCUE; - catch_table->start = 0; - catch_table->end = ULONG_MAX; - catch_table->cont = cont; - catch_table->sp = 0; - - return(catch_table); -} - static int set_thread_event_flag_i(st_data_t key, st_data_t val, st_data_t flag) { @@ -735,7 +829,7 @@ FUNC_FASTCALL(do_catch)(rb_thread_t *th, rb_control_frame_t *cfp) thread_context_lookup(th->self, &context, &debug_context, 0); if (debug_context == NULL) - rb_raise(rb_eRuntimeError, "Lost context in catch"); + debug_runtime_error(NULL, "Lost context in catch"); cfp->pc[-2] = debug_context->saved_jump_ins[0]; cfp->pc[-1] = debug_context->saved_jump_ins[1]; @@ -837,6 +931,7 @@ save_frames(debug_context_t *debug_context) if (debug_context->cfp_count == 0) return; + /* save the entire call frame state */ for (size = 0; size < debug_context->cfp_count; size++) { rb_binding_t *bind; @@ -847,6 +942,7 @@ save_frames(debug_context_t *debug_context) debug_context->catch_table.mod_name = rb_obj_class(rb_errinfo()); debug_context->catch_table.errinfo = rb_errinfo(); + debug_context->catch_cfp = GET_THREAD()->cfp; ZFREE(debug_context->saved_frames); @@ -855,7 +951,13 @@ save_frames(debug_context_t *debug_context) debug_context->saved_frames = (rb_control_frame_t*)malloc(size); memcpy(debug_context->saved_frames, debug_context->cfp[0], size); - CTX_FL_SET(debug_context, CTX_FL_IN_EXCEPTION); + ZFREE(debug_context->saved_cfp); + size = sizeof(rb_control_frame_t*) * debug_context->cfp_count; + debug_context->saved_cfp = (rb_control_frame_t**)malloc(size); + memcpy(debug_context->saved_cfp, debug_context->cfp, size); + debug_context->saved_cfp_count = debug_context->cfp_count; + + CTX_FL_SET(debug_context, CTX_FL_EXCEPTION_TEST); } static int @@ -897,9 +999,12 @@ handle_raise_event(rb_thread_t *th, debug_context_t *debug_context) int i; ZFREE(debug_context->saved_frames); - if (debug_context->cfp_count == 0) //|| + if (CTX_FL_TEST(debug_context, CTX_FL_RETHROW)) + { + CTX_FL_UNSET(debug_context, CTX_FL_RETHROW); return(0); - if (!try_thread_lock(th, debug_context)) + } + if (debug_context->cfp_count == 0 || !try_thread_lock(th, debug_context)) return(0); if (skip_next_exception == Qtrue) @@ -938,7 +1043,7 @@ handle_raise_event(rb_thread_t *th, debug_context_t *debug_context) if (hit_count != Qnil) { if (!catch_exception(debug_context, mod_name)) - rb_raise(rb_eRuntimeError, "Could not catch exception"); + debug_runtime_error(debug_context, "Could not catch exception"); return(1); } } @@ -987,10 +1092,47 @@ debug_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE kl if (iseq == NULL || th->cfp->pc == NULL) return; - if ((self == rb_mKernel || self == mDebugger || klass == cContext || klass == 0) && - iseq->defined_method_id == id_binding_n) + if (event == RUBY_EVENT_LINE && CTX_FL_TEST(debug_context, CTX_FL_EXCEPTION_TEST)) + { + if (iseq->type == ISEQ_TYPE_ENSURE) + { + /* don't allow "ensure" blocks to execute yet: jump out of here by re-throwing exception */ + VALUE *jump_pc = + iseq->iseq_encoded + iseq->iseq_size - insn_len(BIN(getdynamic)) - insn_len(BIN(throw)); + + if (jump_pc[0] != bin_getdynamic || jump_pc[insn_len(BIN(getdynamic))] != bin_throw) + debug_runtime_error(debug_context, "Unexpected instructions in ENSURE block"); + + debug_context->jump_cfp = th->cfp; + debug_context->jump_pc = jump_pc; + debug_context->saved_jump_ins[0] = th->cfp->pc[0]; + debug_context->saved_jump_ins[1] = th->cfp->pc[1]; + th->cfp->pc[0] = bin_opt_call_c_function; + th->cfp->pc[1] = (VALUE)do_jump; + CTX_FL_SET(debug_context, CTX_FL_ENSURE_SKIPPED); + return; + } + else if (iseq->type == ISEQ_TYPE_RESCUE) + { + CTX_FL_UNSET(debug_context, CTX_FL_EXCEPTION_TEST); + if (CTX_FL_TEST(debug_context, CTX_FL_ENSURE_SKIPPED)) + { + /* exception was caught by the code; need to start the whole thing over */ + debug_context->saved_jump_ins[0] = th->cfp->pc[0]; + debug_context->saved_jump_ins[1] = th->cfp->pc[1]; + th->cfp->pc[0] = bin_opt_call_c_function; + th->cfp->pc[1] = (VALUE)do_catchall; + CTX_FL_UNSET(debug_context, CTX_FL_ENSURE_SKIPPED); + CTX_FL_SET(debug_context, CTX_FL_RETHROW); + return; + } + } + } + + if (iseq->defined_method_id == id_binding_n && + (self == rb_mKernel || self == mDebugger || klass == cContext || klass == 0)) return; - if ((klass == cContext || klass == 0) && iseq->defined_method_id == id_frame_binding) + if (iseq->defined_method_id == id_frame_binding && (klass == cContext || klass == 0)) return; if (event == RUBY_EVENT_LINE || event == RUBY_EVENT_CALL) @@ -1006,7 +1148,6 @@ debug_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE kl if (iseq->type != ISEQ_TYPE_RESCUE && iseq->type != ISEQ_TYPE_ENSURE) { - CTX_FL_UNSET(debug_context, CTX_FL_IN_EXCEPTION); if (debug_context->start_cfp == NULL || th->cfp > debug_context->start_cfp) create_exception_catchall(debug_context); } @@ -1040,7 +1181,7 @@ debug_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE kl goto cleanup; debug_context->cur_cfp = th->cfp; - if (iseq && iseq->type != ISEQ_TYPE_RESCUE && iseq->type != ISEQ_TYPE_ENSURE) + if (iseq->type != ISEQ_TYPE_RESCUE && iseq->type != ISEQ_TYPE_ENSURE) set_cfp(debug_context); /* There can be many event calls per line, but we only want @@ -1397,14 +1538,14 @@ debug_set_tracing(VALUE self, VALUE value) static VALUE debug_debug(VALUE self) { - return debug; + return debug_flag; } /* :nodoc: */ static VALUE debug_set_debug(VALUE self, VALUE value) { - debug = RTEST(value) ? Qtrue : Qfalse; + debug_flag = RTEST(value) ? Qtrue : Qfalse; return value; } @@ -2155,71 +2296,6 @@ context_stop_reason(VALUE self) return ID2SYM(rb_intern(sym_name)); } -static rb_control_frame_t * -FUNC_FASTCALL(do_jump)(rb_thread_t *th, rb_control_frame_t *cfp) -{ - VALUE context; - debug_context_t *debug_context; - rb_control_frame_t *jump_cfp; - VALUE *jump_pc; - - thread_context_lookup(th->self, &context, &debug_context, 0); - if (debug_context == NULL) - rb_raise(rb_eRuntimeError, "Lost context in jump"); - cfp->pc[-2] = debug_context->saved_jump_ins[0]; - cfp->pc[-1] = debug_context->saved_jump_ins[1]; - - if ((debug_context->jump_pc < debug_context->jump_cfp->iseq->iseq_encoded) || - (debug_context->jump_pc >= debug_context->jump_cfp->iseq->iseq_encoded + debug_context->jump_cfp->iseq->iseq_size)) - rb_raise(rb_eRuntimeError, "Invalid jump PC target"); - - jump_cfp = debug_context->jump_cfp; - jump_pc = debug_context->jump_pc; - debug_context->jump_pc = NULL; - debug_context->jump_cfp = NULL; - debug_context->last_line = 0; - debug_context->last_file = NULL; - debug_context->stop_next = 1; - - if (cfp < jump_cfp) - { - /* save all intermediate-frame catch tables - +1 for target frame - +1 for array terminator - */ - int frames = jump_cfp - cfp + 2; - debug_context->old_iseq_catch = (iseq_catch_t*)malloc(frames * sizeof(iseq_catch_t)); - MEMZERO(debug_context->old_iseq_catch, iseq_catch_t, frames); - frames = 0; - do - { - if (cfp->iseq != NULL) - { - debug_context->old_iseq_catch[frames].iseq = cfp->iseq; - debug_context->old_iseq_catch[frames].catch_table = cfp->iseq->catch_table; - debug_context->old_iseq_catch[frames].catch_table_size = cfp->iseq->catch_table_size; - cfp->iseq->catch_table = NULL; - cfp->iseq->catch_table_size = 0; - - frames++; - } - cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); - } while (cfp <= jump_cfp); - - jump_cfp->iseq->catch_table_size = 1; - jump_cfp->iseq->catch_table = - create_catch_table(debug_context, jump_pc - jump_cfp->iseq->iseq_encoded); - jump_cfp->iseq->catch_table->sp = -1; - - JUMP_TAG(TAG_RAISE); - } - else if (cfp > jump_cfp) - rb_raise(rb_eRuntimeError, "Invalid jump frame target"); - - cfp->pc = jump_pc; - return(cfp); -} - /* * call-seq: * context.jump(line, file) -> bool @@ -2410,8 +2486,8 @@ void Init_ruby_debug() { TRANSLATE_INSNS(opt_call_c_function); - TRANSLATE_INSNS(putnil); - TRANSLATE_INSNS(leave); + TRANSLATE_INSNS(getdynamic); + TRANSLATE_INSNS(throw); mDebugger = rb_define_module("Debugger"); rb_define_const(mDebugger, "VERSION", rb_str_new2(DEBUG_VERSION)); diff --git a/ext/ruby_debug/ruby_debug.h b/ext/ruby_debug/ruby_debug.h index cbaa4f1..bebb6ff 100644 --- a/ext/ruby_debug/ruby_debug.h +++ b/ext/ruby_debug/ruby_debug.h @@ -3,17 +3,19 @@ enum ctx_stop_reason {CTX_STOP_NONE, CTX_STOP_STEP, CTX_STOP_BREAKPOINT, CTX_STOP_CATCHPOINT}; /* Context flags */ -#define CTX_FL_SUSPEND (1<<1) -#define CTX_FL_TRACING (1<<2) -#define CTX_FL_SKIPPED (1<<3) -#define CTX_FL_IGNORE (1<<4) -#define CTX_FL_DEAD (1<<5) -#define CTX_FL_WAS_RUNNING (1<<6) -#define CTX_FL_ENABLE_BKPT (1<<7) -#define CTX_FL_STEPPED (1<<8) -#define CTX_FL_FORCE_MOVE (1<<9) -#define CTX_FL_CATCHING (1<<10) -#define CTX_FL_IN_EXCEPTION (1<<11) +#define CTX_FL_SUSPEND (1<<1) +#define CTX_FL_TRACING (1<<2) +#define CTX_FL_SKIPPED (1<<3) +#define CTX_FL_IGNORE (1<<4) +#define CTX_FL_DEAD (1<<5) +#define CTX_FL_WAS_RUNNING (1<<6) +#define CTX_FL_ENABLE_BKPT (1<<7) +#define CTX_FL_STEPPED (1<<8) +#define CTX_FL_FORCE_MOVE (1<<9) +#define CTX_FL_CATCHING (1<<10) +#define CTX_FL_EXCEPTION_TEST (1<<11) +#define CTX_FL_ENSURE_SKIPPED (1<<12) +#define CTX_FL_RETHROW (1<<13) #define CTX_FL_TEST(c,f) ((c)->flags & (f)) #define CTX_FL_SET(c,f) do { (c)->flags |= (f); } while (0) @@ -59,6 +61,8 @@ typedef struct { rb_control_frame_t *top_cfp; rb_control_frame_t *catch_cfp; rb_control_frame_t *saved_frames; + rb_control_frame_t **saved_cfp; + int saved_cfp_count; // struct RData catch_rdata; struct rb_iseq_struct catch_iseq; diff --git a/test/data/catch3.cmd b/test/data/catch3.cmd index a8c9398..2f8296a 100644 --- a/test/data/catch3.cmd +++ b/test/data/catch3.cmd @@ -6,5 +6,6 @@ set autoeval off set basename on c w -jump +1 +p $var +jump +4 c diff --git a/test/data/catch3.right b/test/data/catch3.right index e69de29..463b40f 100644 --- a/test/data/catch3.right +++ b/test/data/catch3.right @@ -0,0 +1,37 @@ +pm-catch3.rb:3 +def get_exception(arg) +# # *************************************************** +# # Test catch +# # *************************************************** +# set debuggertesting on +Currently testing the debugger is on. +# set autoeval off +autoeval is off. +# set basename on +basename is on. +# c +divide by zero +start1:foo begin:bar begin:foo ensure:zero_div rescue: +Catchpoint at pm-catch3.rb:15: `divided by 0' (ZeroDivisionError) + from tdebug.rb:61:in `debug_program' + from tdebug.rb:247:in `' + from ../rdbg.rb:23:in `load' + from ../rdbg.rb:23:in `runner' + from ../rdbg.rb:32:in `
' +pm-catch3.rb:15 +1/0 if arg +# w +--> #0 Object.bar(arg#Fixnum) at line pm-catch3.rb:15 + #1 at line pm-catch3.rb:34 + #2 Object.foo(arg#Fixnum) at line pm-catch3.rb:24 + #3 Object.zero_div(arg#Fixnum) at line pm-catch3.rb:34 + #4 at line pm-catch3.rb:45 +# p $var +"start2:foo begin:bar begin:" +# jump +4 +pm-catch3.rb:19 +$var = $var + "bar end:" +# c +7 +start2:foo begin:bar begin:bar end:foo end:foo ensure: +done diff --git a/test/data/save.cmd b/test/data/save.cmd index 69dae60..9775559 100644 --- a/test/data/save.cmd +++ b/test/data/save.cmd @@ -31,3 +31,4 @@ source temp ##info break ##info catch ##show listsize +c diff --git a/test/pm-catch3.rb b/test/pm-catch3.rb old mode 100755 new mode 100644 index 0b946a3..dbec030 --- a/test/pm-catch3.rb +++ b/test/pm-catch3.rb @@ -1,37 +1,47 @@ -#!/usr/bin/env ruby -# Test Debugger.catchpoint -def get_exception - LoadError -end - -def bar(arg) - $var = $var + "bar:" - puts "bar begin" - 1/0 if arg - puts "bar end" - if false - raise LoadError - end -end - -def foo - puts "foo begin" - yield 1 - puts "foo end" -rescue get_exception, NameError - $var = $var + "rescue:" - puts "get_exception rescue" -ensure - $var = $var + "ensure:" -end - -def zero_div(arg) - x = 5 - foo { |i| bar(i) } - x + arg -end - -$var = "start:" -puts zero_div(10) -puts $var -puts "done" +#!/usr/bin/env ruby +# Test catching uncaught exceptions +def get_exception(arg) + result = case arg + when 0 then LoadError + when 1 then ZeroDivisionError + when 2 then NoMethodError + else RuntimeError + end + return result +end + +def bar(arg) + $var = $var + "bar begin:" + 1/0 if arg + if false + raise LoadError + end + $var = $var + "bar end:" +end + +def foo(arg) + $var = $var + "foo begin:" + yield arg + $var = $var + "foo end:" +rescue get_exception(0), NameError + $var = $var + "foo rescue:" +ensure + $var = $var + "foo ensure:" +end + +def zero_div(arg) + x = 5 + foo(arg) { |i| bar(i) } + x + arg +rescue get_exception(arg) + $var = $var + "zero_div rescue:" + return "divide by zero" +end + +$var = "start1:" +puts zero_div(1) +puts $var +$var = "start2:" +puts zero_div(2) +puts $var +puts "done"