irb 1.9.1 → 1.10.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 +4 -4
- data/Gemfile +4 -4
- data/README.md +10 -9
- data/Rakefile +2 -2
- data/lib/irb/cmd/history.rb +47 -0
- data/lib/irb/cmd/show_cmds.rb +6 -0
- data/lib/irb/cmd/show_source.rb +9 -2
- data/lib/irb/completion.rb +21 -0
- data/lib/irb/context.rb +22 -14
- data/lib/irb/debug.rb +18 -0
- data/lib/irb/extend-command.rb +6 -0
- data/lib/irb/history.rb +1 -1
- data/lib/irb/init.rb +3 -4
- data/lib/irb/lc/help-message +1 -0
- data/lib/irb/pager.rb +12 -11
- data/lib/irb/source_finder.rb +26 -6
- data/lib/irb/version.rb +2 -2
- data/lib/irb.rb +48 -40
- metadata +3 -7
- data/lib/irb/type_completion/completor.rb +0 -235
- data/lib/irb/type_completion/methods.rb +0 -13
- data/lib/irb/type_completion/scope.rb +0 -412
- data/lib/irb/type_completion/type_analyzer.rb +0 -1169
- data/lib/irb/type_completion/types.rb +0 -426
@@ -1,235 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'prism'
|
4
|
-
require 'irb/completion'
|
5
|
-
require_relative 'type_analyzer'
|
6
|
-
|
7
|
-
module IRB
|
8
|
-
module TypeCompletion
|
9
|
-
class Completor < BaseCompletor # :nodoc:
|
10
|
-
HIDDEN_METHODS = %w[Namespace TypeName] # defined by rbs, should be hidden
|
11
|
-
|
12
|
-
class << self
|
13
|
-
attr_accessor :last_completion_error
|
14
|
-
end
|
15
|
-
|
16
|
-
def inspect
|
17
|
-
name = 'TypeCompletion::Completor'
|
18
|
-
prism_info = "Prism: #{Prism::VERSION}"
|
19
|
-
if Types.rbs_builder
|
20
|
-
"#{name}(#{prism_info}, RBS: #{RBS::VERSION})"
|
21
|
-
elsif Types.rbs_load_error
|
22
|
-
"#{name}(#{prism_info}, RBS: #{Types.rbs_load_error.inspect})"
|
23
|
-
else
|
24
|
-
"#{name}(#{prism_info}, RBS: loading)"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def completion_candidates(preposing, target, _postposing, bind:)
|
29
|
-
@preposing = preposing
|
30
|
-
verbose, $VERBOSE = $VERBOSE, nil
|
31
|
-
code = "#{preposing}#{target}"
|
32
|
-
@result = analyze code, bind
|
33
|
-
name, candidates = candidates_from_result(@result)
|
34
|
-
|
35
|
-
all_symbols_pattern = /\A[ -\/:-@\[-`\{-~]*\z/
|
36
|
-
candidates.map(&:to_s).select { !_1.match?(all_symbols_pattern) && _1.start_with?(name) }.uniq.sort.map do
|
37
|
-
target + _1[name.size..]
|
38
|
-
end
|
39
|
-
rescue SyntaxError, StandardError => e
|
40
|
-
Completor.last_completion_error = e
|
41
|
-
handle_error(e)
|
42
|
-
[]
|
43
|
-
ensure
|
44
|
-
$VERBOSE = verbose
|
45
|
-
end
|
46
|
-
|
47
|
-
def doc_namespace(preposing, matched, postposing, bind:)
|
48
|
-
name = matched[/[a-zA-Z_0-9]*[!?=]?\z/]
|
49
|
-
method_doc = -> type do
|
50
|
-
type = type.types.find { _1.all_methods.include? name.to_sym }
|
51
|
-
case type
|
52
|
-
when Types::SingletonType
|
53
|
-
"#{Types.class_name_of(type.module_or_class)}.#{name}"
|
54
|
-
when Types::InstanceType
|
55
|
-
"#{Types.class_name_of(type.klass)}##{name}"
|
56
|
-
end
|
57
|
-
end
|
58
|
-
call_or_const_doc = -> type do
|
59
|
-
if name =~ /\A[A-Z]/
|
60
|
-
type = type.types.grep(Types::SingletonType).find { _1.module_or_class.const_defined?(name) }
|
61
|
-
type.module_or_class == Object ? name : "#{Types.class_name_of(type.module_or_class)}::#{name}" if type
|
62
|
-
else
|
63
|
-
method_doc.call(type)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
value_doc = -> type do
|
68
|
-
return unless type
|
69
|
-
type.types.each do |t|
|
70
|
-
case t
|
71
|
-
when Types::SingletonType
|
72
|
-
return Types.class_name_of(t.module_or_class)
|
73
|
-
when Types::InstanceType
|
74
|
-
return Types.class_name_of(t.klass)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
nil
|
78
|
-
end
|
79
|
-
|
80
|
-
case @result
|
81
|
-
in [:call_or_const, type, _name, _self_call]
|
82
|
-
call_or_const_doc.call type
|
83
|
-
in [:const, type, _name, scope]
|
84
|
-
if type
|
85
|
-
call_or_const_doc.call type
|
86
|
-
else
|
87
|
-
value_doc.call scope[name]
|
88
|
-
end
|
89
|
-
in [:gvar, _name, scope]
|
90
|
-
value_doc.call scope["$#{name}"]
|
91
|
-
in [:ivar, _name, scope]
|
92
|
-
value_doc.call scope["@#{name}"]
|
93
|
-
in [:cvar, _name, scope]
|
94
|
-
value_doc.call scope["@@#{name}"]
|
95
|
-
in [:call, type, _name, _self_call]
|
96
|
-
method_doc.call type
|
97
|
-
in [:lvar_or_method, _name, scope]
|
98
|
-
if scope.local_variables.include?(name)
|
99
|
-
value_doc.call scope[name]
|
100
|
-
else
|
101
|
-
method_doc.call scope.self_type
|
102
|
-
end
|
103
|
-
else
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def candidates_from_result(result)
|
108
|
-
candidates = case result
|
109
|
-
in [:require, name]
|
110
|
-
retrieve_files_to_require_from_load_path
|
111
|
-
in [:require_relative, name]
|
112
|
-
retrieve_files_to_require_relative_from_current_dir
|
113
|
-
in [:call_or_const, type, name, self_call]
|
114
|
-
((self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS) | type.constants
|
115
|
-
in [:const, type, name, scope]
|
116
|
-
if type
|
117
|
-
scope_constants = type.types.flat_map do |t|
|
118
|
-
scope.table_module_constants(t.module_or_class) if t.is_a?(Types::SingletonType)
|
119
|
-
end
|
120
|
-
(scope_constants.compact | type.constants.map(&:to_s)).sort
|
121
|
-
else
|
122
|
-
scope.constants.sort | ReservedWords
|
123
|
-
end
|
124
|
-
in [:ivar, name, scope]
|
125
|
-
ivars = scope.instance_variables.sort
|
126
|
-
name == '@' ? ivars + scope.class_variables.sort : ivars
|
127
|
-
in [:cvar, name, scope]
|
128
|
-
scope.class_variables
|
129
|
-
in [:gvar, name, scope]
|
130
|
-
scope.global_variables
|
131
|
-
in [:symbol, name]
|
132
|
-
Symbol.all_symbols.map { _1.inspect[1..] }
|
133
|
-
in [:call, type, name, self_call]
|
134
|
-
(self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS
|
135
|
-
in [:lvar_or_method, name, scope]
|
136
|
-
scope.self_type.all_methods.map(&:to_s) | scope.local_variables | ReservedWords
|
137
|
-
else
|
138
|
-
[]
|
139
|
-
end
|
140
|
-
[name || '', candidates]
|
141
|
-
end
|
142
|
-
|
143
|
-
def analyze(code, binding = Object::TOPLEVEL_BINDING)
|
144
|
-
# Workaround for https://github.com/ruby/prism/issues/1592
|
145
|
-
return if code.match?(/%[qQ]\z/)
|
146
|
-
|
147
|
-
ast = Prism.parse(code, scopes: [binding.local_variables]).value
|
148
|
-
name = code[/(@@|@|\$)?\w*[!?=]?\z/]
|
149
|
-
*parents, target_node = find_target ast, code.bytesize - name.bytesize
|
150
|
-
return unless target_node
|
151
|
-
|
152
|
-
calculate_scope = -> { TypeAnalyzer.calculate_target_type_scope(binding, parents, target_node).last }
|
153
|
-
calculate_type_scope = ->(node) { TypeAnalyzer.calculate_target_type_scope binding, [*parents, target_node], node }
|
154
|
-
|
155
|
-
case target_node
|
156
|
-
when Prism::StringNode, Prism::InterpolatedStringNode
|
157
|
-
call_node, args_node = parents.last(2)
|
158
|
-
return unless call_node.is_a?(Prism::CallNode) && call_node.receiver.nil?
|
159
|
-
return unless args_node.is_a?(Prism::ArgumentsNode) && args_node.arguments.size == 1
|
160
|
-
|
161
|
-
case call_node.name
|
162
|
-
when :require
|
163
|
-
[:require, name.rstrip]
|
164
|
-
when :require_relative
|
165
|
-
[:require_relative, name.rstrip]
|
166
|
-
end
|
167
|
-
when Prism::SymbolNode
|
168
|
-
if parents.last.is_a? Prism::BlockArgumentNode # method(&:target)
|
169
|
-
receiver_type, _scope = calculate_type_scope.call target_node
|
170
|
-
[:call, receiver_type, name, false]
|
171
|
-
else
|
172
|
-
[:symbol, name] unless name.empty?
|
173
|
-
end
|
174
|
-
when Prism::CallNode
|
175
|
-
return [:lvar_or_method, name, calculate_scope.call] if target_node.receiver.nil?
|
176
|
-
|
177
|
-
self_call = target_node.receiver.is_a? Prism::SelfNode
|
178
|
-
op = target_node.call_operator
|
179
|
-
receiver_type, _scope = calculate_type_scope.call target_node.receiver
|
180
|
-
receiver_type = receiver_type.nonnillable if op == '&.'
|
181
|
-
[op == '::' ? :call_or_const : :call, receiver_type, name, self_call]
|
182
|
-
when Prism::LocalVariableReadNode, Prism::LocalVariableTargetNode
|
183
|
-
[:lvar_or_method, name, calculate_scope.call]
|
184
|
-
when Prism::ConstantReadNode, Prism::ConstantTargetNode
|
185
|
-
if parents.last.is_a? Prism::ConstantPathNode
|
186
|
-
path_node = parents.last
|
187
|
-
if path_node.parent # A::B
|
188
|
-
receiver, scope = calculate_type_scope.call(path_node.parent)
|
189
|
-
[:const, receiver, name, scope]
|
190
|
-
else # ::A
|
191
|
-
scope = calculate_scope.call
|
192
|
-
[:const, Types::SingletonType.new(Object), name, scope]
|
193
|
-
end
|
194
|
-
else
|
195
|
-
[:const, nil, name, calculate_scope.call]
|
196
|
-
end
|
197
|
-
when Prism::GlobalVariableReadNode, Prism::GlobalVariableTargetNode
|
198
|
-
[:gvar, name, calculate_scope.call]
|
199
|
-
when Prism::InstanceVariableReadNode, Prism::InstanceVariableTargetNode
|
200
|
-
[:ivar, name, calculate_scope.call]
|
201
|
-
when Prism::ClassVariableReadNode, Prism::ClassVariableTargetNode
|
202
|
-
[:cvar, name, calculate_scope.call]
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
def find_target(node, position)
|
207
|
-
location = (
|
208
|
-
case node
|
209
|
-
when Prism::CallNode
|
210
|
-
node.message_loc
|
211
|
-
when Prism::SymbolNode
|
212
|
-
node.value_loc
|
213
|
-
when Prism::StringNode
|
214
|
-
node.content_loc
|
215
|
-
when Prism::InterpolatedStringNode
|
216
|
-
node.closing_loc if node.parts.empty?
|
217
|
-
end
|
218
|
-
)
|
219
|
-
return [node] if location&.start_offset == position
|
220
|
-
|
221
|
-
node.compact_child_nodes.each do |n|
|
222
|
-
match = find_target(n, position)
|
223
|
-
next unless match
|
224
|
-
match.unshift node
|
225
|
-
return match
|
226
|
-
end
|
227
|
-
|
228
|
-
[node] if node.location.start_offset == position
|
229
|
-
end
|
230
|
-
|
231
|
-
def handle_error(e)
|
232
|
-
end
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module IRB
|
4
|
-
module TypeCompletion
|
5
|
-
module Methods
|
6
|
-
OBJECT_SINGLETON_CLASS_METHOD = Object.instance_method(:singleton_class)
|
7
|
-
OBJECT_INSTANCE_VARIABLES_METHOD = Object.instance_method(:instance_variables)
|
8
|
-
OBJECT_INSTANCE_VARIABLE_GET_METHOD = Object.instance_method(:instance_variable_get)
|
9
|
-
OBJECT_CLASS_METHOD = Object.instance_method(:class)
|
10
|
-
MODULE_NAME_METHOD = Module.instance_method(:name)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
@@ -1,412 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'set'
|
4
|
-
require_relative 'types'
|
5
|
-
|
6
|
-
module IRB
|
7
|
-
module TypeCompletion
|
8
|
-
|
9
|
-
class RootScope
|
10
|
-
attr_reader :module_nesting, :self_object
|
11
|
-
|
12
|
-
def initialize(binding, self_object, local_variables)
|
13
|
-
@binding = binding
|
14
|
-
@self_object = self_object
|
15
|
-
@cache = {}
|
16
|
-
modules = [*binding.eval('::Module.nesting'), Object]
|
17
|
-
@module_nesting = modules.map { [_1, []] }
|
18
|
-
binding_local_variables = binding.local_variables
|
19
|
-
uninitialized_locals = local_variables - binding_local_variables
|
20
|
-
uninitialized_locals.each { @cache[_1] = Types::NIL }
|
21
|
-
@local_variables = (local_variables | binding_local_variables).map(&:to_s).to_set
|
22
|
-
@global_variables = Kernel.global_variables.map(&:to_s).to_set
|
23
|
-
@owned_constants_cache = {}
|
24
|
-
end
|
25
|
-
|
26
|
-
def level() = 0
|
27
|
-
|
28
|
-
def level_of(_name, _var_type) = 0
|
29
|
-
|
30
|
-
def mutable?() = false
|
31
|
-
|
32
|
-
def module_own_constant?(mod, name)
|
33
|
-
set = (@owned_constants_cache[mod] ||= Set.new(mod.constants.map(&:to_s)))
|
34
|
-
set.include? name
|
35
|
-
end
|
36
|
-
|
37
|
-
def get_const(nesting, path, _key = nil)
|
38
|
-
return unless nesting
|
39
|
-
|
40
|
-
result = path.reduce nesting do |mod, name|
|
41
|
-
return nil unless mod.is_a?(Module) && module_own_constant?(mod, name)
|
42
|
-
mod.const_get name
|
43
|
-
end
|
44
|
-
Types.type_from_object result
|
45
|
-
end
|
46
|
-
|
47
|
-
def get_cvar(nesting, path, name, _key = nil)
|
48
|
-
return Types::NIL unless nesting
|
49
|
-
|
50
|
-
result = path.reduce nesting do |mod, n|
|
51
|
-
return Types::NIL unless mod.is_a?(Module) && module_own_constant?(mod, n)
|
52
|
-
mod.const_get n
|
53
|
-
end
|
54
|
-
value = result.class_variable_get name if result.is_a?(Module) && name.size >= 3 && result.class_variable_defined?(name)
|
55
|
-
Types.type_from_object value
|
56
|
-
end
|
57
|
-
|
58
|
-
def [](name)
|
59
|
-
@cache[name] ||= (
|
60
|
-
value = case RootScope.type_by_name name
|
61
|
-
when :ivar
|
62
|
-
begin
|
63
|
-
Methods::OBJECT_INSTANCE_VARIABLE_GET_METHOD.bind_call(@self_object, name)
|
64
|
-
rescue NameError
|
65
|
-
end
|
66
|
-
when :lvar
|
67
|
-
begin
|
68
|
-
@binding.local_variable_get(name)
|
69
|
-
rescue NameError
|
70
|
-
end
|
71
|
-
when :gvar
|
72
|
-
@binding.eval name if @global_variables.include? name
|
73
|
-
end
|
74
|
-
Types.type_from_object(value)
|
75
|
-
)
|
76
|
-
end
|
77
|
-
|
78
|
-
def self_type
|
79
|
-
Types.type_from_object @self_object
|
80
|
-
end
|
81
|
-
|
82
|
-
def local_variables() = @local_variables.to_a
|
83
|
-
|
84
|
-
def global_variables() = @global_variables.to_a
|
85
|
-
|
86
|
-
def self.type_by_name(name)
|
87
|
-
if name.start_with? '@@'
|
88
|
-
# "@@cvar" or "@@cvar::[module_id]::[module_path]"
|
89
|
-
:cvar
|
90
|
-
elsif name.start_with? '@'
|
91
|
-
:ivar
|
92
|
-
elsif name.start_with? '$'
|
93
|
-
:gvar
|
94
|
-
elsif name.start_with? '%'
|
95
|
-
:internal
|
96
|
-
elsif name[0].downcase != name[0] || name[0].match?(/\d/)
|
97
|
-
# "ConstName" or "[module_id]::[const_path]"
|
98
|
-
:const
|
99
|
-
else
|
100
|
-
:lvar
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
class Scope
|
106
|
-
BREAK_RESULT = '%break'
|
107
|
-
NEXT_RESULT = '%next'
|
108
|
-
RETURN_RESULT = '%return'
|
109
|
-
PATTERNMATCH_BREAK = '%match'
|
110
|
-
|
111
|
-
attr_reader :parent, :mergeable_changes, :level, :module_nesting
|
112
|
-
|
113
|
-
def self.from_binding(binding, locals) = new(RootScope.new(binding, binding.receiver, locals))
|
114
|
-
|
115
|
-
def initialize(parent, table = {}, trace_ivar: true, trace_lvar: true, self_type: nil, nesting: nil)
|
116
|
-
@parent = parent
|
117
|
-
@level = parent.level + 1
|
118
|
-
@trace_ivar = trace_ivar
|
119
|
-
@trace_lvar = trace_lvar
|
120
|
-
@module_nesting = nesting ? [nesting, *parent.module_nesting] : parent.module_nesting
|
121
|
-
@self_type = self_type
|
122
|
-
@terminated = false
|
123
|
-
@jump_branches = []
|
124
|
-
@mergeable_changes = @table = table.transform_values { [level, _1] }
|
125
|
-
end
|
126
|
-
|
127
|
-
def mutable? = true
|
128
|
-
|
129
|
-
def terminated?
|
130
|
-
@terminated
|
131
|
-
end
|
132
|
-
|
133
|
-
def terminate_with(type, value)
|
134
|
-
return if terminated?
|
135
|
-
store_jump type, value, @mergeable_changes
|
136
|
-
terminate
|
137
|
-
end
|
138
|
-
|
139
|
-
def store_jump(type, value, changes)
|
140
|
-
return if terminated?
|
141
|
-
if has_own?(type)
|
142
|
-
changes[type] = [level, value]
|
143
|
-
@jump_branches << changes
|
144
|
-
elsif @parent.mutable?
|
145
|
-
@parent.store_jump(type, value, changes)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def terminate
|
150
|
-
return if terminated?
|
151
|
-
@terminated = true
|
152
|
-
@table = @mergeable_changes.dup
|
153
|
-
end
|
154
|
-
|
155
|
-
def trace?(name)
|
156
|
-
return false unless @parent
|
157
|
-
type = RootScope.type_by_name(name)
|
158
|
-
type == :ivar ? @trace_ivar : type == :lvar ? @trace_lvar : true
|
159
|
-
end
|
160
|
-
|
161
|
-
def level_of(name, var_type)
|
162
|
-
case var_type
|
163
|
-
when :ivar
|
164
|
-
return level unless @trace_ivar
|
165
|
-
when :gvar
|
166
|
-
return 0
|
167
|
-
end
|
168
|
-
variable_level, = @table[name]
|
169
|
-
variable_level || parent.level_of(name, var_type)
|
170
|
-
end
|
171
|
-
|
172
|
-
def get_const(nesting, path, key = nil)
|
173
|
-
key ||= [nesting.__id__, path].join('::')
|
174
|
-
_l, value = @table[key]
|
175
|
-
value || @parent.get_const(nesting, path, key)
|
176
|
-
end
|
177
|
-
|
178
|
-
def get_cvar(nesting, path, name, key = nil)
|
179
|
-
key ||= [name, nesting.__id__, path].join('::')
|
180
|
-
_l, value = @table[key]
|
181
|
-
value || @parent.get_cvar(nesting, path, name, key)
|
182
|
-
end
|
183
|
-
|
184
|
-
def [](name)
|
185
|
-
type = RootScope.type_by_name(name)
|
186
|
-
if type == :const
|
187
|
-
return get_const(nil, nil, name) || Types::NIL if name.include?('::')
|
188
|
-
|
189
|
-
module_nesting.each do |(nesting, path)|
|
190
|
-
value = get_const nesting, [*path, name]
|
191
|
-
return value if value
|
192
|
-
end
|
193
|
-
return Types::NIL
|
194
|
-
elsif type == :cvar
|
195
|
-
return get_cvar(nil, nil, nil, name) if name.include?('::')
|
196
|
-
|
197
|
-
nesting, path = module_nesting.first
|
198
|
-
return get_cvar(nesting, path, name)
|
199
|
-
end
|
200
|
-
level, value = @table[name]
|
201
|
-
if level
|
202
|
-
value
|
203
|
-
elsif trace? name
|
204
|
-
@parent[name]
|
205
|
-
elsif type == :ivar
|
206
|
-
self_instance_variable_get name
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
def set_const(nesting, path, value)
|
211
|
-
key = [nesting.__id__, path].join('::')
|
212
|
-
@table[key] = [0, value]
|
213
|
-
end
|
214
|
-
|
215
|
-
def set_cvar(nesting, path, name, value)
|
216
|
-
key = [name, nesting.__id__, path].join('::')
|
217
|
-
@table[key] = [0, value]
|
218
|
-
end
|
219
|
-
|
220
|
-
def []=(name, value)
|
221
|
-
type = RootScope.type_by_name(name)
|
222
|
-
if type == :const
|
223
|
-
if name.include?('::')
|
224
|
-
@table[name] = [0, value]
|
225
|
-
else
|
226
|
-
parent_module, parent_path = module_nesting.first
|
227
|
-
set_const parent_module, [*parent_path, name], value
|
228
|
-
end
|
229
|
-
return
|
230
|
-
elsif type == :cvar
|
231
|
-
if name.include?('::')
|
232
|
-
@table[name] = [0, value]
|
233
|
-
else
|
234
|
-
parent_module, parent_path = module_nesting.first
|
235
|
-
set_cvar parent_module, parent_path, name, value
|
236
|
-
end
|
237
|
-
return
|
238
|
-
end
|
239
|
-
variable_level = level_of name, type
|
240
|
-
@table[name] = [variable_level, value] if variable_level
|
241
|
-
end
|
242
|
-
|
243
|
-
def self_type
|
244
|
-
@self_type || @parent.self_type
|
245
|
-
end
|
246
|
-
|
247
|
-
def global_variables
|
248
|
-
gvar_keys = @table.keys.select do |name|
|
249
|
-
RootScope.type_by_name(name) == :gvar
|
250
|
-
end
|
251
|
-
gvar_keys | @parent.global_variables
|
252
|
-
end
|
253
|
-
|
254
|
-
def local_variables
|
255
|
-
lvar_keys = @table.keys.select do |name|
|
256
|
-
RootScope.type_by_name(name) == :lvar
|
257
|
-
end
|
258
|
-
lvar_keys |= @parent.local_variables if @trace_lvar
|
259
|
-
lvar_keys
|
260
|
-
end
|
261
|
-
|
262
|
-
def table_constants
|
263
|
-
constants = module_nesting.flat_map do |mod, path|
|
264
|
-
prefix = [mod.__id__, *path].join('::') + '::'
|
265
|
-
@table.keys.select { _1.start_with? prefix }.map { _1.delete_prefix(prefix).split('::').first }
|
266
|
-
end.uniq
|
267
|
-
constants |= @parent.table_constants if @parent.mutable?
|
268
|
-
constants
|
269
|
-
end
|
270
|
-
|
271
|
-
def table_module_constants(mod)
|
272
|
-
prefix = "#{mod.__id__}::"
|
273
|
-
constants = @table.keys.select { _1.start_with? prefix }.map { _1.delete_prefix(prefix).split('::').first }
|
274
|
-
constants |= @parent.table_constants if @parent.mutable?
|
275
|
-
constants
|
276
|
-
end
|
277
|
-
|
278
|
-
def base_scope
|
279
|
-
@parent.mutable? ? @parent.base_scope : @parent
|
280
|
-
end
|
281
|
-
|
282
|
-
def table_instance_variables
|
283
|
-
ivars = @table.keys.select { RootScope.type_by_name(_1) == :ivar }
|
284
|
-
ivars |= @parent.table_instance_variables if @parent.mutable? && @trace_ivar
|
285
|
-
ivars
|
286
|
-
end
|
287
|
-
|
288
|
-
def instance_variables
|
289
|
-
self_singleton_types = self_type.types.grep(Types::SingletonType)
|
290
|
-
singleton_classes = self_type.types.grep(Types::InstanceType).map(&:klass).select(&:singleton_class?)
|
291
|
-
base_self = base_scope.self_object
|
292
|
-
self_instance_variables = singleton_classes.flat_map do |singleton_class|
|
293
|
-
if singleton_class.respond_to? :attached_object
|
294
|
-
Methods::OBJECT_INSTANCE_VARIABLES_METHOD.bind_call(singleton_class.attached_object).map(&:to_s)
|
295
|
-
elsif singleton_class == Methods::OBJECT_SINGLETON_CLASS_METHOD.bind_call(base_self)
|
296
|
-
Methods::OBJECT_INSTANCE_VARIABLES_METHOD.bind_call(base_self).map(&:to_s)
|
297
|
-
else
|
298
|
-
[]
|
299
|
-
end
|
300
|
-
end
|
301
|
-
[
|
302
|
-
self_singleton_types.flat_map { _1.module_or_class.instance_variables.map(&:to_s) },
|
303
|
-
self_instance_variables || [],
|
304
|
-
table_instance_variables
|
305
|
-
].inject(:|)
|
306
|
-
end
|
307
|
-
|
308
|
-
def self_instance_variable_get(name)
|
309
|
-
self_objects = self_type.types.grep(Types::SingletonType).map(&:module_or_class)
|
310
|
-
singleton_classes = self_type.types.grep(Types::InstanceType).map(&:klass).select(&:singleton_class?)
|
311
|
-
base_self = base_scope.self_object
|
312
|
-
singleton_classes.each do |singleton_class|
|
313
|
-
if singleton_class.respond_to? :attached_object
|
314
|
-
self_objects << singleton_class.attached_object
|
315
|
-
elsif singleton_class == base_self.singleton_class
|
316
|
-
self_objects << base_self
|
317
|
-
end
|
318
|
-
end
|
319
|
-
types = self_objects.map do |object|
|
320
|
-
value = begin
|
321
|
-
Methods::OBJECT_INSTANCE_VARIABLE_GET_METHOD.bind_call(object, name)
|
322
|
-
rescue NameError
|
323
|
-
end
|
324
|
-
Types.type_from_object value
|
325
|
-
end
|
326
|
-
Types::UnionType[*types]
|
327
|
-
end
|
328
|
-
|
329
|
-
def table_class_variables
|
330
|
-
cvars = @table.keys.filter_map { _1.split('::', 2).first if RootScope.type_by_name(_1) == :cvar }
|
331
|
-
cvars |= @parent.table_class_variables if @parent.mutable?
|
332
|
-
cvars
|
333
|
-
end
|
334
|
-
|
335
|
-
def class_variables
|
336
|
-
cvars = table_class_variables
|
337
|
-
m, = module_nesting.first
|
338
|
-
cvars |= m.class_variables.map(&:to_s) if m.is_a? Module
|
339
|
-
cvars
|
340
|
-
end
|
341
|
-
|
342
|
-
def constants
|
343
|
-
module_nesting.flat_map do |nest,|
|
344
|
-
nest.constants
|
345
|
-
end.map(&:to_s) | table_constants
|
346
|
-
end
|
347
|
-
|
348
|
-
def merge_jumps
|
349
|
-
if terminated?
|
350
|
-
@terminated = false
|
351
|
-
@table = @mergeable_changes
|
352
|
-
merge @jump_branches
|
353
|
-
@terminated = true
|
354
|
-
else
|
355
|
-
merge [*@jump_branches, {}]
|
356
|
-
end
|
357
|
-
end
|
358
|
-
|
359
|
-
def conditional(&block)
|
360
|
-
run_branches(block, ->(_s) {}).first || Types::NIL
|
361
|
-
end
|
362
|
-
|
363
|
-
def never(&block)
|
364
|
-
block.call Scope.new(self, { BREAK_RESULT => nil, NEXT_RESULT => nil, PATTERNMATCH_BREAK => nil, RETURN_RESULT => nil })
|
365
|
-
end
|
366
|
-
|
367
|
-
def run_branches(*blocks)
|
368
|
-
results = []
|
369
|
-
branches = []
|
370
|
-
blocks.each do |block|
|
371
|
-
scope = Scope.new self
|
372
|
-
result = block.call scope
|
373
|
-
next if scope.terminated?
|
374
|
-
results << result
|
375
|
-
branches << scope.mergeable_changes
|
376
|
-
end
|
377
|
-
terminate if branches.empty?
|
378
|
-
merge branches
|
379
|
-
results
|
380
|
-
end
|
381
|
-
|
382
|
-
def has_own?(name)
|
383
|
-
@table.key? name
|
384
|
-
end
|
385
|
-
|
386
|
-
def update(child_scope)
|
387
|
-
current_level = level
|
388
|
-
child_scope.mergeable_changes.each do |name, (level, value)|
|
389
|
-
self[name] = value if level <= current_level
|
390
|
-
end
|
391
|
-
end
|
392
|
-
|
393
|
-
protected
|
394
|
-
|
395
|
-
def merge(branches)
|
396
|
-
current_level = level
|
397
|
-
merge = {}
|
398
|
-
branches.each do |changes|
|
399
|
-
changes.each do |name, (level, value)|
|
400
|
-
next if current_level < level
|
401
|
-
(merge[name] ||= []) << value
|
402
|
-
end
|
403
|
-
end
|
404
|
-
merge.each do |name, values|
|
405
|
-
values << self[name] unless values.size == branches.size
|
406
|
-
values.compact!
|
407
|
-
self[name] = Types::UnionType[*values.compact] unless values.empty?
|
408
|
-
end
|
409
|
-
end
|
410
|
-
end
|
411
|
-
end
|
412
|
-
end
|