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,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orbacle
4
+ class DefineBuiltins
5
+ def initialize(graph, tree, id_generator)
6
+ @graph = graph
7
+ @tree = tree
8
+ @id_generator = id_generator
9
+ end
10
+
11
+ def call
12
+ add_object_klass
13
+ add_dir_klass
14
+ add_file_klass
15
+ add_integer_klass
16
+ end
17
+
18
+ private
19
+ attr_reader :id_generator
20
+
21
+ def add_object_klass
22
+ klass = @tree.add_klass(nil)
23
+ @tree.add_constant(
24
+ GlobalTree::Constant.new("Object", Scope.empty, nil, klass.id))
25
+
26
+ # BasicObject
27
+ template_just_bool(klass, "==")
28
+ template_just_bool(klass, "!")
29
+ template_just_bool(klass, "!=")
30
+ template_just_bool(klass, "equal?")
31
+ template_just_int(klass, "object_id")
32
+ template_just_int(klass, "__id__")
33
+
34
+ # Object
35
+ template_just_bool(klass, "!~")
36
+ template_maybe_int(klass, "<=>")
37
+ template_just_bool(klass, "===")
38
+ template_just_nil(klass, "display")
39
+ template_just_bool(klass, "eql?")
40
+ template_just_bool(klass, "frozen?")
41
+ template_just_bool(klass, "instance_of?")
42
+ template_just_bool(klass, "instance_variable_defined?")
43
+ template_just_bool(klass, "is_a?")
44
+ template_just_str(klass, "inspect")
45
+ template_just_bool(klass, "kind_of?")
46
+ template_just_bool(klass, "nil?")
47
+ template_just_bool(klass, "respond_to?")
48
+ template_just_bool(klass, "respond_to_missing?")
49
+ template_just_bool(klass, "tainted?")
50
+ template_just_bool(klass, "untrusted?")
51
+ template_just_str(klass, "to_s")
52
+ end
53
+
54
+ def add_integer_klass
55
+ klass = @tree.add_klass(nil)
56
+ @tree.add_constant(
57
+ GlobalTree::Constant.new("Integer", Scope.empty, nil, klass.id))
58
+
59
+ template_just_int(klass, "succ")
60
+ template_just_int(klass, "+")
61
+ template_just_int(klass, "-")
62
+ template_just_int(klass, "*")
63
+ end
64
+
65
+ def add_dir_klass
66
+ klass = @tree.add_klass(nil)
67
+ @tree.add_constant(
68
+ GlobalTree::Constant.new("Dir", Scope.empty, nil, klass.id))
69
+ eigenclass = @tree.get_eigenclass_of_definition(klass.id)
70
+
71
+ template_just_array_of_str(eigenclass, "glob")
72
+ end
73
+
74
+ def add_file_klass
75
+ klass = @tree.add_klass(nil)
76
+ @tree.add_constant(
77
+ GlobalTree::Constant.new("File", Scope.empty, nil, klass.id))
78
+ eigenclass = @tree.get_eigenclass_of_definition(klass.id)
79
+
80
+ template_just_str(eigenclass, "read")
81
+ end
82
+
83
+ def template_just_int(klass, name)
84
+ metod = template_args(klass, name)
85
+ int_node = Node.new(:int, {})
86
+ @graph.add_edge(int_node, @graph.get_metod_nodes(metod.id).result)
87
+ end
88
+
89
+ def template_maybe_int(klass, name)
90
+ metod = template_args(klass, name)
91
+ int_node = Node.new(:int, {})
92
+ nil_node = Node.new(:nil, {})
93
+ @graph.add_edge(int_node, @graph.get_metod_nodes(metod.id).result)
94
+ @graph.add_edge(nil_node, @graph.get_metod_nodes(metod.id).result)
95
+ end
96
+
97
+ def template_just_str(klass, name)
98
+ metod = template_args(klass, name)
99
+ str_node = Node.new(:str, {})
100
+ @graph.add_edge(str_node, @graph.get_metod_nodes(metod.id).result)
101
+ end
102
+
103
+ def template_just_bool(klass, name)
104
+ metod = template_args(klass, name)
105
+ str_node = Node.new(:bool, {})
106
+ @graph.add_edge(str_node, @graph.get_metod_nodes(metod.id).result)
107
+ end
108
+
109
+ def template_just_nil(klass, name)
110
+ metod = template_args(klass, name)
111
+ str_node = Node.new(:nil, {})
112
+ @graph.add_edge(str_node, @graph.get_metod_nodes(metod.id).result)
113
+ end
114
+
115
+ def template_just_array_of_str(klass, name)
116
+ metod = template_args(klass, name)
117
+ str_node = Node.new(:str, {})
118
+ array_node = Node.new(:array, {})
119
+ @graph.add_edge(str_node, array_node)
120
+ @graph.add_edge(array_node, @graph.get_metod_nodes(metod.id).result)
121
+ end
122
+
123
+ def template_args(klass, name)
124
+ metod = @tree.add_method(
125
+ generate_id,
126
+ klass.id,
127
+ name,
128
+ nil,
129
+ :public,
130
+ GlobalTree::ArgumentsTree.new([], []))
131
+ @graph.store_metod_nodes(metod.id, {})
132
+ metod
133
+ end
134
+
135
+ def generate_id
136
+ id_generator.call
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orbacle
4
+ class Engine
5
+ def initialize(logger)
6
+ @logger = logger
7
+ end
8
+
9
+ attr_reader :stats_recorder
10
+
11
+ def index(project_root)
12
+ @stats_recorder = Indexer::StatsRecorder.new
13
+ service = Indexer.new(logger, stats_recorder)
14
+ @state, @graph, @worklist = service.(project_root: project_root)
15
+ end
16
+
17
+ def get_type_information(filepath, searched_position)
18
+ relevant_nodes = @graph
19
+ .vertices
20
+ .select {|n| n.location && n.location.uri.eql?(filepath) && n.location.position_range.include_position?(searched_position) }
21
+ .sort_by {|n| n.location.span }
22
+
23
+ pretty_print_type(@state.type_of(relevant_nodes.at(0)))
24
+ end
25
+
26
+ def locations_for_definition_under_position(file_path, file_content, position)
27
+ result = find_definition_under_position(file_content, position.line, position.character)
28
+ case result
29
+ when FindDefinitionUnderPosition::ConstantResult
30
+ constants = @state.solve_reference2(result.const_ref)
31
+ constants.map(&:location)
32
+ when FindDefinitionUnderPosition::MessageResult
33
+ caller_type = get_type_of_caller_from_message_send(file_path, result.position_range)
34
+ methods_definitions = get_methods_definitions_for_type(caller_type, result.name)
35
+ methods_definitions = @state.get_methods(result.name) if methods_definitions.empty?
36
+ methods_definitions.map(&:location).compact
37
+ else
38
+ []
39
+ end
40
+ end
41
+
42
+ private
43
+ def get_type_of_caller_from_message_send(file_path, position_range)
44
+ message_send = @worklist
45
+ .message_sends
46
+ .find {|ms| ms.location && ms.location.uri.eql?(file_path) && ms.location.position_range.include_position?(position_range.start) }
47
+ @state.type_of(message_send.send_obj)
48
+ end
49
+
50
+ def get_methods_definitions_for_type(type, method_name)
51
+ case type
52
+ when NominalType
53
+ @state.get_instance_methods_from_class_name(type.name, method_name)
54
+ when ClassType
55
+ @state.get_class_methods_from_class_name(type.name, method_name)
56
+ when UnionType
57
+ type.types_set.flat_map {|t| get_methods_definitions_for_type(t, method_name) }
58
+ else
59
+ []
60
+ end
61
+ end
62
+
63
+ private
64
+ attr_reader :logger
65
+
66
+ def find_definition_under_position(content, line, character)
67
+ FindDefinitionUnderPosition.new(RubyParser.new).process_file(content, Position.new(line, character))
68
+ end
69
+
70
+ def pretty_print_type(type)
71
+ TypePrettyPrinter.new.(type)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orbacle
4
+ class FindDefinitionUnderPosition < Parser::AST::Processor
5
+ include AstUtils
6
+
7
+ ConstantResult = Struct.new(:const_ref)
8
+ MessageResult = Struct.new(:name, :position_range)
9
+
10
+ def initialize(parser)
11
+ @parser = parser
12
+ end
13
+
14
+ def process_file(file_content, searched_position)
15
+ ast = parser.parse(file_content)
16
+
17
+ @current_nesting = Nesting.empty
18
+ @searched_position = searched_position
19
+
20
+ process(ast)
21
+
22
+ @result
23
+ end
24
+
25
+ attr_reader :parser
26
+
27
+ def process(ast)
28
+ new_ast = super
29
+ raise unless ast.equal?(new_ast)
30
+ new_ast
31
+ end
32
+
33
+ def on_const(ast)
34
+ if build_position_range_from_ast(ast).include_position?(@searched_position)
35
+ @result = ConstantResult.new(ConstRef.from_ast(ast, @current_nesting))
36
+ end
37
+ nil
38
+ end
39
+
40
+ def on_class(ast)
41
+ klass_name_ast, _ = ast.children
42
+ klass_name_ref = ConstRef.from_ast(klass_name_ast, @current_nesting)
43
+ with_new_nesting(@current_nesting.increase_nesting_const(klass_name_ref)) do
44
+ super
45
+ end
46
+ nil
47
+ end
48
+
49
+ def on_module(ast)
50
+ module_name_ast, _ = ast.children
51
+ module_name_ref = ConstRef.from_ast(module_name_ast, @current_nesting)
52
+ with_new_nesting(@current_nesting.increase_nesting_const(module_name_ref)) do
53
+ super
54
+ end
55
+ nil
56
+ end
57
+
58
+ def on_send(ast)
59
+ if ast.loc.selector && build_position_range_from_parser_range(ast.loc.selector).include_position?(@searched_position)
60
+ message_name = ast.children.fetch(1).to_s
61
+ selector_position_range = build_position_range_from_parser_range(ast.loc.selector)
62
+ @result = MessageResult.new(message_name, selector_position_range)
63
+ else
64
+ super
65
+ end
66
+ nil
67
+ end
68
+
69
+ def with_new_nesting(new_nesting)
70
+ previous_nesting = @current_nesting
71
+ @current_nesting = new_nesting
72
+ yield
73
+ @current_nesting = previous_nesting
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orbacle
4
+ class GenericType
5
+ def initialize(name, parameters)
6
+ @name = name
7
+ @parameters = parameters
8
+ end
9
+
10
+ attr_reader :name, :parameters
11
+
12
+ def ==(other)
13
+ self.class == other.class &&
14
+ self.name == other.name &&
15
+ self.parameters == other.parameters
16
+ end
17
+
18
+ def hash
19
+ [
20
+ self.class,
21
+ self.name,
22
+ self.parameters,
23
+ ].hash ^ BIG_VALUE
24
+ end
25
+ alias eql? ==
26
+
27
+ def each_possible_type
28
+ yield self
29
+ end
30
+
31
+ def bottom?
32
+ false
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,280 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orbacle
4
+ class GlobalTree
5
+ class ArgumentsTree < Struct.new(:args, :kwargs, :blockarg)
6
+ Regular = Struct.new(:name)
7
+ Optional = Struct.new(:name)
8
+ Splat = Struct.new(:name)
9
+ Nested = Struct.new(:args)
10
+ end
11
+
12
+ class Method
13
+ def initialize(id, place_of_definition_id, name, location, visibility, args)
14
+ raise ArgumentError.new(visibility) if ![:public, :private, :protected].include?(visibility)
15
+
16
+ @id = id
17
+ @place_of_definition_id = place_of_definition_id
18
+ @name = name
19
+ @location = location
20
+ @visibility = visibility
21
+ @args = args
22
+ end
23
+
24
+ attr_reader :id, :name, :location, :args, :place_of_definition_id
25
+ attr_accessor :visibility
26
+ end
27
+
28
+ class Lambda
29
+ def initialize(id, args)
30
+ @id = id
31
+ @args = args
32
+ end
33
+
34
+ attr_reader :id, :args
35
+ end
36
+
37
+ class Klass
38
+ def initialize(id, parent_ref, eigenclass_id = nil)
39
+ @id = id
40
+ @parent_ref = parent_ref
41
+ @eigenclass_id = eigenclass_id
42
+ end
43
+
44
+ attr_reader :id, :parent_ref, :eigenclass_id
45
+ attr_writer :eigenclass_id
46
+
47
+ def ==(other)
48
+ @id == other.id &&
49
+ @parent_ref == other.parent_ref &&
50
+ @eigenclass_id == other.eigenclass_id
51
+ end
52
+ end
53
+
54
+ class Mod
55
+ def initialize(id, eigenclass_id = nil)
56
+ @id = id
57
+ @eigenclass_id = eigenclass_id
58
+ end
59
+
60
+ attr_reader :id, :eigenclass_id
61
+ attr_writer :eigenclass_id
62
+
63
+ def ==(other)
64
+ @id == other.id &&
65
+ @eigenclass_id == other.eigenclass_id
66
+ end
67
+ end
68
+
69
+ class Constant
70
+ def initialize(name, scope, location, definition_id = nil)
71
+ @name = name
72
+ @scope = scope
73
+ @location = location
74
+ @definition_id = definition_id
75
+ end
76
+
77
+ attr_reader :name, :scope, :location, :definition_id
78
+
79
+ def ==(other)
80
+ @name == other.name &&
81
+ @scope == other.scope &&
82
+ @location == other.location &&
83
+ @definition_id == other.definition_id
84
+ end
85
+
86
+ def full_name
87
+ [*scope.elems, @name].join("::")
88
+ end
89
+ end
90
+
91
+ def initialize(id_generator)
92
+ @id_generator = id_generator
93
+ @constants = ConstantsTree.new
94
+ @classes_by_id = {}
95
+ @modules_by_id = {}
96
+ @methods_by_class_id = Hash.new {|h,k| h[k] = Hash.new {|h2, k2| h2[k2] = [] } }
97
+ @methods_by_id = {}
98
+ @lambdas_by_id = {}
99
+ @type_mapping = Hash.new(BottomType.new)
100
+ end
101
+
102
+ ### Methods
103
+
104
+ def add_method(id, place_of_definition_id, name, location, visibility, args)
105
+ metod = Method.new(id, place_of_definition_id, name, location, visibility, args)
106
+ @methods_by_class_id[metod.place_of_definition_id][metod.name] << metod
107
+ @methods_by_id[metod.id] = metod
108
+ return metod
109
+ end
110
+
111
+ def find_instance_method_from_class_name(class_name, method_name)
112
+ get_instance_methods_from_class_name(class_name, method_name).first
113
+ end
114
+
115
+ def find_instance_method_from_class_id(class_id, method_name)
116
+ get_instance_methods_from_class_id(class_id, method_name).first
117
+ end
118
+
119
+ def get_instance_methods_from_class_id(class_id, method_name)
120
+ @methods_by_class_id[class_id][method_name]
121
+ end
122
+
123
+ def get_instance_methods_from_class_name(class_name, method_name)
124
+ klass = find_class_by_name(class_name)
125
+ return [] if klass.nil?
126
+ get_instance_methods_from_class_id(klass.id, method_name)
127
+ end
128
+
129
+ def get_class_methods_from_class_name(class_name, method_name)
130
+ klass = find_class_by_name(class_name)
131
+ return [] if klass.nil?
132
+ eigenclass = get_eigenclass_of_definition(klass.id)
133
+ get_instance_methods_from_class_id(eigenclass.id, method_name)
134
+ end
135
+
136
+ def find_class_method_from_class_name(class_name, method_name)
137
+ get_class_methods_from_class_name(class_name, method_name).first
138
+ end
139
+
140
+ def get_methods(method_name)
141
+ @methods_by_id.values.select do |m|
142
+ m.name.eql?(method_name)
143
+ end
144
+ end
145
+
146
+ def find_super_method(method_id)
147
+ analyzed_method = @methods_by_id.fetch(method_id)
148
+ klass_of_this_method = get_class(analyzed_method.place_of_definition_id)
149
+ return nil if klass_of_this_method.nil? || klass_of_this_method.parent_ref.nil?
150
+ parent_klass = solve_reference(klass_of_this_method.parent_ref)
151
+ return nil if parent_klass.nil?
152
+ find_instance_method_from_class_name(parent_klass.full_name, analyzed_method.name)
153
+ end
154
+
155
+ def change_method_visibility(klass_id, name, new_visibility)
156
+ @methods_by_class_id[klass_id][name].each do |m|
157
+ m.visibility = new_visibility
158
+ end
159
+ end
160
+
161
+ ### Definitions
162
+
163
+ def add_klass(parent_ref)
164
+ klass = Klass.new(id_generator.call, parent_ref)
165
+ @classes_by_id[klass.id] = klass
166
+ klass
167
+ end
168
+
169
+ def add_mod
170
+ mod = Mod.new(id_generator.call)
171
+ @modules_by_id[mod.id] = mod
172
+ mod
173
+ end
174
+
175
+ def get_class(class_id)
176
+ @classes_by_id[class_id]
177
+ end
178
+
179
+ def get_module(module_id)
180
+ @modules_by_id[module_id]
181
+ end
182
+
183
+ def get_definition(definition_id)
184
+ get_class(definition_id) || get_module(definition_id)
185
+ end
186
+
187
+ def get_eigenclass_of_definition(definition_id)
188
+ definition = get_definition(definition_id)
189
+ if definition.eigenclass_id
190
+ get_class(definition.eigenclass_id)
191
+ else
192
+ eigenclass = add_klass(nil)
193
+ definition.eigenclass_id = eigenclass.id
194
+ eigenclass
195
+ end
196
+ end
197
+
198
+ ### Constants
199
+
200
+ def add_constant(constant)
201
+ @constants.add_element(constant.scope, constant.name, constant)
202
+ return constant
203
+ end
204
+
205
+ def solve_reference(const_ref)
206
+ @constants.find_by_const_ref(const_ref)
207
+ end
208
+
209
+ def solve_reference2(const_ref)
210
+ @constants.select_by_const_ref(const_ref)
211
+ end
212
+
213
+ ### Lambdas
214
+
215
+ def add_lambda(args)
216
+ lamba = Lambda.new(id_generator.call, args)
217
+ @lambdas_by_id[lamba.id] = lamba
218
+ lamba
219
+ end
220
+
221
+ def get_lambda(lambda_id)
222
+ @lambdas_by_id[lambda_id]
223
+ end
224
+
225
+ ### Other
226
+
227
+ def get_parent_of(class_name)
228
+ return nil if class_name.eql?("Object")
229
+
230
+ const = find_constant_by_name(class_name)
231
+ return "Object" if const.nil?
232
+
233
+ klass = get_class(const.definition_id)
234
+ return "Object" if klass.nil?
235
+
236
+ return "Object" if klass.parent_ref.nil?
237
+ parent_const = solve_reference(klass.parent_ref)
238
+ if parent_const
239
+ parent_const.full_name
240
+ else
241
+ klass.parent_ref.relative_name
242
+ end
243
+ end
244
+
245
+ def find_class_by_name(full_name)
246
+ const = find_constant_by_name(full_name)
247
+ return nil if const.nil?
248
+ get_class(const.definition_id)
249
+ end
250
+
251
+ def find_module_by_name(full_name)
252
+ const = find_constant_by_name(full_name)
253
+ return nil if const.nil?
254
+ get_module(const.definition_id)
255
+ end
256
+
257
+ def find_constant_by_name(full_name)
258
+ @constants.find_by_const_name(ConstName.from_string(full_name))
259
+ end
260
+
261
+ def find_constant_for_definition(definition_id)
262
+ @constants.find do |constant|
263
+ constant.definition_id == definition_id
264
+ end
265
+ end
266
+
267
+ ### Types
268
+
269
+ def type_of(node)
270
+ @type_mapping[node]
271
+ end
272
+
273
+ def set_type_of(node, new_type)
274
+ @type_mapping[node] = new_type
275
+ end
276
+
277
+ private
278
+ attr_reader :id_generator
279
+ end
280
+ end