byebug-dap 0.1.2 → 0.1.3

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -3
  3. data/README.md +5 -5
  4. data/bin/byebug-dap +25 -8
  5. data/lib/byebug/dap.rb +48 -18
  6. data/lib/byebug/dap/command.rb +250 -0
  7. data/lib/byebug/dap/command_processor.rb +50 -49
  8. data/lib/byebug/dap/commands/attach.rb +13 -0
  9. data/lib/byebug/dap/commands/breakpoint_locations.rb +28 -0
  10. data/lib/byebug/dap/commands/configuration_done.rb +12 -0
  11. data/lib/byebug/dap/commands/continue.rb +18 -0
  12. data/lib/byebug/dap/commands/disconnect.rb +16 -0
  13. data/lib/byebug/dap/commands/evaluate.rb +27 -0
  14. data/lib/byebug/dap/commands/exception_info.rb +40 -0
  15. data/lib/byebug/dap/commands/initialize.rb +27 -0
  16. data/lib/byebug/dap/commands/launch.rb +13 -0
  17. data/lib/byebug/dap/commands/next.rb +20 -0
  18. data/lib/byebug/dap/commands/pause.rb +20 -0
  19. data/lib/byebug/dap/commands/scopes.rb +56 -0
  20. data/lib/byebug/dap/commands/set_breakpoints.rb +48 -0
  21. data/lib/byebug/dap/commands/set_exception_breakpoints.rb +28 -0
  22. data/lib/byebug/dap/commands/set_function_breakpoints.rb +96 -0
  23. data/lib/byebug/dap/commands/source.rb +12 -0
  24. data/lib/byebug/dap/commands/stack_trace.rb +50 -0
  25. data/lib/byebug/dap/commands/step_in.rb +24 -0
  26. data/lib/byebug/dap/commands/step_out.rb +21 -0
  27. data/lib/byebug/dap/commands/threads.rb +20 -0
  28. data/lib/byebug/dap/commands/variables.rb +33 -0
  29. data/lib/byebug/dap/contextual_command.rb +30 -0
  30. data/lib/byebug/dap/helpers/captured_io.rb +65 -0
  31. data/lib/byebug/dap/helpers/captured_output.rb +21 -0
  32. data/lib/byebug/dap/{channel.rb → helpers/channel.rb} +0 -0
  33. data/lib/byebug/dap/{child_spawned_event_body.rb → helpers/child_spawned_event_body.rb} +0 -0
  34. data/lib/byebug/dap/{handles.rb → helpers/handles.rb} +0 -0
  35. data/lib/byebug/dap/{invalid_request_argument_error.rb → helpers/invalid_request_argument_error.rb} +0 -0
  36. data/lib/byebug/dap/helpers/safe_helpers.rb +17 -0
  37. data/lib/byebug/dap/helpers/scalar.rb +19 -0
  38. data/lib/byebug/dap/helpers/stdio.rb +21 -0
  39. data/lib/byebug/dap/helpers/value_helpers.rb +60 -0
  40. data/lib/byebug/dap/server.rb +52 -36
  41. data/lib/byebug/dap/session.rb +176 -0
  42. data/lib/byebug/gem.rb +11 -0
  43. metadata +38 -10
  44. data/lib/byebug/dap/controller.rb +0 -252
  45. data/lib/byebug/dap/interface.rb +0 -303
  46. data/lib/byebug/dap/safe_helpers.rb +0 -55
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.2
4
+ version: 0.1.3
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-09 00:00:00.000000000 Z
11
+ date: 2020-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: byebug
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: 0.1.2
41
41
  description: Implements a Debug Adapter Protocol interface for Byebug
42
- email: ethan.reesor@gmail.com
42
+ email:
43
43
  executables:
44
44
  - byebug-dap
45
45
  extensions: []
@@ -51,15 +51,43 @@ files:
51
51
  - README.md
52
52
  - bin/byebug-dap
53
53
  - lib/byebug/dap.rb
54
- - lib/byebug/dap/channel.rb
55
- - lib/byebug/dap/child_spawned_event_body.rb
54
+ - lib/byebug/dap/command.rb
56
55
  - lib/byebug/dap/command_processor.rb
57
- - lib/byebug/dap/controller.rb
58
- - lib/byebug/dap/handles.rb
59
- - lib/byebug/dap/interface.rb
60
- - lib/byebug/dap/invalid_request_argument_error.rb
61
- - lib/byebug/dap/safe_helpers.rb
56
+ - lib/byebug/dap/commands/attach.rb
57
+ - lib/byebug/dap/commands/breakpoint_locations.rb
58
+ - lib/byebug/dap/commands/configuration_done.rb
59
+ - lib/byebug/dap/commands/continue.rb
60
+ - lib/byebug/dap/commands/disconnect.rb
61
+ - lib/byebug/dap/commands/evaluate.rb
62
+ - lib/byebug/dap/commands/exception_info.rb
63
+ - lib/byebug/dap/commands/initialize.rb
64
+ - lib/byebug/dap/commands/launch.rb
65
+ - lib/byebug/dap/commands/next.rb
66
+ - lib/byebug/dap/commands/pause.rb
67
+ - lib/byebug/dap/commands/scopes.rb
68
+ - lib/byebug/dap/commands/set_breakpoints.rb
69
+ - lib/byebug/dap/commands/set_exception_breakpoints.rb
70
+ - lib/byebug/dap/commands/set_function_breakpoints.rb
71
+ - lib/byebug/dap/commands/source.rb
72
+ - lib/byebug/dap/commands/stack_trace.rb
73
+ - lib/byebug/dap/commands/step_in.rb
74
+ - lib/byebug/dap/commands/step_out.rb
75
+ - lib/byebug/dap/commands/threads.rb
76
+ - lib/byebug/dap/commands/variables.rb
77
+ - lib/byebug/dap/contextual_command.rb
78
+ - lib/byebug/dap/helpers/captured_io.rb
79
+ - lib/byebug/dap/helpers/captured_output.rb
80
+ - lib/byebug/dap/helpers/channel.rb
81
+ - lib/byebug/dap/helpers/child_spawned_event_body.rb
82
+ - lib/byebug/dap/helpers/handles.rb
83
+ - lib/byebug/dap/helpers/invalid_request_argument_error.rb
84
+ - lib/byebug/dap/helpers/safe_helpers.rb
85
+ - lib/byebug/dap/helpers/scalar.rb
86
+ - lib/byebug/dap/helpers/stdio.rb
87
+ - lib/byebug/dap/helpers/value_helpers.rb
62
88
  - lib/byebug/dap/server.rb
89
+ - lib/byebug/dap/session.rb
90
+ - lib/byebug/gem.rb
63
91
  homepage: https://gitlab.com/firelizzard/byebug-dap
64
92
  licenses:
65
93
  - Apache-2.0
@@ -1,252 +0,0 @@
1
- module Byebug
2
- module DAP
3
- class Controller
4
- def initialize(interface, &block)
5
- @interface = interface
6
- @on_configured = block
7
-
8
- @trace = TracePoint.new(:thread_begin, :thread_end) { |t| process_trace t }
9
- end
10
-
11
- def run
12
- loop do
13
- @request = @interface.receive
14
- result = process_command @request
15
- return if result == :stop
16
- end
17
-
18
- rescue IOError, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNABORTED
19
- STDERR.puts "\nClient disconnected"
20
-
21
- ensure
22
- exit if @exit_on_disconnect
23
-
24
- @interface.stop!
25
- @trace.disable
26
- end
27
-
28
- private
29
-
30
- def process_trace(trace)
31
- return unless Byebug.started?
32
-
33
- ctx = Byebug.contexts.find { |c| c.thread == Thread.current }
34
-
35
- case trace.event
36
- when :thread_begin
37
- @interface.event! 'thread', reason: 'started', threadId: ctx.thnum
38
- when :thread_end
39
- @interface.event! 'thread', reason: 'exited', threadId: ctx.thnum
40
- end
41
-
42
- rescue IOError, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNABORTED
43
- # client disconnected, ignore error
44
- end
45
-
46
- def process_command(request)
47
- case request.command
48
- when 'attach', 'launch'
49
- if Byebug.started?
50
- respond! success: false, message: "Cannot #{request.command} - debugger is already running"
51
- return
52
- end
53
- when 'pause', 'next', 'stepIn', 'stepOut', 'continue', 'evaluate', 'variables', 'scopes', 'threads', 'stackTrace'
54
- unless Byebug.started?
55
- respond! success: false, message: "Cannot #{request.command} - debugger is not running"
56
- return
57
- end
58
- end
59
-
60
- case request.command
61
- when 'initialize'
62
- # "The ‘initialize’ request is sent as the first request from the client to the debug adapter
63
- # "in order to configure it with client capabilities and to retrieve capabilities from the debug adapter.
64
- # "Until the debug adapter has responded to with an ‘initialize’ response, the client must not send any additional requests or events to the debug adapter.
65
- # "In addition the debug adapter is not allowed to send any requests or events to the client until it has responded with an ‘initialize’ response.
66
- # "The ‘initialize’ request may only be sent once.
67
-
68
- respond! body: ::DAP::Capabilities.new(
69
- supportsConfigurationDoneRequest: true)
70
-
71
- @interface.event! 'initialized'
72
- return
73
-
74
- when 'disconnect'
75
- # "The ‘disconnect’ request is sent from the client to the debug adapter in order to stop debugging.
76
- # "It asks the debug adapter to disconnect from the debuggee and to terminate the debug adapter.
77
- # "If the debuggee has been started with the ‘launch’ request, the ‘disconnect’ request terminates the debuggee.
78
- # "If the ‘attach’ request was used to connect to the debuggee, ‘disconnect’ does not terminate the debuggee.
79
- # "This behavior can be controlled with the ‘terminateDebuggee’ argument (if supported by the debug adapter).
80
-
81
- respond!
82
- return :stop
83
-
84
- when 'attach'
85
- # "The attach request is sent from the client to the debug adapter to attach to a debuggee that is already running.
86
-
87
- Byebug.mode = :attached
88
- Byebug.start
89
- @trace.enable
90
-
91
- respond!
92
- return
93
-
94
- when 'launch'
95
- # "This launch request is sent from the client to the debug adapter to start the debuggee with or without debugging (if ‘noDebug’ is true).
96
-
97
- unless request.arguments.noDebug
98
- Byebug.mode = :launched
99
- Byebug.start
100
- @trace.enable
101
- end
102
-
103
- @exit_on_disconnect = true
104
-
105
- respond!
106
- return
107
-
108
- when 'configurationDone'
109
- # "This optional request indicates that the client has finished initialization of the debug adapter.
110
-
111
-
112
- @on_configured&.call
113
- respond!
114
- return
115
-
116
- when 'pause', 'next', 'stepIn', 'stepOut', 'continue'
117
- ctx = @interface.find_thread(request.arguments.threadId)
118
- ctx.interrupt if request.command == 'pause'
119
-
120
- ctx.__send__(:processor) << request
121
- respond!
122
-
123
- when 'evaluate'
124
- # "Evaluates the given expression in the context of the top most stack frame.
125
- # "The expression has access to any variables and arguments that are in scope.
126
-
127
- respond! body: @interface.evaluate(request.arguments.frameId, request.arguments.expression)
128
-
129
- when 'variables'
130
- # "Retrieves all child variables for the given variable reference.
131
- # "An optional filter can be used to limit the fetched children to either named or indexed children
132
-
133
- variables = @interface.variables(
134
- request.arguments.variablesReference,
135
- at: request.arguments.start,
136
- count: request.arguments.count,
137
- filter: request.arguments.filter)
138
-
139
- respond! body: ::DAP::VariablesResponseBody.new(variables: variables)
140
-
141
- when 'scopes'
142
- # "The request returns the variable scopes for a given stackframe ID.
143
-
144
- respond! body: ::DAP::ScopesResponseBody.new(
145
- scopes: @interface.scopes(request.arguments.frameId))
146
-
147
- when 'threads'
148
- # "The request retrieves a list of all threads.
149
-
150
- respond! body: ::DAP::ThreadsResponseBody.new(threads: @interface.threads)
151
-
152
- when 'stackTrace'
153
- # "The request returns a stacktrace from the current execution state.
154
-
155
- frames, stack_size = @interface.frames(
156
- request.arguments.threadId,
157
- at: request.arguments.startFrame,
158
- count: request.arguments.levels)
159
-
160
- respond! body: ::DAP::StackTraceResponseBody.new(
161
- stackFrames: frames,
162
- totalFrames: stack_size)
163
-
164
- when 'source'
165
- # "The request retrieves the source code for a given source reference.
166
-
167
- path = request.arguments.source.path
168
- if File.readable?(path)
169
- respond! body: ::DAP::SourceResponseBody.new(content: IO.read(path))
170
-
171
- elsif File.exist?(path)
172
- respond! success: false, message: "Source file '#{path}' exists but cannot be read"
173
-
174
- else
175
- respond! success: false, message: "No source file available for '#{path}'"
176
- end
177
-
178
- when 'setBreakpoints'
179
- # "Sets multiple breakpoints for a single source and clears all previous breakpoints in that source.
180
- # "To clear all breakpoint for a source, specify an empty array.
181
- # "When a breakpoint is hit, a ‘stopped’ event (with reason ‘breakpoint’) is generated.
182
-
183
- unless File.exist?(request.arguments.source.path)
184
- # file doesn't exist, no breakpoints set
185
- respond! body: ::DAP::SetBreakpointsResponseBody.new(breakpoints: [])
186
- return
187
- end
188
-
189
- path = File.realpath(request.arguments.source.path)
190
- ::Byebug.breakpoints.each { |bp| ::Byebug::Breakpoint.remove(bp.id) if bp.source == path }
191
-
192
- lines = ::Byebug::Breakpoint.potential_lines(path)
193
- verified = []
194
- request.arguments.breakpoints.each do |requested|
195
- next unless lines.include? requested.line
196
-
197
- bp = ::Byebug::Breakpoint.add(path, requested.line)
198
- verified << ::DAP::Breakpoint.new(
199
- id: bp.id,
200
- verified: true,
201
- line: requested.line)
202
- end
203
-
204
- respond! body: ::DAP::SetBreakpointsResponseBody.new(breakpoints: verified)
205
-
206
- else
207
- respond! success: false, message: 'Invalid command'
208
- end
209
-
210
- rescue InvalidRequestArgumentError => e
211
- case e.error
212
- when :missing_argument
213
- respond! success: false, message: "Missing #{e.scope}"
214
-
215
- when :missing_entry
216
- respond! success: false, message: "Invalid #{e.scope} #{e.value}"
217
-
218
- when :missing_thread
219
- respond! success: false, message: "Cannot locate thread ##{e.value}"
220
-
221
- when :missing_frame
222
- respond! success: false, message: "Cannot locate frame ##{e.value}"
223
-
224
- when :invalid_entry
225
- respond! success: false, message: "Error resolving #{e.scope}: #{e.value}"
226
-
227
- else
228
- respond! success: false, message: "An internal error occured"
229
- STDERR.puts "#{e.message} (#{e.class})", *e.backtrace
230
- end
231
-
232
- rescue CommandProcessor::TimeoutError => e
233
- respond! success: false, message: "Debugger on thread ##{e.context.thnum} is not responding"
234
-
235
- rescue StandardError => e
236
- respond! success: false, message: "An internal error occured"
237
- STDERR.puts "#{e.message} (#{e.class})", *e.backtrace
238
- end
239
-
240
- def respond!(body = {}, success: true, message: 'Success', **values)
241
- # TODO make body default to nil?
242
- @interface << ::DAP::Response.new(
243
- request_seq: @request.seq,
244
- command: @request.command,
245
- success: success,
246
- message: message,
247
- body: body,
248
- **values)
249
- end
250
- end
251
- end
252
- end
@@ -1,303 +0,0 @@
1
- module Byebug
2
- module DAP
3
- class Interface
4
- include SafeHelpers
5
-
6
- @@children = []
7
-
8
- def self.child_spawned(name, pid, socket)
9
- child = ChildSpawnedEventBody.new(name: name, pid: pid, socket: socket)
10
- @@children << child
11
-
12
- interface = Context.interface
13
- interface.event! child if interface.is_a?(Byebug::DAP::Interface)
14
-
15
- return true
16
-
17
- rescue IOError, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNABORTED
18
- return false
19
- end
20
-
21
- attr_reader :socket
22
-
23
- def initialize(socket)
24
- @socket = socket
25
-
26
- begin
27
- @@children.each { |c| event! c }
28
- rescue IOError, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNABORTED
29
- end
30
- end
31
-
32
- def stop!
33
- Byebug.mode = :off
34
- Byebug.stop
35
- socket.close
36
- end
37
-
38
- def <<(message)
39
- STDERR.puts "#{Process.pid} > #{message.to_wire}" if Debug.protocol
40
- message.validate!
41
- socket.write ::DAP::Encoding.encode(message)
42
- end
43
-
44
- def event!(event, **values)
45
- if (cls = event.class.name.split('::').last) && cls.end_with?('EventBody')
46
- body, event = event, cls[0].downcase + cls[1...-9]
47
-
48
- elsif event.is_a?(String) && !values.empty?
49
- body = ::DAP.const_get("#{event[0].upcase}#{event[1..]}EventBody").new(values)
50
- end
51
-
52
- self << ::DAP::Event.new(event: event, body: body)
53
- end
54
-
55
- def receive
56
- m = ::DAP::Encoding.decode(socket)
57
- STDERR.puts "#{Process.pid} < #{m.to_wire}" if Debug.protocol
58
- m
59
- end
60
-
61
- def invalidate_handles!
62
- frame_ids.clear!
63
- variable_refs.clear!
64
- end
65
-
66
- def threads
67
- Byebug.contexts
68
- .filter { |ctx| !ctx.thread.is_a?(DebugThread) }
69
- .map { |ctx| ::DAP::Thread.new(
70
- id: ctx.thnum,
71
- name: ctx.thread.name || "Thread ##{ctx.thnum}")
72
- .validate! }
73
- end
74
-
75
- def find_thread(id)
76
- raise InvalidRequestArgumentError.new(:missing_argument, scope: 'thread ID') unless id
77
-
78
- ctx = Byebug.contexts.find { |c| c.thnum == id }
79
- raise InvalidRequestArgumentError.new(:missing_thread, value: id) unless ctx
80
-
81
- ctx
82
- end
83
-
84
- def resolve_frame_id(id)
85
- entry = frame_ids[id]
86
- raise InvalidRequestArgumentError.new(:missing_entry, value: id, scope: 'frame ID') unless entry
87
-
88
- thnum, frnum = entry
89
- ctx = Byebug.contexts.find { |c| c.thnum == thnum }
90
- raise InvalidRequestArgumentError.new(:missing_thread, value: thnum) unless ctx
91
- raise InvalidRequestArgumentError.new(:missing_frame, value: frnum) unless frnum < ctx.stack_size
92
-
93
- return ::Byebug::Frame.new(ctx, frnum), thnum, frnum
94
- end
95
-
96
- def frames(thnum, at:, count:)
97
- ctx = find_thread(thnum)
98
-
99
- first = at || 0
100
- if !count
101
- last = ctx.stack_size
102
- else
103
- last = first + count
104
- if last > ctx.stack_size
105
- last = ctx.stack_size
106
- end
107
- end
108
-
109
- frames = (first...last).map do |i|
110
- frame = ::Byebug::Frame.new(ctx, i)
111
- ::DAP::StackFrame.new(
112
- id: frame_ids << [ctx.thnum, i],
113
- name: frame_name(frame),
114
- source: ::DAP::Source.new(
115
- name: File.basename(frame.file),
116
- path: File.expand_path(frame.file)),
117
- line: frame.line,
118
- column: 0) # TODO real column
119
- .validate!
120
- end
121
-
122
- return frames, ctx.stack_size
123
- end
124
-
125
- def resolve_variable_reference(varRef)
126
- raise InvalidRequestArgumentError.new(:missing_argument, scope: 'variables reference') unless varRef
127
-
128
- entry = variable_refs[varRef]
129
- raise InvalidRequestArgumentError.new(:missing_entry, value: ref, scope: 'variables reference') unless entry
130
-
131
- entry
132
- end
133
-
134
- def scopes(frameId)
135
- raise InvalidRequestArgumentError.new(:missing_argument, scope: 'frame ID') unless frameId
136
-
137
- frame, thnum, frnum = resolve_frame_id(frameId)
138
- return unless frame
139
-
140
- scopes = []
141
-
142
- locals = frame_local_names(frame).sort
143
- unless locals.empty?
144
- scopes << ::DAP::Scope.new(
145
- name: 'Locals',
146
- presentationHint: 'locals',
147
- variablesReference: variable_refs << [thnum, frnum, :locals, locals],
148
- namedVariables: locals.size,
149
- indexedVariables: 0,
150
- expensive: false)
151
- .validate!
152
- end
153
-
154
- globals = global_names.sort
155
- unless globals.empty?
156
- scopes << ::DAP::Scope.new(
157
- name: 'Globals',
158
- presentationHint: 'globals',
159
- variablesReference: variable_refs << [thnum, frnum, :globals, globals],
160
- namedVariables: globals.size,
161
- indexedVariables: 0,
162
- expensive: true)
163
- .validate!
164
- end
165
-
166
- scopes
167
- end
168
-
169
- def variables(varRef, at:, count:, filter: nil)
170
- thnum, frnum, kind, *entry = resolve_variable_reference(varRef)
171
-
172
- case kind
173
- when :locals, :globals
174
- ctx = find_thread(thnum)
175
- raise InvalidRequestArgumentError.new(:missing_frame, value: frnum) unless frnum < ctx.stack_size
176
-
177
- frame = ::Byebug::Frame.new(ctx, frnum)
178
- end
179
-
180
- case kind
181
- when :locals
182
- named, indexed = entry[0], []
183
- get = ->(key) {
184
- return frame._self if key == :self
185
- values ||= frame.locals
186
- values[key]
187
- }
188
-
189
- when :globals
190
- named, indexed = entry[0], []
191
- get = ->(key) { frame._binding.eval(key.to_s) }
192
-
193
- when :variable, :evalate
194
- value, named, indexed = entry
195
- get = ->(key) { value.instance_eval { binding }.eval(key.to_s) }
196
- index = ->(key) { value[key] }
197
-
198
- else
199
- raise InvalidRequestArgumentError.new(:invalid_entry, value: kind, scope: 'variable scope')
200
- end
201
-
202
- case filter
203
- when 'named'
204
- indexed = []
205
- when 'indexed'
206
- named = []
207
- end
208
-
209
- vars = named.map { |k| [k, get] } + indexed.map { |k| [k, index] }
210
-
211
- first = at || 0
212
- last = count ? first + count : vars.size
213
- last = vars.size unless last < vars.size
214
-
215
- vars[first...last].map { |var, get| prepare_value_response(thnum, frnum, :variable, name: var) { get.call(var) } }
216
- end
217
-
218
- def evaluate(frameId, expression)
219
- return prepare_value_response(0, 0, :evaluate) { TOPLEVEL_BINDING.eval(expression) } unless frameId
220
-
221
- frame, thnum, frnum = resolve_frame_id(frameId)
222
- return unless frame
223
-
224
- prepare_value_response(thnum, frnum, :evaluate) { frame._binding.eval(expression) }
225
- end
226
-
227
- private
228
-
229
- def frame_name(frame)
230
- frame.deco_call
231
- rescue
232
- frame.deco_block + frame.deco_class + frame.deco_method + "(?)"
233
- end
234
-
235
- def describe_thread(context)
236
- if context.thread.name
237
- "##{context.thnum} (#{context.thread.name})"
238
- else
239
- "##{context.thnum}"
240
- end
241
- end
242
-
243
- def prepare_value_response(thnum, frnum, kind, name: nil, &block)
244
- err = nil
245
- if thnum == 0
246
- raw = safe(block, :call) { |e| err = e; nil }
247
- else
248
- processor = find_thread(thnum).__send__(:processor)
249
- raw = safe(-> { processor.execute(&block) }, :call) { |e| err = e; nil }
250
- end
251
-
252
- if err.nil?
253
- value, type, named, indexed = prepare_value(raw) { next "*Error in evaluation*", nil, [], [] }
254
- else
255
- type, named, indexed = nil, [], []
256
- if err.is_a?(CommandProcessor::TimeoutError)
257
- value = "*Thread #{describe_thread err.context} unresponsive*"
258
- else
259
- value = "*Error in evaluation*"
260
- end
261
- end
262
-
263
- case kind
264
- when :variable
265
- klazz = ::DAP::Variable
266
- args = { name: safe(name, :to_s) { safe(name, :inspect) { '???' } }, value: value, type: type }
267
- when :evaluate
268
- klazz = ::DAP::EvaluateResponseBody
269
- args = { result: value, type: type }
270
- end
271
-
272
- if named.empty? && indexed.empty?
273
- args[:variablesReference] = 0
274
- else
275
- args[:variablesReference] = variable_refs << [thnum, frnum, kind, raw, named, indexed]
276
- args[:namedVariables] = named.size
277
- args[:indexedVariables] = indexed.size
278
- end
279
-
280
- klazz.new(args).validate!
281
- end
282
-
283
- def frame_ids
284
- @frame_ids ||= Handles.new
285
- end
286
-
287
- def variable_refs
288
- @variable_refs ||= Handles.new
289
- end
290
-
291
- def frame_local_names(frame)
292
- locals = frame.locals
293
- locals = locals.keys unless locals == [] # BUG in Byebug?
294
- locals << :self if frame._self.to_s != 'main'
295
- locals
296
- end
297
-
298
- def global_names
299
- global_variables - %i[$IGNORECASE $= $KCODE $-K $binding]
300
- end
301
- end
302
- end
303
- end