orbacle 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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