system_navigation 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d0877e736a201bde13a94bb2fd54462777c30878
4
- data.tar.gz: 0f072306d97e8b2d3f2697c3601e279bc5b6ffdd
3
+ metadata.gz: b9f601aa746ea3e24e18be9512a868d9da486641
4
+ data.tar.gz: d73ce895b246cf274edefb11849d0d28a92637a1
5
5
  SHA512:
6
- metadata.gz: 4d9106cdc061404ae1ab11859ce526994ed3e478d4f21fc200304af7cf56bff513bb4c6b2fb1485ddb6963048d7eaa722e257920269a50f971eee7c92a30fc4a
7
- data.tar.gz: 41cb455b27e76c9bb4b54d5197d25a17f7823dc5f661ddebd3cdc5a15dc513834afb347279dda38f16f5da338bc709d30d174bf5166ab23a151c08f0c8085d16
6
+ metadata.gz: 6b54b78bbe3cb2bdb37ca96e13b0cd2a45bf4acff23cf0a453a450f46d9b4622f600775fed775d93eedaf5d7452c1544040d9fb2883119c26eb4e60c74e53004
7
+ data.tar.gz: 43f8a64c12f39bf151260de576446b2ac445b081160eb6fd806cd79d0960582688324f55a6924ce39bbd93a1f642d0334b8903905de38bdd4ea1feeb1f43016e
@@ -1,6 +1,10 @@
1
1
  System Navigation changelog
2
2
  ===========================
3
3
 
4
+ ### v0.2.0 (June 19, 2015)
5
+
6
+ * Initial release (for real now)
7
+
4
8
  ### v0.1.0 (June 11, 2015)
5
9
 
6
10
  * Initial release (sorta, not ready yet)
data/README.md CHANGED
@@ -1,16 +1,39 @@
1
1
  SystemNavigation
2
2
  ==
3
3
 
4
- * Repository: [https://github.com/kyrylo/system_navigation/](https://github.com/kyrylo/system_navigation/)
4
+ * [Repository](https://github.com/kyrylo/system_navigation/)
5
+ * [Documentation](http://www.rubydoc.info/gems/system_navigation)
5
6
 
6
7
  Description
7
8
  -----------
8
9
 
9
- SystemNavigation is a Ruby library that provides support for the navigation and
10
- introspection of Ruby programs. It is inspired by the eponymous class in
11
- Pharo/Squeak.
10
+ SystemNavigation is a Ruby library that provides additional introspection
11
+ capabilities for Ruby programs. The library defines a number of useful methods
12
+ that allow querying:
12
13
 
13
- Sneak peek:
14
+ * methods for instance, class or global variables
15
+ * methods for literals such as numbers, strings, symbols, etc.
16
+ * methods for finding a specific string (method source search)
17
+ * classes and modules that implement given methods
18
+ * classes and modules that send messages
19
+ * all classes and modules in gems
20
+ * and many more...
21
+
22
+ For the complete list of features please read the documentation or _read the
23
+ tests_. All interaction with the library is done via the `SystemNavigation`
24
+ class and its class methods. The description of the methods can be found in
25
+ these places:
26
+
27
+ * [SystemNavigation](http://www.rubydoc.info/gems/system_navigation/SystemNavigation)
28
+ * [SystemNavigation::RubyEnvironment](http://www.rubydoc.info/gems/system_navigation/SystemNavigation/RubyEnvironment)
29
+
30
+ Examples (full list in the documentation)
31
+ --
32
+
33
+ ##### #all_accesses
34
+
35
+ Retrieve all methods that access instance variables in the given class/module
36
+ including its ancestors and children.
14
37
 
15
38
  ```ruby
16
39
  module M
@@ -35,27 +58,48 @@ sn.all_accesses(to: :@num, from: A)
35
58
  #=> [#<UnboundMethod: A#num>, #<UnboundMethod: A(M)#increment>, #<UnboundMethod: A#initialize>]
36
59
  ```
37
60
 
38
- And many more...
61
+ ##### #all_implementors_of
39
62
 
40
- Installation
41
- ------------
63
+ Find all classes and modules that implement the given message.
42
64
 
43
- All you need is to install the gem.
65
+ ```ruby
66
+ sn = SystemNavigation.default
44
67
 
45
- gem install system_navigation
68
+ sn.all_implementors_of(:puts)
69
+ #=> [ARGF.class, IO, Kernel, ..., YARD::Logger]
70
+ ```
46
71
 
47
- Synopsis
48
- ---
72
+ ##### #all_calls
49
73
 
50
- ### Precaution
74
+ Find all methods in Bundler that invoke the `1` literal.
51
75
 
52
- The library is very young and its API is far from stable. Some behaviours
53
- might be unexpected or bugged. Feel free to file issues if you feel like the
54
- result you expect isn't what you actually get.
76
+ ```ruby
77
+ require 'bundler'
78
+
79
+ sn = SystemNavigation.default
80
+
81
+ sn.all_calls(on: 1, gem: 'bundler')
82
+ #=> [#<UnboundMethod: #<Class:Bundler>#with_clean_env>, #<UnboundMethod: #<Class:Bundler>#eval_gemspec>]
83
+ ```
55
84
 
56
- ### API
85
+ ##### #all_objects
57
86
 
58
- See the [API.md](/docs/API.md) file.
87
+ Retrieve all objects defined in the system.
88
+
89
+ ```ruby
90
+ sn = SystemNavigation.default
91
+
92
+ sn.all_objects.map(&:class).uniq.count #=> 158
93
+ ```
94
+
95
+ And many more...
96
+
97
+ Installation
98
+ ------------
99
+
100
+ All you need is to install the gem.
101
+
102
+ gem install system_navigation
59
103
 
60
104
  Limitations
61
105
  -----------
@@ -64,6 +108,11 @@ Supports *only* CRuby.
64
108
 
65
109
  * CRuby 2.2.2 and higher
66
110
 
111
+ Credits
112
+ -------
113
+
114
+ * Inspired by Smalltalk
115
+
67
116
  License
68
117
  -------
69
118
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -17,6 +17,12 @@ require_relative 'system_navigation/instruction_stream/decoder'
17
17
  require_relative 'system_navigation/instruction_stream/instruction'
18
18
  require_relative 'system_navigation/instruction_stream/instruction/attr_instruction'
19
19
 
20
+ # SystemNavigation is a class that provides some introspection capabilities. It
21
+ # is based on a Smalltalk class with a similar name. This is the only public
22
+ # class in this library.
23
+ #
24
+ # @api public
25
+ # @since 0.1.0
20
26
  class SystemNavigation
21
27
  # The VERSION file must be in the root directory of the library.
22
28
  VERSION_FILE = File.expand_path('../../VERSION', __FILE__)
@@ -31,19 +37,35 @@ class SystemNavigation
31
37
  :all_behaviors, :all_classes, :all_classes_and_modules,
32
38
  :all_modules, :all_objects
33
39
 
40
+ ##
41
+ # Creates a new instance of SystemNavigation. It is added for compatibility
42
+ # with Smalltalk users.
43
+ #
44
+ # @example
45
+ # require 'system_navigation'
46
+ #
47
+ # sn = SystemNavigation.default
34
48
  def self.default
35
49
  self.new
36
50
  end
37
51
 
52
+ VAR_TEMPLATE = /\A[\$@]@?.+/
53
+ private_constant :VAR_TEMPLATE
54
+
38
55
  def initialize
39
56
  @environment = SystemNavigation::RubyEnvironment.new
40
57
  end
41
58
 
42
59
  ##
43
- # Query methods for instance variables in descending (subclasses) and
44
- # ascending (superclasses) fashion.
60
+ # Query methods for instance/global/class variables in descending (subclasses)
61
+ # and ascending (superclasses) fashion.
45
62
  #
46
- # @example Global
63
+ # @note This is a very costly operation, if you don't provide the +from+
64
+ # argument
65
+ # @note This method _does_ _not_ perform _global_ queries,
66
+ # only relative to +from+
67
+ #
68
+ # @example Global scope (start search from BasicObject)
47
69
  # class A
48
70
  # def initialize
49
71
  # @foo = 1
@@ -57,7 +79,7 @@ class SystemNavigation
57
79
  # sn.all_accesses(to: :@foo)
58
80
  # #=> [#<UnboundMethod: A#initialize>, #<UnboundMethod: B#foo>]
59
81
  #
60
- # @example Local
82
+ # @example Local scope
61
83
  # class A
62
84
  # def initialize
63
85
  # @foo = 1
@@ -83,20 +105,45 @@ class SystemNavigation
83
105
  # end
84
106
  #
85
107
  # sn.all_accesses(to: :@foo, only_get: true)
86
- # #=> [#<UnboundMethod: B#foo>]
108
+ # #=> [#<UnboundMethod: B#initialize>]
109
+ #
110
+ # @example Only set invokations
111
+ # class A
112
+ # def initialize
113
+ # @foo = 1
114
+ # end
115
+ # end
116
+ #
117
+ # class B
118
+ # attr_reader :foo
119
+ # end
120
+ #
121
+ # sn.all_accesses(to: :@foo, only_set: true)
122
+ # #=> [#<UnboundMethod: A#initialize>]
87
123
  #
88
- # @param to [Symbol] The name of the instance variable to search for
124
+ # @example Accesses to global variables
125
+ # sn.all_accesses(to: :$DEBUG)
126
+ # #=> [#<UnboundMethod: Gem::Specification#inspect>, ...]
127
+ #
128
+ # @example Accesses to class variables
129
+ # sn.all_accesses(to: :@@required_attributes)
130
+ # #=> [#<UnboundMethod: Gem::Specification#validate>, ...]
131
+ #
132
+ # @param to [Symbol] The name of the instance/global/class variable to search
133
+ # for
89
134
  # @param from [Class] The class that limits the scope of the query. Optional.
90
135
  # If omitted, performs the query starting from the top of the object
91
136
  # hierarchy (BasicObject)
92
137
  # @param only_get [Boolean] Limits the scope of the query only to methods that
93
- # write into the +ivar+. Optional. Mutually exclusive with +only_set+
138
+ # write into the +var+. Optional. Mutually exclusive with +only_set+
94
139
  # @param only_set [Boolean] Limits the scope of the query only to methods that
95
- # read from the +ivar+. Optional. Mutually exclusive with +only_get+
96
- # @return [Array<UnboundMethod>] methods that access the +ivar+ according to
140
+ # read from the +var+. Optional. Mutually exclusive with +only_get+
141
+ # @return [Array<UnboundMethod>] methods that access the +var+ according to
97
142
  # the given scope
98
- # @note This is a very costly operation, if you don't provide the +from+
99
- # argument
143
+ # @raise [ArgumentError] if both +:only_get+ and +:only_set+ were provided
144
+ # @raise [TypeError] if +:from+ is not a class
145
+ # @raise [ArgumentError] if +:to+ is not a Symbol representing either of
146
+ # these: class variable, instance variable, global variable
100
147
  def all_accesses(to:, from: nil, only_get: nil, only_set: nil)
101
148
  if only_set && only_get
102
149
  fail ArgumentError, 'both only_get and only_set were provided'
@@ -106,15 +153,32 @@ class SystemNavigation
106
153
  fail TypeError, "from must be a Class (#{from.class} given)"
107
154
  end
108
155
 
156
+ unless to.match(VAR_TEMPLATE)
157
+ fail ArgumentError, 'invalid argument for the `to:` attribute'
158
+ end
159
+
109
160
  (from || BasicObject).with_all_sub_and_superclasses.flat_map do |klass|
110
161
  klass.select_methods_that_access(to, only_get, only_set)
111
162
  end
112
163
  end
113
164
 
114
165
  ##
115
- # Query methods for literals they call.
166
+ # Query methods for literals they call. The supported literals:
167
+ # * Hashes (only simple Hashes that consist of literals itself)
168
+ # * Arrays (only simple Arrays that consist of literals itself)
169
+ # * +true+, +false+ and +nil+
170
+ # * Integers (same Integers represented with different notations are treated
171
+ # as the same number)
172
+ # * Floats
173
+ # * Strings
174
+ # * Ranges
116
175
  #
117
- # @example Global
176
+ # @note This is a very costly operation, if you don't provide the +from+
177
+ # argument
178
+ # @note The list of supported literals can be found here:
179
+ # http://ruby-doc.org/core-2.2.2/doc/syntax/literals_rdoc.html
180
+ #
181
+ # @example Global scope (every behaviour in this process)
118
182
  # class A
119
183
  # def foo
120
184
  # :hello
@@ -130,7 +194,7 @@ class SystemNavigation
130
194
  # sn.all_calls(on: :hello)
131
195
  # #=> [#<UnboundMethod: A#foo>, #<UnboundMethod: B#bar>]
132
196
  #
133
- # @example Local
197
+ # @example Local scope
134
198
  # class A
135
199
  # def foo
136
200
  # :hello
@@ -158,10 +222,7 @@ class SystemNavigation
158
222
  # @param gem [String] Limits the scope of the query only to methods
159
223
  # that are defined in the RubyGem +gem+ classes and modules. Optional.
160
224
  # @return [Array<UnboundMethod>] methods that call the given +literal+
161
- # @note This is a very costly operation, if you don't provide the +from+
162
- # argument
163
- # @note The list of supported literals can be found here:
164
- # http://ruby-doc.org/core-2.2.2/doc/syntax/literals_rdoc.html
225
+ # @raise [ArgumentError] if both keys (+from:+ and +gem:+) are given
165
226
  def all_calls(on:, from: nil, gem: nil)
166
227
  if from && gem
167
228
  fail ArgumentError, 'both from and gem were provided'
@@ -179,7 +240,7 @@ class SystemNavigation
179
240
  end
180
241
 
181
242
  ##
182
- # Query classes for the methods they implement.
243
+ # Query classes for methods they implement.
183
244
  #
184
245
  # @example
185
246
  # sn.all_classes_implementing(:~)
@@ -223,6 +284,9 @@ class SystemNavigation
223
284
  ##
224
285
  # Query gems for classes they implement.
225
286
  #
287
+ # @note This method is not precise. If a class/method in the given gem does
288
+ # not implement any methods, it won't be included in the result.
289
+ #
226
290
  # @example
227
291
  # sn.all_classes_in_gem_named('system_navigation')
228
292
  # #=> [SystemNavigation::AncestorMethodFinder, ..., SystemNavigation]
@@ -237,6 +301,9 @@ class SystemNavigation
237
301
  ##
238
302
  # Query gems for modules they implement.
239
303
  #
304
+ # @note This method is not precise. If a class/method in the given gem does
305
+ # not implement any methods, it won't be included in the result.
306
+ #
240
307
  # @example
241
308
  # sn.all_modules_in_gem_named('pry-theme')
242
309
  # #=> [PryTheme::Theme::DefaultAttrs, ..., PryTheme]
@@ -250,6 +317,9 @@ class SystemNavigation
250
317
  ##
251
318
  # Query gems for classes and modules they implement.
252
319
  #
320
+ # @note This method is not precise. If a class/method in the given gem does
321
+ # not implement any methods, it won't be included in the result.
322
+ #
253
323
  # @example
254
324
  # sn.all_classes_and_modules_in_gem_named('pry-theme')
255
325
  # #=> [PryTheme::Preview, ..., PryTheme::Color256]
@@ -262,7 +332,7 @@ class SystemNavigation
262
332
  end
263
333
 
264
334
  ##
265
- # Get all methods defined in current Ruby process.
335
+ # Get all methods defined in the current Ruby process.
266
336
  #
267
337
  # @example
268
338
  # sn.all_methods
@@ -298,9 +368,8 @@ class SystemNavigation
298
368
  # end
299
369
  # end
300
370
  #
301
- #
302
- # sn.all_methods_with_source(string: 'hello_hi')
303
- # #=> [#<UnboundMethod: B#bar>, #<UnboundMethod: A#foo>, #<UnboundMethod: M#foo>]
371
+ # sn.all_methods_with_source(string: 'hello_hi')
372
+ # #=> [#<UnboundMethod: B#bar>, #<UnboundMethod: A#foo>, #<UnboundMethod: M#foo>]
304
373
  #
305
374
  # @param string [String] The string to be searched for
306
375
  # @param match_case [Boolean] Whether to match case or not. Optional
@@ -4,6 +4,12 @@ class SystemNavigation
4
4
  self.new(method).compile
5
5
  end
6
6
 
7
+ IVAR = /\A@[^@]/
8
+ CVAR = /\A@@/
9
+ GVAR = /\A\$/
10
+
11
+ attr_reader :source
12
+
7
13
  def initialize(method)
8
14
  @method = method
9
15
  @scanner = SystemNavigation::InstructionStream.on(method)
@@ -47,12 +53,30 @@ class SystemNavigation
47
53
  exptree.includes?(literal)
48
54
  end
49
55
 
50
- def reads_field?(ivar)
51
- self.scan_for { @decoder.ivar_read_scan(ivar) }
56
+ def reads_field?(var)
57
+ case var
58
+ when IVAR
59
+ self.scan_for { @decoder.ivar_read_scan(var) }
60
+ when CVAR
61
+ self.scan_for { @decoder.cvar_read_scan(var) }
62
+ when GVAR
63
+ self.scan_for { @decoder.gvar_read_scan(var) }
64
+ else
65
+ raise ArgumentError, "unknown argument #{var}"
66
+ end
52
67
  end
53
68
 
54
- def writes_field?(ivar)
55
- self.scan_for { @decoder.ivar_write_scan(ivar) }
69
+ def writes_field?(var)
70
+ case var
71
+ when IVAR
72
+ self.scan_for { @decoder.ivar_write_scan(var) }
73
+ when CVAR
74
+ self.scan_for { @decoder.cvar_write_scan(var) }
75
+ when GVAR
76
+ self.scan_for { @decoder.gvar_write_scan(var) }
77
+ else
78
+ raise ArgumentError, "unknown argument #{var}"
79
+ end
56
80
  end
57
81
 
58
82
  def sends_message?(message)
@@ -60,6 +60,8 @@ class SystemNavigation
60
60
  end
61
61
 
62
62
  def unify(node)
63
+ return unless node
64
+
63
65
  node.each do |n|
64
66
  if n.instance_of?(Array)
65
67
  if n.size == 2 && n.all? { |num| num.instance_of?(Fixnum) }
@@ -42,6 +42,30 @@ class SystemNavigation
42
42
  end
43
43
  end
44
44
 
45
+ def gvar_read_scan(gvar)
46
+ self.select_instructions(literal: gvar) do |_prev_prev, prev, instruction|
47
+ next instruction if instruction.reads_gvar?(gvar)
48
+ end
49
+ end
50
+
51
+ def gvar_write_scan(gvar)
52
+ self.select_instructions(literal: gvar) do |prev_prev, prev, instruction|
53
+ next instruction if instruction.writes_gvar?(gvar)
54
+ end
55
+ end
56
+
57
+ def cvar_read_scan(cvar)
58
+ self.select_instructions(literal: cvar) do |_prev_prev, prev, instruction|
59
+ next instruction if instruction.reads_cvar?(cvar)
60
+ end
61
+ end
62
+
63
+ def cvar_write_scan(cvar)
64
+ self.select_instructions(literal: cvar) do |prev_prev, prev, instruction|
65
+ next instruction if instruction.writes_cvar?(cvar)
66
+ end
67
+ end
68
+
45
69
  def literal_scan(literal)
46
70
  name = @scanner.method.original_name
47
71
 
@@ -14,6 +14,8 @@ class SystemNavigation
14
14
  @lineno = nil
15
15
  @op_id = nil
16
16
  @ivar = nil
17
+ @gvar = nil
18
+ @cvar = nil
17
19
  @service_instruction = false
18
20
  end
19
21
 
@@ -25,7 +27,7 @@ class SystemNavigation
25
27
  parse_operand
26
28
  parse_lineno
27
29
  parse_op_id
28
- parse_ivar
30
+ parse_var
29
31
 
30
32
  self
31
33
  end
@@ -57,7 +59,7 @@ class SystemNavigation
57
59
  elsif @raw.check(%r{/})
58
60
  @operand = @raw.scan(%r{/.*/})
59
61
  else
60
- @operand = @raw.scan(/-?[0-9a-zA-Z:@_=.]+/)
62
+ @operand = @raw.scan(/-?[0-9a-zA-Z:@_=.$]+/)
61
63
 
62
64
  if @raw.peek(1) == ','
63
65
  @operand << @raw.scan(/[^\(]*/).rstrip
@@ -83,18 +85,51 @@ class SystemNavigation
83
85
  callinfo.terminate
84
86
  end
85
87
 
88
+ def parse_var
89
+ parse_ivar
90
+ parse_gvar
91
+ parse_cvar
92
+ end
93
+
86
94
  def parse_ivar
87
95
  return unless accessing_ivar?
88
96
 
89
97
  ivar = StringScanner.new(@operand)
90
98
  @ivar = ivar.scan(/:[^,]+/)[1..-1].to_sym
91
99
  ivar.terminate
100
+ @ivar
101
+ end
102
+
103
+ def parse_gvar
104
+ return unless accessing_gvar?
105
+
106
+ gvar = StringScanner.new(@operand)
107
+ @gvar = gvar.scan(/[^,]+/).to_sym
108
+ gvar.terminate
109
+ @gvar
110
+ end
111
+
112
+ def parse_cvar
113
+ return unless accessing_cvar?
114
+
115
+ cvar = StringScanner.new(@operand)
116
+ @cvar = cvar.scan(/:[^,]+/)[1..-1].to_sym
117
+ cvar.terminate
118
+ @cvar
92
119
  end
93
120
 
94
121
  def accessing_ivar?
95
122
  @opcode == 'getinstancevariable' || @opcode == 'setinstancevariable'
96
123
  end
97
124
 
125
+ def accessing_gvar?
126
+ @opcode == 'getglobal' || @opcode == 'setglobal'
127
+ end
128
+
129
+ def accessing_cvar?
130
+ @opcode == 'getclassvariable' || @opcode == 'setclassvariable'
131
+ end
132
+
98
133
  def vm_operative?
99
134
  @service_instruction == false
100
135
  end
@@ -108,19 +143,35 @@ class SystemNavigation
108
143
  end
109
144
 
110
145
  def dynamically_reads_ivar?
111
- @op_id == 'instance_variable_get'
146
+ self.op_id == 'instance_variable_get'
112
147
  end
113
148
 
114
149
  def dynamically_writes_ivar?
115
150
  @op_id == 'instance_variable_set'
116
151
  end
117
152
 
153
+ def reads_gvar?(gvar)
154
+ @opcode == 'getglobal' && @gvar == gvar
155
+ end
156
+
157
+ def writes_gvar?(gvar)
158
+ @opcode == 'setglobal' && @gvar == gvar
159
+ end
160
+
161
+ def reads_cvar?(cvar)
162
+ @opcode == 'getclassvariable' && @cvar == cvar
163
+ end
164
+
165
+ def writes_cvar?(cvar)
166
+ @opcode == 'setclassvariable' && @cvar == cvar
167
+ end
168
+
118
169
  def evals?
119
- @op_id == 'eval'
170
+ self.op_id == 'eval'
120
171
  end
121
172
 
122
173
  def putstrings?(str)
123
- return false unless @opcode == 'putstring'
174
+ return false unless self.opcode == 'putstring'
124
175
 
125
176
  s = str.inspect
126
177
 
@@ -137,29 +188,32 @@ class SystemNavigation
137
188
  def putobjects?(str)
138
189
  return false unless @opcode == 'putobject'
139
190
 
140
- return true if @operand.match(/(?::#{str}\z|\[.*:#{str},.*\])/)
191
+ s = (str.instance_of?(String) ? Regexp.escape(str) : str)
192
+
193
+ return true if @operand.match(/(?::#{s}\z|\[.*:#{s},.*\])/)
141
194
  return true if @operand == str.inspect
142
195
 
143
196
  false
144
197
  end
145
198
 
146
199
  def putnils?(str)
147
- return false unless @opcode == 'putnil'
200
+ return false unless self.opcode == 'putnil'
148
201
  @operand == str.inspect
149
202
  end
150
203
 
151
204
  def duparrays?(str)
152
- !!(@opcode == 'duparray' && @operand.match(/:#{str}[,\]]/))
205
+ s = case str
206
+ when Array, Hash then Regexp.escape(str.inspect)
207
+ else str
208
+ end
209
+
210
+ !!(self.opcode == 'duparray' && @operand.match(/:#{s}[,\]]/))
153
211
  end
154
212
 
155
213
  def sends_msg?(message)
156
214
  !!(sending? && @op_id == message.to_s)
157
215
  end
158
216
 
159
- def operand
160
- @operand
161
- end
162
-
163
217
  def evaling_str
164
218
  @evaling_str ||= @operand.sub!(/\A"(.+)"/, '\1')
165
219
  end
@@ -175,6 +229,10 @@ class SystemNavigation
175
229
  def sending?
176
230
  @opcode == 'opt_send_without_block' || @opcode == 'send'
177
231
  end
232
+
233
+ protected
234
+
235
+ attr_reader :opcode, :op_id
178
236
  end
179
237
  end
180
238
  end
@@ -44,17 +44,17 @@ class SystemNavigation
44
44
  ).as_array.uniq.count == 1
45
45
  end
46
46
 
47
- def find_accessing_methods(ivar:, only_set:, only_get:)
47
+ def find_accessing_methods(var:, only_set:, only_get:)
48
48
  self.instance_and_singleton_do(
49
49
  for_all: proc { |_scope, _selectors, method|
50
50
  compiled_method = CompiledMethod.compile(method)
51
51
  if only_set
52
- compiled_method.unwrap if compiled_method.writes_field?(ivar)
52
+ compiled_method.unwrap if compiled_method.writes_field?(var)
53
53
  elsif only_get
54
- compiled_method.unwrap if compiled_method.reads_field?(ivar)
54
+ compiled_method.unwrap if compiled_method.reads_field?(var)
55
55
  else
56
- if compiled_method.reads_field?(ivar) ||
57
- compiled_method.writes_field?(ivar)
56
+ if compiled_method.reads_field?(var) ||
57
+ compiled_method.writes_field?(var)
58
58
  compiled_method.unwrap
59
59
  end
60
60
  end
@@ -35,7 +35,7 @@ class SystemNavigation
35
35
  end
36
36
  end
37
37
 
38
- def select_methods_that_access(ivar, only_get, only_set)
38
+ def select_methods_that_access(var, only_get, only_set)
39
39
  own_methods = self.own_methods
40
40
  if ancestor_methods.any?
41
41
  ancestor_methods.each do |methods|
@@ -48,7 +48,7 @@ class SystemNavigation
48
48
  MethodQuery.execute(
49
49
  collection: own_methods,
50
50
  query: :find_accessing_methods,
51
- ivar: ivar,
51
+ var: var,
52
52
  only_get: only_get,
53
53
  only_set: only_set,
54
54
  behavior: self).as_array
@@ -207,10 +207,10 @@ class SystemNavigation
207
207
  query: :select_sent_messages)
208
208
  end
209
209
 
210
- def which_selectors_store_into(ivar)
210
+ def which_selectors_store_into(var)
211
211
  self.selectors.select do |sel|
212
212
  meth = self.instance_method(sel)
213
- meth.writes_field?(ivar)
213
+ meth.writes_field?(var)
214
214
  end
215
215
  end
216
216
 
@@ -1,6 +1,12 @@
1
1
  class SystemNavigation
2
+ # @api private
3
+ # @since 0.1.0
2
4
  class RubyEnvironment
3
- # Execute block on each class, metaclass, module and module's metaclass.
5
+ ##
6
+ # Execute +block+ on each class, metaclass, module and module's metaclass.
7
+ #
8
+ # @return [Enumerator] if +block+ was given
9
+ # @return [Enumerator] if +block+ is missing
4
10
  def all_behaviors(&block)
5
11
  enum = Enumerator.new do |y|
6
12
  ObjectSpace.each_object(Module) do |klass|
@@ -16,6 +22,11 @@ class SystemNavigation
16
22
  end
17
23
  end
18
24
 
25
+ ##
26
+ # Execute +block+ on each class (but not its metaclass).
27
+ #
28
+ # @return [Enumerator] if +block+ was given
29
+ # @return [Enumerator] if +block+ is missing
19
30
  def all_classes(&block)
20
31
  enum = Enumerator.new do |y|
21
32
  ObjectSpace.each_object(Class) do |klass|
@@ -30,6 +41,11 @@ class SystemNavigation
30
41
  end
31
42
  end
32
43
 
44
+ ##
45
+ # Execute +block+ on each class and module (but not their metaclasses).
46
+ #
47
+ # @return [Enumerator] if +block+ was given
48
+ # @return [Enumerator] if +block+ is missing
33
49
  def all_classes_and_modules(&block)
34
50
  enum = Enumerator.new do |y|
35
51
  ObjectSpace.each_object(Module) do |klass|
@@ -44,6 +60,11 @@ class SystemNavigation
44
60
  end
45
61
  end
46
62
 
63
+ ##
64
+ # Execute +block+ on each module (but not its metaclass).
65
+ #
66
+ # @return [Enumerator] if +block+ was given
67
+ # @return [Enumerator] if +block+ is missing
47
68
  def all_modules(&block)
48
69
  enum = Enumerator.new do |y|
49
70
  self.all_classes_and_modules.each do |klass|
@@ -58,10 +79,15 @@ class SystemNavigation
58
79
  end
59
80
  end
60
81
 
82
+ ##
83
+ # Execute +block+ on each object.
84
+ #
85
+ # @return [Enumerator] if +block+ was given
86
+ # @return [Enumerator] if +block+ is missing
61
87
  def all_objects(&block)
62
88
  enum = Enumerator.new do |y|
63
- ObjectSpace.each_object do |klass|
64
- y.yield klass
89
+ ObjectSpace.each_object do |obj|
90
+ y.yield obj
65
91
  end
66
92
  end
67
93
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: system_navigation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kyrylo Silin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-11 00:00:00.000000000 Z
11
+ date: 2015-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fast_method_source
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.1'
19
+ version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.1'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement