system_navigation 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,90 @@
1
+ class SystemNavigation
2
+ class InstructionStream
3
+ class Instruction
4
+ class AttrInstruction < Instruction
5
+ def self.parse(method, sym)
6
+ instr = self.new(method)
7
+ self.attrreaderinstr(instr, sym) || self.attrwriterinstr(instr, sym)
8
+ end
9
+
10
+ def self.attrreaderinstr(instr, sym)
11
+ instr.accept(AttrReaderInstruction.new(sym)) && instr.parse
12
+ end
13
+
14
+ def self.attrwriterinstr(instr, sym)
15
+ instr.accept(AttrWriterInstruction.new(sym)) && instr.parse
16
+ end
17
+
18
+ attr_accessor :visitor
19
+ attr_reader :method
20
+
21
+ def initialize(method)
22
+ @method = method
23
+ end
24
+
25
+ def accept(visitor)
26
+ visitor.visit(self)
27
+ end
28
+
29
+ def parse
30
+ [self.visitor]
31
+ end
32
+
33
+ private
34
+
35
+ def convert_accessor_to_name(sym)
36
+ sym.to_s.tr('@', '').downcase
37
+ end
38
+ end
39
+
40
+ class AttrReaderInstruction < AttrInstruction
41
+ def initialize(sym)
42
+ @sym = sym
43
+ end
44
+
45
+ def visit(obj)
46
+ matched = obj.method.original_name.to_s == convert_accessor_to_name(@sym)
47
+ obj.visitor = self if matched
48
+ matched
49
+ end
50
+
51
+ def reads_ivar?(_sym)
52
+ true
53
+ end
54
+
55
+ def writes_ivar?(_sym)
56
+ false
57
+ end
58
+
59
+ def putobjects?(sym)
60
+ @sym == sym
61
+ end
62
+ end
63
+
64
+ class AttrWriterInstruction < AttrInstruction
65
+ def initialize(sym)
66
+ @sym = sym
67
+ end
68
+
69
+ def visit(obj)
70
+ name = obj.method.original_name.to_s
71
+ matched = (name[-1] == '=') && (name[0..-2] == convert_accessor_to_name(@sym))
72
+ obj.visitor = self if matched
73
+ matched
74
+ end
75
+
76
+ def reads_ivar?(_sym)
77
+ false
78
+ end
79
+
80
+ def writes_ivar?(_sym)
81
+ true
82
+ end
83
+
84
+ def putobjects?(sym)
85
+ @sym == sym
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,49 @@
1
+ class SystemNavigation
2
+ class MethodHash < Hash
3
+ def self.create(**args)
4
+ self.new(args)
5
+ end
6
+
7
+
8
+ def initialize(based_on: nil, include_super: nil)
9
+ @hash = super()
10
+
11
+ if based_on
12
+ @hash.merge!({
13
+ public: {
14
+ instance: based_on.public_instance_methods(include_super),
15
+ singleton: based_on.singleton_class.public_instance_methods(include_super)
16
+ },
17
+ private: {
18
+ instance: based_on.private_instance_methods(include_super),
19
+ singleton: based_on.singleton_class.private_instance_methods(include_super)
20
+ },
21
+ protected: {
22
+ instance: based_on.protected_instance_methods(include_super),
23
+ singleton: based_on.singleton_class.protected_instance_methods(include_super)
24
+ }
25
+ })
26
+ else
27
+ @hash.merge!(empty_hash)
28
+ end
29
+ end
30
+
31
+ def as_array
32
+ self.values.map { |h| h[:instance] + h[:singleton] }.flatten.compact
33
+ end
34
+
35
+ def empty?
36
+ self == empty_hash
37
+ end
38
+
39
+ protected
40
+
41
+ def empty_hash
42
+ {
43
+ public: {instance: [], singleton: []},
44
+ private: {instance: [], singleton: []},
45
+ protected: {instance: [], singleton: []}
46
+ }
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,98 @@
1
+ class SystemNavigation
2
+ class MethodQuery
3
+ def self.execute(collection:, query:, behavior: nil, **rest)
4
+ args = [query]
5
+ args << rest unless rest.empty?
6
+ self.new(collection, behavior).__send__(*args)
7
+ end
8
+
9
+ def initialize(collection, behavior)
10
+ @collection = collection
11
+ @behavior = behavior
12
+ end
13
+
14
+ def convert_to_methods
15
+ self.instance_and_singleton_do(
16
+ for_instance: proc { |_scope, _selectors, selector|
17
+ @behavior.instance_method(selector)
18
+ },
19
+
20
+ for_singleton: proc { |_scope, _selectors, selector|
21
+ @behavior.singleton_class.instance_method(selector)
22
+ }
23
+ )
24
+ end
25
+
26
+ def tupleize
27
+ self.instance_and_singleton_do(
28
+ for_all: proc { |_scope, _selectors, method| [method.name, method] }
29
+ )
30
+ end
31
+
32
+ def find_literal(literal:)
33
+ self.instance_and_singleton_do(
34
+ for_all: proc { |_scope, _selectors, method|
35
+ compiled_method = CompiledMethod.compile(method)
36
+ compiled_method.unwrap if compiled_method.has_literal?(literal)
37
+ }
38
+ )
39
+ end
40
+
41
+ def all_in_the_same_file?
42
+ self.instance_and_singleton_do(
43
+ for_all: proc { |_scope, _selectors, method| method.source_location[0] }
44
+ ).as_array.uniq.count == 1
45
+ end
46
+
47
+ def find_accessing_methods(ivar:, only_set:, only_get:)
48
+ self.instance_and_singleton_do(
49
+ for_all: proc { |_scope, _selectors, method|
50
+ compiled_method = CompiledMethod.compile(method)
51
+ if only_set
52
+ compiled_method.unwrap if compiled_method.writes_field?(ivar)
53
+ elsif only_get
54
+ compiled_method.unwrap if compiled_method.reads_field?(ivar)
55
+ else
56
+ if compiled_method.reads_field?(ivar) ||
57
+ compiled_method.writes_field?(ivar)
58
+ compiled_method.unwrap
59
+ end
60
+ end
61
+ }
62
+ )
63
+ end
64
+
65
+ def select_sent_messages
66
+ self.instance_and_singleton_do(
67
+ for_all: proc { |_scope, _selectors, method|
68
+ compiled_method = CompiledMethod.compile(method)
69
+ compiled_method.sent_messages.uniq.map(&:to_sym)
70
+ }
71
+ )
72
+ end
73
+
74
+ protected
75
+
76
+ def instance_and_singleton_do(for_instance: nil, for_singleton: nil, for_all: nil)
77
+ for_instance = for_all && for_singleton = for_all if for_all
78
+
79
+ @collection.inject(MethodHash.new) do |h, (scope, selectors)|
80
+ self.evaluate(callable: for_instance, group: :instance, hash: h,
81
+ scope: scope, selectors: selectors)
82
+ self.evaluate(callable: for_singleton, group: :singleton, hash: h,
83
+ scope: scope, selectors: selectors)
84
+ h
85
+ end
86
+ end
87
+
88
+ def evaluate(callable:, group:, hash:, scope:, selectors:)
89
+ return if callable.nil?
90
+
91
+ result = selectors[group].map do |selector|
92
+ callable.call(scope, selectors, selector)
93
+ end
94
+
95
+ hash[scope][group].concat(result)
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,226 @@
1
+ class SystemNavigation
2
+ module ModuleRefinement
3
+ refine Module do
4
+ def with_all_sub_and_superclasses
5
+ Enumerator.new do |y|
6
+ self.with_all_subclasses.each { |klass| y << klass }
7
+ self.with_all_superclasses.each { |klass| y << klass }
8
+ end
9
+ end
10
+
11
+ def with_all_subclasses
12
+ Enumerator.new do |y|
13
+ self.all_subclasses.push(self).each { |subclass| y << subclass }
14
+ end
15
+ end
16
+
17
+ def all_subclasses
18
+ all_subclasses = []
19
+
20
+ ObjectSpace.each_object(self.singleton_class) do |klass|
21
+ all_subclasses.unshift(klass) if klass != self
22
+ end
23
+
24
+ all_subclasses
25
+ end
26
+
27
+ def with_all_superclasses
28
+ if self.superclass
29
+ Enumerator.new do |y|
30
+ y.yield self.superclass
31
+ self.superclass.with_all_superclasses.each { |klass| y << klass }
32
+ end
33
+ else
34
+ []
35
+ end
36
+ end
37
+
38
+ def select_methods_that_access(ivar, only_get, only_set)
39
+ own_methods = self.own_methods
40
+ if ancestor_methods.any?
41
+ ancestor_methods.each do |methods|
42
+ own_methods.merge!(methods) do |_group, old_h, new_h|
43
+ old_h.merge!(new_h) { |_key, oldval, newval| oldval | newval }
44
+ end
45
+ end
46
+ end
47
+
48
+ MethodQuery.execute(
49
+ collection: own_methods,
50
+ query: :find_accessing_methods,
51
+ ivar: ivar,
52
+ only_get: only_get,
53
+ only_set: only_set,
54
+ behavior: self).as_array
55
+ end
56
+
57
+ def select_methods_that_refer_to(literal)
58
+ MethodQuery.execute(
59
+ collection: self.own_methods,
60
+ query: :find_literal,
61
+ literal: literal,
62
+ behavior: self).as_array
63
+ end
64
+
65
+ def reachable_selectors
66
+ MethodHash.create(based_on: self, include_super: true)
67
+ end
68
+
69
+ def own_selectors
70
+ MethodHash.create(based_on: self, include_super: false)
71
+ end
72
+
73
+ def reachable_methods
74
+ MethodQuery.execute(
75
+ collection: self.reachable_selectors,
76
+ query: :convert_to_methods,
77
+ behavior: self)
78
+ end
79
+
80
+ def own_methods
81
+ MethodQuery.execute(
82
+ collection: self.own_selectors,
83
+ query: :convert_to_methods,
84
+ behavior: self)
85
+ end
86
+
87
+ def reachable_method_hash
88
+ MethodQuery.execute(
89
+ collection: self.reachable_methods,
90
+ query: :tupleize)
91
+ end
92
+
93
+ def own_method_hash
94
+ MethodQuery.execute(
95
+ collection: self.own_methods,
96
+ query: :tupleize)
97
+ end
98
+
99
+ def which_global_selectors_refer_to(literal)
100
+ who = []
101
+
102
+ self.own_selectors_and_methods do |selector, method|
103
+ if method.has_literal?(literal)
104
+ who << selector
105
+ end
106
+ end
107
+
108
+ who
109
+ end
110
+
111
+ def belongs_to?(gem_name)
112
+ gemspec = Gem::Specification.find_all_by_name(gem_name).last
113
+
114
+ return false if gemspec.nil? || self.own_selectors.empty?
115
+
116
+ pattern = %r{(?:/gems/#{gem_name}-#{gemspec.version}/)|(?:/lib/ruby/[[0-9]\.]+/#{gem_name}/)}
117
+ match_location = proc { |locations|
118
+ !!locations.max_by { |_k, value| value }[0].match(pattern)
119
+ }
120
+
121
+ if self.contains_only_rb_methods?
122
+ if self.all_neighbour_methods?
123
+ self.own_methods.as_array.all? do |method|
124
+ method.source_location.first.match(pattern)
125
+ end
126
+ else
127
+ grouped_locations = self.group_locations_by_path
128
+ return false if grouped_locations.empty?
129
+
130
+ if grouped_locations.all? { |l| l[0].match(pattern) }
131
+ true
132
+ else
133
+ match_location.call(grouped_locations)
134
+ end
135
+ end
136
+ else
137
+ grouped_locations = self.group_locations_by_path
138
+ grouped_locations.delete_if { |k, v| k.nil? }
139
+
140
+ if grouped_locations.empty?
141
+ false
142
+ else
143
+ match_location.call(grouped_locations)
144
+ end
145
+ end
146
+ end
147
+
148
+ def contains_only_rb_methods?
149
+ self.own_methods.as_array.all? { |method| method.source_location }
150
+ end
151
+
152
+ def all_neighbour_methods?
153
+ MethodQuery.execute(
154
+ collection: self.own_methods,
155
+ query: :all_in_the_same_file?)
156
+ end
157
+
158
+ def group_locations_by_path
159
+ Hash[
160
+ self.own_methods.as_array.map do |method|
161
+ method.source_location && method.source_location.first || nil
162
+ end.group_by(&:itself).map do |key, value|
163
+ [key, value.count]
164
+ end.reject { |k, _v| k.nil? }
165
+ ]
166
+ end
167
+
168
+ def select_matching_methods(string, match_case)
169
+ self.own_methods.as_array.select do |method|
170
+ compiled_method = CompiledMethod.compile(method)
171
+ if compiled_method.source_contains?(string, match_case)
172
+ compiled_method.unwrap
173
+ end
174
+ end
175
+ end
176
+
177
+ def select_c_methods
178
+ self.own_methods.as_array.select do |method|
179
+ compiled_method = CompiledMethod.compile(method)
180
+ if compiled_method.c_method?
181
+ compiled_method.unwrap
182
+ end
183
+ end
184
+ end
185
+
186
+ def select_rb_methods
187
+ self.own_methods.as_array.select do |method|
188
+ compiled_method = CompiledMethod.compile(method)
189
+ if compiled_method.rb_method?
190
+ compiled_method.unwrap
191
+ end
192
+ end
193
+ end
194
+
195
+ def select_senders_of(message)
196
+ self.own_methods.as_array.select do |method|
197
+ compiled_method = CompiledMethod.compile(method)
198
+ if compiled_method.sends_message?(message)
199
+ compiled_method.unwrap
200
+ end
201
+ end
202
+ end
203
+
204
+ def all_messages
205
+ MethodQuery.execute(
206
+ collection: self.own_methods,
207
+ query: :select_sent_messages)
208
+ end
209
+
210
+ def which_selectors_store_into(ivar)
211
+ self.selectors.select do |sel|
212
+ meth = self.instance_method(sel)
213
+ meth.writes_field?(ivar)
214
+ end
215
+ end
216
+
217
+ def ancestor_methods
218
+ AncestorMethodFinder.find_all_ancestors(of: self)
219
+ end
220
+
221
+ def includes_selector?(selector)
222
+ self.own_selectors.as_array.include?(selector)
223
+ end
224
+ end
225
+ end
226
+ end