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
4 changes: 3 additions & 1 deletion lib/debug/server_dap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,9 @@ def value_inspect obj, short: true

def dap_eval b, expr, _context, prompt: '(repl_eval)'
begin
b.eval(expr.to_s, prompt)
tp_allow_reentry do
b.eval(expr.to_s, prompt)
end
rescue Exception => e
e
end
Expand Down
52 changes: 52 additions & 0 deletions test/protocol/break_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,56 @@ def test_break_stops_at_the_extra_file
end
end
end

class NestedBreakTest < ProtocolTestCase
PROGRAM = <<~RUBY
1| def foo(x)
2| x
3| end
4|
5| foo("foo")
RUBY

def test_breakpoint_can_be_triggered_inside_suspenssion
run_protocol_scenario PROGRAM, cdp: false do
req_add_breakpoint 2
req_continue
assert_line_num 2

assert_locals_result(
[
{ name: "%self", value: "main", type: "Object" },
{ name: "x", value: "foo", type: "String" },
]
)

# Only if TracePoint.allow_reentry is available, we can trigger TracePoint events
# inside another TracePoint event, which is essential for nested breakpoints.
if TracePoint.respond_to? :allow_reentry
evaluate("foo('bar')")

assert_line_num 2
assert_locals_result(
[
{ name: "%self", value: "main", type: "Object" },
{ name: "x", value: "bar", type: "String" },
]
)
end

req_terminate_debuggee
end
end

private

def evaluate(expression)
res = send_dap_request 'stackTrace',
threadId: 1,
startFrame: 0,
levels: 20
f_id = res.dig(:body, :stackFrames, 0, :id)
send_request 'evaluate', expression: expression, frameId: f_id, context: "repl"
end
end
end