readapt 0.7.1 → 1.1.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +16 -14
  3. data/.rspec +2 -2
  4. data/.travis.yml +18 -13
  5. data/CHANGELOG.md +76 -53
  6. data/Gemfile +4 -4
  7. data/LICENSE.txt +21 -21
  8. data/README.md +37 -29
  9. data/Rakefile +14 -25
  10. data/bin/console +14 -14
  11. data/bin/setup +8 -8
  12. data/exe/readapt +5 -5
  13. data/ext/readapt/breakpoints.c +83 -88
  14. data/ext/readapt/breakpoints.h +11 -12
  15. data/ext/readapt/extconf.rb +0 -0
  16. data/ext/readapt/frame.c +137 -0
  17. data/ext/readapt/frame.h +17 -0
  18. data/ext/readapt/hash_table.c +211 -212
  19. data/ext/readapt/hash_table.h +30 -32
  20. data/ext/readapt/inspector.c +51 -0
  21. data/ext/readapt/inspector.h +8 -0
  22. data/ext/readapt/monitor.c +40 -27
  23. data/ext/readapt/monitor.h +0 -0
  24. data/ext/readapt/normalize.c +59 -53
  25. data/ext/readapt/normalize.h +7 -7
  26. data/ext/readapt/readapt.c +18 -16
  27. data/ext/readapt/stack.c +86 -0
  28. data/ext/readapt/stack.h +20 -0
  29. data/ext/readapt/threads.c +111 -17
  30. data/ext/readapt/threads.h +11 -4
  31. data/lib/readapt.rb +21 -19
  32. data/lib/readapt/adapter.rb +98 -138
  33. data/lib/readapt/breakpoint.rb +20 -13
  34. data/lib/readapt/data_reader.rb +62 -0
  35. data/lib/readapt/debugger.rb +220 -204
  36. data/lib/readapt/error.rb +63 -0
  37. data/lib/readapt/finder.rb +20 -20
  38. data/lib/readapt/frame.rb +40 -42
  39. data/lib/readapt/input.rb +7 -0
  40. data/lib/readapt/message.rb +62 -59
  41. data/lib/readapt/message/attach.rb +11 -11
  42. data/lib/readapt/message/base.rb +32 -32
  43. data/lib/readapt/message/configuration_done.rb +11 -11
  44. data/lib/readapt/message/continue.rb +15 -15
  45. data/lib/readapt/message/disconnect.rb +13 -14
  46. data/lib/readapt/message/evaluate.rb +18 -18
  47. data/lib/readapt/message/initialize.rb +13 -13
  48. data/lib/readapt/message/launch.rb +11 -11
  49. data/lib/readapt/message/next.rb +12 -12
  50. data/lib/readapt/message/pause.rb +11 -11
  51. data/lib/readapt/message/scopes.rb +26 -25
  52. data/lib/readapt/message/set_breakpoints.rb +25 -25
  53. data/lib/readapt/message/set_exception_breakpoints.rb +8 -8
  54. data/lib/readapt/message/stack_trace.rb +38 -26
  55. data/lib/readapt/message/step_in.rb +11 -11
  56. data/lib/readapt/message/step_out.rb +11 -11
  57. data/lib/readapt/message/threads.rb +18 -18
  58. data/lib/readapt/message/variables.rb +61 -57
  59. data/lib/readapt/monitor.rb +0 -0
  60. data/lib/readapt/output.rb +25 -0
  61. data/lib/readapt/references.rb +27 -0
  62. data/lib/readapt/server.rb +22 -0
  63. data/lib/readapt/shell.rb +104 -39
  64. data/lib/readapt/snapshot.rb +1 -13
  65. data/lib/readapt/thread.rb +23 -39
  66. data/lib/readapt/variable.rb +1 -1
  67. data/lib/readapt/version.rb +3 -3
  68. data/readapt.gemspec +39 -39
  69. metadata +19 -8
  70. data/lib/readapt/location.rb +0 -25
@@ -0,0 +1,20 @@
1
+ #ifndef STACK_H_
2
+ #define STACK_H_
3
+
4
+ #include "stddef.h"
5
+
6
+ typedef struct readapt_stack_struct {
7
+ int size;
8
+ size_t elem_size;
9
+ void (*free_func)(void *);
10
+ int capacity;
11
+ void **elements;
12
+ } readapt_stack_t;
13
+
14
+ readapt_stack_t *stack_alloc(size_t elem_size, void(*free_func)(void*));
15
+ void stack_push(readapt_stack_t *stack, void *element);
16
+ void *stack_peek(readapt_stack_t *stack);
17
+ void stack_pop(readapt_stack_t *stack);
18
+ void stack_free(readapt_stack_t *stack);
19
+
20
+ #endif
@@ -1,15 +1,24 @@
1
1
  #include "ruby.h"
2
2
  #include "ruby/debug.h"
3
3
  #include "threads.h"
4
+ #include "frame.h"
5
+ #include "inspector.h"
4
6
 
7
+ static VALUE c_Thread;
5
8
  static VALUE threads;
9
+ static VALUE ids;
10
+ static int next_id;
6
11
 
7
- void thread_reference_free(void* data)
12
+ static void thread_reference_free(void* data)
8
13
  {
9
- free(data);
14
+ thread_reference_t* thr;
15
+
16
+ thr = data;
17
+ stack_free(thr->frames);
18
+ free(thr);
10
19
  }
11
20
 
12
- size_t thread_reference_size(const void* data)
21
+ static size_t thread_reference_size(const void* data)
13
22
  {
14
23
  return sizeof(thread_reference_t);
15
24
  }
@@ -25,20 +34,16 @@ static const rb_data_type_t thread_reference_type = {
25
34
  .flags = RUBY_TYPED_FREE_IMMEDIATELY,
26
35
  };
27
36
 
28
- void initialize_threads()
29
- {
30
- threads = rb_hash_new();
31
- rb_global_variable(&threads);
32
- }
33
-
34
- VALUE thread_reference_new(VALUE thr)
37
+ static VALUE thread_reference_new(VALUE thr)
35
38
  {
36
39
  thread_reference_t *data = malloc(sizeof(thread_reference_t));
37
- VALUE obj = TypedData_Make_Struct(rb_cData, thread_reference_t, &thread_reference_type, data);
38
- data->id = NUM2LONG(rb_funcall(thr, rb_intern("object_id"), 0));
39
- data->depth = 0;
40
+ VALUE obj = TypedData_Make_Struct(c_Thread, thread_reference_t, &thread_reference_type, data);
41
+ data->id = next_id;
40
42
  data->cursor = 0;
43
+ data->depth = 0;
41
44
  data->control = rb_intern("continue");
45
+ data->frames = stack_alloc(sizeof(frame_t), frame_free);
46
+ next_id++;
42
47
  return obj;
43
48
  }
44
49
 
@@ -61,22 +66,25 @@ VALUE thread_reference(VALUE thr)
61
66
 
62
67
  VALUE thread_reference_id(VALUE id)
63
68
  {
64
- return rb_hash_aref(threads, id);
69
+ return rb_hash_aref(ids, id);
65
70
  }
66
71
 
67
72
  VALUE thread_add_reference(VALUE thr)
68
73
  {
69
74
  VALUE ref;
75
+ thread_reference_t *ptr;
70
76
 
71
77
  ref = thread_reference_new(thr);
78
+ ptr = thread_reference_pointer(ref);
72
79
  rb_hash_aset(threads, rb_obj_id(thr), ref);
80
+ rb_hash_aset(ids, INT2NUM(ptr->id), ref);
73
81
  return ref;
74
82
  }
75
83
 
76
84
  VALUE thread_delete_reference(VALUE thr)
77
85
  {
78
- rb_hash_delete(threads, thr);
79
- return Qnil;
86
+ // TODO: Do we need to delete from ids here?
87
+ return rb_hash_delete(threads, rb_obj_id(thr));
80
88
  }
81
89
 
82
90
  void thread_pause()
@@ -95,7 +103,93 @@ void thread_pause()
95
103
  }
96
104
  }
97
105
 
98
- void thread_reset()
106
+ void thread_clear()
99
107
  {
100
108
  rb_funcall(threads, rb_intern("clear"), 0);
109
+ rb_funcall(ids, rb_intern("clear"), 0);
110
+ }
111
+
112
+ static VALUE thread_allocate_s(VALUE self)
113
+ {
114
+ thread_reference_t *data = malloc(sizeof(thread_reference_t));
115
+ data->control = rb_intern("continue");
116
+ data->depth = 0;
117
+ data->cursor = 0;
118
+ data->frames = stack_alloc(sizeof(frame_t), frame_free);
119
+ data->id = 0;
120
+ return TypedData_Wrap_Struct(self, &thread_reference_type, data);
121
+ }
122
+
123
+ static VALUE thread_all_s(VALUE self)
124
+ {
125
+ return rb_funcall(threads, rb_intern("values"), 0);
126
+ }
127
+
128
+ static VALUE thread_find_s(VALUE self, VALUE id)
129
+ {
130
+ return thread_reference_id(id);
131
+ }
132
+
133
+ static VALUE thread_include_s(VALUE self, VALUE id)
134
+ {
135
+ return rb_funcall(ids, rb_intern("include?"), 1, id);
136
+ }
137
+
138
+ static VALUE thread_id_m(VALUE self)
139
+ {
140
+ thread_reference_t *data = thread_reference_pointer(self);
141
+ return LONG2NUM(data->id);
142
+ }
143
+
144
+ static VALUE frames_m(VALUE self)
145
+ {
146
+ thread_reference_t *data;
147
+ VALUE ary;
148
+ VALUE frm;
149
+ int i;
150
+ frame_t *fd;
151
+
152
+ ary = rb_ary_new();
153
+ data = thread_reference_pointer(self);
154
+ for (i = data->frames->size - 1; i >= 0; i--)
155
+ {
156
+ fd = data->frames->elements[i];
157
+ // TODO This condition should probably not be necessary.
158
+ if (fd->binding != Qnil)
159
+ {
160
+ frm = frame_new_from_data(fd);
161
+ rb_ary_push(ary, frm);
162
+ }
163
+ }
164
+
165
+ return ary;
166
+ }
167
+
168
+ void thread_reference_build_frames(thread_reference_t *ptr)
169
+ {
170
+ inspector_inspect(ptr);
171
+ }
172
+
173
+ void thread_reference_clear_frames(thread_reference_t *ptr)
174
+ {
175
+ stack_free(ptr->frames);
176
+ ptr->frames = stack_alloc(sizeof(frame_t), frame_free);
177
+ }
178
+
179
+ void initialize_threads(VALUE m_Readapt)
180
+ {
181
+ next_id = 1;
182
+
183
+ c_Thread = rb_define_class_under(m_Readapt, "Thread", rb_cData);
184
+ rb_define_alloc_func(c_Thread, thread_allocate_s);
185
+ rb_define_method(c_Thread, "id", thread_id_m, 0);
186
+ rb_define_method(c_Thread, "frames", frames_m, 0);
187
+ rb_define_singleton_method(c_Thread, "all", thread_all_s, 0);
188
+ rb_define_singleton_method(c_Thread, "find", thread_find_s, 1);
189
+ rb_define_singleton_method(c_Thread, "include?", thread_include_s, 1);
190
+
191
+ threads = rb_hash_new();
192
+ rb_global_variable(&threads);
193
+ ids = rb_hash_new();
194
+ rb_global_variable(&ids);
101
195
  }
@@ -1,21 +1,28 @@
1
1
  #ifndef THREADS_H_
2
2
  #define THREADS_H_
3
3
 
4
+ #include "ruby.h"
5
+ #include "frame.h"
6
+ #include "stack.h"
7
+
4
8
  typedef struct thread_reference_struct {
5
- long id;
6
- int depth;
9
+ int id;
7
10
  int cursor;
11
+ int depth;
8
12
  ID control;
13
+ readapt_stack_t *frames;
9
14
  } thread_reference_t;
10
15
 
11
- void initialize_threads();
16
+ void initialize_threads(VALUE);
12
17
  VALUE thread_current_reference();
13
18
  VALUE thread_reference(VALUE);
14
19
  VALUE thread_reference_id(VALUE);
15
20
  VALUE thread_add_reference(VALUE);
16
21
  VALUE thread_delete_reference(VALUE);
17
22
  thread_reference_t *thread_reference_pointer(VALUE);
23
+ void thread_reference_build_frames(thread_reference_t *);
24
+ void thread_reference_clear_frames(thread_reference_t *);
18
25
  void thread_pause();
19
- void thread_reset();
26
+ void thread_clear();
20
27
 
21
28
  #endif
@@ -1,19 +1,21 @@
1
- require 'backport'
2
-
3
- require 'readapt/version'
4
- require 'readapt/breakpoint'
5
- require 'readapt/location'
6
- require 'readapt/thread'
7
- require 'readapt/frame'
8
- require 'readapt/monitor'
9
- require 'readapt/snapshot'
10
- require 'readapt/finder'
11
- require 'readapt/debugger'
12
- require 'readapt/message'
13
- require 'readapt/variable'
14
- require 'readapt/adapter'
15
- require 'readapt/readapt'
16
- require 'readapt/shell'
17
-
18
- module Readapt
19
- end
1
+ require 'backport'
2
+
3
+ require 'readapt/version'
4
+ require 'readapt/readapt'
5
+ require 'readapt/references'
6
+ require 'readapt/breakpoint'
7
+ require 'readapt/thread'
8
+ require 'readapt/frame'
9
+ require 'readapt/monitor'
10
+ require 'readapt/snapshot'
11
+ require 'readapt/finder'
12
+ require 'readapt/debugger'
13
+ require 'readapt/message'
14
+ require 'readapt/variable'
15
+ require 'readapt/data_reader'
16
+ require 'readapt/server'
17
+ require 'readapt/adapter'
18
+ require 'readapt/input'
19
+ require 'readapt/output'
20
+ require 'readapt/error'
21
+ require 'readapt/shell'
@@ -1,138 +1,98 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
-
5
- module Readapt
6
- module Adapter
7
- # @!parse include Backport::Adapter
8
-
9
- @@debugger = nil
10
-
11
- def self.host debugger
12
- @@debugger = debugger
13
- end
14
-
15
- def format result
16
- write_line result.to_protocol.to_json
17
- end
18
-
19
- def opening
20
- @@debugger.add_observer self
21
- @data_reader = DataReader.new
22
- @data_reader.set_message_handler do |message|
23
- process message
24
- end
25
- end
26
-
27
- def closing
28
- @@debugger.delete_observer(self)
29
- end
30
-
31
- def receiving data
32
- @data_reader.receive data
33
- end
34
-
35
- def update event, data
36
- obj = {
37
- type: 'event',
38
- event: event
39
- }
40
- obj[:body] = data unless data.nil?
41
- json = obj.to_json
42
- envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
43
- write envelope
44
- end
45
-
46
- private
47
-
48
- # @param data [Hash]
49
- # @return [void]
50
- def process data
51
- # @todo Better solution than nil frames
52
- message = Message.process(data, @@debugger)
53
- if data['seq']
54
- json = {
55
- type: 'response',
56
- request_seq: data['seq'],
57
- success: true,
58
- command: data['command'],
59
- body: message.body
60
- }.to_json
61
- envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
62
- write envelope
63
- close if data['command'] == 'disconnect'
64
- return unless data['command'] == 'initialize'
65
- json = {
66
- type: 'event',
67
- event: 'initialized'
68
- }.to_json
69
- envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
70
- write envelope
71
- end
72
- rescue Exception => e
73
- STDERR.puts e.message
74
- STDERR.puts e.backtrace
75
- end
76
- end
77
-
78
- class DataReader
79
- def initialize
80
- @in_header = true
81
- @content_length = 0
82
- @buffer = String.new
83
- end
84
-
85
- # Declare a block to be executed for each message received from the
86
- # client.
87
- #
88
- # @yieldparam [Hash] The message received from the client
89
- def set_message_handler &block
90
- @message_handler = block
91
- end
92
-
93
- # Process raw data received from the client. The data will be parsed
94
- # into messages based on the JSON-RPC protocol. Each message will be
95
- # passed to the block declared via set_message_handler. Incomplete data
96
- # will be buffered and subsequent data will be appended to the buffer.
97
- #
98
- # @param data [String]
99
- def receive data
100
- data.each_char do |char|
101
- @buffer.concat char
102
- if @in_header
103
- prepare_to_parse_message if @buffer.end_with?("\r\n\r\n")
104
- else
105
- parse_message_from_buffer if @buffer.bytesize == @content_length
106
- end
107
- end
108
- end
109
-
110
- private
111
-
112
- def prepare_to_parse_message
113
- @in_header = false
114
- @buffer.each_line do |line|
115
- parts = line.split(':').map(&:strip)
116
- if parts[0] == 'Content-Length'
117
- @content_length = parts[1].to_i
118
- break
119
- end
120
- end
121
- @buffer.clear
122
- end
123
-
124
- def parse_message_from_buffer
125
- begin
126
- msg = JSON.parse(@buffer)
127
- @message_handler.call msg unless @message_handler.nil?
128
- rescue JSON::ParserError => e
129
- Solargraph::Logging.logger.warn "Failed to parse request: #{e.message}"
130
- Solargraph::Logging.logger.debug "Buffer: #{@buffer}"
131
- ensure
132
- @buffer.clear
133
- @in_header = true
134
- @content_length = 0
135
- end
136
- end
137
- end
138
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Readapt
6
+ module Adapter
7
+ # @!parse include Backport::Adapter
8
+
9
+ @@debugger = nil
10
+
11
+ def self.host debugger
12
+ @@debugger = debugger
13
+ end
14
+
15
+ def self.procid= pid
16
+ @@procid = pid
17
+ end
18
+
19
+ def procid
20
+ @@procid
21
+ end
22
+
23
+ def open_message
24
+ @@open_message ||= "<readapt-#{procid}>"
25
+ end
26
+
27
+ def close_message
28
+ @@close_message ||= "</readapt-#{procid}>"
29
+ end
30
+
31
+ def format result
32
+ write_line result.to_protocol.to_json
33
+ end
34
+
35
+ def opening
36
+ @@debugger.add_observer self
37
+ @data_reader = DataReader.new
38
+ @data_reader.set_message_handler do |message|
39
+ process message
40
+ end
41
+ end
42
+
43
+ def closing
44
+ @@debugger.delete_observer(self)
45
+ end
46
+
47
+ def receiving data
48
+ @data_reader.receive data
49
+ end
50
+
51
+ def update event, data
52
+ obj = {
53
+ type: 'event',
54
+ event: event
55
+ }
56
+ obj[:body] = data unless data.nil?
57
+ json = obj.to_json
58
+ envelope = "#{open_message}Content-Length: #{json.bytesize}\r\n\r\n#{json}#{close_message}"
59
+ write envelope
60
+ write "#{open_message}__TERMINATE__#{close_message}" if event == 'terminated'
61
+ end
62
+
63
+ private
64
+
65
+ # @param data [Hash]
66
+ # @return [void]
67
+ def process data
68
+ message = Message.process(data, @@debugger)
69
+ if data['seq']
70
+ json = {
71
+ type: 'response',
72
+ request_seq: data['seq'],
73
+ success: true,
74
+ command: data['command'],
75
+ body: message.body
76
+ }.to_json
77
+ envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
78
+ write "#{open_message}#{envelope}#{close_message}"
79
+ if data['command'] == 'disconnect'
80
+ @@debugger.disconnect
81
+ # @todo It does not appear necessary to close the adapter after
82
+ # disconnecting the debugger.
83
+ # close
84
+ end
85
+ return unless data['command'] == 'initialize'
86
+ json = {
87
+ type: 'event',
88
+ event: 'initialized'
89
+ }.to_json
90
+ envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
91
+ write "#{open_message}#{envelope}#{close_message}"
92
+ end
93
+ rescue RuntimeError => e
94
+ STDERR.puts "[#{e.class}] #{e.message}"
95
+ STDERR.puts e.backtrace.join
96
+ end
97
+ end
98
+ end