orbacle 0.1.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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile +2 -0
  7. data/Gemfile.lock +119 -0
  8. data/LICENSE +22 -0
  9. data/Makefile +57 -0
  10. data/README.md +53 -0
  11. data/circle.yml +82 -0
  12. data/exe/orbaclerun +6 -0
  13. data/index.html +106 -0
  14. data/lib/orbacle.rb +96 -0
  15. data/lib/orbacle/ast_utils.rb +35 -0
  16. data/lib/orbacle/bottom_type.rb +23 -0
  17. data/lib/orbacle/builder.rb +1414 -0
  18. data/lib/orbacle/builder/context.rb +71 -0
  19. data/lib/orbacle/builder/operator_assignment_processors.rb +80 -0
  20. data/lib/orbacle/class_type.rb +32 -0
  21. data/lib/orbacle/command_line_interface.rb +107 -0
  22. data/lib/orbacle/const_name.rb +33 -0
  23. data/lib/orbacle/const_ref.rb +53 -0
  24. data/lib/orbacle/constants_tree.rb +73 -0
  25. data/lib/orbacle/define_builtins.rb +139 -0
  26. data/lib/orbacle/engine.rb +74 -0
  27. data/lib/orbacle/find_definition_under_position.rb +76 -0
  28. data/lib/orbacle/generic_type.rb +35 -0
  29. data/lib/orbacle/global_tree.rb +280 -0
  30. data/lib/orbacle/graph.rb +126 -0
  31. data/lib/orbacle/indexer.rb +151 -0
  32. data/lib/orbacle/integer_id_generator.rb +13 -0
  33. data/lib/orbacle/lambda_type.rb +37 -0
  34. data/lib/orbacle/lang_server.rb +64 -0
  35. data/lib/orbacle/main_type.rb +23 -0
  36. data/lib/orbacle/nesting.rb +78 -0
  37. data/lib/orbacle/node.rb +23 -0
  38. data/lib/orbacle/nominal_type.rb +32 -0
  39. data/lib/orbacle/ruby_parser.rb +19 -0
  40. data/lib/orbacle/scope.rb +63 -0
  41. data/lib/orbacle/selfie.rb +41 -0
  42. data/lib/orbacle/type_pretty_printer.rb +24 -0
  43. data/lib/orbacle/typing_service.rb +816 -0
  44. data/lib/orbacle/union_type.rb +40 -0
  45. data/lib/orbacle/uuid_id_generator.rb +11 -0
  46. data/lib/orbacle/worklist.rb +51 -0
  47. data/orbacle.gemspec +33 -0
  48. metadata +258 -0
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orbacle
4
+ class Node
5
+ def initialize(type, params, location = nil)
6
+ @type = type
7
+ raise if !params.is_a?(Hash)
8
+ @params = params
9
+ @location = location
10
+ end
11
+
12
+ attr_reader :type, :params
13
+ attr_accessor :location
14
+
15
+ def ==(other)
16
+ @type == other.type && @params == other.params
17
+ end
18
+
19
+ def to_s
20
+ "#<#{self.class.name}:#{self.object_id} @type=#{@type.inspect}>"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orbacle
4
+ class NominalType
5
+ def initialize(name)
6
+ @name = name
7
+ end
8
+
9
+ attr_reader :name
10
+
11
+ def ==(other)
12
+ self.class == other.class &&
13
+ self.name == other.name
14
+ end
15
+
16
+ def hash
17
+ [
18
+ self.class,
19
+ self.name,
20
+ ].hash ^ BIG_VALUE
21
+ end
22
+ alias eql? ==
23
+
24
+ def each_possible_type
25
+ yield self
26
+ end
27
+
28
+ def bottom?
29
+ false
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'parser/current'
4
+
5
+ module Orbacle
6
+ class RubyParser
7
+ Error = Class.new(StandardError)
8
+ SyntaxError = Class.new(Error)
9
+ EncodingError = Class.new(Error)
10
+
11
+ def parse(content)
12
+ Parser::CurrentRuby.parse(content)
13
+ rescue Parser::SyntaxError
14
+ raise SyntaxError
15
+ rescue ::EncodingError
16
+ raise EncodingError
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orbacle
4
+ class Scope
5
+ def self.empty
6
+ new([], false)
7
+ end
8
+
9
+ def initialize(elems, is_eigenclass)
10
+ @elems = elems
11
+ @is_eigenclass = is_eigenclass
12
+ end
13
+
14
+ attr_reader :elems
15
+
16
+ def increase_by_ref(const_ref)
17
+ if const_ref.absolute?
18
+ Scope.new(const_ref.const_name.elems, false)
19
+ else
20
+ Scope.new(elems + const_ref.const_name.elems, eigenclass?)
21
+ end
22
+ end
23
+
24
+ def increase_by_eigenclass
25
+ raise if eigenclass?
26
+ Scope.new(elems, true)
27
+ end
28
+
29
+ def decrease
30
+ if eigenclass?
31
+ Scope.new(elems, false)
32
+ elsif elems.empty?
33
+ raise
34
+ else
35
+ Scope.new(elems[0..-2], false)
36
+ end
37
+ end
38
+
39
+ def empty?
40
+ elems.empty?
41
+ end
42
+
43
+ def absolute_str
44
+ elems.join("::")
45
+ end
46
+
47
+ def eigenclass?
48
+ @is_eigenclass
49
+ end
50
+
51
+ def ==(other)
52
+ @elems == other.elems && eigenclass? == other.eigenclass?
53
+ end
54
+
55
+ def to_s
56
+ absolute_str
57
+ end
58
+
59
+ def to_const_name
60
+ ConstName.new(elems)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orbacle
4
+ class Selfie
5
+ def self.klass_from_scope(scope)
6
+ new(:klass, scope)
7
+ end
8
+
9
+ def self.instance_from_scope(scope)
10
+ new(:instance, scope)
11
+ end
12
+
13
+ def self.main
14
+ new(:main, nil)
15
+ end
16
+
17
+ def initialize(kind, scope)
18
+ @kind = kind
19
+ @scope = scope
20
+ raise if ![:klass, :instance, :main].include?(kind)
21
+ end
22
+
23
+ def klass?
24
+ @kind == :klass
25
+ end
26
+
27
+ def instance?
28
+ @kind == :instance
29
+ end
30
+
31
+ def main?
32
+ @kind == :main
33
+ end
34
+
35
+ attr_reader :kind, :scope
36
+
37
+ def ==(other)
38
+ @kind == other.kind && @scope == other.scope
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orbacle
4
+ class TypePrettyPrinter
5
+ def call(type)
6
+ case type
7
+ when BottomType
8
+ "unknown"
9
+ when ClassType
10
+ "class(#{type.name})"
11
+ when NominalType
12
+ type.name
13
+ when GenericType
14
+ pretty_parameters = type.parameters.map(&method(:call))
15
+ "generic(#{type.name}, [#{pretty_parameters.join(", ")}])"
16
+ when MainType
17
+ "main"
18
+ when UnionType
19
+ included_types = type.types.map(&method(:call))
20
+ "Union(#{included_types.join(" or ")})"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,816 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orbacle
4
+ class TypingService
5
+ TypingError = Class.new(StandardError)
6
+ class UnknownNodeKindError < TypingError
7
+ def initialize(node)
8
+ @node = node
9
+ end
10
+ attr_reader :node
11
+ end
12
+
13
+ def initialize(logger, stats)
14
+ @logger = logger
15
+ @stats = stats
16
+ end
17
+
18
+ def call(graph, worklist, state)
19
+ @worklist = worklist
20
+ @graph = graph
21
+ @state = state
22
+
23
+ stats.set_value(:initial_nodes, @graph.vertices.size)
24
+ stats.set_value(:initial_message_sends, @worklist.message_sends.size)
25
+
26
+ @graph.vertices.to_a.each {|v| @worklist.enqueue_node(v) }
27
+ while !@worklist.nodes.empty?
28
+ while !@worklist.nodes.empty?
29
+ while !@worklist.nodes.empty?
30
+ node = @worklist.pop_node
31
+ @worklist.count_node(node)
32
+ if !@worklist.limit_exceeded?(node)
33
+ current_result = @state.type_of(node)
34
+ new_result = compute_result(node, @graph.parent_vertices(node))
35
+ raise ArgumentError.new(node) if new_result.nil?
36
+ @state.set_type_of(node, new_result)
37
+ stats.inc(:processed_nodes)
38
+ logger.debug("Processed nodes: #{stats.counter(:processed_nodes)} remaining nodes #{@worklist.nodes.size} msends #{@worklist.handled_message_sends.size} / #{@worklist.message_sends.size}") if stats.counter(:processed_nodes) % 1000 == 0
39
+
40
+ if current_result != @state.type_of(node)
41
+ @graph.adjacent_vertices(node).each do |adjacent_node|
42
+ @worklist.enqueue_node(adjacent_node)
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ @worklist.message_sends.each do |message_send|
49
+ case message_send
50
+ when Worklist::MessageSend
51
+ if satisfied_message_send?(message_send) && !@worklist.message_send_handled?(message_send)
52
+ handle_message_send(message_send)
53
+ @worklist.mark_message_send_as_handled(message_send)
54
+ end
55
+ when Worklist::SuperSend
56
+ if satisfied_super_send?(message_send) && !@worklist.message_send_handled?(message_send)
57
+ handle_super_send(message_send)
58
+ @worklist.mark_message_send_as_handled(message_send)
59
+ end
60
+ else raise "Not handled message send"
61
+ end
62
+ end
63
+ end
64
+
65
+ @worklist.message_sends.each do |message_send|
66
+ case message_send
67
+ when Worklist::MessageSend
68
+ if !@worklist.message_send_handled?(message_send)
69
+ handle_message_send(message_send)
70
+ @worklist.mark_message_send_as_handled(message_send)
71
+ end
72
+ when Worklist::SuperSend
73
+ if !@worklist.message_send_handled?(message_send)
74
+ handle_super_send(message_send)
75
+ @worklist.mark_message_send_as_handled(message_send)
76
+ end
77
+ else raise "Not handled message send"
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ private
84
+ attr_reader :logger, :stats
85
+
86
+ def compute_result(node, sources)
87
+ case node.type
88
+ when :int then handle_int(node, sources)
89
+ when :float then handle_float(node, sources)
90
+ when :nil then handle_nil(node, sources)
91
+ when :bool then handle_bool(node, sources)
92
+ when :str then handle_just_string(node, sources)
93
+ when :dstr then handle_just_string(node, sources)
94
+ when :xstr then handle_just_string(node, sources)
95
+ when :sym then handle_just_symbol(node, sources)
96
+ when :dsym then handle_just_symbol(node, sources)
97
+ when :regexp then handle_regexp(node, sources)
98
+
99
+ when :array then handle_wrap_array(node, sources)
100
+ when :splat_array then handle_unwrap_array(node, sources)
101
+
102
+ when :hash_keys then handle_group(node, sources)
103
+ when :hash_values then handle_group(node, sources)
104
+ when :hash then handle_hash(node, sources)
105
+
106
+ when :range_from then handle_group(node, sources)
107
+ when :range_to then handle_group(node, sources)
108
+ when :range then handle_range(node, sources)
109
+
110
+ when :lvar then handle_group(node, sources)
111
+ when :lvasgn then handle_pass_lte1(node, sources)
112
+
113
+ when :ivasgn then handle_group(node, sources)
114
+ when :ivar_definition then handle_group(node, sources)
115
+ when :clivar_definition then handle_group(node, sources)
116
+ when :ivar then handle_pass1(node, sources)
117
+
118
+ when :cvasgn then handle_group(node, sources)
119
+ when :cvar_definition then handle_group(node, sources)
120
+ when :cvar then handle_pass1(node, sources)
121
+
122
+ when :gvasgn then handle_group(node, sources)
123
+ when :gvar_definition then handle_group(node, sources)
124
+ when :gvar then handle_pass1(node, sources)
125
+ when :backref then handle_just_string(node, sources)
126
+ when :nthref then handle_just_string(node, sources)
127
+
128
+ when :defined then handle_maybe_string(node, sources)
129
+
130
+ when :casgn then handle_group(node, sources)
131
+ when :const then handle_const(node, sources)
132
+
133
+ when :self then handle_self(node, sources)
134
+
135
+ when :call_obj then handle_pass1(node, sources)
136
+ when :call_result then handle_group(node, sources)
137
+ when :call_arg then handle_group(node, sources)
138
+ when :call_splatarg then handle_group(node, sources)
139
+ when :formal_arg then handle_group(node, sources)
140
+ when :formal_optarg then handle_group(node, sources)
141
+ when :formal_restarg then handle_wrap_array(node, sources)
142
+ when :formal_kwarg then handle_group(node, sources)
143
+ when :formal_kwoptarg then handle_group(node, sources)
144
+ when :formal_kwrestarg then handle_group(node, sources)
145
+ when :formal_blockarg then handle_group(node, sources)
146
+ when :block_result then handle_pass_lte1(node, sources)
147
+
148
+ when :loop_operator then handle_bottom(node, sources)
149
+
150
+ when :rescue then handle_group(node, sources)
151
+ when :ensure then handle_group(node, sources)
152
+ when :retry then handle_bottom(node, sources)
153
+ when :unwrap_error_array then handle_unwrap_error_array(node, sources)
154
+
155
+ when :if_result then handle_group(node, sources)
156
+
157
+ when :and then handle_and(node, sources)
158
+ when :or then handle_or(node, sources)
159
+
160
+ when :unwrap_array then handle_unwrap_array(node, sources)
161
+ when :wrap_array then handle_wrap_array(node, sources)
162
+
163
+ when :case_result then handle_group(node, sources)
164
+
165
+ when :for then handle_pass1(node, sources)
166
+
167
+ # not really tested
168
+ when :dynamic_const then handle_bottom(node, sources)
169
+ when :unwrap_hash_values then handle_unwrap_hash_values(node, sources)
170
+ when :unwrap_hash_keys then handle_unwrap_hash_keys(node, sources)
171
+ when :const_definition then handle_group(node, sources)
172
+ when :constructor then handle_constructor(node, sources)
173
+ when :method_result then handle_group(node, sources)
174
+ when :extract_class then handle_extract_class(node, sources)
175
+ when :lambda then handle_lambda(node, sources)
176
+ when :definition_by_id then handle_definition_by_id(node, sources)
177
+ when :yield_result then handle_group(node, sources)
178
+
179
+ else raise UnknownNodeKindError.new(node)
180
+ end
181
+ rescue UnknownNodeKindError => e
182
+ logger.error("Unknown node kind '#{e.node.type}' at #{e.node.location}")
183
+ raise
184
+ rescue => e
185
+ logger.error("Typing failed with error '#{e.inspect}' at node #{node.type} at #{node.location}")
186
+ raise
187
+ end
188
+
189
+ def handle_int(node, sources)
190
+ NominalType.new("Integer")
191
+ end
192
+
193
+ def handle_regexp(_node, _sources)
194
+ NominalType.new("Regexp")
195
+ end
196
+
197
+ def handle_nil(_node, _sources)
198
+ NominalType.new("Nil")
199
+ end
200
+
201
+ def handle_bool(*args)
202
+ NominalType.new("Boolean")
203
+ end
204
+
205
+ def handle_float(*args)
206
+ NominalType.new("Float")
207
+ end
208
+
209
+ def handle_just_string(node, sources)
210
+ NominalType.new("String")
211
+ end
212
+
213
+ def handle_and(node, sources)
214
+ build_union([
215
+ handle_group(node, sources),
216
+ NominalType.new("Boolean"),
217
+ NominalType.new("Nil"),
218
+ ])
219
+ end
220
+
221
+ def handle_or(node, sources)
222
+ build_union([
223
+ handle_group(node, sources),
224
+ NominalType.new("Boolean"),
225
+ NominalType.new("Nil"),
226
+ ])
227
+ end
228
+
229
+ def handle_maybe_string(node, sources)
230
+ build_union([NominalType.new("String"), NominalType.new("Nil")])
231
+ end
232
+
233
+ def handle_just_symbol(node, sources)
234
+ NominalType.new("Symbol")
235
+ end
236
+
237
+ def handle_bottom(node, sources)
238
+ BottomType.new
239
+ end
240
+
241
+ def handle_lambda(node, sources)
242
+ ProcType.new(node.params.fetch(:id))
243
+ end
244
+
245
+ def handle_unwrap_hash_keys(node, sources)
246
+ raise if sources.size != 1
247
+ source = sources.first
248
+ if @state.type_of(source).is_a?(GenericType)
249
+ @state.type_of(source).parameters.at(0)
250
+ else
251
+ BottomType.new
252
+ end
253
+ end
254
+
255
+ def handle_unwrap_hash_values(node, sources)
256
+ raise if sources.size != 1
257
+ source = sources.first
258
+ if @state.type_of(source).is_a?(GenericType)
259
+ @state.type_of(source).parameters.at(1)
260
+ else
261
+ BottomType.new
262
+ end
263
+ end
264
+
265
+ def handle_group(node, sources)
266
+ sources_types = sources.map {|source_node| @state.type_of(source_node) }
267
+ build_union(sources_types)
268
+ end
269
+
270
+ def handle_pass1(node, sources)
271
+ raise if sources.size != 1
272
+ source = sources.first
273
+ @state.type_of(source)
274
+ end
275
+
276
+ def handle_hash(_node, sources)
277
+ hash_keys_node = sources.find {|s| s.type == :hash_keys }
278
+ hash_values_node = sources.find {|s| s.type == :hash_values }
279
+ GenericType.new("Hash", [@state.type_of(hash_keys_node), @state.type_of(hash_values_node)])
280
+ end
281
+
282
+ def handle_range(node, sources)
283
+ sources_types = sources.map {|source_node| @state.type_of(source_node) }
284
+ GenericType.new("Range", [build_union(sources_types)])
285
+ end
286
+
287
+ def handle_self(node, sources)
288
+ selfie = node.params.fetch(:selfie)
289
+ if selfie.klass?
290
+ if selfie.scope.empty?
291
+ BottomType.new
292
+ else
293
+ ClassType.new(selfie.scope.absolute_str)
294
+ end
295
+ elsif selfie.instance?
296
+ if selfie.scope.empty?
297
+ BottomType.new
298
+ else
299
+ type_from_class_name(selfie.scope.absolute_str)
300
+ end
301
+ elsif selfie.main?
302
+ MainType.new
303
+ else
304
+ raise
305
+ end
306
+ end
307
+
308
+ def type_from_class_name(name)
309
+ if ["Array", "Hash", "Range"].include?(name)
310
+ GenericType.new(name, [])
311
+ else
312
+ NominalType.new(name)
313
+ end
314
+ end
315
+
316
+ def handle_unwrap_array(node, sources)
317
+ types_inside_arrays = []
318
+ sources
319
+ .each do |s|
320
+ @state.type_of(s).each_possible_type do |t|
321
+ if t.name == "Array"
322
+ types_inside_arrays << t.parameters.first
323
+ end
324
+ end
325
+ end
326
+ build_union(types_inside_arrays)
327
+ end
328
+
329
+ def handle_unwrap_error_array(node, sources)
330
+ result = []
331
+ handle_unwrap_array(node, sources).each_possible_type do |t|
332
+ if t.is_a?(ClassType)
333
+ result << NominalType.new(t.name)
334
+ end
335
+ end
336
+ build_union(result)
337
+ end
338
+
339
+ def handle_wrap_array(_node, sources)
340
+ GenericType.new("Array", [build_union(sources.map {|s| @state.type_of(s) })])
341
+ end
342
+
343
+ def handle_pass_lte1(_node, sources)
344
+ raise if sources.size > 1
345
+ @state.type_of(sources.first)
346
+ end
347
+
348
+ def build_union(sources_types)
349
+ sources_types_without_unknowns = sources_types.compact.reject(&:bottom?).uniq
350
+ if sources_types_without_unknowns.size == 0
351
+ BottomType.new
352
+ elsif sources_types_without_unknowns.size == 1
353
+ sources_types_without_unknowns.first
354
+ else
355
+ UnionType.new(sources_types_without_unknowns.flat_map {|t| get_possible_types(t) }.uniq)
356
+ end
357
+ end
358
+
359
+ def get_possible_types(type)
360
+ case type
361
+ when UnionType then type.types.flat_map {|t| get_possible_types(t) }
362
+ else [type]
363
+ end
364
+ end
365
+
366
+ def handle_const(node, sources)
367
+ const_ref = node.params.fetch(:const_ref)
368
+ ref_result = @state.solve_reference(const_ref)
369
+ if ref_result && @graph.constants[ref_result.full_name]
370
+ const_definition_node = @graph.constants[ref_result.full_name]
371
+ @graph.add_edge(const_definition_node, node)
372
+ @worklist.enqueue_node(const_definition_node)
373
+ @state.type_of(const_definition_node)
374
+ elsif ref_result
375
+ ClassType.new(ref_result.full_name)
376
+ else
377
+ ClassType.new(const_ref.relative_name)
378
+ end
379
+ end
380
+
381
+ def handle_extract_class(node, sources)
382
+ res = sources.map do |source|
383
+ extract_class(@state.type_of(sources.first))
384
+ end
385
+ build_union(res)
386
+ end
387
+
388
+ def extract_class(type)
389
+ case type
390
+ when NominalType then ClassType.new(type.name)
391
+ when GenericType then ClassType.new(type.name)
392
+ when ClassType then ClassType.new("Class")
393
+ when UnionType then build_union(type.types.map {|t| extract_class(t) })
394
+ when MainType then ClassType.new("Object")
395
+ end
396
+ end
397
+
398
+ def defined_type?(t)
399
+ t.class != BottomType
400
+ end
401
+
402
+ def satisfied_message_send?(message_send)
403
+ defined_type?(@state.type_of(message_send.send_obj)) &&
404
+ message_send.send_args.all? {|a| defined_type?(@state.type_of(a)) }
405
+ end
406
+
407
+ def satisfied_super_send?(super_send)
408
+ super_send.send_args.all? {|a| defined_type?(@state.type_of(a)) }
409
+ end
410
+
411
+ def handle_message_send(message_send)
412
+ @state.type_of(message_send.send_obj).each_possible_type do |possible_type|
413
+ if constructor_send?(possible_type, message_send.message_send)
414
+ handle_constructor_send(possible_type.name, possible_type.name, message_send)
415
+ elsif possible_type.instance_of?(ProcType) && message_send.message_send == "call"
416
+ handle_proc_call(possible_type, message_send)
417
+ elsif possible_type.is_a?(ClassType)
418
+ handle_class_send(possible_type.name, message_send)
419
+ else
420
+ handle_instance_send(possible_type.name, message_send)
421
+ end
422
+ end
423
+ end
424
+
425
+ def handle_proc_call(lambda_type, message_send)
426
+ found_lambda = @state.get_lambda(lambda_type.lambda_id)
427
+ found_lambda_nodes = @graph.get_lambda_nodes(found_lambda.id)
428
+ connect_actual_args_to_formal_args(found_lambda.args, found_lambda_nodes.args, message_send.send_args)
429
+ @graph.add_edge(found_lambda_nodes.result, message_send.send_result)
430
+ @worklist.enqueue_node(message_send.send_result)
431
+ end
432
+
433
+ def handle_constructor_send(original_class_name, class_name, message_send)
434
+ found_method = @state.find_instance_method_from_class_name(class_name, "initialize")
435
+ if found_method.nil?
436
+ parent_name = @state.get_parent_of(class_name)
437
+ if parent_name
438
+ handle_constructor_send(original_class_name, parent_name, message_send)
439
+ else
440
+ connect_constructor_to_node(original_class_name, message_send.send_result)
441
+ end
442
+ else
443
+ handle_custom_message_send(found_method, message_send)
444
+ connect_constructor_to_node(class_name, message_send.send_result)
445
+ end
446
+ end
447
+
448
+ def connect_constructor_to_node(class_name, node)
449
+ constructor_node = Node.new(:constructor, { name: class_name }, nil)
450
+ @graph.add_vertex(constructor_node)
451
+ @graph.add_edge(constructor_node, node)
452
+ @worklist.enqueue_node(constructor_node)
453
+ end
454
+
455
+ def handle_instance_nonprimitive_send(class_name, message_send)
456
+ found_method = @state.find_instance_method_from_class_name(class_name, message_send.message_send)
457
+ if found_method.nil?
458
+ parent_name = @state.get_parent_of(class_name)
459
+ if parent_name
460
+ handle_instance_send(parent_name, message_send)
461
+ else
462
+ # logger.debug("Method #{message_send.message_send} not found in #{class_name} (location: #{message_send.location})\n")
463
+ end
464
+ else
465
+ handle_custom_message_send(found_method, message_send)
466
+ connect_method_result_to_node(found_method.id, message_send.send_result)
467
+ end
468
+ end
469
+
470
+ def handle_class_nonprimitive_send(class_name, message_send)
471
+ found_method = @state.find_class_method_from_class_name(class_name, message_send.message_send)
472
+ if found_method.nil?
473
+ parent_name = @state.get_parent_of(class_name)
474
+ if parent_name
475
+ handle_class_send(parent_name, message_send)
476
+ else
477
+ # logger.debug("Method #{message_send.message_send} not found in #{class_name} (location: #{message_send.location})\n")
478
+ end
479
+ else
480
+ handle_custom_message_send(found_method, message_send)
481
+ connect_method_result_to_node(found_method.id, message_send.send_result)
482
+ end
483
+ end
484
+
485
+ def connect_actual_args_to_formal_args(found_method_argtree, found_method_nodes, send_args)
486
+ if send_args.last
487
+ if found_method_argtree.kwargs.empty?
488
+ regular_args = send_args
489
+ connect_regular_args(found_method_argtree.args, found_method_nodes, regular_args)
490
+ else
491
+ @state.type_of(send_args.last).each_possible_type do |type|
492
+ if type.name == "Hash"
493
+ regular_args = send_args[0..-2]
494
+ kwarg_arg = send_args.last
495
+
496
+ connect_regular_args(found_method_argtree.args, found_method_nodes, regular_args)
497
+ connect_keyword_args(found_method_argtree.kwargs, found_method_nodes, kwarg_arg)
498
+ else
499
+ regular_args = send_args
500
+ connect_regular_args(found_method_argtree.args, found_method_nodes, regular_args)
501
+ end
502
+ end
503
+ end
504
+ end
505
+ end
506
+
507
+ def handle_custom_message_send(found_method, message_send)
508
+ found_method_nodes = @graph.get_metod_nodes(found_method.id)
509
+ connect_actual_args_to_formal_args(found_method.args, found_method_nodes.args, message_send.send_args)
510
+
511
+ found_method_nodes.zsupers.each do |zsuper_call|
512
+ super_method = @state.find_super_method(found_method.id)
513
+ next if super_method.nil?
514
+
515
+ super_method_nodes = @graph.get_metod_nodes(super_method.id)
516
+ connect_actual_args_to_formal_args(super_method.args, super_method_nodes.args, message_send.send_args)
517
+ if zsuper_call.block.nil?
518
+ connect_yields_to_block_lambda(@graph.get_metod_nodes(super_method.id).yields, message_send.block)
519
+ else
520
+ connect_yields_to_block_lambda(@graph.get_metod_nodes(super_method.id).yields, zsuper_call.block)
521
+ end
522
+ connect_method_result_to_node(super_method.id, zsuper_call.send_result)
523
+ end
524
+
525
+ connect_yields_to_block_lambda(@graph.get_metod_nodes(found_method.id).yields, message_send.block)
526
+ end
527
+
528
+ def lambda_ids_of_block(block)
529
+ case block
530
+ when Worklist::BlockLambda
531
+ [block.lambda_id]
532
+ when Worklist::BlockNode
533
+ @state.type_of(block.node).enum_for(:each_possible_type).map do |possible_type|
534
+ if possible_type.instance_of?(ProcType)
535
+ possible_type.lambda_id
536
+ end
537
+ end.compact
538
+ when NilClass
539
+ []
540
+ else raise
541
+ end
542
+ end
543
+
544
+ def connect_yields_to_block_lambda(yields_nodes, block_node)
545
+ yields_nodes.each do |yield_nodes|
546
+ lambda_ids = lambda_ids_of_block(block_node)
547
+ lambda_ids.each do |lambda_id|
548
+ block_lambda = @state.get_lambda(lambda_id)
549
+ block_lambda_nodes = @graph.get_lambda_nodes(lambda_id)
550
+ connect_actual_args_to_formal_args(block_lambda.args, block_lambda_nodes.args, yield_nodes.send_args)
551
+
552
+ @graph.add_edge(block_lambda_nodes.result, yield_nodes.send_result)
553
+ @worklist.enqueue_node(yield_nodes.send_result)
554
+ end
555
+ end
556
+ end
557
+
558
+ def generate_possible_args(splatsize, send_args)
559
+ if send_args.empty?
560
+ [[]]
561
+ else
562
+ head, *rest = send_args
563
+ tails = generate_possible_args(splatsize, rest)
564
+ if head.type == :call_arg
565
+ tails.map {|tail| [head, *tail] }
566
+ elsif head.type == :call_splatarg
567
+ unwrap_node = Node.new(:unwrap_array, {})
568
+ @graph.add_edge(head, unwrap_node)
569
+ @worklist.enqueue_node(unwrap_node)
570
+ (splatsize + 1).times.flat_map do |splat_array_possible_size|
571
+ unwraps = [unwrap_node] * splat_array_possible_size
572
+ tails.map do |tail|
573
+ [*unwraps, *tail]
574
+ end
575
+ end
576
+ else raise
577
+ end
578
+ end
579
+ end
580
+
581
+ def connect_regular_args(found_method_args, found_method_nodes, basic_send_args)
582
+ possible_send_args = generate_possible_args(found_method_args.size, basic_send_args)
583
+ possible_send_args.each do |send_args|
584
+ found_method_args.each_with_index do |formal_arg, i|
585
+ next if formal_arg.name.nil?
586
+ node_formal_arg = found_method_nodes[formal_arg.name]
587
+
588
+ next if send_args[i].nil?
589
+ case formal_arg
590
+ when GlobalTree::ArgumentsTree::Regular
591
+ @graph.add_edge(send_args[i], node_formal_arg)
592
+ @worklist.enqueue_node(node_formal_arg)
593
+ when GlobalTree::ArgumentsTree::Optional
594
+ @graph.add_edge(send_args[i], node_formal_arg)
595
+ @worklist.enqueue_node(node_formal_arg)
596
+ when GlobalTree::ArgumentsTree::Splat
597
+ send_args[i..-1].each do |send_arg|
598
+ @graph.add_edge(send_arg, node_formal_arg)
599
+ @worklist.enqueue_node(node_formal_arg)
600
+ end
601
+ else raise
602
+ end
603
+ end
604
+ end
605
+ end
606
+
607
+ def connect_keyword_args(found_method_kwargs, found_method_nodes, kwarg_arg)
608
+ unwrapping_node = Node.new(:unwrap_hash_values, {}, nil)
609
+ @graph.add_vertex(unwrapping_node)
610
+ @graph.add_edge(kwarg_arg, unwrapping_node)
611
+ @worklist.enqueue_node(unwrapping_node)
612
+
613
+ found_method_kwargs.each do |formal_kwarg|
614
+ next if formal_kwarg.name.nil?
615
+ node_formal_kwarg = found_method_nodes[formal_kwarg.name]
616
+
617
+ case formal_kwarg
618
+ when GlobalTree::ArgumentsTree::Regular
619
+ @graph.add_edge(unwrapping_node, node_formal_kwarg)
620
+ @worklist.enqueue_node(node_formal_kwarg)
621
+ when GlobalTree::ArgumentsTree::Optional
622
+ @graph.add_edge(unwrapping_node, node_formal_kwarg)
623
+ @worklist.enqueue_node(node_formal_kwarg)
624
+ when GlobalTree::ArgumentsTree::Splat
625
+ @graph.add_edge(kwarg_arg, node_formal_kwarg)
626
+ @worklist.enqueue_node(node_formal_kwarg)
627
+ else raise
628
+ end
629
+ end
630
+ end
631
+
632
+ def handle_instance_send(class_name, message_send)
633
+ if primitive_send?(class_name, message_send.message_send)
634
+ handle_primitive(class_name, message_send)
635
+ else
636
+ handle_instance_nonprimitive_send(class_name, message_send)
637
+ end
638
+ end
639
+
640
+ def handle_class_send(class_name, message_send)
641
+ if primitive_class_send?(class_name, message_send.message_send)
642
+ handle_class_primitive(class_name, message_send)
643
+ else
644
+ handle_class_nonprimitive_send(class_name, message_send)
645
+ end
646
+ end
647
+
648
+ def handle_super_send(super_send)
649
+ return if super_send.method_id.nil?
650
+ super_method = @state.find_super_method(super_send.method_id)
651
+ return if super_method.nil?
652
+
653
+ handle_custom_message_send(super_method, super_send)
654
+
655
+ connect_method_result_to_node(super_method.id, super_send.send_result)
656
+ end
657
+
658
+ def connect_method_result_to_node(method_id, node)
659
+ method_result_node = @graph.get_metod_nodes(method_id).result
660
+ if !@graph.has_edge?(method_result_node, node)
661
+ @graph.add_edge(method_result_node, node)
662
+ @worklist.enqueue_node(node)
663
+ end
664
+ end
665
+
666
+ def primitive_mapping
667
+ {
668
+ "Array" => {
669
+ "map" => method(:send_primitive_array_map),
670
+ "each" => method(:send_primitive_array_each),
671
+ },
672
+ "Object" => {
673
+ "class" => method(:send_primitive_object_class),
674
+ "clone" => method(:send_primitive_object_freeze),
675
+ "dup" => method(:send_primitive_object_freeze),
676
+ "freeze" => method(:send_primitive_object_freeze),
677
+ "itself" => method(:send_primitive_object_freeze),
678
+ "taint" => method(:send_primitive_object_freeze),
679
+ "trust" => method(:send_primitive_object_freeze),
680
+ "untaint" => method(:send_primitive_object_freeze),
681
+ "untrust" => method(:send_primitive_object_freeze),
682
+ },
683
+ }
684
+ end
685
+
686
+ def primitive_class_mapping
687
+ {
688
+ "Object" => {
689
+ "class" => method(:send_class_primitive_object_class),
690
+ },
691
+ }
692
+ end
693
+
694
+ def primitive_send?(class_name, message_name)
695
+ if primitive_mapping[class_name] && primitive_mapping[class_name][message_name]
696
+ true
697
+ else
698
+ false
699
+ end
700
+ end
701
+
702
+ def primitive_class_send?(class_name, message_name)
703
+ if primitive_class_mapping[class_name] && primitive_class_mapping[class_name][message_name]
704
+ true
705
+ else
706
+ false
707
+ end
708
+ end
709
+
710
+ def constructor_send?(type, message_name)
711
+ type.is_a?(ClassType) && message_name == "new"
712
+ end
713
+
714
+ def handle_primitive(class_name, message_send)
715
+ message_name = message_send.message_send
716
+ primitive_mapping[class_name][message_name].(class_name, message_send)
717
+ end
718
+
719
+ def handle_class_primitive(class_name, message_send)
720
+ message_name = message_send.message_send
721
+ primitive_class_mapping[class_name][message_name].(class_name, message_send)
722
+ end
723
+
724
+ def send_constructor(type, message_send)
725
+ already_handled = @graph.adjacent_vertices(message_send.send_obj).any? do |adjacent_node|
726
+ adjacent_node.type == :constructor
727
+ end
728
+ return if already_handled
729
+
730
+ node = Node.new(:constructor, { name: type.name }, nil)
731
+ @graph.add_vertex(node)
732
+ @graph.add_edge(message_send.send_obj, node)
733
+ @worklist.enqueue_node(node)
734
+ @graph.add_edge(node, message_send.send_result)
735
+ @worklist.enqueue_node(message_send.send_result)
736
+ end
737
+
738
+ def send_primitive_array_map(_class_name, message_send)
739
+ unwrapping_node = Node.new(:unwrap_array, {}, nil)
740
+ @graph.add_vertex(unwrapping_node)
741
+ @graph.add_edge(message_send.send_obj, unwrapping_node)
742
+ @worklist.enqueue_node(unwrapping_node)
743
+
744
+ wrapping_node = Node.new(:wrap_array, {}, nil)
745
+ @graph.add_vertex(wrapping_node)
746
+ @graph.add_edge(wrapping_node, message_send.send_result)
747
+ @worklist.enqueue_node(message_send.send_result)
748
+
749
+ lambda_ids_of_block(message_send.block).each do |lambda_id|
750
+ block_lambda_nodes = @graph.get_lambda_nodes(lambda_id)
751
+ if !block_lambda_nodes.args.values.empty?
752
+ arg_node = block_lambda_nodes.args.values.first
753
+ @graph.add_edge(unwrapping_node, arg_node)
754
+ @worklist.enqueue_node(arg_node)
755
+ end
756
+
757
+ @graph.add_edge(block_lambda_nodes.result, wrapping_node)
758
+ @worklist.enqueue_node(wrapping_node)
759
+ end
760
+ end
761
+
762
+ def send_primitive_array_each(_class_name, message_send)
763
+ return unless Worklist::BlockLambda === message_send.block
764
+
765
+ unwrapping_node = Node.new(:unwrap_array, {}, nil)
766
+ @graph.add_vertex(unwrapping_node)
767
+ @graph.add_edge(message_send.send_obj, unwrapping_node)
768
+ @worklist.enqueue_node(unwrapping_node)
769
+ block_lambda_nodes = @graph.get_lambda_nodes(message_send.block.lambda_id)
770
+ if !block_lambda_nodes.args.values.empty?
771
+ arg_node = block_lambda_nodes.args.values.first
772
+ @graph.add_edge(unwrapping_node, arg_node)
773
+ @worklist.enqueue_node(arg_node)
774
+ end
775
+
776
+ @graph.add_edge(message_send.send_obj, message_send.send_result)
777
+ @worklist.enqueue_node(message_send.send_result)
778
+ end
779
+
780
+ def send_primitive_object_freeze(_class_name, message_send)
781
+ @graph.add_edge(message_send.send_obj, message_send.send_result)
782
+ @worklist.enqueue_node(message_send.send_result)
783
+ end
784
+
785
+ def send_primitive_object_class(_class_name, message_send)
786
+ extract_class_node = Node.new(:extract_class, {}, nil)
787
+ @graph.add_vertex(extract_class_node)
788
+ @graph.add_edge(message_send.send_obj, extract_class_node)
789
+ @worklist.enqueue_node(extract_class_node)
790
+
791
+ @graph.add_edge(extract_class_node, message_send.send_result)
792
+ @worklist.enqueue_node(message_send.send_result)
793
+ end
794
+
795
+ def send_class_primitive_object_class(_class_name, message_send)
796
+ extract_class_node = Node.new(:extract_class, {}, nil)
797
+ @graph.add_vertex(extract_class_node)
798
+ @graph.add_edge(message_send.send_obj, extract_class_node)
799
+ @worklist.enqueue_node(extract_class_node)
800
+
801
+ @graph.add_edge(extract_class_node, message_send.send_result)
802
+ @worklist.enqueue_node(message_send.send_result)
803
+ end
804
+
805
+ def handle_constructor(node, sources)
806
+ name = node.params.fetch(:name)
807
+ type_from_class_name(name)
808
+ end
809
+
810
+ def handle_definition_by_id(node, sources)
811
+ definition_id = node.params.fetch(:id)
812
+ const = @state.find_constant_for_definition(definition_id)
813
+ const ? ClassType.new(const.full_name) : BottomType.new
814
+ end
815
+ end
816
+ end