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.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +119 -0
- data/LICENSE +22 -0
- data/Makefile +57 -0
- data/README.md +53 -0
- data/circle.yml +82 -0
- data/exe/orbaclerun +6 -0
- data/index.html +106 -0
- data/lib/orbacle.rb +96 -0
- data/lib/orbacle/ast_utils.rb +35 -0
- data/lib/orbacle/bottom_type.rb +23 -0
- data/lib/orbacle/builder.rb +1414 -0
- data/lib/orbacle/builder/context.rb +71 -0
- data/lib/orbacle/builder/operator_assignment_processors.rb +80 -0
- data/lib/orbacle/class_type.rb +32 -0
- data/lib/orbacle/command_line_interface.rb +107 -0
- data/lib/orbacle/const_name.rb +33 -0
- data/lib/orbacle/const_ref.rb +53 -0
- data/lib/orbacle/constants_tree.rb +73 -0
- data/lib/orbacle/define_builtins.rb +139 -0
- data/lib/orbacle/engine.rb +74 -0
- data/lib/orbacle/find_definition_under_position.rb +76 -0
- data/lib/orbacle/generic_type.rb +35 -0
- data/lib/orbacle/global_tree.rb +280 -0
- data/lib/orbacle/graph.rb +126 -0
- data/lib/orbacle/indexer.rb +151 -0
- data/lib/orbacle/integer_id_generator.rb +13 -0
- data/lib/orbacle/lambda_type.rb +37 -0
- data/lib/orbacle/lang_server.rb +64 -0
- data/lib/orbacle/main_type.rb +23 -0
- data/lib/orbacle/nesting.rb +78 -0
- data/lib/orbacle/node.rb +23 -0
- data/lib/orbacle/nominal_type.rb +32 -0
- data/lib/orbacle/ruby_parser.rb +19 -0
- data/lib/orbacle/scope.rb +63 -0
- data/lib/orbacle/selfie.rb +41 -0
- data/lib/orbacle/type_pretty_printer.rb +24 -0
- data/lib/orbacle/typing_service.rb +816 -0
- data/lib/orbacle/union_type.rb +40 -0
- data/lib/orbacle/uuid_id_generator.rb +11 -0
- data/lib/orbacle/worklist.rb +51 -0
- data/orbacle.gemspec +33 -0
- metadata +258 -0
data/lib/orbacle/node.rb
ADDED
@@ -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
|