readapt 0.3.1

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 (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +13 -0
  5. data/CHANGELOG.md +15 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +29 -0
  9. data/Rakefile +25 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/exe/readapt +5 -0
  13. data/ext/readapt/extconf.rb +3 -0
  14. data/ext/readapt/monitor.c +332 -0
  15. data/ext/readapt/monitor.h +1 -0
  16. data/ext/readapt/readapt.c +12 -0
  17. data/ext/readapt/threads.c +101 -0
  18. data/ext/readapt/threads.h +18 -0
  19. data/lib/readapt.rb +34 -0
  20. data/lib/readapt/adapter.rb +138 -0
  21. data/lib/readapt/breakpoint.rb +16 -0
  22. data/lib/readapt/breakpoints.rb +58 -0
  23. data/lib/readapt/debugger.rb +173 -0
  24. data/lib/readapt/finder.rb +20 -0
  25. data/lib/readapt/frame.rb +68 -0
  26. data/lib/readapt/location.rb +25 -0
  27. data/lib/readapt/message.rb +57 -0
  28. data/lib/readapt/message/attach.rb +11 -0
  29. data/lib/readapt/message/base.rb +32 -0
  30. data/lib/readapt/message/configuration_done.rb +11 -0
  31. data/lib/readapt/message/continue.rb +15 -0
  32. data/lib/readapt/message/disconnect.rb +14 -0
  33. data/lib/readapt/message/initialize.rb +13 -0
  34. data/lib/readapt/message/launch.rb +11 -0
  35. data/lib/readapt/message/next.rb +12 -0
  36. data/lib/readapt/message/pause.rb +11 -0
  37. data/lib/readapt/message/scopes.rb +25 -0
  38. data/lib/readapt/message/set_breakpoints.rb +26 -0
  39. data/lib/readapt/message/set_exception_breakpoints.rb +8 -0
  40. data/lib/readapt/message/stack_trace.rb +26 -0
  41. data/lib/readapt/message/step_in.rb +11 -0
  42. data/lib/readapt/message/step_out.rb +11 -0
  43. data/lib/readapt/message/threads.rb +18 -0
  44. data/lib/readapt/message/variables.rb +57 -0
  45. data/lib/readapt/monitor.rb +31 -0
  46. data/lib/readapt/shell.rb +48 -0
  47. data/lib/readapt/snapshot.rb +50 -0
  48. data/lib/readapt/thread.rb +39 -0
  49. data/lib/readapt/variable.rb +70 -0
  50. data/lib/readapt/version.rb +3 -0
  51. data/readapt.gemspec +39 -0
  52. metadata +184 -0
@@ -0,0 +1,20 @@
1
+ require 'pathname'
2
+
3
+ module Readapt
4
+ module Finder
5
+ module_function
6
+
7
+ def find program
8
+ return program if File.exist?(program)
9
+ which(program) || raise(LoadError, "#{program} is not a valid Ruby file or program")
10
+ end
11
+
12
+ def which program
13
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
14
+ exe = File.join(path, program)
15
+ return exe if File.file?(exe)
16
+ end
17
+ nil
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ class Frame
5
+ attr_reader :location
6
+
7
+ def initialize location, binding_id
8
+ @location = location
9
+ @binding = ObjectSpace._id2ref(binding_id)
10
+ end
11
+
12
+ def local_id
13
+ @binding.object_id
14
+ end
15
+
16
+ def locals
17
+ return [] if @binding.nil?
18
+ result = []
19
+ @binding.local_variables.each do |sym|
20
+ var = @binding.local_variable_get(sym)
21
+ result.push Variable.new(sym, var)
22
+ end
23
+ if @binding.receiver != TOPLEVEL_BINDING.receiver
24
+ result.push Variable.new(:self, @binding.receiver)
25
+ end
26
+ result
27
+ end
28
+
29
+ def variables
30
+ return [] if @binding.nil?
31
+ result = []
32
+ @binding.local_variables.each do |sym|
33
+ var = @binding.local_variable_get(sym)
34
+ result.push Variable.new(sym, var)
35
+ end
36
+ if @binding.receiver != TOPLEVEL_BINDING.receiver
37
+ @binding.instance_variables.each do |sym|
38
+ var = @binding.receiver.instance_variable_get(sym)
39
+ result.push Variable.new(sym, var)
40
+ end
41
+ @binding.class.class_variables.each do |sym|
42
+ var = @binding.receiver.class.class_variable_get(sym)
43
+ result.push Variable.new(sym, var)
44
+ end
45
+ end
46
+ result
47
+ end
48
+
49
+ def local sym
50
+ return @binding.receiver if sym == :self
51
+ @binding.local_variable_get sym
52
+ end
53
+
54
+ def globals
55
+ global_variables
56
+ end
57
+
58
+ def global sym
59
+ global sym
60
+ end
61
+
62
+ def evaluate code
63
+ @binding.eval code
64
+ end
65
+
66
+ NULL_FRAME = Frame.new(nil, nil.object_id)
67
+ end
68
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ # A simple file/line reference.
5
+ #
6
+ class Location
7
+ # @return [String]
8
+ attr_reader :file
9
+
10
+ # @return [Integer]
11
+ attr_reader :line
12
+
13
+ # @param file [String]
14
+ # @param line [Integer]
15
+ def initialize file, line
16
+ @file = file
17
+ @line = line
18
+ end
19
+
20
+ def match? other
21
+ return false unless other.is_a?(Location)
22
+ file == other.file && line == other.line
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'readapt/message/base'
4
+ require 'readapt/message/initialize'
5
+ require 'readapt/message/launch'
6
+ require 'readapt/message/set_breakpoints'
7
+ require 'readapt/message/set_exception_breakpoints'
8
+ require 'readapt/message/configuration_done'
9
+ require 'readapt/message/threads'
10
+ require 'readapt/message/stack_trace'
11
+ require 'readapt/message/scopes'
12
+ require 'readapt/message/continue'
13
+ require 'readapt/message/variables'
14
+ require 'readapt/message/next'
15
+ require 'readapt/message/step_in'
16
+ require 'readapt/message/step_out'
17
+ require 'readapt/message/disconnect'
18
+ require 'readapt/message/attach'
19
+ require 'readapt/message/pause'
20
+
21
+ module Readapt
22
+ module Message
23
+ @@messages = {}
24
+ @@seq = 0
25
+
26
+ def self.register name, klass
27
+ @@messages[name] = klass
28
+ end
29
+
30
+ def self.process arguments, debugger
31
+ message = @@messages[arguments['command']].new(arguments['arguments'], debugger)
32
+ message.run
33
+ message
34
+ rescue Exception => e
35
+ STDERR.puts "Skipping error: #{e.message}"
36
+ STDERR.puts "Received: #{arguments}"
37
+ STDERR.puts e.backtrace
38
+ end
39
+
40
+ register 'initialize', Message::Initialize
41
+ register 'attach', Message::Attach
42
+ register 'launch', Message::Launch
43
+ register 'setBreakpoints', Message::SetBreakpoints
44
+ register 'setExceptionBreakpoints', Message::SetExceptionBreakpoints
45
+ register 'configurationDone', Message::ConfigurationDone
46
+ register 'threads', Message::Threads
47
+ register 'stackTrace', Message::StackTrace
48
+ register 'scopes', Message::Scopes
49
+ register 'continue', Message::Continue
50
+ register 'variables', Message::Variables
51
+ register 'next', Message::Next
52
+ register 'stepIn', Message::StepIn
53
+ register 'stepOut', Message::StepOut
54
+ register 'disconnect', Message::Disconnect
55
+ register 'pause', Message::Pause
56
+ end
57
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Attach < Base
6
+ def run
7
+ debugger.config arguments, :attach
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Base
6
+ # @return [Hash]
7
+ attr_reader :arguments
8
+
9
+ # @return [Debugger]
10
+ attr_reader :debugger
11
+
12
+ def initialize arguments, debugger
13
+ @arguments = arguments
14
+ @debugger = debugger
15
+ end
16
+
17
+ def run; end
18
+
19
+ def body
20
+ @body ||= {}
21
+ end
22
+
23
+ def set_body hash
24
+ @body = hash
25
+ end
26
+
27
+ def self.run arguments, debugger
28
+ new(arguments, debugger).run
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class ConfigurationDone < Base
6
+ def run
7
+ debugger.start
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Continue < Base
6
+ def run
7
+ thread = debugger.thread(arguments['threadId'])
8
+ thread.control = :continue
9
+ set_body({
10
+ allThreadsContinued: false
11
+ })
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Disconnect < Base
6
+ def run
7
+ # HACK: Wait a moment to make sure the output is flushed
8
+ # @todo Find a better way
9
+ sleep 1
10
+ debugger.disconnect
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Initialize < Base
6
+ def run
7
+ set_body({
8
+ supportsConfigurationDoneRequest: true
9
+ })
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Launch < Base
6
+ def run
7
+ debugger.config arguments, :launch
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Next < Base
6
+ def run
7
+ thread = debugger.thread(arguments['threadId'])
8
+ thread.control = :next
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Pause < Base
6
+ def run
7
+ Monitor.pause arguments['threadId']
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Scopes < Base
6
+ def run
7
+ frame = debugger.frame(arguments['frameId'])
8
+ set_body({
9
+ scopes: [
10
+ {
11
+ name: 'Local',
12
+ variablesReference: frame.local_id,
13
+ expensive: false
14
+ },
15
+ {
16
+ name: 'Global',
17
+ variablesReference: TOPLEVEL_BINDING.receiver.object_id,
18
+ expensive: true
19
+ }
20
+ ]
21
+ })
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class SetBreakpoints < Base
6
+ def run
7
+ path = Readapt.normalize_path(arguments['source']['path'])
8
+ Monitor.breakpoints.clear path
9
+ result = arguments['lines'].map do |l|
10
+ # @todo Assuming breakpoints are verified
11
+ Breakpoint.new(path, l, true)
12
+ end
13
+ Monitor.breakpoints.concat result
14
+ set_body({
15
+ breakpoints: result.map { |bp|
16
+ {
17
+ verified: bp.verified,
18
+ source: arguments['source'],
19
+ line: bp.line
20
+ }
21
+ }
22
+ })
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class SetExceptionBreakpoints < Base
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class StackTrace < Base
6
+ def run
7
+ frames = debugger.thread(arguments['threadId']).frames
8
+ set_body({
9
+ stackFrames: frames.map do |frm|
10
+ {
11
+ name: "#{File.basename(frm.location.file)}:#{frm.location.line}",
12
+ source: {
13
+ name: File.basename(frm.location.file),
14
+ path: frm.location.file
15
+ },
16
+ id: frm.local_id,
17
+ line: frm.location.line,
18
+ column: 0
19
+ }
20
+ end,
21
+ totalFrames: frames.length
22
+ })
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class StepIn < Base
6
+ def run
7
+ debugger.thread(arguments['threadId']).control = :step_in
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class StepOut < Base
6
+ def run
7
+ debugger.thread(arguments['threadId']).control = :step_out
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Readapt
4
+ module Message
5
+ class Threads < Base
6
+ def run
7
+ set_body({
8
+ threads: debugger.threads.map do |thr|
9
+ {
10
+ id: thr.id,
11
+ name: thr.name
12
+ }
13
+ end
14
+ })
15
+ end
16
+ end
17
+ end
18
+ end