debug 1.6.3 → 1.7.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 +4 -4
- data/CONTRIBUTING.md +19 -7
- data/Gemfile +0 -0
- data/LICENSE.txt +0 -0
- data/README.md +37 -14
- data/Rakefile +0 -0
- data/TODO.md +0 -0
- data/debug.gemspec +1 -1
- data/exe/rdbg +1 -1
- data/ext/debug/debug.c +15 -1
- data/ext/debug/extconf.rb +0 -0
- data/ext/debug/iseq_collector.c +0 -0
- data/lib/debug/abbrev_command.rb +77 -0
- data/lib/debug/breakpoint.rb +17 -11
- data/lib/debug/client.rb +26 -9
- data/lib/debug/color.rb +0 -0
- data/lib/debug/config.rb +34 -16
- data/lib/debug/console.rb +0 -0
- data/lib/debug/frame_info.rb +0 -0
- data/lib/debug/local.rb +16 -10
- data/lib/debug/open.rb +0 -0
- data/lib/debug/open_nonstop.rb +0 -0
- data/lib/debug/prelude.rb +0 -0
- data/lib/debug/server.rb +25 -21
- data/lib/debug/server_cdp.rb +281 -80
- data/lib/debug/server_dap.rb +109 -37
- data/lib/debug/session.rb +384 -204
- data/lib/debug/source_repository.rb +39 -19
- data/lib/debug/start.rb +1 -1
- data/lib/debug/thread_client.rb +186 -60
- data/lib/debug/tracer.rb +0 -0
- data/lib/debug/version.rb +1 -1
- data/lib/debug.rb +7 -3
- data/misc/README.md.erb +9 -3
- metadata +5 -4
data/lib/debug/prelude.rb
CHANGED
File without changes
|
data/lib/debug/server.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'socket'
|
4
|
-
require 'etc'
|
5
4
|
require_relative 'config'
|
6
5
|
require_relative 'version'
|
7
6
|
|
@@ -22,6 +21,7 @@ module DEBUGGER__
|
|
22
21
|
|
23
22
|
class Terminate < StandardError; end
|
24
23
|
class GreetingError < StandardError; end
|
24
|
+
class RetryConnection < StandardError; end
|
25
25
|
|
26
26
|
def deactivate
|
27
27
|
@reader_thread.raise Terminate
|
@@ -78,6 +78,8 @@ module DEBUGGER__
|
|
78
78
|
next
|
79
79
|
rescue Terminate
|
80
80
|
raise # should catch at outer scope
|
81
|
+
rescue RetryConnection
|
82
|
+
next
|
81
83
|
rescue => e
|
82
84
|
DEBUGGER__.warn "ReaderThreadError: #{e}"
|
83
85
|
pp e.backtrace
|
@@ -128,6 +130,8 @@ module DEBUGGER__
|
|
128
130
|
def greeting
|
129
131
|
case g = @sock.gets
|
130
132
|
when /^info cookie:\s+(.*)$/
|
133
|
+
require 'etc'
|
134
|
+
|
131
135
|
check_cookie $1
|
132
136
|
@sock.puts "PID: #{Process.pid}, $0: #{$0}"
|
133
137
|
@sock.puts "debug #{VERSION} on #{RUBY_DESCRIPTION}"
|
@@ -140,7 +144,8 @@ module DEBUGGER__
|
|
140
144
|
|
141
145
|
# TODO: protocol version
|
142
146
|
if v != VERSION
|
143
|
-
|
147
|
+
@sock.puts msg = "out DEBUGGER: Incompatible version (server:#{VERSION} and client:#{$1})"
|
148
|
+
raise GreetingError, msg
|
144
149
|
end
|
145
150
|
parse_option(params)
|
146
151
|
|
@@ -157,16 +162,13 @@ module DEBUGGER__
|
|
157
162
|
@need_pause_at_first = false
|
158
163
|
dap_setup @sock.read($1.to_i)
|
159
164
|
|
160
|
-
when /^GET
|
165
|
+
when /^GET\s\/json\sHTTP\/1.1/, /^GET\s\/json\/version\sHTTP\/1.1/, /^GET\s\/\w{8}-\w{4}-\w{4}-\w{4}-\w{12}\sHTTP\/1.1/
|
166
|
+
# The reason for not using @uuid here is @uuid is nil if users run debugger without `--open=chrome`.
|
167
|
+
|
161
168
|
require_relative 'server_cdp'
|
162
169
|
|
163
170
|
self.extend(UI_CDP)
|
164
|
-
|
165
|
-
@need_pause_at_first = false
|
166
|
-
CONFIG.set_config no_color: true
|
167
|
-
|
168
|
-
@ws_server = UI_CDP::WebSocketServer.new(@sock)
|
169
|
-
@ws_server.handshake
|
171
|
+
send_chrome_response g
|
170
172
|
else
|
171
173
|
raise GreetingError, "Unknown greeting message: #{g}"
|
172
174
|
end
|
@@ -174,17 +176,17 @@ module DEBUGGER__
|
|
174
176
|
|
175
177
|
def process
|
176
178
|
while true
|
177
|
-
DEBUGGER__.
|
178
|
-
|
179
|
-
DEBUGGER__.
|
179
|
+
DEBUGGER__.debug{ "sleep IO.select" }
|
180
|
+
_r = IO.select([@sock])
|
181
|
+
DEBUGGER__.debug{ "wakeup IO.select" }
|
180
182
|
|
181
183
|
line = @session.process_group.sync do
|
182
184
|
unless IO.select([@sock], nil, nil, 0)
|
183
|
-
DEBUGGER__.
|
185
|
+
DEBUGGER__.debug{ "UI_Server can not read" }
|
184
186
|
break :can_not_read
|
185
187
|
end
|
186
188
|
@sock.gets&.chomp.tap{|line|
|
187
|
-
DEBUGGER__.
|
189
|
+
DEBUGGER__.debug{ "UI_Server received: #{line}" }
|
188
190
|
}
|
189
191
|
end
|
190
192
|
|
@@ -340,12 +342,12 @@ module DEBUGGER__
|
|
340
342
|
if @repl
|
341
343
|
raise "not in subsession, but received: #{line.inspect}" unless @session.in_subsession?
|
342
344
|
line = "input #{Process.pid}"
|
343
|
-
DEBUGGER__.
|
345
|
+
DEBUGGER__.debug{ "send: #{line}" }
|
344
346
|
s.puts line
|
345
347
|
end
|
346
348
|
sleep 0.01 until @q_msg
|
347
349
|
@q_msg.pop.tap{|msg|
|
348
|
-
DEBUGGER__.
|
350
|
+
DEBUGGER__.debug{ "readline: #{msg.inspect}" }
|
349
351
|
}
|
350
352
|
end || 'continue')
|
351
353
|
|
@@ -361,7 +363,7 @@ module DEBUGGER__
|
|
361
363
|
Process.kill(TRAP_SIGNAL, Process.pid)
|
362
364
|
end
|
363
365
|
|
364
|
-
def quit n
|
366
|
+
def quit n, &_b
|
365
367
|
# ignore n
|
366
368
|
sock do |s|
|
367
369
|
s.puts "quit"
|
@@ -395,6 +397,7 @@ module DEBUGGER__
|
|
395
397
|
raise "Specify digits for port number"
|
396
398
|
end
|
397
399
|
end
|
400
|
+
@uuid = nil # for CDP
|
398
401
|
|
399
402
|
super()
|
400
403
|
end
|
@@ -402,11 +405,12 @@ module DEBUGGER__
|
|
402
405
|
def chrome_setup
|
403
406
|
require_relative 'server_cdp'
|
404
407
|
|
405
|
-
|
408
|
+
@uuid = SecureRandom.uuid
|
409
|
+
unless @chrome_pid = UI_CDP.setup_chrome(@local_addr.inspect_sockaddr, @uuid)
|
406
410
|
DEBUGGER__.warn <<~EOS
|
407
411
|
With Chrome browser, type the following URL in the address-bar:
|
408
412
|
|
409
|
-
devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{@local_addr.inspect_sockaddr}/#{
|
413
|
+
devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{@local_addr.inspect_sockaddr}/#{@uuid}
|
410
414
|
|
411
415
|
EOS
|
412
416
|
end
|
@@ -434,7 +438,7 @@ module DEBUGGER__
|
|
434
438
|
#
|
435
439
|
EOS
|
436
440
|
|
437
|
-
case CONFIG[:
|
441
|
+
case CONFIG[:open]
|
438
442
|
when 'chrome'
|
439
443
|
chrome_setup
|
440
444
|
when 'vscode'
|
@@ -491,7 +495,7 @@ module DEBUGGER__
|
|
491
495
|
end
|
492
496
|
|
493
497
|
::DEBUGGER__.warn "Debugger can attach via UNIX domain socket (#{@sock_path})"
|
494
|
-
vscode_setup @sock_path if CONFIG[:
|
498
|
+
vscode_setup @sock_path if CONFIG[:open] == 'vscode'
|
495
499
|
|
496
500
|
begin
|
497
501
|
Socket.unix_server_loop @sock_path do |sock, client|
|
data/lib/debug/server_cdp.rb
CHANGED
@@ -7,13 +7,18 @@ require 'securerandom'
|
|
7
7
|
require 'stringio'
|
8
8
|
require 'open3'
|
9
9
|
require 'tmpdir'
|
10
|
+
require 'tempfile'
|
11
|
+
require 'timeout'
|
10
12
|
|
11
13
|
module DEBUGGER__
|
12
14
|
module UI_CDP
|
13
15
|
SHOW_PROTOCOL = ENV['RUBY_DEBUG_CDP_SHOW_PROTOCOL'] == '1'
|
14
16
|
|
17
|
+
class UnsupportedError < StandardError; end
|
18
|
+
class NotFoundChromeEndpointError < StandardError; end
|
19
|
+
|
15
20
|
class << self
|
16
|
-
def setup_chrome addr
|
21
|
+
def setup_chrome addr, uuid
|
17
22
|
return if CONFIG[:chrome_path] == ''
|
18
23
|
|
19
24
|
port, path, pid = run_new_chrome
|
@@ -39,6 +44,8 @@ module DEBUGGER__
|
|
39
44
|
}
|
40
45
|
when res['id'] == 2
|
41
46
|
s_id = res.dig('result', 'sessionId')
|
47
|
+
# TODO: change id
|
48
|
+
ws_client.send sessionId: s_id, id: 100, method: 'Network.enable'
|
42
49
|
ws_client.send sessionId: s_id, id: 3,
|
43
50
|
method: 'Page.enable'
|
44
51
|
when res['id'] == 3
|
@@ -51,57 +58,191 @@ module DEBUGGER__
|
|
51
58
|
ws_client.send sessionId: s_id, id: 5,
|
52
59
|
method: 'Page.navigate',
|
53
60
|
params: {
|
54
|
-
url: "devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{addr}/#{
|
61
|
+
url: "devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{addr}/#{uuid}",
|
55
62
|
frameId: f_id
|
56
63
|
}
|
57
|
-
when res['method'] == '
|
64
|
+
when res['method'] == 'Network.webSocketWillSendHandshakeRequest'
|
65
|
+
s_id = res['sessionId']
|
66
|
+
# Display the console by entering ESC key
|
67
|
+
ws_client.send sessionId: s_id, id: 101, # TODO: change id
|
68
|
+
method:"Input.dispatchKeyEvent",
|
69
|
+
params: {
|
70
|
+
type:"keyDown",
|
71
|
+
windowsVirtualKeyCode:27 # ESC key
|
72
|
+
}
|
73
|
+
when res['id'] == 101
|
58
74
|
break
|
59
75
|
end
|
60
76
|
end
|
61
77
|
pid
|
62
|
-
rescue Errno::ENOENT
|
78
|
+
rescue Errno::ENOENT, UnsupportedError, NotFoundChromeEndpointError
|
63
79
|
nil
|
64
80
|
end
|
65
81
|
|
66
|
-
|
67
|
-
|
82
|
+
TIMEOUT_SEC = 5
|
83
|
+
|
84
|
+
def run_new_chrome
|
85
|
+
path = CONFIG[:chrome_path]
|
86
|
+
|
87
|
+
data = nil
|
88
|
+
port = nil
|
89
|
+
wait_thr = nil
|
68
90
|
|
69
91
|
# The process to check OS is based on `selenium` project.
|
70
92
|
case RbConfig::CONFIG['host_os']
|
71
93
|
when /mswin|msys|mingw|cygwin|emc/
|
72
|
-
|
94
|
+
if path.nil?
|
95
|
+
candidates = ['C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe', 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe']
|
96
|
+
path = get_chrome_path candidates
|
97
|
+
end
|
98
|
+
uuid = SecureRandom.uuid
|
99
|
+
# The path is based on https://github.com/sindresorhus/open/blob/v8.4.0/index.js#L128.
|
100
|
+
stdin, stdout, stderr, wait_thr = *Open3.popen3("#{ENV['SystemRoot']}\\System32\\WindowsPowerShell\\v1.0\\powershell")
|
101
|
+
tf = Tempfile.create(['debug-', '.txt'])
|
102
|
+
|
103
|
+
stdin.puts("Start-process '#{path}' -Argumentlist '--remote-debugging-port=0', '--no-first-run', '--no-default-browser-check', '--user-data-dir=C:\\temp' -Wait -RedirectStandardError #{tf.path}")
|
104
|
+
stdin.close
|
105
|
+
stdout.close
|
106
|
+
stderr.close
|
107
|
+
port, path = get_devtools_endpoint(tf.path)
|
108
|
+
|
109
|
+
at_exit{
|
110
|
+
DEBUGGER__.skip_all
|
111
|
+
|
112
|
+
stdin, stdout, stderr, wait_thr = *Open3.popen3("#{ENV['SystemRoot']}\\System32\\WindowsPowerShell\\v1.0\\powershell")
|
113
|
+
stdin.puts("Stop-process -Name chrome")
|
114
|
+
stdin.close
|
115
|
+
stdout.close
|
116
|
+
stderr.close
|
117
|
+
tf.close
|
118
|
+
begin
|
119
|
+
File.unlink(tf)
|
120
|
+
rescue Errno::EACCES
|
121
|
+
end
|
122
|
+
}
|
73
123
|
when /darwin|mac os/
|
74
|
-
'/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome'
|
124
|
+
path = path || '/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome'
|
125
|
+
dir = Dir.mktmpdir
|
126
|
+
# The command line flags are based on: https://developer.mozilla.org/en-US/docs/Tools/Remote_Debugging/Chrome_Desktop#connecting.
|
127
|
+
stdin, stdout, stderr, wait_thr = *Open3.popen3("#{path} --remote-debugging-port=0 --no-first-run --no-default-browser-check --user-data-dir=#{dir}")
|
128
|
+
stdin.close
|
129
|
+
stdout.close
|
130
|
+
data = stderr.readpartial 4096
|
131
|
+
stderr.close
|
132
|
+
if data.match /DevTools listening on ws:\/\/127.0.0.1:(\d+)(.*)/
|
133
|
+
port = $1
|
134
|
+
path = $2
|
135
|
+
end
|
136
|
+
|
137
|
+
at_exit{
|
138
|
+
DEBUGGER__.skip_all
|
139
|
+
FileUtils.rm_rf dir
|
140
|
+
}
|
75
141
|
when /linux/
|
76
|
-
'google-chrome'
|
142
|
+
path = path || 'google-chrome'
|
143
|
+
dir = Dir.mktmpdir
|
144
|
+
# The command line flags are based on: https://developer.mozilla.org/en-US/docs/Tools/Remote_Debugging/Chrome_Desktop#connecting.
|
145
|
+
stdin, stdout, stderr, wait_thr = *Open3.popen3("#{path} --remote-debugging-port=0 --no-first-run --no-default-browser-check --user-data-dir=#{dir}")
|
146
|
+
stdin.close
|
147
|
+
stdout.close
|
148
|
+
data = ''
|
149
|
+
begin
|
150
|
+
Timeout.timeout(TIMEOUT_SEC) do
|
151
|
+
until data.match?(/DevTools listening on ws:\/\/127.0.0.1:\d+.*/)
|
152
|
+
data = stderr.readpartial 4096
|
153
|
+
end
|
154
|
+
end
|
155
|
+
rescue Exception
|
156
|
+
raise NotFoundChromeEndpointError
|
157
|
+
end
|
158
|
+
stderr.close
|
159
|
+
if data.match /DevTools listening on ws:\/\/127.0.0.1:(\d+)(.*)/
|
160
|
+
port = $1
|
161
|
+
path = $2
|
162
|
+
end
|
163
|
+
|
164
|
+
at_exit{
|
165
|
+
DEBUGGER__.skip_all
|
166
|
+
FileUtils.rm_rf dir
|
167
|
+
}
|
77
168
|
else
|
78
|
-
raise
|
169
|
+
raise UnsupportedError
|
79
170
|
end
|
171
|
+
|
172
|
+
[port, path, wait_thr.pid]
|
80
173
|
end
|
81
174
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
175
|
+
def get_chrome_path candidates
|
176
|
+
candidates.each{|c|
|
177
|
+
if File.exist? c
|
178
|
+
return c
|
179
|
+
end
|
180
|
+
}
|
181
|
+
raise UnsupportedError
|
182
|
+
end
|
183
|
+
|
184
|
+
ITERATIONS = 50
|
185
|
+
|
186
|
+
def get_devtools_endpoint tf
|
187
|
+
i = 1
|
188
|
+
while i < ITERATIONS
|
189
|
+
i += 1
|
190
|
+
if File.exist?(tf) && data = File.read(tf)
|
191
|
+
if data.match /DevTools listening on ws:\/\/127.0.0.1:(\d+)(.*)/
|
192
|
+
port = $1
|
193
|
+
path = $2
|
194
|
+
return [port, path]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
sleep 0.1
|
93
198
|
end
|
94
|
-
|
199
|
+
raise NotFoundChromeEndpointError
|
200
|
+
end
|
201
|
+
end
|
95
202
|
|
96
|
-
|
97
|
-
|
98
|
-
|
203
|
+
def send_chrome_response req
|
204
|
+
@repl = false
|
205
|
+
case req
|
206
|
+
when /^GET\s\/json\/version\sHTTP\/1.1/
|
207
|
+
body = {
|
208
|
+
Browser: "ruby/v#{RUBY_VERSION}",
|
209
|
+
'Protocol-Version': "1.1"
|
99
210
|
}
|
100
|
-
|
101
|
-
|
211
|
+
send_http_res body
|
212
|
+
raise UI_ServerBase::RetryConnection
|
213
|
+
|
214
|
+
when /^GET\s\/json\sHTTP\/1.1/
|
215
|
+
@uuid = @uuid || SecureRandom.uuid
|
216
|
+
addr = @local_addr.inspect_sockaddr
|
217
|
+
body = [{
|
218
|
+
description: "ruby instance",
|
219
|
+
devtoolsFrontendUrl: "devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=#{addr}/#{@uuid}",
|
220
|
+
id: @uuid,
|
221
|
+
title: $0,
|
222
|
+
type: "node",
|
223
|
+
url: "file://#{File.absolute_path($0)}",
|
224
|
+
webSocketDebuggerUrl: "ws://#{addr}/#{@uuid}"
|
225
|
+
}]
|
226
|
+
send_http_res body
|
227
|
+
raise UI_ServerBase::RetryConnection
|
228
|
+
|
229
|
+
when /^GET\s\/(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})\sHTTP\/1.1/
|
230
|
+
raise 'Incorrect uuid' unless $1 == @uuid
|
231
|
+
|
232
|
+
@need_pause_at_first = false
|
233
|
+
CONFIG.set_config no_color: true
|
234
|
+
|
235
|
+
@ws_server = WebSocketServer.new(@sock)
|
236
|
+
@ws_server.handshake
|
102
237
|
end
|
103
238
|
end
|
104
239
|
|
240
|
+
def send_http_res body
|
241
|
+
json = JSON.generate body
|
242
|
+
header = "HTTP/1.0 200 OK\r\nContent-Type: application/json; charset=UTF-8\r\nCache-Control: no-cache\r\nContent-Length: #{json.bytesize}\r\n\r\n"
|
243
|
+
@sock.puts "#{header}#{json}"
|
244
|
+
end
|
245
|
+
|
105
246
|
module WebSocketUtils
|
106
247
|
class Frame
|
107
248
|
attr_reader :b
|
@@ -528,12 +669,6 @@ module DEBUGGER__
|
|
528
669
|
|
529
670
|
## Called by the SESSION thread
|
530
671
|
|
531
|
-
def readline prompt
|
532
|
-
return 'c' unless @q_msg
|
533
|
-
|
534
|
-
@q_msg.pop || 'kill!'
|
535
|
-
end
|
536
|
-
|
537
672
|
def respond req, **result
|
538
673
|
send_response req, **result
|
539
674
|
end
|
@@ -561,6 +696,28 @@ module DEBUGGER__
|
|
561
696
|
end
|
562
697
|
|
563
698
|
class Session
|
699
|
+
# FIXME: unify this method with ThreadClient#propertyDescriptor.
|
700
|
+
def get_type obj
|
701
|
+
case obj
|
702
|
+
when Array
|
703
|
+
['object', 'array']
|
704
|
+
when Hash
|
705
|
+
['object', 'map']
|
706
|
+
when String
|
707
|
+
['string']
|
708
|
+
when TrueClass, FalseClass
|
709
|
+
['boolean']
|
710
|
+
when Symbol
|
711
|
+
['symbol']
|
712
|
+
when Integer, Float
|
713
|
+
['number']
|
714
|
+
when Exception
|
715
|
+
['object', 'error']
|
716
|
+
else
|
717
|
+
['object']
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
564
721
|
def fail_response req, **result
|
565
722
|
@ui.respond_fail req, **result
|
566
723
|
return :retry
|
@@ -584,17 +741,38 @@ module DEBUGGER__
|
|
584
741
|
code: INVALID_PARAMS,
|
585
742
|
message: "'callFrameId' is an invalid"
|
586
743
|
end
|
587
|
-
when 'Runtime.getProperties'
|
588
|
-
oid = req.dig('params', 'objectId')
|
744
|
+
when 'Runtime.getProperties', 'Runtime.getExceptionDetails'
|
745
|
+
oid = req.dig('params', 'objectId') || req.dig('params', 'errorObjectId')
|
589
746
|
if ref = @obj_map[oid]
|
590
747
|
case ref[0]
|
591
748
|
when 'local'
|
592
749
|
frame_id = ref[1]
|
593
750
|
fid = @frame_map[frame_id]
|
594
751
|
request_tc [:cdp, :scope, req, fid]
|
752
|
+
when 'global'
|
753
|
+
vars = global_variables.sort.map do |name|
|
754
|
+
gv = eval(name.to_s)
|
755
|
+
prop = {
|
756
|
+
name: name,
|
757
|
+
value: {
|
758
|
+
description: gv.inspect
|
759
|
+
},
|
760
|
+
configurable: true,
|
761
|
+
enumerable: true
|
762
|
+
}
|
763
|
+
type, subtype = get_type(gv)
|
764
|
+
prop[:value][:type] = type
|
765
|
+
prop[:value][:subtype] = subtype if subtype
|
766
|
+
prop
|
767
|
+
end
|
768
|
+
|
769
|
+
@ui.respond req, result: vars
|
770
|
+
return :retry
|
595
771
|
when 'properties'
|
596
772
|
request_tc [:cdp, :properties, req, oid]
|
597
|
-
when '
|
773
|
+
when 'exception'
|
774
|
+
request_tc [:cdp, :exception, req, oid]
|
775
|
+
when 'script'
|
598
776
|
# TODO: Support script and global types
|
599
777
|
@ui.respond req, result: []
|
600
778
|
return :retry
|
@@ -730,6 +908,9 @@ module DEBUGGER__
|
|
730
908
|
frame[:scriptId] = s_id
|
731
909
|
end
|
732
910
|
}
|
911
|
+
if oid = exc[:exception][:objectId]
|
912
|
+
@obj_map[oid] = ['exception']
|
913
|
+
end
|
733
914
|
end
|
734
915
|
rs = result.dig(:response, :result)
|
735
916
|
[rs].each{|obj|
|
@@ -767,6 +948,8 @@ module DEBUGGER__
|
|
767
948
|
}
|
768
949
|
}
|
769
950
|
@ui.respond req, **result
|
951
|
+
when :exception
|
952
|
+
@ui.respond req, **result
|
770
953
|
end
|
771
954
|
end
|
772
955
|
end
|
@@ -872,8 +1055,8 @@ module DEBUGGER__
|
|
872
1055
|
result = b.local_variable_get(expr)
|
873
1056
|
rescue NameError
|
874
1057
|
# try to check method
|
875
|
-
if b.receiver
|
876
|
-
result = b.receiver
|
1058
|
+
if M_RESPOND_TO_P.bind_call(b.receiver, expr, include_all: true)
|
1059
|
+
result = M_METHOD.bind_call(b.receiver, expr)
|
877
1060
|
else
|
878
1061
|
message = "Error: Can not evaluate: #{expr.inspect}"
|
879
1062
|
end
|
@@ -886,35 +1069,7 @@ module DEBUGGER__
|
|
886
1069
|
result = current_frame.binding.eval(expr.to_s, '(DEBUG CONSOLE)')
|
887
1070
|
rescue Exception => e
|
888
1071
|
result = e
|
889
|
-
|
890
|
-
frames = [
|
891
|
-
{
|
892
|
-
columnNumber: 0,
|
893
|
-
functionName: 'eval',
|
894
|
-
lineNumber: 0,
|
895
|
-
url: ''
|
896
|
-
}
|
897
|
-
]
|
898
|
-
e.backtrace_locations&.each do |loc|
|
899
|
-
break if loc.path == __FILE__
|
900
|
-
path = loc.absolute_path || loc.path
|
901
|
-
frames << {
|
902
|
-
columnNumber: 0,
|
903
|
-
functionName: loc.base_label,
|
904
|
-
lineNumber: loc.lineno - 1,
|
905
|
-
url: path
|
906
|
-
}
|
907
|
-
end
|
908
|
-
res[:exceptionDetails] = {
|
909
|
-
exceptionId: 1,
|
910
|
-
text: 'Uncaught',
|
911
|
-
lineNumber: 0,
|
912
|
-
columnNumber: 0,
|
913
|
-
exception: evaluate_result(result),
|
914
|
-
stackTrace: {
|
915
|
-
callFrames: frames
|
916
|
-
}
|
917
|
-
}
|
1072
|
+
res[:exceptionDetails] = exceptionDetails(e, 'Uncaught')
|
918
1073
|
ensure
|
919
1074
|
output = $stdout.string
|
920
1075
|
$stdout = orig_stdout
|
@@ -987,13 +1142,51 @@ module DEBUGGER__
|
|
987
1142
|
]
|
988
1143
|
end
|
989
1144
|
|
990
|
-
result += obj.
|
991
|
-
variable(iv,
|
1145
|
+
result += M_INSTANCE_VARIABLES.bind_call(obj).map{|iv|
|
1146
|
+
variable(iv, M_INSTANCE_VARIABLE_GET.bind_call(obj, iv))
|
992
1147
|
}
|
993
|
-
prop += [internalProperty('#class', obj
|
1148
|
+
prop += [internalProperty('#class', M_CLASS.bind_call(obj))]
|
994
1149
|
end
|
995
1150
|
event! :cdp_result, :properties, req, result: result, internalProperties: prop
|
1151
|
+
when :exception
|
1152
|
+
oid = args.shift
|
1153
|
+
exc = nil
|
1154
|
+
if obj = @obj_map[oid]
|
1155
|
+
exc = exceptionDetails obj, obj.to_s
|
1156
|
+
end
|
1157
|
+
event! :cdp_result, :exception, req, exceptionDetails: exc
|
1158
|
+
end
|
1159
|
+
end
|
1160
|
+
|
1161
|
+
def exceptionDetails exc, text
|
1162
|
+
frames = [
|
1163
|
+
{
|
1164
|
+
columnNumber: 0,
|
1165
|
+
functionName: 'eval',
|
1166
|
+
lineNumber: 0,
|
1167
|
+
url: ''
|
1168
|
+
}
|
1169
|
+
]
|
1170
|
+
exc.backtrace_locations&.each do |loc|
|
1171
|
+
break if loc.path == __FILE__
|
1172
|
+
path = loc.absolute_path || loc.path
|
1173
|
+
frames << {
|
1174
|
+
columnNumber: 0,
|
1175
|
+
functionName: loc.base_label,
|
1176
|
+
lineNumber: loc.lineno - 1,
|
1177
|
+
url: path
|
1178
|
+
}
|
996
1179
|
end
|
1180
|
+
{
|
1181
|
+
exceptionId: 1,
|
1182
|
+
text: text,
|
1183
|
+
lineNumber: 0,
|
1184
|
+
columnNumber: 0,
|
1185
|
+
exception: evaluate_result(exc),
|
1186
|
+
stackTrace: {
|
1187
|
+
callFrames: frames
|
1188
|
+
}
|
1189
|
+
}
|
997
1190
|
end
|
998
1191
|
|
999
1192
|
def search_const b, expr
|
@@ -1001,7 +1194,11 @@ module DEBUGGER__
|
|
1001
1194
|
[Object, *b.eval('::Module.nesting')].reverse_each{|mod|
|
1002
1195
|
if cs.all?{|c|
|
1003
1196
|
if mod.const_defined?(c)
|
1004
|
-
|
1197
|
+
begin
|
1198
|
+
mod = mod.const_get(c)
|
1199
|
+
rescue Exception
|
1200
|
+
false
|
1201
|
+
end
|
1005
1202
|
else
|
1006
1203
|
false
|
1007
1204
|
end
|
@@ -1045,25 +1242,29 @@ module DEBUGGER__
|
|
1045
1242
|
v = prop[:value]
|
1046
1243
|
v.delete :value
|
1047
1244
|
v[:subtype] = subtype if subtype
|
1048
|
-
v[:className] = obj.
|
1245
|
+
v[:className] = (klass = M_CLASS.bind_call(obj)).name || klass.to_s
|
1049
1246
|
end
|
1050
1247
|
prop
|
1051
1248
|
end
|
1052
1249
|
|
1053
1250
|
def preview_ value, hash, overflow
|
1251
|
+
# The reason for not using "map" method is to prevent the object overriding it from causing bugs.
|
1252
|
+
# https://github.com/ruby/debug/issues/781
|
1253
|
+
props = []
|
1254
|
+
hash.each{|k, v|
|
1255
|
+
pd = propertyDescriptor k, v
|
1256
|
+
props << {
|
1257
|
+
name: pd[:name],
|
1258
|
+
type: pd[:value][:type],
|
1259
|
+
value: pd[:value][:description]
|
1260
|
+
}
|
1261
|
+
}
|
1054
1262
|
{
|
1055
1263
|
type: value[:type],
|
1056
1264
|
subtype: value[:subtype],
|
1057
1265
|
description: value[:description],
|
1058
1266
|
overflow: overflow,
|
1059
|
-
properties:
|
1060
|
-
pd = propertyDescriptor k, v
|
1061
|
-
{
|
1062
|
-
name: pd[:name],
|
1063
|
-
type: pd[:value][:type],
|
1064
|
-
value: pd[:value][:description]
|
1065
|
-
}
|
1066
|
-
}
|
1267
|
+
properties: props
|
1067
1268
|
}
|
1068
1269
|
end
|
1069
1270
|
|