byebug 3.5.1 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/.rubocop.yml +18 -1
- data/.travis.yml +21 -1
- data/CHANGELOG.md +356 -308
- data/CONTRIBUTING.md +31 -15
- data/GUIDE.md +859 -475
- data/Gemfile +8 -10
- data/LICENSE +1 -1
- data/README.md +41 -45
- data/Rakefile +30 -28
- data/byebug.gemspec +18 -18
- data/ext/byebug/breakpoint.c +88 -75
- data/ext/byebug/byebug.c +253 -252
- data/ext/byebug/byebug.h +53 -53
- data/ext/byebug/context.c +188 -159
- data/ext/byebug/extconf.rb +9 -6
- data/ext/byebug/locker.c +53 -11
- data/ext/byebug/threads.c +137 -39
- data/lib/byebug/attacher.rb +7 -2
- data/lib/byebug/breakpoint.rb +30 -0
- data/lib/byebug/command.rb +36 -32
- data/lib/byebug/commands/break.rb +49 -48
- data/lib/byebug/commands/catch.rb +64 -0
- data/lib/byebug/commands/condition.rb +13 -9
- data/lib/byebug/commands/continue.rb +8 -4
- data/lib/byebug/commands/delete.rb +10 -4
- data/lib/byebug/commands/display.rb +33 -25
- data/lib/byebug/commands/edit.rb +18 -13
- data/lib/byebug/commands/enable_disable.rb +26 -24
- data/lib/byebug/commands/eval.rb +77 -35
- data/lib/byebug/commands/finish.rb +9 -5
- data/lib/byebug/commands/frame.rb +66 -125
- data/lib/byebug/commands/help.rb +14 -21
- data/lib/byebug/commands/history.rb +5 -1
- data/lib/byebug/commands/info.rb +41 -106
- data/lib/byebug/commands/interrupt.rb +6 -2
- data/lib/byebug/commands/irb.rb +5 -2
- data/lib/byebug/commands/kill.rb +6 -2
- data/lib/byebug/commands/list.rb +21 -14
- data/lib/byebug/commands/method.rb +17 -9
- data/lib/byebug/commands/pry.rb +13 -3
- data/lib/byebug/commands/quit.rb +10 -5
- data/lib/byebug/commands/restart.rb +12 -19
- data/lib/byebug/commands/save.rb +10 -6
- data/lib/byebug/commands/set.rb +15 -14
- data/lib/byebug/commands/show.rb +8 -8
- data/lib/byebug/commands/source.rb +14 -8
- data/lib/byebug/commands/stepping.rb +15 -29
- data/lib/byebug/commands/threads.rb +73 -49
- data/lib/byebug/commands/tracevar.rb +56 -0
- data/lib/byebug/commands/undisplay.rb +8 -4
- data/lib/byebug/commands/untracevar.rb +38 -0
- data/lib/byebug/commands/var.rb +107 -0
- data/lib/byebug/context.rb +78 -42
- data/lib/byebug/core.rb +78 -40
- data/lib/byebug/helper.rb +58 -42
- data/lib/byebug/history.rb +12 -1
- data/lib/byebug/interface.rb +91 -11
- data/lib/byebug/interfaces/local_interface.rb +12 -19
- data/lib/byebug/interfaces/remote_interface.rb +12 -15
- data/lib/byebug/interfaces/script_interface.rb +14 -18
- data/lib/byebug/interfaces/test_interface.rb +54 -0
- data/lib/byebug/printers/base.rb +64 -0
- data/lib/byebug/printers/plain.rb +53 -0
- data/lib/byebug/processor.rb +20 -1
- data/lib/byebug/processors/command_processor.rb +57 -172
- data/lib/byebug/processors/control_command_processor.rb +16 -43
- data/lib/byebug/remote.rb +13 -7
- data/lib/byebug/runner.rb +102 -54
- data/lib/byebug/setting.rb +45 -68
- data/lib/byebug/settings/autoeval.rb +2 -0
- data/lib/byebug/settings/autoirb.rb +3 -0
- data/lib/byebug/settings/autolist.rb +3 -0
- data/lib/byebug/settings/autosave.rb +2 -0
- data/lib/byebug/settings/basename.rb +2 -0
- data/lib/byebug/settings/callstyle.rb +2 -0
- data/lib/byebug/settings/fullpath.rb +2 -0
- data/lib/byebug/settings/histfile.rb +2 -0
- data/lib/byebug/settings/histsize.rb +2 -0
- data/lib/byebug/settings/linetrace.rb +2 -0
- data/lib/byebug/settings/listsize.rb +2 -0
- data/lib/byebug/settings/post_mortem.rb +7 -2
- data/lib/byebug/settings/stack_on_error.rb +2 -0
- data/lib/byebug/settings/verbose.rb +2 -0
- data/lib/byebug/settings/width.rb +2 -0
- data/lib/byebug/state.rb +12 -0
- data/lib/byebug/states/control_state.rb +26 -0
- data/lib/byebug/states/regular_state.rb +178 -0
- data/lib/byebug/version.rb +1 -1
- metadata +24 -109
- data/lib/byebug/commands/catchpoint.rb +0 -53
- data/lib/byebug/commands/reload.rb +0 -29
- data/lib/byebug/commands/trace.rb +0 -50
- data/lib/byebug/commands/variables.rb +0 -206
- data/lib/byebug/options.rb +0 -46
- data/lib/byebug/settings/autoreload.rb +0 -12
- data/lib/byebug/settings/forcestep.rb +0 -14
- data/lib/byebug/settings/testing.rb +0 -12
- data/lib/byebug/settings/tracing_plus.rb +0 -11
- data/test/commands/break_test.rb +0 -364
- data/test/commands/condition_test.rb +0 -85
- data/test/commands/continue_test.rb +0 -47
- data/test/commands/delete_test.rb +0 -26
- data/test/commands/display_test.rb +0 -37
- data/test/commands/edit_test.rb +0 -52
- data/test/commands/eval_test.rb +0 -89
- data/test/commands/finish_test.rb +0 -74
- data/test/commands/frame_test.rb +0 -223
- data/test/commands/help_test.rb +0 -66
- data/test/commands/history_test.rb +0 -61
- data/test/commands/info_test.rb +0 -238
- data/test/commands/interrupt_test.rb +0 -45
- data/test/commands/irb_test.rb +0 -28
- data/test/commands/kill_test.rb +0 -50
- data/test/commands/list_test.rb +0 -174
- data/test/commands/method_test.rb +0 -52
- data/test/commands/post_mortem_test.rb +0 -71
- data/test/commands/pry_test.rb +0 -26
- data/test/commands/quit_test.rb +0 -53
- data/test/commands/reload_test.rb +0 -39
- data/test/commands/restart_test.rb +0 -46
- data/test/commands/save_test.rb +0 -67
- data/test/commands/set_test.rb +0 -140
- data/test/commands/show_test.rb +0 -76
- data/test/commands/source_test.rb +0 -46
- data/test/commands/stepping_test.rb +0 -192
- data/test/commands/thread_test.rb +0 -164
- data/test/commands/trace_test.rb +0 -71
- data/test/commands/undisplay_test.rb +0 -75
- data/test/commands/variables_test.rb +0 -105
- data/test/debugger_alias_test.rb +0 -7
- data/test/runner_test.rb +0 -150
- data/test/support/matchers.rb +0 -65
- data/test/support/test_interface.rb +0 -59
- data/test/support/utils.rb +0 -122
- data/test/test_helper.rb +0 -58
data/ext/byebug/extconf.rb
CHANGED
@@ -5,13 +5,16 @@ end
|
|
5
5
|
|
6
6
|
require 'mkmf'
|
7
7
|
|
8
|
-
RbConfig::MAKEFILE_CONFIG
|
8
|
+
makefile_config = RbConfig::MAKEFILE_CONFIG
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
makefile_config['CC'] = ENV['CC'] if ENV['CC']
|
11
|
+
|
12
|
+
makefile_config['CFLAGS'] << ' -Wall -Werror'
|
13
|
+
makefile_config['CFLAGS'] << ' -gdwarf-2 -g3 -O0' if ENV['debug']
|
14
|
+
|
15
|
+
if makefile_config['CC'] =~ /clang/
|
16
|
+
makefile_config['CFLAGS'] << ' -Wno-unknown-warning-option'
|
14
17
|
end
|
15
18
|
|
16
19
|
dir_config('ruby')
|
17
|
-
create_makefile('byebug/byebug')
|
20
|
+
with_cflags(makefile_config['CFLAGS']) { create_makefile('byebug/byebug') }
|
data/ext/byebug/locker.c
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
#include <byebug.h>
|
2
2
|
|
3
|
-
|
3
|
+
/**
|
4
|
+
* A simple linked list containing locked threads, FIFO style.
|
5
|
+
*/
|
6
|
+
|
7
|
+
typedef struct locked_thread_t
|
8
|
+
{
|
4
9
|
VALUE thread;
|
5
10
|
struct locked_thread_t *next;
|
6
11
|
} locked_thread_t;
|
@@ -13,12 +18,13 @@ is_in_locked(VALUE thread)
|
|
13
18
|
{
|
14
19
|
locked_thread_t *node;
|
15
20
|
|
16
|
-
if (!locked_head)
|
21
|
+
if (!locked_head)
|
22
|
+
return 0;
|
17
23
|
|
18
24
|
for (node = locked_head; node != locked_tail; node = node->next)
|
19
|
-
|
20
|
-
|
21
|
-
|
25
|
+
if (node->thread == thread)
|
26
|
+
return 1;
|
27
|
+
|
22
28
|
return 0;
|
23
29
|
}
|
24
30
|
|
@@ -27,28 +33,64 @@ add_to_locked(VALUE thread)
|
|
27
33
|
{
|
28
34
|
locked_thread_t *node;
|
29
35
|
|
30
|
-
if (is_in_locked(thread))
|
36
|
+
if (is_in_locked(thread))
|
37
|
+
return;
|
31
38
|
|
32
39
|
node = ALLOC(locked_thread_t);
|
33
40
|
node->thread = thread;
|
34
41
|
node->next = NULL;
|
35
|
-
|
42
|
+
|
43
|
+
if (locked_tail)
|
44
|
+
locked_tail->next = node;
|
45
|
+
|
36
46
|
locked_tail = node;
|
37
|
-
|
47
|
+
|
48
|
+
if (!locked_head)
|
49
|
+
locked_head = node;
|
38
50
|
}
|
39
51
|
|
40
52
|
extern VALUE
|
41
|
-
|
53
|
+
pop_from_locked()
|
42
54
|
{
|
43
55
|
VALUE thread;
|
44
56
|
locked_thread_t *node;
|
45
57
|
|
46
|
-
if (locked_head
|
58
|
+
if (!locked_head)
|
59
|
+
return Qnil;
|
47
60
|
|
48
61
|
node = locked_head;
|
49
62
|
locked_head = locked_head->next;
|
50
|
-
|
63
|
+
|
64
|
+
if (locked_tail == node)
|
65
|
+
locked_tail = NULL;
|
66
|
+
|
51
67
|
thread = node->thread;
|
52
68
|
xfree(node);
|
69
|
+
|
53
70
|
return thread;
|
54
71
|
}
|
72
|
+
|
73
|
+
extern void
|
74
|
+
remove_from_locked(VALUE thread)
|
75
|
+
{
|
76
|
+
locked_thread_t *node;
|
77
|
+
locked_thread_t *next_node;
|
78
|
+
|
79
|
+
if (NIL_P(thread) || !locked_head || !is_in_locked(thread))
|
80
|
+
return;
|
81
|
+
|
82
|
+
if (locked_head->thread == thread)
|
83
|
+
{
|
84
|
+
pop_from_locked();
|
85
|
+
return;
|
86
|
+
}
|
87
|
+
|
88
|
+
for (node = locked_head; node != locked_tail; node = node->next)
|
89
|
+
if (node->next && node->next->thread == thread)
|
90
|
+
{
|
91
|
+
next_node = node->next;
|
92
|
+
node->next = next_node->next;
|
93
|
+
xfree(next_node);
|
94
|
+
return;
|
95
|
+
}
|
96
|
+
}
|
data/ext/byebug/threads.c
CHANGED
@@ -3,31 +3,41 @@
|
|
3
3
|
/* Threads table class */
|
4
4
|
static VALUE cThreadsTable;
|
5
5
|
|
6
|
+
/* If not Qnil, holds the next thread that must be run */
|
7
|
+
VALUE next_thread = Qnil;
|
8
|
+
|
9
|
+
/* To allow thread syncronization, we must stop threads when debugging */
|
10
|
+
VALUE locker = Qnil;
|
11
|
+
|
6
12
|
static int
|
7
13
|
t_tbl_mark_keyvalue(st_data_t key, st_data_t value, st_data_t tbl)
|
8
14
|
{
|
9
|
-
|
15
|
+
UNUSED(tbl);
|
10
16
|
|
11
|
-
|
17
|
+
rb_gc_mark((VALUE) key);
|
12
18
|
|
13
|
-
|
14
|
-
|
19
|
+
if (!value)
|
20
|
+
return ST_CONTINUE;
|
21
|
+
|
22
|
+
rb_gc_mark((VALUE) value);
|
15
23
|
|
16
24
|
return ST_CONTINUE;
|
17
25
|
}
|
18
26
|
|
19
27
|
static void
|
20
|
-
t_tbl_mark(void*
|
28
|
+
t_tbl_mark(void *data)
|
21
29
|
{
|
22
|
-
threads_table_t *t_tbl = (threads_table_t *)data;
|
30
|
+
threads_table_t *t_tbl = (threads_table_t *) data;
|
23
31
|
st_table *tbl = t_tbl->tbl;
|
24
|
-
|
32
|
+
|
33
|
+
st_foreach(tbl, t_tbl_mark_keyvalue, (st_data_t) tbl);
|
25
34
|
}
|
26
35
|
|
27
36
|
static void
|
28
|
-
t_tbl_free(void*
|
37
|
+
t_tbl_free(void *data)
|
29
38
|
{
|
30
|
-
threads_table_t *t_tbl = (threads_table_t*)data;
|
39
|
+
threads_table_t *t_tbl = (threads_table_t *) data;
|
40
|
+
|
31
41
|
st_free_table(t_tbl->tbl);
|
32
42
|
xfree(t_tbl);
|
33
43
|
}
|
@@ -46,16 +56,6 @@ create_threads_table(void)
|
|
46
56
|
return Data_Wrap_Struct(cThreadsTable, t_tbl_mark, t_tbl_free, t_tbl);
|
47
57
|
}
|
48
58
|
|
49
|
-
/*
|
50
|
-
* The condition to be in the thread's table is to be either running or
|
51
|
-
* sleeping, namely, to be Thread#alive?
|
52
|
-
*/
|
53
|
-
static int
|
54
|
-
is_living_thread(VALUE thread)
|
55
|
-
{
|
56
|
-
return rb_funcall(thread, rb_intern("alive?"), 0) == Qtrue;
|
57
|
-
}
|
58
|
-
|
59
59
|
/*
|
60
60
|
* Checks a single entry in the threads table.
|
61
61
|
*
|
@@ -63,20 +63,42 @@ is_living_thread(VALUE thread)
|
|
63
63
|
* thread, the entry is removed from the thread's list.
|
64
64
|
*/
|
65
65
|
static int
|
66
|
-
check_thread_i(st_data_t key, st_data_t value, st_data_t
|
66
|
+
check_thread_i(st_data_t key, st_data_t value, st_data_t data)
|
67
67
|
{
|
68
|
-
|
68
|
+
UNUSED(data);
|
69
69
|
|
70
|
-
if (!
|
70
|
+
if (!value)
|
71
|
+
return ST_DELETE;
|
72
|
+
|
73
|
+
if (!is_living_thread((VALUE) key))
|
74
|
+
return ST_DELETE;
|
71
75
|
|
72
76
|
return ST_CONTINUE;
|
73
77
|
}
|
74
78
|
|
79
|
+
/*
|
80
|
+
* Checks whether a thread is either in the running or sleeping state.
|
81
|
+
*/
|
82
|
+
int
|
83
|
+
is_living_thread(VALUE thread)
|
84
|
+
{
|
85
|
+
VALUE status = rb_funcall(thread, rb_intern("status"), 0);
|
86
|
+
|
87
|
+
if (NIL_P(status) || status == Qfalse)
|
88
|
+
return 0;
|
89
|
+
|
90
|
+
if (rb_str_cmp(status, rb_str_new2("run")) == 0
|
91
|
+
|| rb_str_cmp(status, rb_str_new2("sleep")) == 0)
|
92
|
+
return 1;
|
93
|
+
|
94
|
+
return 0;
|
95
|
+
}
|
96
|
+
|
75
97
|
/*
|
76
98
|
* Checks threads table for dead/finished threads.
|
77
99
|
*/
|
78
100
|
void
|
79
|
-
|
101
|
+
cleanup_dead_threads(void)
|
80
102
|
{
|
81
103
|
threads_table_t *t_tbl;
|
82
104
|
|
@@ -84,12 +106,16 @@ check_threads_table(void)
|
|
84
106
|
st_foreach(t_tbl->tbl, check_thread_i, 0);
|
85
107
|
}
|
86
108
|
|
109
|
+
/*
|
110
|
+
* Looks up a context in the threads table. If not present, it creates it.
|
111
|
+
*/
|
87
112
|
void
|
88
|
-
thread_context_lookup(VALUE thread, VALUE *context)
|
113
|
+
thread_context_lookup(VALUE thread, VALUE * context)
|
89
114
|
{
|
90
115
|
threads_table_t *t_tbl;
|
91
116
|
|
92
117
|
Data_Get_Struct(threads, threads_table_t, t_tbl);
|
118
|
+
|
93
119
|
if (!st_lookup(t_tbl->tbl, thread, context) || !*context)
|
94
120
|
{
|
95
121
|
*context = context_create(thread);
|
@@ -97,26 +123,95 @@ thread_context_lookup(VALUE thread, VALUE *context)
|
|
97
123
|
}
|
98
124
|
}
|
99
125
|
|
126
|
+
/*
|
127
|
+
* Holds thread execution while another thread is active.
|
128
|
+
*
|
129
|
+
* Thanks to this, all threads are "frozen" while the user is typing commands.
|
130
|
+
*/
|
100
131
|
void
|
101
|
-
|
132
|
+
acquire_lock(debug_context_t * dc)
|
102
133
|
{
|
103
|
-
while (
|
134
|
+
while ((!NIL_P(locker) && locker != rb_thread_current())
|
135
|
+
|| CTX_FL_TEST(dc, CTX_FL_SUSPEND))
|
104
136
|
{
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
rb_thread_stop();
|
110
|
-
}
|
111
|
-
|
112
|
-
/* stop the current thread if it's marked as suspended */
|
113
|
-
if (CTX_FL_TEST(dc, CTX_FL_SUSPEND) && locker != rb_thread_current())
|
114
|
-
{
|
137
|
+
add_to_locked(rb_thread_current());
|
138
|
+
rb_thread_stop();
|
139
|
+
|
140
|
+
if (CTX_FL_TEST(dc, CTX_FL_SUSPEND))
|
115
141
|
CTX_FL_SET(dc, CTX_FL_WAS_RUNNING);
|
116
|
-
rb_thread_stop();
|
117
|
-
}
|
118
|
-
else break;
|
119
142
|
}
|
143
|
+
|
144
|
+
locker = rb_thread_current();
|
145
|
+
}
|
146
|
+
|
147
|
+
/*
|
148
|
+
* Releases our global lock and passes execution on to another thread, either
|
149
|
+
* the thread specified by +next_thread+ or any other thread if +next_thread+
|
150
|
+
* is nil.
|
151
|
+
*/
|
152
|
+
void
|
153
|
+
release_lock(void)
|
154
|
+
{
|
155
|
+
VALUE thread;
|
156
|
+
|
157
|
+
cleanup_dead_threads();
|
158
|
+
|
159
|
+
locker = Qnil;
|
160
|
+
|
161
|
+
if (NIL_P(next_thread))
|
162
|
+
thread = pop_from_locked();
|
163
|
+
else
|
164
|
+
{
|
165
|
+
remove_from_locked(next_thread);
|
166
|
+
thread = next_thread;
|
167
|
+
}
|
168
|
+
|
169
|
+
if (thread == next_thread)
|
170
|
+
next_thread = Qnil;
|
171
|
+
|
172
|
+
if (!NIL_P(thread) && is_living_thread(thread))
|
173
|
+
rb_thread_run(thread);
|
174
|
+
}
|
175
|
+
|
176
|
+
/*
|
177
|
+
* call-seq:
|
178
|
+
* Byebug.unlock -> nil
|
179
|
+
*
|
180
|
+
* Unlocks global switch so other threads can run.
|
181
|
+
*/
|
182
|
+
static VALUE
|
183
|
+
Unlock(VALUE self)
|
184
|
+
{
|
185
|
+
UNUSED(self);
|
186
|
+
|
187
|
+
release_lock();
|
188
|
+
|
189
|
+
return locker;
|
190
|
+
}
|
191
|
+
|
192
|
+
/*
|
193
|
+
* call-seq:
|
194
|
+
* Byebug.lock -> Thread.current
|
195
|
+
*
|
196
|
+
* Locks global switch to reserve execution to current thread exclusively.
|
197
|
+
*/
|
198
|
+
static VALUE
|
199
|
+
Lock(VALUE self)
|
200
|
+
{
|
201
|
+
debug_context_t *dc;
|
202
|
+
VALUE context;
|
203
|
+
|
204
|
+
UNUSED(self);
|
205
|
+
|
206
|
+
if (!is_living_thread(rb_thread_current()))
|
207
|
+
rb_raise(rb_eRuntimeError, "Current thread is dead!");
|
208
|
+
|
209
|
+
thread_context_lookup(rb_thread_current(), &context);
|
210
|
+
Data_Get_Struct(context, debug_context_t, dc);
|
211
|
+
|
212
|
+
acquire_lock(dc);
|
213
|
+
|
214
|
+
return locker;
|
120
215
|
}
|
121
216
|
|
122
217
|
/*
|
@@ -131,4 +226,7 @@ void
|
|
131
226
|
Init_threads_table(VALUE mByebug)
|
132
227
|
{
|
133
228
|
cThreadsTable = rb_define_class_under(mByebug, "ThreadsTable", rb_cObject);
|
229
|
+
|
230
|
+
rb_define_module_function(mByebug, "unlock", Unlock, 0);
|
231
|
+
rb_define_module_function(mByebug, "lock", Lock, 0);
|
134
232
|
}
|
data/lib/byebug/attacher.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
|
+
#
|
2
|
+
# Main Container for all of Byebug's code
|
3
|
+
#
|
1
4
|
module Byebug
|
2
5
|
#
|
3
6
|
# Enters byebug right before (or right after if _before_ is false) return
|
4
7
|
# events occur. Before entering byebug the init script is read.
|
5
8
|
#
|
6
9
|
def self.attach(steps_out, before)
|
10
|
+
setup_cmd_line_args
|
11
|
+
|
7
12
|
start
|
8
|
-
|
9
|
-
|
13
|
+
run_init_script
|
14
|
+
|
10
15
|
current_context.step_out(steps_out, before)
|
11
16
|
end
|
12
17
|
end
|
data/lib/byebug/breakpoint.rb
CHANGED
@@ -39,6 +39,36 @@ module Byebug
|
|
39
39
|
Byebug.breakpoints.reject! { |b| b.id == id }
|
40
40
|
end
|
41
41
|
|
42
|
+
#
|
43
|
+
# Returns an array of line numbers in file named +filename+ where
|
44
|
+
# breakpoints could be set. The list will contain an entry for each
|
45
|
+
# distinct line event call so it is possible (and possibly useful) for a
|
46
|
+
# line number appear more than once.
|
47
|
+
#
|
48
|
+
# @param filename [String] File name to inspect for possible breakpoints
|
49
|
+
#
|
50
|
+
def self.potential_lines(filename)
|
51
|
+
name, lines = "#{Time.new.to_i}_#{rand(2**31)}", {}
|
52
|
+
iseq = RubyVM::InstructionSequence.compile(File.read(filename), name)
|
53
|
+
|
54
|
+
iseq.disasm.each_line do |line|
|
55
|
+
res = /^\d+ (?<insn>\w+)\s+.+\(\s*(?<lineno>\d+)\)$/.match(line)
|
56
|
+
next unless res && res[:insn] == 'trace'
|
57
|
+
|
58
|
+
lines[res[:lineno].to_i] = true
|
59
|
+
end
|
60
|
+
|
61
|
+
lines.keys
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Returns true if a breakpoint could be set in line number +lineno+ in file
|
66
|
+
# name +filename.
|
67
|
+
#
|
68
|
+
def self.potential_line?(filename, lineno)
|
69
|
+
potential_lines(filename).member?(lineno)
|
70
|
+
end
|
71
|
+
|
42
72
|
#
|
43
73
|
# True if there's no breakpoints
|
44
74
|
#
|
data/lib/byebug/command.rb
CHANGED
@@ -9,6 +9,11 @@ module Byebug
|
|
9
9
|
# Subclasses need to implement a `regexp` and an `execute` command.
|
10
10
|
#
|
11
11
|
class Command
|
12
|
+
extend Forwardable
|
13
|
+
|
14
|
+
include ParseFunctions
|
15
|
+
include FileFunctions
|
16
|
+
|
12
17
|
Subcmd = Struct.new(:name, :min, :help)
|
13
18
|
|
14
19
|
def initialize(state)
|
@@ -19,36 +24,49 @@ module Byebug
|
|
19
24
|
@match = regexp.match(input)
|
20
25
|
end
|
21
26
|
|
22
|
-
|
27
|
+
def_delegator :"Byebug.printer", :print, :pr
|
28
|
+
def_delegator :"Byebug.printer", :print_collection, :prc
|
29
|
+
def_delegator :"Byebug.printer", :print_variables, :prv
|
23
30
|
|
24
|
-
|
25
|
-
def_delegators :@state, :errmsg, :puts
|
31
|
+
protected
|
26
32
|
|
27
|
-
|
28
|
-
@state.confirm(msg) == 'y'
|
29
|
-
end
|
33
|
+
def_delegators :@state, :errmsg, :puts, :print, :confirm
|
30
34
|
|
35
|
+
#
|
36
|
+
# Evaluates a string containing Ruby code, using binding +b+. In case of
|
37
|
+
# error full stack trace and error are printed.
|
38
|
+
#
|
31
39
|
def bb_eval(str, b = get_binding)
|
32
|
-
eval(str
|
40
|
+
b.eval(str)
|
33
41
|
rescue StandardError, ScriptError => e
|
34
|
-
at =
|
35
|
-
|
36
|
-
at.
|
42
|
+
at = e.backtrace
|
43
|
+
locations = []
|
44
|
+
locations << "#{at.shift}: #{e.class} Exception(#{e.message})"
|
45
|
+
locations += at.map { |path| "\tfrom #{path}" }
|
46
|
+
|
47
|
+
errmsg(pr('eval.exception', text_message: locations.join("\n")))
|
37
48
|
nil
|
38
49
|
end
|
39
50
|
|
51
|
+
#
|
52
|
+
# Evaluates a string containing Ruby code, using binding +b+. In case of
|
53
|
+
# error, an error message with the exception is printed.
|
54
|
+
#
|
40
55
|
def bb_warning_eval(str, b = get_binding)
|
41
|
-
eval(str
|
56
|
+
b.eval(str)
|
42
57
|
rescue StandardError, ScriptError => e
|
43
|
-
|
58
|
+
text_message = "#{e.class} Exception: #{e.message}"
|
59
|
+
errmsg(pr('eval.exception', text_message: text_message))
|
44
60
|
nil
|
45
61
|
end
|
46
62
|
|
47
|
-
def get_binding(pos = @state.
|
63
|
+
def get_binding(pos = @state.frame)
|
48
64
|
@state.context ? @state.context.frame_binding(pos) : TOPLEVEL_BINDING
|
49
65
|
end
|
50
66
|
|
51
67
|
class << self
|
68
|
+
include StringFunctions
|
69
|
+
|
52
70
|
attr_accessor :allow_in_control
|
53
71
|
attr_writer :allow_in_post_mortem, :always_run
|
54
72
|
|
@@ -60,13 +78,11 @@ module Byebug
|
|
60
78
|
@always_run ||= 0
|
61
79
|
end
|
62
80
|
|
63
|
-
def help(
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
output += format_subcmds if defined? self::Subcommands
|
69
|
-
end
|
81
|
+
def help(subcmd = nil)
|
82
|
+
return format_subcmd(subcmd) if subcmd
|
83
|
+
|
84
|
+
output = description
|
85
|
+
output += format_subcmds if defined? self::Subcommands
|
70
86
|
output
|
71
87
|
end
|
72
88
|
|
@@ -106,18 +122,6 @@ module Byebug
|
|
106
122
|
def inherited(klass)
|
107
123
|
commands << klass
|
108
124
|
end
|
109
|
-
|
110
|
-
def load_commands
|
111
|
-
Dir.glob(File.expand_path('../commands/*.rb', __FILE__)).each do |file|
|
112
|
-
require file
|
113
|
-
end
|
114
|
-
|
115
|
-
Byebug.constants.grep(/Functions$/).map do |name|
|
116
|
-
include Byebug.const_get(name)
|
117
|
-
end
|
118
|
-
end
|
119
125
|
end
|
120
126
|
end
|
121
|
-
|
122
|
-
Command.load_commands
|
123
127
|
end
|