byebug-dap 0.1.3 → 0.1.4
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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +2 -0
- data/bin/byebug-dap +8 -2
- data/lib/byebug/dap.rb +26 -13
- data/lib/byebug/dap/command.rb +40 -2
- data/lib/byebug/dap/command_processor.rb +95 -49
- data/lib/byebug/dap/commands/disconnect.rb +1 -0
- data/lib/byebug/dap/commands/exception_info.rb +1 -1
- data/lib/byebug/dap/commands/scopes.rb +3 -3
- data/lib/byebug/dap/commands/set_function_breakpoints.rb +2 -2
- data/lib/byebug/dap/commands/threads.rb +2 -2
- data/lib/byebug/dap/commands/variables.rb +1 -1
- data/lib/byebug/dap/contextual_command.rb +15 -0
- data/lib/byebug/dap/helpers/captured_io.rb +15 -0
- data/lib/byebug/dap/helpers/captured_output.rb +13 -1
- data/lib/byebug/dap/helpers/channel.rb +9 -0
- data/lib/byebug/dap/helpers/child_spawned_event_body.rb +16 -2
- data/lib/byebug/dap/helpers/handles.rb +8 -0
- data/lib/byebug/dap/helpers/invalid_request_argument_error.rb +11 -1
- data/lib/byebug/dap/helpers/safe_helpers.rb +8 -0
- data/lib/byebug/dap/helpers/scalar.rb +7 -0
- data/lib/byebug/dap/helpers/stdio.rb +2 -0
- data/lib/byebug/dap/helpers/value_helpers.rb +19 -4
- data/lib/byebug/dap/server.rb +20 -0
- data/lib/byebug/dap/session.rb +70 -11
- data/lib/byebug/gem.rb +15 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 581f5d93c98be98b115c7a20ed8d4ab1579cc1bf51cd56a6bff24ff22fb76695
|
4
|
+
data.tar.gz: 4fd205fedf08c249cc2b7484a3ecffd4a7885751d030fbd99f50e167fcf9f7f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a27c677b2a123d5a390b99a42bfe6d572b595239cf53b7ff47480c13412f8aa233ab06d40acb2b022ee53775d3d8332a2eada659f96cea2f95191216e7d333d5
|
7
|
+
data.tar.gz: 46c3a3ca0868a4a2f13c53985620504107a3897e53d5d15a7335bc61d17510766caae4f527bbac9ae24f798d9430868027ef4a43a5d526de696026632dfe9273
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.1.4
|
4
|
+
|
5
|
+
- Workaround a bug caused by
|
6
|
+
[byebug#734](https://github.com/deivid-rodriguez/byebug/issues/734) by setting
|
7
|
+
the breakpoint hit condition to `>= 0` when the condition should be `nil`.
|
8
|
+
|
3
9
|
## 0.1.3
|
4
10
|
|
5
11
|
- Support for output capture
|
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
[](https://badge.fury.io/rb/byebug-dap) [](https://firelizzard.gitlab.io/byebug-dap/)
|
2
|
+
|
1
3
|
# Byebug Debug Adapter Protocol
|
2
4
|
|
3
5
|
This gem adds [Debug Adapter
|
data/bin/byebug-dap
CHANGED
@@ -8,7 +8,7 @@ Usage: byebug-dap [options] <--stdio|--unix dap.socket|--listen 12345> <program>
|
|
8
8
|
EOS
|
9
9
|
|
10
10
|
def next_arg
|
11
|
-
arg = ARGV.
|
11
|
+
arg = ARGV.shift
|
12
12
|
return arg if arg
|
13
13
|
|
14
14
|
LOG.puts USAGE
|
@@ -88,7 +88,13 @@ begin
|
|
88
88
|
|
89
89
|
LOG.puts "ok" unless options[:start_code]
|
90
90
|
|
91
|
-
|
91
|
+
if File.exist?(program)
|
92
|
+
require program
|
93
|
+
elsif defined?(Bundler)
|
94
|
+
Bundler::CLI.start(['exec', program, *ARGV])
|
95
|
+
else
|
96
|
+
require program
|
97
|
+
end
|
92
98
|
|
93
99
|
rescue => e
|
94
100
|
LOG.puts "#{e.message} (#{e.class.name})", *e.backtrace
|
data/lib/byebug/dap.rb
CHANGED
@@ -5,6 +5,11 @@ require 'byebug/remote'
|
|
5
5
|
|
6
6
|
require_relative 'gem'
|
7
7
|
|
8
|
+
module Byebug::DAP
|
9
|
+
# An alias for `ruby-dap`'s {DAP} module.
|
10
|
+
Protocol = ::DAP
|
11
|
+
end
|
12
|
+
|
8
13
|
# load helpers
|
9
14
|
Dir[File.join(__dir__, 'dap', 'helpers', '*.rb')].each { |file| require file }
|
10
15
|
|
@@ -22,8 +27,13 @@ require_relative 'dap/server'
|
|
22
27
|
|
23
28
|
module Byebug
|
24
29
|
class << self
|
25
|
-
|
26
|
-
|
30
|
+
# Creates and starts the server. See {DAP::Server#initialize} and
|
31
|
+
# {DAP::Server#start}.
|
32
|
+
# @param host the host passed to {DAP::Server#start}
|
33
|
+
# @param port the port passed to {DAP::Server#start}
|
34
|
+
# @return [DAP::Server]
|
35
|
+
def start_dap(host, port = 0)
|
36
|
+
DAP::Server.new.start(host, port)
|
27
37
|
end
|
28
38
|
end
|
29
39
|
|
@@ -37,28 +47,31 @@ module Byebug
|
|
37
47
|
end
|
38
48
|
|
39
49
|
module Byebug::DAP
|
40
|
-
Protocol = ::DAP
|
41
|
-
|
42
50
|
class << self
|
43
|
-
|
44
|
-
Session.child_spawned(*args)
|
45
|
-
end
|
46
|
-
|
51
|
+
# (see Session.stop!)
|
47
52
|
def stop!
|
48
|
-
|
49
|
-
|
53
|
+
Session.stop!
|
54
|
+
end
|
50
55
|
|
51
|
-
|
52
|
-
|
56
|
+
# (see Session.child_spawned)
|
57
|
+
def child_spawned(*args)
|
58
|
+
Session.child_spawned(*args)
|
53
59
|
end
|
54
60
|
end
|
55
61
|
end
|
56
62
|
|
63
|
+
# Debug logging
|
57
64
|
module Byebug::DAP::Debug
|
58
65
|
class << self
|
59
66
|
@protocol = false
|
60
67
|
@evaluate = false
|
61
68
|
|
62
|
-
|
69
|
+
# Log all sent and received protocol messages.
|
70
|
+
# @return [Boolean]
|
71
|
+
attr_accessor :protocol
|
72
|
+
|
73
|
+
# Log evaluation failures.
|
74
|
+
# @return [Boolean]
|
75
|
+
attr_accessor :evaluate
|
63
76
|
end
|
64
77
|
end
|
data/lib/byebug/dap/command.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
module Byebug::DAP
|
2
|
+
# Implementation of a DAP command.
|
3
|
+
# @abstract Subclasses must implement {#execute}
|
2
4
|
class Command
|
5
|
+
# The error message returned when a variable or expression cannot be evaluated.
|
3
6
|
EVAL_ERROR = "*Error in evaluation*"
|
4
7
|
|
5
8
|
include SafeHelpers
|
6
9
|
|
10
|
+
# The DAP command assocated with the receiver.
|
11
|
+
# @return [std:String]
|
7
12
|
def self.command
|
8
13
|
return @command_name if defined?(@command_name)
|
9
14
|
|
@@ -15,10 +20,16 @@ module Byebug::DAP
|
|
15
20
|
@command_name = "#{last[0].downcase}#{last[1..]}"
|
16
21
|
end
|
17
22
|
|
23
|
+
# Register the receiver as a DAP command.
|
18
24
|
def self.register!
|
19
25
|
(@@commands ||= {})[command] = self
|
20
26
|
end
|
21
27
|
|
28
|
+
# Resolve the requested command. Calls {Session#respond!} indicating a
|
29
|
+
# failed request if the command cannot be found.
|
30
|
+
# @param session [Session] the debug session
|
31
|
+
# @param request [Protocol::Request] the DAP request
|
32
|
+
# @return [std:Class] the {Command} class
|
22
33
|
def self.resolve!(session, request)
|
23
34
|
cls = @@commands[request.command]
|
24
35
|
return cls if cls
|
@@ -26,21 +37,33 @@ module Byebug::DAP
|
|
26
37
|
session.respond! request, success: false, message: 'Invalid command'
|
27
38
|
end
|
28
39
|
|
40
|
+
# Resolve and execute the requested command. The command is {.resolve!
|
41
|
+
# resolved}, {#initialize initialized}, and {#safe_execute safely executed}.
|
42
|
+
# @param session [Session] the debug session
|
43
|
+
# @param request [Protocol::Request] the DAP request
|
44
|
+
# @param args [std:Array] additional arguments for {#initialize}
|
45
|
+
# @return the return value of {#safe_execute}
|
29
46
|
def self.execute(session, request, *args)
|
30
47
|
return unless command = resolve!(session, request)
|
31
48
|
|
32
49
|
command.new(session, request, *args).safe_execute
|
33
50
|
end
|
34
51
|
|
52
|
+
# Create a new instance of the receiver.
|
53
|
+
# @param session [Session] the debug session
|
54
|
+
# @param request [Protocol::Request] the DAP request
|
35
55
|
def initialize(session, request)
|
36
56
|
@session = session
|
37
57
|
@request = request
|
38
58
|
end
|
39
59
|
|
60
|
+
# (see Session#log)
|
40
61
|
def log(*args)
|
41
62
|
@session.log(*args)
|
42
63
|
end
|
43
64
|
|
65
|
+
# Call {#execute} safely, handling any errors that arise.
|
66
|
+
# @return the return value of {#execute}
|
44
67
|
def safe_execute
|
45
68
|
execute
|
46
69
|
|
@@ -91,12 +114,18 @@ module Byebug::DAP
|
|
91
114
|
return
|
92
115
|
end
|
93
116
|
|
117
|
+
# Raises an error if the debugger is running
|
118
|
+
# @api private
|
119
|
+
# @!visibility public
|
94
120
|
def stopped!
|
95
121
|
return if !Byebug.started?
|
96
122
|
|
97
123
|
respond! success: false, message: "Cannot #{@request.command} - debugger is already running"
|
98
124
|
end
|
99
125
|
|
126
|
+
# Raises an error unless the debugger is running
|
127
|
+
# @api private
|
128
|
+
# @!visibility public
|
100
129
|
def started!
|
101
130
|
return if Byebug.started?
|
102
131
|
|
@@ -111,6 +140,13 @@ module Byebug::DAP
|
|
111
140
|
safe(-> { "#{ex.message} (#{ex.class.name})" }, :call) { EVAL_ERROR }
|
112
141
|
end
|
113
142
|
|
143
|
+
# Execute a code block on the specified thread, {SafeHelpers#safe safely}.
|
144
|
+
# @param thnum [std:Integer] the thread number
|
145
|
+
# @param block [std:Proc] the code block
|
146
|
+
# @yield called on error
|
147
|
+
# @yieldparam ex [std:Exception] the execution error
|
148
|
+
# @api private
|
149
|
+
# @!visibility public
|
114
150
|
def execute_on_thread(thnum, block, &on_error)
|
115
151
|
return safe(block, :call, &on_error) if thnum == 0 || @context&.thnum == thnum
|
116
152
|
|
@@ -207,8 +243,10 @@ module Byebug::DAP
|
|
207
243
|
end
|
208
244
|
|
209
245
|
def convert_breakpoint_hit_condition(condition)
|
210
|
-
|
211
|
-
|
246
|
+
# Because of https://github.com/deivid-rodriguez/byebug/issues/739,
|
247
|
+
# Breakpoint#hit_condition can't be set to nil.
|
248
|
+
return :ge, 0 if condition.nil? || condition.empty?
|
249
|
+
return :ge, 0 unless condition.is_a?(String)
|
212
250
|
|
213
251
|
m = /^(?<op><|<=|=|==|===|=>|>|%)?\s*(?<value>[0-9]+)$/.match(condition)
|
214
252
|
raise InvalidRequestArgumentError.new("'#{condition}' is not a valid hit condition") unless m
|
@@ -1,10 +1,14 @@
|
|
1
1
|
module Byebug
|
2
2
|
module DAP
|
3
|
+
# Processes thread-specific commands and handles Byebug/TracePoint events.
|
3
4
|
class CommandProcessor
|
4
5
|
extend Forwardable
|
5
6
|
include SafeHelpers
|
6
7
|
|
8
|
+
# Indicates a timeout while sending a message to the context.
|
7
9
|
class TimeoutError < StandardError
|
10
|
+
# The receiving context.
|
11
|
+
# @return [gem:byebug:Byebug::Context]
|
8
12
|
attr_reader :context
|
9
13
|
|
10
14
|
def initialize(context)
|
@@ -12,9 +16,25 @@ module Byebug
|
|
12
16
|
end
|
13
17
|
end
|
14
18
|
|
15
|
-
|
19
|
+
# The thread context.
|
20
|
+
# @return [gem:byebug:Byebug::Context]
|
21
|
+
attr_reader :context
|
22
|
+
|
23
|
+
# The last exception that occured.
|
24
|
+
# @return [std:Exception]
|
25
|
+
attr_reader :last_exception
|
26
|
+
|
27
|
+
# Indicates that the client requested a pause.
|
28
|
+
# @return [Boolean]
|
29
|
+
# @note This should only be set by {Command::Pause}
|
30
|
+
# @api private
|
16
31
|
attr_writer :pause_requested
|
17
32
|
|
33
|
+
# Create a new command processor.
|
34
|
+
# @param context [gem:byebug:Byebug::Context] the thread context
|
35
|
+
# @param session [Session] the debugging session
|
36
|
+
# @note This should only be used by Byebug internals
|
37
|
+
# @api private
|
18
38
|
def initialize(context, session)
|
19
39
|
@context = context
|
20
40
|
@session = session
|
@@ -23,14 +43,21 @@ module Byebug
|
|
23
43
|
@exec_ch = Channel.new
|
24
44
|
end
|
25
45
|
|
46
|
+
# (see Session#log)
|
26
47
|
def log(*args)
|
27
48
|
@session.log(*args)
|
28
49
|
end
|
29
50
|
|
51
|
+
# Send a message to the thread context.
|
52
|
+
# @param message the message to send
|
53
|
+
# @note Raises a {TimeoutError timeout error} after 1 second if the thread is not paused or not responding.
|
30
54
|
def <<(message)
|
31
55
|
@requests.push(message, timeout: 1) { raise TimeoutError.new(context) }
|
32
56
|
end
|
33
57
|
|
58
|
+
# Execute a code block in the thread.
|
59
|
+
# @yield the code block to execute
|
60
|
+
# @note This calls {#\<\<} and thus may raise a {TimeoutError timeout error}.
|
34
61
|
def execute(&block)
|
35
62
|
raise "Block required" unless block_given?
|
36
63
|
|
@@ -47,6 +74,51 @@ module Byebug
|
|
47
74
|
end
|
48
75
|
end
|
49
76
|
|
77
|
+
# Line handler.
|
78
|
+
# @note This should only be called by Byebug internals
|
79
|
+
# @api private
|
80
|
+
def at_line
|
81
|
+
stopped!
|
82
|
+
end
|
83
|
+
|
84
|
+
# End of class/module handler.
|
85
|
+
# @note This should only be called by Byebug internals
|
86
|
+
# @api private
|
87
|
+
def at_end
|
88
|
+
stopped!
|
89
|
+
end
|
90
|
+
|
91
|
+
# Return handler.
|
92
|
+
# @note This should only be called by Byebug internals
|
93
|
+
# @api private
|
94
|
+
def at_return(return_value)
|
95
|
+
@at_return = return_value
|
96
|
+
stopped!
|
97
|
+
end
|
98
|
+
|
99
|
+
# Tracing handler.
|
100
|
+
# @note This should only be called by Byebug internals
|
101
|
+
# @api private
|
102
|
+
def at_tracing
|
103
|
+
# @session.puts "Tracing: #{context.full_location}"
|
104
|
+
end
|
105
|
+
|
106
|
+
# Breakpoint handler.
|
107
|
+
# @note This should only be called by Byebug internals
|
108
|
+
# @api private
|
109
|
+
def at_breakpoint(breakpoint)
|
110
|
+
@last_breakpoint = breakpoint
|
111
|
+
end
|
112
|
+
|
113
|
+
# Catchpoint handler.
|
114
|
+
# @note This should only be called by Byebug internals
|
115
|
+
# @api private
|
116
|
+
def at_catchpoint(exception)
|
117
|
+
@last_exception = exception
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
50
122
|
def process_requests
|
51
123
|
loop do
|
52
124
|
request = @requests.pop
|
@@ -69,35 +141,6 @@ module Byebug
|
|
69
141
|
log "\n! #{e.message} (#{e.class})", *e.backtrace
|
70
142
|
end
|
71
143
|
|
72
|
-
def logpoint!
|
73
|
-
return false unless @last_breakpoint
|
74
|
-
|
75
|
-
breakpoint, @last_breakpoint = @last_breakpoint, nil
|
76
|
-
expr = @session.get_log_point(breakpoint)
|
77
|
-
return false unless expr
|
78
|
-
|
79
|
-
binding = @context.frame._binding
|
80
|
-
msg = expr.gsub(/\{([^\}]+)\}/) do |x|
|
81
|
-
safe(binding, :eval, x[1...-1]) { return true } # ignore bad log points
|
82
|
-
end
|
83
|
-
|
84
|
-
body = {
|
85
|
-
category: 'console',
|
86
|
-
output: msg + "\n",
|
87
|
-
}
|
88
|
-
|
89
|
-
if breakpoint.pos.is_a?(Integer)
|
90
|
-
body[:line] = breakpoint.pos
|
91
|
-
body[:source] = {
|
92
|
-
name: File.basename(breakpoint.source),
|
93
|
-
path: breakpoint.source,
|
94
|
-
}
|
95
|
-
end
|
96
|
-
|
97
|
-
@session.event! 'output', **body
|
98
|
-
return true
|
99
|
-
end
|
100
|
-
|
101
144
|
def stopped!
|
102
145
|
return if logpoint!
|
103
146
|
|
@@ -141,30 +184,33 @@ module Byebug
|
|
141
184
|
process_requests
|
142
185
|
end
|
143
186
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
def at_end
|
148
|
-
stopped!
|
149
|
-
end
|
187
|
+
def logpoint!
|
188
|
+
return false unless @last_breakpoint
|
150
189
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
end
|
190
|
+
breakpoint, @last_breakpoint = @last_breakpoint, nil
|
191
|
+
expr = @session.get_log_point(breakpoint)
|
192
|
+
return false unless expr
|
155
193
|
|
156
|
-
|
157
|
-
|
194
|
+
binding = @context.frame._binding
|
195
|
+
msg = expr.gsub(/\{([^\}]+)\}/) do |x|
|
196
|
+
safe(binding, :eval, x[1...-1]) { return true } # ignore bad log points
|
197
|
+
end
|
158
198
|
|
159
|
-
|
160
|
-
|
199
|
+
body = {
|
200
|
+
category: 'console',
|
201
|
+
output: msg + "\n",
|
202
|
+
}
|
161
203
|
|
162
|
-
|
163
|
-
|
164
|
-
|
204
|
+
if breakpoint.pos.is_a?(Integer)
|
205
|
+
body[:line] = breakpoint.pos
|
206
|
+
body[:source] = {
|
207
|
+
name: File.basename(breakpoint.source),
|
208
|
+
path: breakpoint.source,
|
209
|
+
}
|
210
|
+
end
|
165
211
|
|
166
|
-
|
167
|
-
|
212
|
+
@session.event! 'output', **body
|
213
|
+
return true
|
168
214
|
end
|
169
215
|
end
|
170
216
|
end
|
@@ -14,7 +14,7 @@ module Byebug::DAP
|
|
14
14
|
|
15
15
|
locals = frame_local_names(frame).sort
|
16
16
|
unless locals.empty?
|
17
|
-
scopes << ::
|
17
|
+
scopes << Protocol::Scope.new(
|
18
18
|
name: 'Locals',
|
19
19
|
presentationHint: 'locals',
|
20
20
|
variablesReference: @session.save_variables(thnum, frnum, :locals, locals),
|
@@ -26,7 +26,7 @@ module Byebug::DAP
|
|
26
26
|
|
27
27
|
globals = global_names.sort
|
28
28
|
unless globals.empty?
|
29
|
-
scopes << ::
|
29
|
+
scopes << Protocol::Scope.new(
|
30
30
|
name: 'Globals',
|
31
31
|
presentationHint: 'globals',
|
32
32
|
variablesReference: @session.save_variables(thnum, frnum, :globals, globals),
|
@@ -36,7 +36,7 @@ module Byebug::DAP
|
|
36
36
|
.validate!
|
37
37
|
end
|
38
38
|
|
39
|
-
respond! body: ::
|
39
|
+
respond! body: Protocol::ScopesResponseBody.new(scopes: scopes)
|
40
40
|
end
|
41
41
|
|
42
42
|
private
|
@@ -42,7 +42,7 @@ module Byebug::DAP
|
|
42
42
|
results << {
|
43
43
|
id: bp.id,
|
44
44
|
verified: true,
|
45
|
-
source: ::
|
45
|
+
source: Protocol::Source.new(name: File.basename(cm[0]), path: cm[0]),
|
46
46
|
line: cm[1]
|
47
47
|
}
|
48
48
|
end
|
@@ -51,7 +51,7 @@ module Byebug::DAP
|
|
51
51
|
results << {
|
52
52
|
id: bp.id,
|
53
53
|
verified: true,
|
54
|
-
source: ::
|
54
|
+
source: Protocol::Source.new(name: File.basename(im[0]), path: im[0]),
|
55
55
|
line: im[1]
|
56
56
|
}
|
57
57
|
end
|
@@ -7,11 +7,11 @@ module Byebug::DAP
|
|
7
7
|
def execute
|
8
8
|
started!
|
9
9
|
|
10
|
-
respond! body: ::
|
10
|
+
respond! body: Protocol::ThreadsResponseBody.new(
|
11
11
|
threads: Byebug
|
12
12
|
.contexts
|
13
13
|
.filter { |ctx| !ctx.thread.is_a?(::Byebug::DebugThread) }
|
14
|
-
.map { |ctx| ::
|
14
|
+
.map { |ctx| Protocol::Thread.new(
|
15
15
|
id: ctx.thnum,
|
16
16
|
name: ctx.thread.name || "Thread ##{ctx.thnum}"
|
17
17
|
).validate! })
|
@@ -27,7 +27,7 @@ module Byebug::DAP
|
|
27
27
|
|
28
28
|
variables = vars[first...last].map { |var, get| prepare_value_response(thnum, frnum, :variable, name: var) { get.call(var) } }
|
29
29
|
|
30
|
-
respond! body: ::
|
30
|
+
respond! body: Protocol::VariablesResponseBody.new(variables: variables)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
@@ -1,5 +1,9 @@
|
|
1
1
|
module Byebug::DAP
|
2
|
+
# Implementation of a DAP command that must be executed in-context.
|
3
|
+
# @abstract Subclasses must implement {#execute_in_context}
|
2
4
|
class ContextualCommand < Command
|
5
|
+
# (see Command.resolve!)
|
6
|
+
# @note Raises an error if the resolved class is not a subclass of {ContextualCommand}
|
3
7
|
def self.resolve!(session, request)
|
4
8
|
return unless cls = super
|
5
9
|
return cls if cls < ContextualCommand
|
@@ -7,12 +11,19 @@ module Byebug::DAP
|
|
7
11
|
raise "Not a contextual command: #{command}"
|
8
12
|
end
|
9
13
|
|
14
|
+
# Create a new instance of the receiver.
|
15
|
+
# @param session [Session] the debug session
|
16
|
+
# @param request [Protocol::Request] the DAP request
|
17
|
+
# @param processor [CommandProcessor] the command processor associated with the context
|
10
18
|
def initialize(session, request, processor = nil)
|
11
19
|
super(session, request)
|
12
20
|
@processor = processor
|
13
21
|
@context = processor&.context
|
14
22
|
end
|
15
23
|
|
24
|
+
# {#execute_in_context Execute in-context} if `processor` is defined.
|
25
|
+
# Otherwise, ensure debugging is {#started! started}, find the requested
|
26
|
+
# thread context, and {#forward_to_context forward the request}.
|
16
27
|
def execute
|
17
28
|
return execute_in_context if @processor
|
18
29
|
|
@@ -23,6 +34,10 @@ module Byebug::DAP
|
|
23
34
|
|
24
35
|
private
|
25
36
|
|
37
|
+
# Forward the request to the context's thread.
|
38
|
+
# @param ctx [gem:byebug:Byebug::Context] the context
|
39
|
+
# @api private
|
40
|
+
# @!visibility public
|
26
41
|
def forward_to_context(ctx)
|
27
42
|
ctx.processor << @request
|
28
43
|
end
|
@@ -1,5 +1,12 @@
|
|
1
1
|
module Byebug::DAP
|
2
|
+
# Captures STDOUT and STDERR. See {CapturedOutput}.
|
3
|
+
# @api private
|
2
4
|
class CapturedIO
|
5
|
+
# Capture STDOUT and STDERR and create a new
|
6
|
+
# {gem:byebug:Byebug::DebugThread} running {#capture}. See
|
7
|
+
# {CapturedOutput#initialize}.
|
8
|
+
# @param forward_stdout [Boolean] if true, captured STDOUT is forwarded to the original STDOUT.
|
9
|
+
# @param forward_stderr [Boolean] if true, captured STDERR is forwarded to the original STDERR.
|
3
10
|
def initialize(forward_stdout, forward_stderr)
|
4
11
|
@forward_stdout = forward_stdout
|
5
12
|
@forward_stderr = forward_stderr
|
@@ -10,6 +17,8 @@ module Byebug::DAP
|
|
10
17
|
Byebug::DebugThread.new { capture }
|
11
18
|
end
|
12
19
|
|
20
|
+
# Return an IO that can be used for logging.
|
21
|
+
# @return [std:IO]
|
13
22
|
def log
|
14
23
|
if defined?(LOG)
|
15
24
|
LOG
|
@@ -20,6 +29,7 @@ module Byebug::DAP
|
|
20
29
|
end
|
21
30
|
end
|
22
31
|
|
32
|
+
# {CapturedOutput#restore Restore} the original STDOUT and STDERR.
|
23
33
|
def restore
|
24
34
|
@stop = true
|
25
35
|
@stdout.restore
|
@@ -28,6 +38,11 @@ module Byebug::DAP
|
|
28
38
|
|
29
39
|
private
|
30
40
|
|
41
|
+
# In a loop, read from the captured STDOUT and STDERR and send an output
|
42
|
+
# event to the active session's client (if there is an active session), and
|
43
|
+
# optionally forward the output to the original STDOUT/STDERR.
|
44
|
+
# @api private
|
45
|
+
# @!visibility public
|
31
46
|
def capture
|
32
47
|
until @stop do
|
33
48
|
r, = IO.select([@stdout.captured, @stderr.captured])
|
@@ -1,7 +1,18 @@
|
|
1
1
|
module Byebug::DAP
|
2
|
+
# Captures an IO output stream.
|
3
|
+
# @api private
|
2
4
|
class CapturedOutput
|
3
|
-
|
5
|
+
# The original stream, {std:IO#dup duplicated} from `io`. Writing to this IO
|
6
|
+
# will write to the original file.
|
7
|
+
# @return [std:IO]
|
8
|
+
attr_reader :original
|
4
9
|
|
10
|
+
# The captured stream. Captured output can be read from this IO.
|
11
|
+
# @return [std:IO]
|
12
|
+
attr_reader :captured
|
13
|
+
|
14
|
+
# Capture `io`, {std:IO#dup duplicate} the original, open an {std:IO.pipe
|
15
|
+
# pipe} pair, and {std:IO#reopen reopen} `io` to redirect it to the pipe.
|
5
16
|
def initialize(io)
|
6
17
|
@io = io
|
7
18
|
@original = io.dup
|
@@ -11,6 +22,7 @@ module Byebug::DAP
|
|
11
22
|
pw.close
|
12
23
|
end
|
13
24
|
|
25
|
+
# Restore `io` to the original file.
|
14
26
|
def restore
|
15
27
|
@io.reopen(@original)
|
16
28
|
@original.close
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module Byebug
|
2
2
|
module DAP
|
3
|
+
# A channel for synchronously passing values between threads.
|
4
|
+
# @api private
|
3
5
|
class Channel
|
4
6
|
def initialize
|
5
7
|
@mu = Mutex.new
|
@@ -8,6 +10,7 @@ module Byebug
|
|
8
10
|
@have = false
|
9
11
|
end
|
10
12
|
|
13
|
+
# Close the channel.
|
11
14
|
def close
|
12
15
|
@mu.synchronize {
|
13
16
|
@closed = true
|
@@ -15,6 +18,8 @@ module Byebug
|
|
15
18
|
}
|
16
19
|
end
|
17
20
|
|
21
|
+
# Pop an item off the channel. Blocks until {#push} or {#close} is called.
|
22
|
+
# @return a value that was pushed or `nil` if the channel is closed.
|
18
23
|
def pop
|
19
24
|
synchronize_loop {
|
20
25
|
return if @closed
|
@@ -29,6 +34,10 @@ module Byebug
|
|
29
34
|
}
|
30
35
|
end
|
31
36
|
|
37
|
+
# Push an item onto the channel. Raises an error if the channel is closed.
|
38
|
+
# If `timeout` is nil, blocks until {#push} or {#close} is called.
|
39
|
+
# @param message the value to push
|
40
|
+
# @yield called on timeout
|
32
41
|
def push(message, timeout: nil)
|
33
42
|
deadline = timeout + Time.now.to_f unless timeout.nil?
|
34
43
|
|
@@ -1,10 +1,24 @@
|
|
1
1
|
module Byebug
|
2
2
|
module DAP
|
3
|
-
|
4
|
-
|
3
|
+
# `childSpawned` is a custom DAP event used to notify the client that a
|
4
|
+
# child process has spawned.
|
5
|
+
# @api private
|
6
|
+
class ChildSpawnedEventBody < Protocol::Base
|
7
|
+
Protocol::Event.bodies[:childSpawned] = self
|
5
8
|
|
9
|
+
# The child process's name
|
10
|
+
# @return [std:String]
|
11
|
+
# @!attribute [r]
|
6
12
|
property :name
|
13
|
+
|
14
|
+
# The child's process ID
|
15
|
+
# @return [std:Integer]
|
16
|
+
# @!attribute [r]
|
7
17
|
property :pid
|
18
|
+
|
19
|
+
# The debug socket to connect to
|
20
|
+
# @return [std:String]
|
21
|
+
# @!attribute [r]
|
8
22
|
property :socket
|
9
23
|
end
|
10
24
|
end
|
@@ -1,19 +1,27 @@
|
|
1
1
|
module Byebug
|
2
2
|
module DAP
|
3
|
+
# Tracks opaque handles used by DAP.
|
4
|
+
# @api private
|
3
5
|
class Handles
|
4
6
|
def initialize
|
5
7
|
@mu = Mutex.new
|
6
8
|
@entries = []
|
7
9
|
end
|
8
10
|
|
11
|
+
# Delete all handles.
|
9
12
|
def clear!
|
10
13
|
sync { @entries = []; nil }
|
11
14
|
end
|
12
15
|
|
16
|
+
# Retrieve the entry with the specified handle.
|
17
|
+
# @param id [std:Integer] the handle
|
18
|
+
# @return the entry
|
13
19
|
def [](id)
|
14
20
|
sync { @entries[id-1] }
|
15
21
|
end
|
16
22
|
|
23
|
+
# Add a new entry.
|
24
|
+
# @return [std:Integer] the handle
|
17
25
|
def <<(entry)
|
18
26
|
sync do
|
19
27
|
@entries << entry
|
@@ -1,7 +1,17 @@
|
|
1
1
|
module Byebug
|
2
2
|
module DAP
|
3
|
+
# Raised when the client sends a request with invalid arguments
|
4
|
+
# @api private
|
3
5
|
class InvalidRequestArgumentError < StandardError
|
4
|
-
|
6
|
+
# The error kind or message.
|
7
|
+
# @return [std:Symbol|std:String]
|
8
|
+
attr_reader :error
|
9
|
+
|
10
|
+
# The error value.
|
11
|
+
attr_reader :value
|
12
|
+
|
13
|
+
# The error scope.
|
14
|
+
attr_reader :scope
|
5
15
|
|
6
16
|
def initialize(error, value: nil, scope: nil)
|
7
17
|
@error = error
|
@@ -1,6 +1,14 @@
|
|
1
1
|
module Byebug
|
2
2
|
module DAP
|
3
|
+
# Methods to safely execute methods.
|
4
|
+
# @api private
|
3
5
|
module SafeHelpers
|
6
|
+
# Safely execute `method` on `target` with `args`.
|
7
|
+
# @param target the receiver
|
8
|
+
# @param method [std:Symbol] the method name
|
9
|
+
# @param args [std:Array] the method arguments
|
10
|
+
# @yield called on error
|
11
|
+
# @yieldparam ex [std:StandardError] the execution error
|
4
12
|
def safe(target, method, *args, &block)
|
5
13
|
if method.is_a?(Array) && args.empty?
|
6
14
|
method.each { |m| target = target.__send__(m) }
|
@@ -1,5 +1,12 @@
|
|
1
1
|
module Byebug::DAP
|
2
|
+
# Used in case statements to identify scalar types.
|
3
|
+
# @api private
|
2
4
|
module Scalar
|
5
|
+
# Match scalar values. {std:NilClass nil}, {std:TrueClass true},
|
6
|
+
# {std:FalseClass false}, {std:String strings}, {std:Numeric numbers},
|
7
|
+
# {std:Time times}, {std:Range ranges}, {std:date:Date dates}, and
|
8
|
+
# {std:date:DateTime date-times} are considered scalars.
|
9
|
+
# @return [Boolean]
|
3
10
|
def ===(value)
|
4
11
|
case value
|
5
12
|
when nil, true, false
|
@@ -1,5 +1,11 @@
|
|
1
1
|
module Byebug::DAP
|
2
|
+
# Methods to prepare values for DAP responses.
|
3
|
+
# @api private
|
2
4
|
module ValueHelpers
|
5
|
+
# Safely inspect a value and retrieve its class name, class and instance
|
6
|
+
# variables, and indexed members. {Scalar} values do not have variables or
|
7
|
+
# members. Only {std:Array arrays} and {std:Hash hashes} have members.
|
8
|
+
# @return `val.inspect`, `val.class.name`, variables, and members.
|
3
9
|
def prepare_value(val)
|
4
10
|
str = safe(val, :inspect) { safe(val, :to_s) { return yield } }
|
5
11
|
cls = safe(val, :class) { nil }
|
@@ -8,8 +14,8 @@ module Byebug::DAP
|
|
8
14
|
scalar = safe(-> { Scalar === val }, :call) { true }
|
9
15
|
return str, typ, [], [] if scalar
|
10
16
|
|
11
|
-
named = safe(val, :instance_variables) { [] }
|
12
|
-
named += safe(val, :class_variables) { [] }
|
17
|
+
named = safe(val, :instance_variables) { [] } || []
|
18
|
+
named += safe(val, :class_variables) { [] } || []
|
13
19
|
# named += safe(val, :constants) { [] }
|
14
20
|
|
15
21
|
indexed = safe(-> {
|
@@ -21,6 +27,15 @@ module Byebug::DAP
|
|
21
27
|
return str, typ, named, indexed
|
22
28
|
end
|
23
29
|
|
30
|
+
# Prepare a {Protocol::Variable} or {Protocol::EvaluateResponseBody} for a
|
31
|
+
# calculated value. For global variables and evaluations, `thnum` and
|
32
|
+
# `frnum` should be 0. Local variables and evaluations are
|
33
|
+
# {Command#execute_on_thread executed on the specified thread}.
|
34
|
+
# @param thnum [std:Integer] the thread number
|
35
|
+
# @param frnum [std:Integer] the frame number
|
36
|
+
# @param kind [std:Symbol] `:variable` or `:evaluate`
|
37
|
+
# @param name [std:String] the variable name (ignored for evaluations)
|
38
|
+
# @yield retrieves an variable or evaluates an expression
|
24
39
|
def prepare_value_response(thnum, frnum, kind, name: nil, &block)
|
25
40
|
err = nil
|
26
41
|
raw = execute_on_thread(thnum, block) { |e| err = e; nil }
|
@@ -39,10 +54,10 @@ module Byebug::DAP
|
|
39
54
|
|
40
55
|
case kind
|
41
56
|
when :variable
|
42
|
-
klazz = ::
|
57
|
+
klazz = Protocol::Variable
|
43
58
|
args = { name: safe(name, :to_s) { safe(name, :inspect) { '???' } }, value: value, type: type }
|
44
59
|
when :evaluate
|
45
|
-
klazz = ::
|
60
|
+
klazz = Protocol::EvaluateResponseBody
|
46
61
|
args = { result: value, type: type }
|
47
62
|
end
|
48
63
|
|
data/lib/byebug/dap/server.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
module Byebug
|
2
2
|
module DAP
|
3
|
+
# Byebug DAP Server
|
3
4
|
class Server
|
5
|
+
# Create a new server.
|
6
|
+
# @param capture [Boolean] if `true`, the debugee's STDOUT and STDERR will be captured
|
7
|
+
# @param forward [Boolean] if `false`, the debugee's STDOUT and STDERR will be supressed
|
4
8
|
def initialize(capture: true, forward: true)
|
5
9
|
@started = false
|
6
10
|
@mu = Mutex.new
|
@@ -10,6 +14,12 @@ module Byebug
|
|
10
14
|
@forward = forward
|
11
15
|
end
|
12
16
|
|
17
|
+
# Starts the server. Calls {#start_stdio} if `host == :stdio`. Calls
|
18
|
+
# {#start_unix} with `port` if `host == :unix`. Calls {#start_tcp} with
|
19
|
+
# `host` and `port` otherwise.
|
20
|
+
# @param host `:stdio`, `:unix`, or the TCP host name
|
21
|
+
# @param port the Unix socket path or TCP port
|
22
|
+
# @return [Server]
|
13
23
|
def start(host, port = 0)
|
14
24
|
case host
|
15
25
|
when :stdio
|
@@ -21,6 +31,10 @@ module Byebug
|
|
21
31
|
end
|
22
32
|
end
|
23
33
|
|
34
|
+
# Starts the server, listening on a TCP socket.
|
35
|
+
# @param host [std:String] the IP to listen on
|
36
|
+
# @param port [std:Number] the port to listen on
|
37
|
+
# @return [Server]
|
24
38
|
def start_tcp(host, port)
|
25
39
|
return if @started
|
26
40
|
@started = true
|
@@ -29,6 +43,9 @@ module Byebug
|
|
29
43
|
launch_accept TCPServer.new(host, port)
|
30
44
|
end
|
31
45
|
|
46
|
+
# Starts the server, listening on a Unix socket.
|
47
|
+
# @param socket [std:String] the Unix socket path
|
48
|
+
# @return [Server]
|
32
49
|
def start_unix(socket)
|
33
50
|
return if @started
|
34
51
|
@started = true
|
@@ -37,6 +54,8 @@ module Byebug
|
|
37
54
|
launch_accept UNIXServer.new(socket)
|
38
55
|
end
|
39
56
|
|
57
|
+
# Starts the server using STDIN and STDOUT to communicate.
|
58
|
+
# @return [Server]
|
40
59
|
def start_stdio
|
41
60
|
return if @started
|
42
61
|
@started = true
|
@@ -47,6 +66,7 @@ module Byebug
|
|
47
66
|
launch stream
|
48
67
|
end
|
49
68
|
|
69
|
+
# Blocks until a client connects and begins debugging.
|
50
70
|
def wait_for_client
|
51
71
|
@mu.synchronize do
|
52
72
|
loop do
|
data/lib/byebug/dap/session.rb
CHANGED
@@ -1,24 +1,42 @@
|
|
1
1
|
module Byebug
|
2
2
|
module DAP
|
3
|
+
# A Byebug DAP session
|
3
4
|
class Session
|
4
5
|
include SafeHelpers
|
5
6
|
|
6
|
-
|
7
|
+
# Call {Session#stop!} on {gem:byebug:Byebug::Context.interface} if it is
|
8
|
+
# a {Session}.
|
9
|
+
# @return [Boolean] whether {gem:byebug:Byebug::Context.interface} was a {Session}
|
10
|
+
def self.stop!
|
11
|
+
session = Byebug::Context.interface
|
12
|
+
return false unless session.is_a?(Session)
|
7
13
|
|
14
|
+
session.stop!
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
# Record a {ChildSpawnedEventBody childSpawned event} and send the event
|
19
|
+
# to the current session's client, if
|
20
|
+
# {gem:byebug:Byebug::Context.interface} is a {Session}.
|
8
21
|
def self.child_spawned(name, pid, socket)
|
9
22
|
child = ChildSpawnedEventBody.new(name: name, pid: pid, socket: socket)
|
10
|
-
@@children << child
|
23
|
+
(@@children ||= []) << child
|
11
24
|
|
12
25
|
session = Context.interface
|
13
|
-
|
26
|
+
return false unless session.is_a?(Session)
|
14
27
|
|
28
|
+
session.event! child
|
15
29
|
return true
|
16
30
|
|
17
31
|
rescue IOError, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNABORTED
|
18
32
|
return false
|
19
33
|
end
|
20
34
|
|
21
|
-
|
35
|
+
# Create a new session instance.
|
36
|
+
# @param connection [std:IO] the connection to the client
|
37
|
+
# @param ios [CapturedIO] the captured IO
|
38
|
+
# @yield called once the client is done configuring the session (optional)
|
39
|
+
def initialize(connection, ios = nil, &block)
|
22
40
|
@connection = connection
|
23
41
|
@ios = ios
|
24
42
|
@on_configured = block
|
@@ -31,6 +49,7 @@ module Byebug
|
|
31
49
|
notify_of_children
|
32
50
|
end
|
33
51
|
|
52
|
+
# Write a message to the log.
|
34
53
|
def log(*args)
|
35
54
|
logger =
|
36
55
|
if @ios
|
@@ -43,20 +62,28 @@ module Byebug
|
|
43
62
|
logger.puts(*args)
|
44
63
|
end
|
45
64
|
|
65
|
+
# Execute requests from the client until the connection is closed.
|
46
66
|
def execute
|
47
67
|
Context.interface = self
|
48
|
-
Context.processor =
|
68
|
+
Context.processor = DAP::CommandProcessor
|
49
69
|
|
50
70
|
Command.execute(self, receive) until @connection.closed?
|
51
71
|
|
52
72
|
Context.interface = LocalInterface.new
|
53
73
|
end
|
54
74
|
|
75
|
+
# Invalidate frame IDs and variables references.
|
76
|
+
# @note This should only be used by a {ContextualCommand contextual command} that un-pauses its context
|
77
|
+
# @api private
|
55
78
|
def invalidate_handles!
|
56
79
|
@frame_ids.clear!
|
57
80
|
@variable_refs.clear!
|
58
81
|
end
|
59
82
|
|
83
|
+
# Start Byebug.
|
84
|
+
# @param mode [std:Symbol] `:attached` or `:launched`
|
85
|
+
# @note This should only be used by the {Command::Attach attach} or {Command::Launch launch} commands
|
86
|
+
# @api private
|
60
87
|
def start!(mode)
|
61
88
|
@trace.enable
|
62
89
|
Byebug.mode = mode
|
@@ -64,6 +91,9 @@ module Byebug
|
|
64
91
|
@exit_on_stop = true if mode == :launched
|
65
92
|
end
|
66
93
|
|
94
|
+
# Call the block passed to {#initialize}.
|
95
|
+
# @note This should only be used by the {Command::ConfigurationDone configurationDone} commands
|
96
|
+
# @api private
|
67
97
|
def configured!
|
68
98
|
return unless @on_configured
|
69
99
|
|
@@ -71,6 +101,8 @@ module Byebug
|
|
71
101
|
callback.call
|
72
102
|
end
|
73
103
|
|
104
|
+
# Stop Byebug and close the client's connection.
|
105
|
+
# @note If the session was started with the `launch` command, this will {std:Kernel#exit exit}
|
74
106
|
def stop!
|
75
107
|
exit if @exit_on_stop && @pid == Process.pid
|
76
108
|
|
@@ -80,19 +112,29 @@ module Byebug
|
|
80
112
|
@connection.close
|
81
113
|
end
|
82
114
|
|
115
|
+
# Send an event to the client. Either call with an event name and body
|
116
|
+
# attributes, or call with an already constructed body.
|
117
|
+
# @param event [std:String|Protocol::Base] the event name or event body
|
118
|
+
# @param values [std:Hash] event body attributes
|
83
119
|
def event!(event, **values)
|
84
120
|
if (cls = event.class.name.split('::').last) && cls.end_with?('EventBody')
|
85
121
|
body, event = event, cls[0].downcase + cls[1...-9]
|
86
122
|
|
87
123
|
elsif event.is_a?(String) && !values.empty?
|
88
|
-
body =
|
124
|
+
body = Protocol.const_get("#{event[0].upcase}#{event[1..]}EventBody").new(values)
|
89
125
|
end
|
90
126
|
|
91
|
-
send ::
|
127
|
+
send Protocol::Event.new(event: event, body: body)
|
92
128
|
end
|
93
129
|
|
130
|
+
# Send a response to the client.
|
131
|
+
# @param request [Protocol::Request] the request to respond to
|
132
|
+
# @param body [std:Hash|Protocol::Base] the response body
|
133
|
+
# @param success [std:Boolean] whether the request was successful
|
134
|
+
# @param message [std:String] the response message
|
135
|
+
# @param values [std:Hash] additional response attributes
|
94
136
|
def respond!(request, body = nil, success: true, message: 'Success', **values)
|
95
|
-
send ::
|
137
|
+
send Protocol::Response.new(
|
96
138
|
request_seq: request.seq,
|
97
139
|
command: request.command,
|
98
140
|
success: success,
|
@@ -101,26 +143,40 @@ module Byebug
|
|
101
143
|
**values)
|
102
144
|
end
|
103
145
|
|
146
|
+
# Create a variables reference.
|
104
147
|
def save_variables(*args)
|
105
148
|
@variable_refs << args
|
106
149
|
end
|
107
150
|
|
151
|
+
# Retrieve variables from a reference.
|
108
152
|
def restore_variables(ref)
|
109
153
|
@variable_refs[ref]
|
110
154
|
end
|
111
155
|
|
156
|
+
# Create a frame ID.
|
112
157
|
def save_frame(*args)
|
113
158
|
@frame_ids << args
|
114
159
|
end
|
115
160
|
|
161
|
+
# Restore a frame from an ID.
|
116
162
|
def restore_frame(id)
|
117
163
|
@frame_ids[id]
|
118
164
|
end
|
119
165
|
|
166
|
+
# Get the log point expression associated with `breakpoint`.
|
167
|
+
# @param breakpoint [gem:byebug:Byebug::Breakpoint] the breakpoint
|
168
|
+
# @return [std:String] the log point expression
|
169
|
+
# @note This should only be used by a {CommandProcessor command processor}
|
170
|
+
# @api private
|
120
171
|
def get_log_point(breakpoint)
|
121
172
|
@log_points[breakpoint.id]
|
122
173
|
end
|
123
174
|
|
175
|
+
# Associate a log point expression with `breakpoint`.
|
176
|
+
# @param breakpoint [gem:byebug:Byebug::Breakpoint] the breakpoint
|
177
|
+
# @param expr [std:String] the log point expression
|
178
|
+
# @note This should only be used by a {CommandProcessor command processor}
|
179
|
+
# @api private
|
124
180
|
def set_log_point(breakpoint, expr)
|
125
181
|
if expr.nil? || expr.empty?
|
126
182
|
@log_points.delete(breakpoint.id)
|
@@ -129,6 +185,9 @@ module Byebug
|
|
129
185
|
end
|
130
186
|
end
|
131
187
|
|
188
|
+
# Delete the specified breakpoints and any log points associated with
|
189
|
+
# them.
|
190
|
+
# @param breakpoints [std:Array<gem:byebug:Byebug::Breakpoint>] the breakpoints
|
132
191
|
def clear_breakpoints(*breakpoints)
|
133
192
|
breakpoints.each do |breakpoint|
|
134
193
|
Byebug.breakpoints.delete(breakpoint)
|
@@ -139,7 +198,7 @@ module Byebug
|
|
139
198
|
private
|
140
199
|
|
141
200
|
def notify_of_children
|
142
|
-
@@children.each { |c| event! c }
|
201
|
+
@@children.each { |c| event! c } if defined?(@@children)
|
143
202
|
rescue IOError, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNABORTED
|
144
203
|
# client is closed
|
145
204
|
end
|
@@ -147,11 +206,11 @@ module Byebug
|
|
147
206
|
def send(message)
|
148
207
|
log "#{Process.pid} > #{message.to_wire}" if Debug.protocol
|
149
208
|
message.validate!
|
150
|
-
@connection.write
|
209
|
+
@connection.write Protocol.encode(message)
|
151
210
|
end
|
152
211
|
|
153
212
|
def receive
|
154
|
-
m =
|
213
|
+
m = Protocol.decode(@connection)
|
155
214
|
log "#{Process.pid} < #{m.to_wire}" if Debug.protocol
|
156
215
|
m
|
157
216
|
end
|
data/lib/byebug/gem.rb
CHANGED
@@ -1,11 +1,25 @@
|
|
1
1
|
module Byebug
|
2
|
+
# Debug Adapter Protocol support for Byebug
|
2
3
|
module DAP
|
4
|
+
# Gem name
|
3
5
|
NAME = 'byebug-dap'
|
4
|
-
|
6
|
+
|
7
|
+
# Gem version
|
8
|
+
VERSION = '0.1.4'
|
9
|
+
|
10
|
+
# Gem summary
|
5
11
|
SUMMARY = 'Debug Adapter Protocol for Byebug'
|
12
|
+
|
13
|
+
# Gem description
|
6
14
|
DESCRIPTION = 'Implements a Debug Adapter Protocol interface for Byebug'
|
15
|
+
|
16
|
+
# Gem authors
|
7
17
|
AUTHORS = ['Ethan Reesor']
|
18
|
+
|
19
|
+
# Gem website
|
8
20
|
WEBSITE = 'https://gitlab.com/firelizzard/byebug-dap'
|
21
|
+
|
22
|
+
# Gem license
|
9
23
|
LICENSE = 'Apache-2.0'
|
10
24
|
end
|
11
25
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: byebug-dap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ethan Reesor
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-10-
|
11
|
+
date: 2020-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: byebug
|