From 1c93e7931028c8336d0b120a087dba44388983c0 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Fri, 18 Nov 2022 06:14:45 +0900 Subject: [PATCH] `until` command remove `step into` command too. fix https://github.com/ruby/debug/issues/796 --- lib/debug/session.rb | 30 +++++++---- lib/debug/thread_client.rb | 48 ++++++++++++++--- test/console/control_flow_commands_test.rb | 60 +++++++++++++++++----- 3 files changed, 107 insertions(+), 31 deletions(-) diff --git a/lib/debug/session.rb b/lib/debug/session.rb index a900f893a..b877c20b1 100644 --- a/lib/debug/session.rb +++ b/lib/debug/session.rb @@ -431,8 +431,6 @@ def register_default_command # * Step in. Resume the program until next breakable point. # * `s[tep] ` # * Step in, resume the program at ``th breakable point. - # * `s[tep] into ` or `s[tep] into /regexp/` - # * Stop at the beggining of method `` or the name matched to `/regexp/` register_command 's', 'step', repeat: true, cancel_auto_continue: true, @@ -466,6 +464,21 @@ def register_default_command step_command :finish, arg end + # * `u[ntil]` + # * Similar to `next` command, but only stop later lines or the end of the current frame. + # * Similar to gdb's `advance` command. + # * `u[ntil] <[file:]line> + # * Run til the program reaches given location or the end of the current frame. + # * `u[ntil] + # * Run til the program invokes a method ``. `` can be a regexp with `/name/`. + register_command 'u', 'until', + repeat: true, + cancel_auto_continue: true, + postmortem: false do |arg| + + step_command :until, arg + end + # * `c[ontinue]` # * Resume the program. register_command 'c', 'continue', @@ -1105,6 +1118,11 @@ def repl_open_vscode end def step_command type, arg + if type == :until + leave_subsession [:step, type, arg] + return + end + case arg when nil, /\A\d+\z/ if type == :in && @tc.recorder&.replaying? @@ -1121,14 +1139,6 @@ def step_command type, arg iter = $2&.to_i request_tc [:step, type, iter] end - when /\Ainto\s+(\S+)(\s+(\d+))?\z/ - pat = $1 - iter = $3&.to_i - if /\A\/(.+)\/\z/ =~ pat - pat = Regexp.new($1) - end - - request_tc [:step, :into, pat, iter] else @ui.puts "Unknown option: #{arg}" :retry diff --git a/lib/debug/thread_client.rb b/lib/debug/thread_client.rb index 42e169e47..a1fda86cc 100644 --- a/lib/debug/thread_client.rb +++ b/lib/debug/thread_client.rb @@ -863,13 +863,6 @@ def wait_next_action_ break end - when :into - pat, iter = args[1], args[2] - step_tp iter, [:call, :c_call] do |tp| - pat === tp.callee_id.to_s - end - break - when :next frame = @target_frames.first path = frame.location.absolute_path || "!eval:#{frame.path}" @@ -908,6 +901,47 @@ def wait_next_action_ end break + when :until + location = iter&.strip + frame = @target_frames.first + depth = frame.frame_depth + target_location_label = frame.location.base_label + + case location + when nil, /\A(?:(.+):)?(\d+)\z/ + file = $1 + line = ($2 || frame.location.lineno + 1).to_i + + step_tp nil, [:line, :return] do |tp| + if tp.event == :line + next true if file && tp.path.end_with?(file) + next true if tp.lineno >= line + else + next true if depth >= DEBUGGER__.frame_depth - 3 && + caller_locations(2, 1).first.label == target_location_label + # TODO: imcomplete condition + end + end + else + pat = location + if /\A\/(.+)\/\z/ =~ pat + pat = Regexp.new($1) + end + + step_tp nil, [:call, :c_call, :return] do |tp| + case tp.event + when :call, :c_call + next true if pat === tp.callee_id.to_s + else # :return, :b_return + next true if depth >= DEBUGGER__.frame_depth - 3 && + caller_locations(2, 1).first.label == target_location_label + # TODO: imcomplete condition + end + end + end + + break + when :back iter = iter || 1 if @recorder&.can_step_back? diff --git a/test/console/control_flow_commands_test.rb b/test/console/control_flow_commands_test.rb index ebd366e5a..7a04eeb95 100644 --- a/test/console/control_flow_commands_test.rb +++ b/test/console/control_flow_commands_test.rb @@ -63,20 +63,6 @@ def test_step_with_number_goes_to_the_next_nth_statement end end - def test_step_into - debug_code program do - type 'step into name' - assert_line_num 7 - type 'step into xyzzy' # doesn't match - end - - debug_code program do - type 'step into /.ame/' - assert_line_num 7 - type 'step into xyzzy' # doesn't match - end - end - def test_next_goes_to_the_next_line debug_code(program) do type 'b 11' @@ -419,6 +405,52 @@ def test_finish_should_be_canceled end end + class UntilTest < ConsoleTestCase + def program + <<~RUBY + 1| 3.times do + 2| a = 1 + 3| b = 2 + 4| end + 5| c = 3 + 6| def foo + 7| x = 1 + 8| end + 9| foo + RUBY + end + + def test_until_line + debug_code program do + type 'u 2' + assert_line_num 2 + type 'u' + assert_line_num 3 + type 'u' + assert_line_num 5 + type 'c' + end + end + + def test_until_line_overrun + debug_code program do + type 'u 2' + assert_line_num 2 + type 'u 100' + end + end + + def test_until_method + debug_code program do + type 'u foo' + assert_line_num 7 + type 'u bar' + assert_line_num 8 + type 'c' + end + end + end + # # Tests that next/finish work for a deep call stack. # We use different logic for computing frame depth when the call stack is above/below 4096.