Skip to content

Commit 5c86103

Browse files
committed
Implement _raised_ and _returned_ special locals
1 parent 72b1069 commit 5c86103

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed

lib/debug/thread_client.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,11 +340,21 @@ def instance_eval_for_cmethod frame_self, src
340340
frame_self.instance_eval(src)
341341
end
342342

343+
SPECIAL_LOCALS = [
344+
[:raised_exception, "_raised_"],
345+
[:return_value, "_returned_"],
346+
]
347+
343348
def frame_eval src, re_raise: false
344349
begin
345350
@success_last_eval = false
346351

347352
b = current_frame&.eval_binding || TOPLEVEL_BINDING
353+
b = b.dup
354+
355+
SPECIAL_LOCALS.each do |m, local_name|
356+
b.local_variable_set(local_name, current_frame.send(m))
357+
end
348358

349359
result = if b
350360
f, _l = b.source_location

test/debug/debugger_local_test.rb

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# frozen_string_literal: truk
2+
3+
require_relative '../support/test_case'
4+
5+
module DEBUGGER__
6+
class DebuggerLocalsTest < TestCase
7+
class RaisedTest < TestCase
8+
def program
9+
<<~RUBY
10+
1| _rescued_ = 1111
11+
2| foo rescue nil
12+
3|
13+
4| # check repl variable doesn't leak to the program
14+
5| result = _rescued_ * 2
15+
6| binding.b
16+
RUBY
17+
end
18+
19+
def test_raised_is_accessible_from_repl
20+
debug_code(program) do
21+
type "catch Exception"
22+
type "c"
23+
type "_raised_"
24+
assert_line_text(/#<NameError: undefined local variable or method `foo' for main:Object/)
25+
type "c"
26+
type "result"
27+
assert_line_text(/2222/)
28+
type "c"
29+
end
30+
end
31+
32+
def test_raised_is_accessible_from_command
33+
debug_code(program) do
34+
type "catch Exception pre: p _raised_"
35+
type "c"
36+
assert_line_text(/#<NameError: undefined local variable or method `foo' for main:Object/)
37+
type "c"
38+
type "result"
39+
assert_line_text(/2222/)
40+
type "c"
41+
end
42+
end
43+
end
44+
45+
class ReturnedTest < TestCase
46+
def program
47+
<<~RUBY
48+
1| _returned_ = 1111
49+
2|
50+
3| def foo
51+
4| "foo"
52+
5| end
53+
6|
54+
7| foo
55+
8|
56+
9| # check repl variable doesn't leak to the program
57+
10| result = _returned_ * 2
58+
11|
59+
12| binding.b
60+
RUBY
61+
end
62+
63+
def test_returned_is_accessible_from_repl
64+
debug_code(program) do
65+
type "b 5"
66+
type "c"
67+
type "_returned_ + 'bar'"
68+
assert_line_text(/"foobar"/)
69+
type "c"
70+
type "result"
71+
assert_line_text(/2222/)
72+
type "q!"
73+
end
74+
end
75+
76+
def test_returned_is_accessible_from_command
77+
debug_code(program) do
78+
type "b 5 pre: p _returned_ + 'bar'"
79+
type "c"
80+
assert_line_text(/"foobar"/)
81+
type "c"
82+
type "result"
83+
assert_line_text(/2222/)
84+
type "q!"
85+
end
86+
end
87+
end
88+
end
89+
end

0 commit comments

Comments
 (0)