runger_byebug 11.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +954 -0
- data/CONTRIBUTING.md +58 -0
- data/GUIDE.md +1806 -0
- data/LICENSE +23 -0
- data/README.md +199 -0
- data/exe/byebug +6 -0
- data/ext/byebug/breakpoint.c +521 -0
- data/ext/byebug/byebug.c +900 -0
- data/ext/byebug/byebug.h +145 -0
- data/ext/byebug/context.c +687 -0
- data/ext/byebug/extconf.rb +12 -0
- data/ext/byebug/locker.c +96 -0
- data/ext/byebug/threads.c +241 -0
- data/lib/byebug/attacher.rb +48 -0
- data/lib/byebug/breakpoint.rb +94 -0
- data/lib/byebug/command.rb +111 -0
- data/lib/byebug/command_list.rb +34 -0
- data/lib/byebug/commands/break.rb +114 -0
- data/lib/byebug/commands/catch.rb +78 -0
- data/lib/byebug/commands/condition.rb +55 -0
- data/lib/byebug/commands/continue.rb +68 -0
- data/lib/byebug/commands/debug.rb +38 -0
- data/lib/byebug/commands/delete.rb +55 -0
- data/lib/byebug/commands/disable/breakpoints.rb +42 -0
- data/lib/byebug/commands/disable/display.rb +43 -0
- data/lib/byebug/commands/disable.rb +33 -0
- data/lib/byebug/commands/display.rb +66 -0
- data/lib/byebug/commands/down.rb +45 -0
- data/lib/byebug/commands/edit.rb +69 -0
- data/lib/byebug/commands/enable/breakpoints.rb +42 -0
- data/lib/byebug/commands/enable/display.rb +43 -0
- data/lib/byebug/commands/enable.rb +33 -0
- data/lib/byebug/commands/finish.rb +57 -0
- data/lib/byebug/commands/frame.rb +57 -0
- data/lib/byebug/commands/help.rb +64 -0
- data/lib/byebug/commands/history.rb +39 -0
- data/lib/byebug/commands/info/breakpoints.rb +65 -0
- data/lib/byebug/commands/info/display.rb +49 -0
- data/lib/byebug/commands/info/file.rb +80 -0
- data/lib/byebug/commands/info/line.rb +35 -0
- data/lib/byebug/commands/info/program.rb +49 -0
- data/lib/byebug/commands/info.rb +37 -0
- data/lib/byebug/commands/interrupt.rb +34 -0
- data/lib/byebug/commands/irb.rb +50 -0
- data/lib/byebug/commands/kill.rb +45 -0
- data/lib/byebug/commands/list.rb +159 -0
- data/lib/byebug/commands/method.rb +53 -0
- data/lib/byebug/commands/next.rb +40 -0
- data/lib/byebug/commands/pry.rb +41 -0
- data/lib/byebug/commands/quit.rb +42 -0
- data/lib/byebug/commands/restart.rb +64 -0
- data/lib/byebug/commands/save.rb +72 -0
- data/lib/byebug/commands/set.rb +79 -0
- data/lib/byebug/commands/show.rb +45 -0
- data/lib/byebug/commands/skip.rb +85 -0
- data/lib/byebug/commands/source.rb +40 -0
- data/lib/byebug/commands/step.rb +40 -0
- data/lib/byebug/commands/thread/current.rb +37 -0
- data/lib/byebug/commands/thread/list.rb +43 -0
- data/lib/byebug/commands/thread/resume.rb +45 -0
- data/lib/byebug/commands/thread/stop.rb +43 -0
- data/lib/byebug/commands/thread/switch.rb +46 -0
- data/lib/byebug/commands/thread.rb +34 -0
- data/lib/byebug/commands/tracevar.rb +54 -0
- data/lib/byebug/commands/undisplay.rb +51 -0
- data/lib/byebug/commands/untracevar.rb +36 -0
- data/lib/byebug/commands/up.rb +45 -0
- data/lib/byebug/commands/var/all.rb +41 -0
- data/lib/byebug/commands/var/args.rb +39 -0
- data/lib/byebug/commands/var/const.rb +49 -0
- data/lib/byebug/commands/var/global.rb +37 -0
- data/lib/byebug/commands/var/instance.rb +39 -0
- data/lib/byebug/commands/var/local.rb +39 -0
- data/lib/byebug/commands/var.rb +37 -0
- data/lib/byebug/commands/where.rb +64 -0
- data/lib/byebug/commands.rb +40 -0
- data/lib/byebug/context.rb +157 -0
- data/lib/byebug/core.rb +115 -0
- data/lib/byebug/errors.rb +29 -0
- data/lib/byebug/frame.rb +185 -0
- data/lib/byebug/helpers/bin.rb +47 -0
- data/lib/byebug/helpers/eval.rb +134 -0
- data/lib/byebug/helpers/file.rb +63 -0
- data/lib/byebug/helpers/frame.rb +75 -0
- data/lib/byebug/helpers/parse.rb +80 -0
- data/lib/byebug/helpers/path.rb +40 -0
- data/lib/byebug/helpers/reflection.rb +19 -0
- data/lib/byebug/helpers/string.rb +33 -0
- data/lib/byebug/helpers/thread.rb +67 -0
- data/lib/byebug/helpers/toggle.rb +62 -0
- data/lib/byebug/helpers/var.rb +70 -0
- data/lib/byebug/history.rb +130 -0
- data/lib/byebug/interface.rb +146 -0
- data/lib/byebug/interfaces/local_interface.rb +63 -0
- data/lib/byebug/interfaces/remote_interface.rb +50 -0
- data/lib/byebug/interfaces/script_interface.rb +33 -0
- data/lib/byebug/interfaces/test_interface.rb +67 -0
- data/lib/byebug/option_setter.rb +95 -0
- data/lib/byebug/printers/base.rb +68 -0
- data/lib/byebug/printers/plain.rb +44 -0
- data/lib/byebug/printers/texts/base.yml +115 -0
- data/lib/byebug/printers/texts/plain.yml +33 -0
- data/lib/byebug/processors/command_processor.rb +173 -0
- data/lib/byebug/processors/control_processor.rb +24 -0
- data/lib/byebug/processors/post_mortem_processor.rb +18 -0
- data/lib/byebug/processors/script_processor.rb +49 -0
- data/lib/byebug/remote/client.rb +57 -0
- data/lib/byebug/remote/server.rb +47 -0
- data/lib/byebug/remote.rb +85 -0
- data/lib/byebug/runner.rb +198 -0
- data/lib/byebug/setting.rb +79 -0
- data/lib/byebug/settings/autoirb.rb +29 -0
- data/lib/byebug/settings/autolist.rb +29 -0
- data/lib/byebug/settings/autopry.rb +29 -0
- data/lib/byebug/settings/autosave.rb +17 -0
- data/lib/byebug/settings/basename.rb +16 -0
- data/lib/byebug/settings/callstyle.rb +20 -0
- data/lib/byebug/settings/fullpath.rb +16 -0
- data/lib/byebug/settings/histfile.rb +20 -0
- data/lib/byebug/settings/histsize.rb +20 -0
- data/lib/byebug/settings/linetrace.rb +22 -0
- data/lib/byebug/settings/listsize.rb +21 -0
- data/lib/byebug/settings/post_mortem.rb +27 -0
- data/lib/byebug/settings/savefile.rb +20 -0
- data/lib/byebug/settings/stack_on_error.rb +15 -0
- data/lib/byebug/settings/width.rb +20 -0
- data/lib/byebug/source_file_formatter.rb +71 -0
- data/lib/byebug/subcommands.rb +54 -0
- data/lib/byebug/version.rb +8 -0
- data/lib/byebug.rb +3 -0
- metadata +194 -0
data/ext/byebug/locker.c
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
#include "byebug.h"
|
2
|
+
|
3
|
+
/**
|
4
|
+
* A simple linked list containing locked threads, FIFO style.
|
5
|
+
*/
|
6
|
+
|
7
|
+
typedef struct locked_thread_t
|
8
|
+
{
|
9
|
+
VALUE thread;
|
10
|
+
struct locked_thread_t *next;
|
11
|
+
} locked_thread_t;
|
12
|
+
|
13
|
+
static locked_thread_t *locked_head = NULL;
|
14
|
+
static locked_thread_t *locked_tail = NULL;
|
15
|
+
|
16
|
+
static int
|
17
|
+
is_in_locked(VALUE thread)
|
18
|
+
{
|
19
|
+
locked_thread_t *node;
|
20
|
+
|
21
|
+
if (!locked_head)
|
22
|
+
return 0;
|
23
|
+
|
24
|
+
for (node = locked_head; node != locked_tail; node = node->next)
|
25
|
+
if (node->thread == thread)
|
26
|
+
return 1;
|
27
|
+
|
28
|
+
return 0;
|
29
|
+
}
|
30
|
+
|
31
|
+
extern void
|
32
|
+
byebug_add_to_locked(VALUE thread)
|
33
|
+
{
|
34
|
+
locked_thread_t *node;
|
35
|
+
|
36
|
+
if (is_in_locked(thread))
|
37
|
+
return;
|
38
|
+
|
39
|
+
node = ALLOC(locked_thread_t);
|
40
|
+
node->thread = thread;
|
41
|
+
node->next = NULL;
|
42
|
+
|
43
|
+
if (locked_tail)
|
44
|
+
locked_tail->next = node;
|
45
|
+
|
46
|
+
locked_tail = node;
|
47
|
+
|
48
|
+
if (!locked_head)
|
49
|
+
locked_head = node;
|
50
|
+
}
|
51
|
+
|
52
|
+
extern VALUE
|
53
|
+
byebug_pop_from_locked()
|
54
|
+
{
|
55
|
+
VALUE thread;
|
56
|
+
locked_thread_t *node;
|
57
|
+
|
58
|
+
if (!locked_head)
|
59
|
+
return Qnil;
|
60
|
+
|
61
|
+
node = locked_head;
|
62
|
+
locked_head = locked_head->next;
|
63
|
+
|
64
|
+
if (locked_tail == node)
|
65
|
+
locked_tail = NULL;
|
66
|
+
|
67
|
+
thread = node->thread;
|
68
|
+
xfree(node);
|
69
|
+
|
70
|
+
return thread;
|
71
|
+
}
|
72
|
+
|
73
|
+
extern void
|
74
|
+
byebug_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
|
+
byebug_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
|
+
}
|
@@ -0,0 +1,241 @@
|
|
1
|
+
#include "byebug.h"
|
2
|
+
|
3
|
+
/* Threads table class */
|
4
|
+
static VALUE cThreadsTable;
|
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
|
+
static VALUE locker = Qnil;
|
11
|
+
|
12
|
+
static int
|
13
|
+
t_tbl_mark_keyvalue(st_data_t key, st_data_t value, st_data_t tbl)
|
14
|
+
{
|
15
|
+
UNUSED(tbl);
|
16
|
+
|
17
|
+
rb_gc_mark((VALUE)key);
|
18
|
+
|
19
|
+
if (!value)
|
20
|
+
return ST_CONTINUE;
|
21
|
+
|
22
|
+
rb_gc_mark((VALUE)value);
|
23
|
+
|
24
|
+
return ST_CONTINUE;
|
25
|
+
}
|
26
|
+
|
27
|
+
static void
|
28
|
+
t_tbl_mark(void *data)
|
29
|
+
{
|
30
|
+
threads_table_t *t_tbl = (threads_table_t *)data;
|
31
|
+
st_table *tbl = t_tbl->tbl;
|
32
|
+
|
33
|
+
st_foreach(tbl, t_tbl_mark_keyvalue, (st_data_t)tbl);
|
34
|
+
}
|
35
|
+
|
36
|
+
static void
|
37
|
+
t_tbl_free(void *data)
|
38
|
+
{
|
39
|
+
threads_table_t *t_tbl = (threads_table_t *)data;
|
40
|
+
|
41
|
+
st_free_table(t_tbl->tbl);
|
42
|
+
xfree(t_tbl);
|
43
|
+
}
|
44
|
+
|
45
|
+
/*
|
46
|
+
* Creates a numeric hash whose keys are the currently active threads and
|
47
|
+
* whose values are their associated contexts.
|
48
|
+
*/
|
49
|
+
VALUE
|
50
|
+
create_threads_table(void)
|
51
|
+
{
|
52
|
+
threads_table_t *t_tbl;
|
53
|
+
|
54
|
+
t_tbl = ALLOC(threads_table_t);
|
55
|
+
t_tbl->tbl = st_init_numtable();
|
56
|
+
return Data_Wrap_Struct(cThreadsTable, t_tbl_mark, t_tbl_free, t_tbl);
|
57
|
+
}
|
58
|
+
|
59
|
+
/*
|
60
|
+
* Checks a single entry in the threads table.
|
61
|
+
*
|
62
|
+
* If it has no associated context or the key doesn't correspond to a living
|
63
|
+
* thread, the entry is removed from the thread's list.
|
64
|
+
*/
|
65
|
+
static int
|
66
|
+
check_thread_i(st_data_t key, st_data_t value, st_data_t data)
|
67
|
+
{
|
68
|
+
UNUSED(data);
|
69
|
+
|
70
|
+
if (!value)
|
71
|
+
return ST_DELETE;
|
72
|
+
|
73
|
+
if (!is_living_thread((VALUE)key))
|
74
|
+
return ST_DELETE;
|
75
|
+
|
76
|
+
return ST_CONTINUE;
|
77
|
+
}
|
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
|
+
|
97
|
+
/*
|
98
|
+
* Checks threads table for dead/finished threads.
|
99
|
+
*/
|
100
|
+
static void
|
101
|
+
cleanup_dead_threads(void)
|
102
|
+
{
|
103
|
+
threads_table_t *t_tbl;
|
104
|
+
|
105
|
+
Data_Get_Struct(threads, threads_table_t, t_tbl);
|
106
|
+
st_foreach(t_tbl->tbl, check_thread_i, 0);
|
107
|
+
}
|
108
|
+
|
109
|
+
/*
|
110
|
+
* Looks up a context in the threads table. If not present, it creates it.
|
111
|
+
*/
|
112
|
+
void
|
113
|
+
thread_context_lookup(VALUE thread, VALUE *context)
|
114
|
+
{
|
115
|
+
threads_table_t *t_tbl;
|
116
|
+
|
117
|
+
Data_Get_Struct(threads, threads_table_t, t_tbl);
|
118
|
+
|
119
|
+
if (!st_lookup(t_tbl->tbl, thread, context) || !*context)
|
120
|
+
{
|
121
|
+
*context = byebug_context_create(thread);
|
122
|
+
st_insert(t_tbl->tbl, thread, *context);
|
123
|
+
}
|
124
|
+
}
|
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
|
+
*/
|
131
|
+
void
|
132
|
+
acquire_lock(debug_context_t *dc)
|
133
|
+
{
|
134
|
+
while ((!NIL_P(locker) && locker != rb_thread_current())
|
135
|
+
|| CTX_FL_TEST(dc, CTX_FL_SUSPEND))
|
136
|
+
{
|
137
|
+
byebug_add_to_locked(rb_thread_current());
|
138
|
+
rb_thread_stop();
|
139
|
+
|
140
|
+
if (CTX_FL_TEST(dc, CTX_FL_SUSPEND))
|
141
|
+
CTX_FL_SET(dc, CTX_FL_WAS_RUNNING);
|
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 = byebug_pop_from_locked();
|
163
|
+
else
|
164
|
+
{
|
165
|
+
byebug_remove_from_locked(next_thread);
|
166
|
+
thread = next_thread;
|
167
|
+
next_thread = Qnil;
|
168
|
+
}
|
169
|
+
|
170
|
+
if (!NIL_P(thread) && is_living_thread(thread))
|
171
|
+
rb_thread_run(thread);
|
172
|
+
}
|
173
|
+
|
174
|
+
/*
|
175
|
+
* call-seq:
|
176
|
+
* Byebug.unlock -> nil
|
177
|
+
*
|
178
|
+
* Unlocks global switch so other threads can run.
|
179
|
+
*/
|
180
|
+
static VALUE
|
181
|
+
Unlock(VALUE self)
|
182
|
+
{
|
183
|
+
debug_context_t *dc;
|
184
|
+
VALUE context;
|
185
|
+
|
186
|
+
UNUSED(self);
|
187
|
+
|
188
|
+
thread_context_lookup(rb_thread_current(), &context);
|
189
|
+
Data_Get_Struct(context, debug_context_t, dc);
|
190
|
+
|
191
|
+
CTX_FL_SET(dc, CTX_FL_IGNORE);
|
192
|
+
|
193
|
+
release_lock();
|
194
|
+
|
195
|
+
return locker;
|
196
|
+
}
|
197
|
+
|
198
|
+
/*
|
199
|
+
* call-seq:
|
200
|
+
* Byebug.lock -> Thread.current
|
201
|
+
*
|
202
|
+
* Locks global switch to reserve execution to current thread exclusively.
|
203
|
+
*/
|
204
|
+
static VALUE
|
205
|
+
Lock(VALUE self)
|
206
|
+
{
|
207
|
+
debug_context_t *dc;
|
208
|
+
VALUE context;
|
209
|
+
|
210
|
+
UNUSED(self);
|
211
|
+
|
212
|
+
if (!is_living_thread(rb_thread_current()))
|
213
|
+
rb_raise(rb_eRuntimeError, "Current thread is dead!");
|
214
|
+
|
215
|
+
thread_context_lookup(rb_thread_current(), &context);
|
216
|
+
Data_Get_Struct(context, debug_context_t, dc);
|
217
|
+
|
218
|
+
acquire_lock(dc);
|
219
|
+
|
220
|
+
CTX_FL_UNSET(dc, CTX_FL_IGNORE);
|
221
|
+
|
222
|
+
return locker;
|
223
|
+
}
|
224
|
+
|
225
|
+
/*
|
226
|
+
*
|
227
|
+
* Document-class: ThreadsTable
|
228
|
+
*
|
229
|
+
* == Sumary
|
230
|
+
*
|
231
|
+
* Hash table holding currently active threads and their associated contexts
|
232
|
+
*/
|
233
|
+
void
|
234
|
+
Init_threads_table(VALUE mByebug)
|
235
|
+
{
|
236
|
+
cThreadsTable = rb_define_class_under(mByebug, "ThreadsTable", rb_cObject);
|
237
|
+
rb_undef_alloc_func(cThreadsTable);
|
238
|
+
|
239
|
+
rb_define_module_function(mByebug, "unlock", Unlock, 0);
|
240
|
+
rb_define_module_function(mByebug, "lock", Lock, 0);
|
241
|
+
}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Main Container for all of Byebug's code
|
5
|
+
#
|
6
|
+
module Byebug
|
7
|
+
#
|
8
|
+
# Starts byebug, and stops at the first line of user's code.
|
9
|
+
#
|
10
|
+
def self.attach
|
11
|
+
unless started?
|
12
|
+
self.mode = :attached
|
13
|
+
|
14
|
+
start
|
15
|
+
run_init_script
|
16
|
+
end
|
17
|
+
|
18
|
+
current_context.step_out(3, true)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.spawn(host = "localhost", port = nil)
|
22
|
+
require_relative "core"
|
23
|
+
|
24
|
+
self.wait_connection = true
|
25
|
+
start_server(host, port || PORT)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Adds a `byebug` method to the Kernel module.
|
31
|
+
#
|
32
|
+
# Dropping a `byebug` call anywhere in your code, you get a debug prompt.
|
33
|
+
#
|
34
|
+
module Kernel
|
35
|
+
def byebug
|
36
|
+
require_relative "core"
|
37
|
+
|
38
|
+
Byebug.attach unless Byebug.mode == :off
|
39
|
+
end
|
40
|
+
|
41
|
+
def remote_byebug(host = "localhost", port = nil)
|
42
|
+
Byebug.spawn(host, port)
|
43
|
+
|
44
|
+
Byebug.attach
|
45
|
+
end
|
46
|
+
|
47
|
+
alias debugger byebug
|
48
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Byebug
|
4
|
+
#
|
5
|
+
# Implements breakpoints
|
6
|
+
#
|
7
|
+
class Breakpoint
|
8
|
+
#
|
9
|
+
# First breakpoint, in order of creation
|
10
|
+
#
|
11
|
+
def self.first
|
12
|
+
Byebug.breakpoints.first
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
# Last breakpoint, in order of creation
|
17
|
+
#
|
18
|
+
def self.last
|
19
|
+
Byebug.breakpoints.last
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Adds a new breakpoint
|
24
|
+
#
|
25
|
+
# @param [String] file
|
26
|
+
# @param [Fixnum] line
|
27
|
+
# @param [String] expr
|
28
|
+
#
|
29
|
+
def self.add(file, line, expr = nil)
|
30
|
+
breakpoint = Breakpoint.new(file, line, expr)
|
31
|
+
Byebug.breakpoints << breakpoint
|
32
|
+
breakpoint
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Removes a breakpoint
|
37
|
+
#
|
38
|
+
# @param id [integer] breakpoint number
|
39
|
+
#
|
40
|
+
def self.remove(id)
|
41
|
+
Byebug.breakpoints.reject! { |b| b.id == id }
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Returns an array of line numbers in file named +filename+ where
|
46
|
+
# breakpoints could be set. The list will contain an entry for each
|
47
|
+
# distinct line event call so it is possible (and possibly useful) for a
|
48
|
+
# line number appear more than once.
|
49
|
+
#
|
50
|
+
# @param filename [String] File name to inspect for possible breakpoints
|
51
|
+
#
|
52
|
+
def self.potential_lines(filename)
|
53
|
+
name = "#{Time.new.to_i}_#{rand(2**31)}"
|
54
|
+
iseq = RubyVM::InstructionSequence.compile(File.read(filename), name)
|
55
|
+
|
56
|
+
potential_lines_with_trace_points(iseq, {})
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.potential_lines_with_trace_points(iseq, lines)
|
60
|
+
iseq.trace_points.each { |(line, _)| lines[line] = true }
|
61
|
+
iseq.each_child do |child|
|
62
|
+
potential_lines_with_trace_points(child, lines)
|
63
|
+
end
|
64
|
+
|
65
|
+
lines.keys.sort
|
66
|
+
end
|
67
|
+
|
68
|
+
private_class_method :potential_lines_with_trace_points
|
69
|
+
|
70
|
+
#
|
71
|
+
# Returns true if a breakpoint could be set in line number +lineno+ in file
|
72
|
+
# name +filename.
|
73
|
+
#
|
74
|
+
def self.potential_line?(filename, lineno)
|
75
|
+
potential_lines(filename).member?(lineno)
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# True if there's no breakpoints
|
80
|
+
#
|
81
|
+
def self.none?
|
82
|
+
Byebug.breakpoints.empty?
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# Prints all information associated to the breakpoint
|
87
|
+
#
|
88
|
+
def inspect
|
89
|
+
meths = %w[id pos source expr hit_condition hit_count hit_value enabled?]
|
90
|
+
values = meths.map { |field| "#{field}: #{send(field)}" }.join(", ")
|
91
|
+
"#<Byebug::Breakpoint #{values}>"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
require_relative "helpers/string"
|
5
|
+
|
6
|
+
module Byebug
|
7
|
+
#
|
8
|
+
# Parent class of all byebug commands.
|
9
|
+
#
|
10
|
+
# Subclass it and name the subclass ending with the word Command to implement
|
11
|
+
# your own custom command.
|
12
|
+
#
|
13
|
+
# @example Define a custom command
|
14
|
+
#
|
15
|
+
# class MyCustomCommand < Command
|
16
|
+
# def self.regexp
|
17
|
+
# /custom_regexp/
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def self.description
|
21
|
+
# "Custom long desc"
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# def.short_description
|
25
|
+
# "Custom short desc"
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# def execute
|
29
|
+
# # My command's implementation
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
class Command
|
34
|
+
extend Forwardable
|
35
|
+
|
36
|
+
attr_reader :processor
|
37
|
+
|
38
|
+
def initialize(processor, input = self.class.to_s)
|
39
|
+
@processor = processor
|
40
|
+
@match = match(input)
|
41
|
+
end
|
42
|
+
|
43
|
+
def context
|
44
|
+
@context ||= processor.context
|
45
|
+
end
|
46
|
+
|
47
|
+
def frame
|
48
|
+
@frame ||= context.frame
|
49
|
+
end
|
50
|
+
|
51
|
+
def arguments
|
52
|
+
@match[0].split(" ").drop(1).join(" ")
|
53
|
+
end
|
54
|
+
|
55
|
+
def_delegators "self.class", :help, :match
|
56
|
+
|
57
|
+
def_delegator "processor.printer", :print, :pr
|
58
|
+
def_delegator "processor.printer", :print_collection, :prc
|
59
|
+
def_delegator "processor.printer", :print_variables, :prv
|
60
|
+
|
61
|
+
def_delegators "processor.interface", :errmsg, :puts, :print, :confirm
|
62
|
+
|
63
|
+
class << self
|
64
|
+
include Helpers::StringHelper
|
65
|
+
|
66
|
+
#
|
67
|
+
# Special methods to allow command filtering in processors
|
68
|
+
#
|
69
|
+
attr_accessor :allow_in_control, :allow_in_post_mortem
|
70
|
+
|
71
|
+
attr_writer :always_run
|
72
|
+
|
73
|
+
def always_run
|
74
|
+
@always_run ||= 0
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Name of the command, as executed by the user.
|
79
|
+
#
|
80
|
+
def to_s
|
81
|
+
name
|
82
|
+
.split("::")
|
83
|
+
.map { |n| n.gsub(/Command$/, "").downcase if /Command$/.match?(n) }
|
84
|
+
.compact
|
85
|
+
.join(" ")
|
86
|
+
end
|
87
|
+
|
88
|
+
def columnize(width)
|
89
|
+
format(
|
90
|
+
" %-<name>#{width}s -- %<description>s\n",
|
91
|
+
name: to_s,
|
92
|
+
description: short_description
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# Default help text for a command.
|
98
|
+
#
|
99
|
+
def help
|
100
|
+
prettify(description)
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Command's regexp match against an input
|
105
|
+
#
|
106
|
+
def match(input)
|
107
|
+
regexp.match(input)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "errors"
|
4
|
+
|
5
|
+
module Byebug
|
6
|
+
#
|
7
|
+
# Holds an array of subcommands for a command
|
8
|
+
#
|
9
|
+
class CommandList
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
def initialize(commands)
|
13
|
+
@commands = commands.sort_by(&:to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
def match(input)
|
17
|
+
find { |cmd| cmd.match(input) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def each
|
21
|
+
@commands.each { |cmd| yield(cmd) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
"\n" + map { |cmd| cmd.columnize(width) }.join + "\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def width
|
31
|
+
@width ||= map(&:to_s).max_by(&:size).size
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|