Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,9 +316,30 @@ Sends request to rdbg to add a breakpoint.

Sends request to rdbg to delete a breakpoint.

- req_set_exception_breakpoints
- req_set_exception_breakpoints(breakpoints)

Sends request to rdbg to set exception breakpoints.
Sends request to rdbg to set exception breakpoints. e.g.

```rb
req_set_exception_breakpoints([{ name: "RuntimeError", condition: "a == 1" }])
```

Please note that `setExceptionBreakpoints` resets all exception breakpoints in every request.

So the following code will only set breakpoint for `Exception`.

```rb
req_set_exception_breakpoints([{ name: "RuntimeError" }])
req_set_exception_breakpoints([{ name: "Exception" }])
```

This means you can also use

```rb
req_set_exception_breakpoints([])
```

to clear all exception breakpoints.

- req_continue

Expand Down
29 changes: 16 additions & 13 deletions lib/debug/server_dap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,14 @@ def dap_setup bytes
{
filter: 'any',
label: 'rescue any exception',
#supportsCondition: true,
supportsCondition: true,
#conditionDescription: '',
},
{
filter: 'RuntimeError',
label: 'rescue RuntimeError',
default: true,
#supportsCondition: true,
supportsCondition: true,
#conditionDescription: '',
},
],
Expand Down Expand Up @@ -252,27 +252,30 @@ def process
when 'setFunctionBreakpoints'
send_response req
when 'setExceptionBreakpoints'
process_filter = ->(filter_id) {
case filter_id
when 'any'
bp = SESSION.add_catch_breakpoint 'Exception'
when 'RuntimeError'
bp = SESSION.add_catch_breakpoint 'RuntimeError'
else
bp = nil
end
process_filter = ->(filter_id, cond = nil) {
bp =
case filter_id
when 'any'
SESSION.add_catch_breakpoint 'Exception', cond: cond
when 'RuntimeError'
SESSION.add_catch_breakpoint 'RuntimeError', cond: cond
else
nil
end
{
verified: bp ? true : false,
verified: !bp.nil?,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bp can be false?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it should be false in any case. If it's not added, it should just be nil. Why do you expect it to be false too?

Copy link
Collaborator

@ko1 ko1 Apr 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to know the reason why the change is needed (and not sure why yet).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a necessary change so if you don't want it I can revert it.

message: bp.inspect,
}
}

SESSION.clear_catch_breakpoints 'Exception', 'RuntimeError'

filters = args.fetch('filters').map {|filter_id|
process_filter.call(filter_id)
}

filters += args.fetch('filterOptions', {}).map{|bp_info|
process_filter.call(bp_info.dig('filterId'))
process_filter.call(bp_info['filterId'], bp_info['condition'])
}

send_response req, breakpoints: filters
Expand Down
28 changes: 22 additions & 6 deletions lib/debug/session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1235,9 +1235,12 @@ def add_bp bp
@repl_prev_line = nil

if @bps.has_key? bp.key
unless bp.duplicable?
if bp.duplicable?
bp
else
@ui.puts "duplicated breakpoint: #{bp}"
bp.disable
nil
end
else
@bps[bp.key] = bp
Expand Down Expand Up @@ -1324,8 +1327,8 @@ def repl_add_watch_breakpoint arg
@tc << [:breakpoint, :watch, expr[:sig], cond, cmd, path]
end

def add_catch_breakpoint pat
bp = CatchBreakpoint.new(pat)
def add_catch_breakpoint pat, cond: nil
bp = CatchBreakpoint.new(pat, cond: cond)
add_bp bp
end

Expand All @@ -1343,15 +1346,28 @@ def add_line_breakpoint file, line, **kw
@ui.puts e.message
end

def clear_line_breakpoints path
path = resolve_path(path)
def clear_breakpoints(&condition)
@bps.delete_if do |k, bp|
if (Array === k) && DEBUGGER__.compare_path(k.first, path)
if condition.call(k, bp)
bp.delete
true
end
end
end

def clear_line_breakpoints path
path = resolve_path(path)
clear_breakpoints do |k, bp|
bp.is_a?(LineBreakpoint) && DEBUGGER__.compare_path(k.first, path)
end
end

def clear_catch_breakpoints *exception_names
clear_breakpoints do |k, bp|
bp.is_a?(CatchBreakpoint) && exception_names.include?(k[1])
end
end

def add_iseq_breakpoint iseq, **kw
bp = ISeqBreakpoint.new(iseq, [:line], **kw)
add_bp bp
Expand Down
2 changes: 1 addition & 1 deletion test/protocol/catch_raw_dap_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ def test_catching_any_exception_works_correctly
},
{
verified: true,
message: "true"
message: /#<DEBUGGER__::CatchBreakpoint:.*/
}
]
}
Expand Down
43 changes: 31 additions & 12 deletions test/protocol/catch_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,41 @@
module DEBUGGER__
class CatchTest < TestCase
PROGRAM = <<~RUBY
1| module Foo
2| class Bar
3| def self.a
4| raise 'foo'
5| end
6| end
7| Bar.a
8| bar = Bar.new
9| end
1| def foo
2| a = 1
3| raise "foo"
4| end
5|
6| foo
RUBY

def test_catch_stops_when_the_runtime_error_raised
def test_set_exception_breakpoints_sets_exception_breakpoints
run_protocol_scenario PROGRAM do
req_set_exception_breakpoints
req_set_exception_breakpoints([{ name: "RuntimeError" }])
req_continue
assert_line_num 4
assert_line_num 3
req_terminate_debuggee
end
end

def test_set_exception_breakpoints_unsets_exception_breakpoints
run_protocol_scenario PROGRAM, cdp: false do
req_set_exception_breakpoints([{ name: "RuntimeError" }])
req_set_exception_breakpoints([])
req_continue
end
end

def test_set_exception_breakpoints_accepts_condition
run_protocol_scenario PROGRAM, cdp: false do
req_set_exception_breakpoints([{ name: "RuntimeError", condition: "a == 2" }])
req_continue
end

run_protocol_scenario PROGRAM, cdp: false do
req_set_exception_breakpoints([{ name: "RuntimeError", condition: "a == 1" }])
req_continue
assert_line_num 3
req_terminate_debuggee
end
end
Expand Down
2 changes: 1 addition & 1 deletion test/protocol/detach_raw_dap_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ def test_1639218122
breakpoints: [
{
verified: true,
message: "true"
message: /#<DEBUGGER__::CatchBreakpoint:.*/
}
]
}
Expand Down
18 changes: 10 additions & 8 deletions test/support/protocol_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,16 @@ def req_finish
end
end

def req_set_exception_breakpoints
def req_set_exception_breakpoints(breakpoints)
case ENV['RUBY_DEBUG_TEST_UI']
when 'vscode'
send_dap_request 'setExceptionBreakpoints',
filters: [],
filterOptions: [
{
filterId: 'RuntimeError'
}
]
filter_options = breakpoints.map do |bp|
filter_option = { filterId: bp[:name] }
filter_option[:condition] = bp[:condition] if bp[:condition]
filter_option
end

send_dap_request 'setExceptionBreakpoints', filters: [], filterOptions: filter_options
when 'chrome'
send_cdp_request 'Debugger.setPauseOnExceptions', state: 'all'
end
Expand Down Expand Up @@ -494,6 +494,8 @@ def send_request command, **kw
send method: command,
params: kw
end
rescue StandardError => e
flunk create_protocol_message "Failed to send request because of #{e.inspect}"
end

def send_dap_request command, **kw
Expand Down