class IRB::TypeCompletion::RootScope::Scope
Constants
- BREAK_RESULT
- NEXT_RESULT
- PATTERNMATCH_BREAK
- RETURN_RESULT
Attributes
level[R]
mergeable_changes[R]
module_nesting[R]
parent[R]
Public Class Methods
from_binding(binding, locals)
click to toggle source
# File irb/type_completion/scope.rb, line 113 def self.from_binding(binding, locals) = new(RootScope.new(binding, binding.receiver, locals)) def initialize(parent, table = {}, trace_ivar: true, trace_lvar: true, self_type: nil, nesting: nil) @parent = parent @level = parent.level + 1 @trace_ivar = trace_ivar @trace_lvar = trace_lvar @module_nesting = nesting ? [nesting, *parent.module_nesting] : parent.module_nesting @self_type = self_type @terminated = false @jump_branches = [] @mergeable_changes = @table = table.transform_values { [level, _1] } end def mutable? = true def terminated? @terminated end def terminate_with(type, value) return if terminated? store_jump type, value, @mergeable_changes terminate end def store_jump(type, value, changes) return if terminated? if has_own?(type) changes[type] = [level, value] @jump_branches << changes elsif @parent.mutable? @parent.store_jump(type, value, changes) end end def terminate return if terminated? @terminated = true @table = @mergeable_changes.dup end def trace?(name) return false unless @parent type = RootScope.type_by_name(name) type == :ivar ? @trace_ivar : type == :lvar ? @trace_lvar : true end def level_of(name, var_type) case var_type when :ivar return level unless @trace_ivar when :gvar return 0 end variable_level, = @table[name] variable_level || parent.level_of(name, var_type) end def get_const(nesting, path, key = nil) key ||= [nesting.__id__, path].join('::') _l, value = @table[key] value || @parent.get_const(nesting, path, key) end def get_cvar(nesting, path, name, key = nil) key ||= [name, nesting.__id__, path].join('::') _l, value = @table[key] value || @parent.get_cvar(nesting, path, name, key) end def [](name) type = RootScope.type_by_name(name) if type == :const return get_const(nil, nil, name) || Types::NIL if name.include?('::') module_nesting.each do |(nesting, path)| value = get_const nesting, [*path, name] return value if value end return Types::NIL elsif type == :cvar return get_cvar(nil, nil, nil, name) if name.include?('::') nesting, path = module_nesting.first return get_cvar(nesting, path, name) end level, value = @table[name] if level value elsif trace? name @parent[name] elsif type == :ivar self_instance_variable_get name end end def set_const(nesting, path, value) key = [nesting.__id__, path].join('::') @table[key] = [0, value] end def set_cvar(nesting, path, name, value) key = [name, nesting.__id__, path].join('::') @table[key] = [0, value] end def []=(name, value) type = RootScope.type_by_name(name) if type == :const if name.include?('::') @table[name] = [0, value] else parent_module, parent_path = module_nesting.first set_const parent_module, [*parent_path, name], value end return elsif type == :cvar if name.include?('::') @table[name] = [0, value] else parent_module, parent_path = module_nesting.first set_cvar parent_module, parent_path, name, value end return end variable_level = level_of name, type @table[name] = [variable_level, value] if variable_level end def self_type @self_type || @parent.self_type end def global_variables gvar_keys = @table.keys.select do |name| RootScope.type_by_name(name) == :gvar end gvar_keys | @parent.global_variables end def local_variables lvar_keys = @table.keys.select do |name| RootScope.type_by_name(name) == :lvar end lvar_keys |= @parent.local_variables if @trace_lvar lvar_keys end def table_constants constants = module_nesting.flat_map do |mod, path| prefix = [mod.__id__, *path].join('::') + '::' @table.keys.select { _1.start_with? prefix }.map { _1.delete_prefix(prefix).split('::').first } end.uniq constants |= @parent.table_constants if @parent.mutable? constants end def table_module_constants(mod) prefix = "#{mod.__id__}::" constants = @table.keys.select { _1.start_with? prefix }.map { _1.delete_prefix(prefix).split('::').first } constants |= @parent.table_constants if @parent.mutable? constants end def base_scope @parent.mutable? ? @parent.base_scope : @parent end def table_instance_variables ivars = @table.keys.select { RootScope.type_by_name(_1) == :ivar } ivars |= @parent.table_instance_variables if @parent.mutable? && @trace_ivar ivars end def instance_variables self_singleton_types = self_type.types.grep(Types::SingletonType) singleton_classes = self_type.types.grep(Types::InstanceType).map(&:klass).select(&:singleton_class?) base_self = base_scope.self_object self_instance_variables = singleton_classes.flat_map do |singleton_class| if singleton_class.respond_to? :attached_object Methods::OBJECT_INSTANCE_VARIABLES_METHOD.bind_call(singleton_class.attached_object).map(&:to_s) elsif singleton_class == Methods::OBJECT_SINGLETON_CLASS_METHOD.bind_call(base_self) Methods::OBJECT_INSTANCE_VARIABLES_METHOD.bind_call(base_self).map(&:to_s) else [] end end [ self_singleton_types.flat_map { _1.module_or_class.instance_variables.map(&:to_s) }, self_instance_variables || [], table_instance_variables ].inject(:|) end def self_instance_variable_get(name) self_objects = self_type.types.grep(Types::SingletonType).map(&:module_or_class) singleton_classes = self_type.types.grep(Types::InstanceType).map(&:klass).select(&:singleton_class?) base_self = base_scope.self_object singleton_classes.each do |singleton_class| if singleton_class.respond_to? :attached_object self_objects << singleton_class.attached_object elsif singleton_class == base_self.singleton_class self_objects << base_self end end types = self_objects.map do |object| value = begin Methods::OBJECT_INSTANCE_VARIABLE_GET_METHOD.bind_call(object, name) rescue NameError end Types.type_from_object value end Types::UnionType[*types] end def table_class_variables cvars = @table.keys.filter_map { _1.split('::', 2).first if RootScope.type_by_name(_1) == :cvar } cvars |= @parent.table_class_variables if @parent.mutable? cvars end def class_variables cvars = table_class_variables m, = module_nesting.first cvars |= m.class_variables.map(&:to_s) if m.is_a? Module cvars end def constants module_nesting.flat_map do |nest,| nest.constants end.map(&:to_s) | table_constants end def merge_jumps if terminated? @terminated = false @table = @mergeable_changes merge @jump_branches @terminated = true else merge [*@jump_branches, {}] end end def conditional(&block) run_branches(block, ->(_s) {}).first || Types::NIL end def never(&block) block.call Scope.new(self, { BREAK_RESULT => nil, NEXT_RESULT => nil, PATTERNMATCH_BREAK => nil, RETURN_RESULT => nil }) end def run_branches(*blocks) results = [] branches = [] blocks.each do |block| scope = Scope.new self result = block.call scope next if scope.terminated? results << result branches << scope.mergeable_changes end terminate if branches.empty? merge branches results end def has_own?(name) @table.key? name end def update(child_scope) current_level = level child_scope.mergeable_changes.each do |name, (level, value)| self[name] = value if level <= current_level end end protected def merge(branches) current_level = level merge = {} branches.each do |changes| changes.each do |name, (level, value)| next if current_level < level (merge[name] ||= []) << value end end merge.each do |name, values| values << self[name] unless values.size == branches.size values.compact! self[name] = Types::UnionType[*values.compact] unless values.empty? end end end end
new(parent, table = {}, trace_ivar: true, trace_lvar: true, self_type: nil, nesting: nil)
click to toggle source
# File irb/type_completion/scope.rb, line 115 def initialize(parent, table = {}, trace_ivar: true, trace_lvar: true, self_type: nil, nesting: nil) @parent = parent @level = parent.level + 1 @trace_ivar = trace_ivar @trace_lvar = trace_lvar @module_nesting = nesting ? [nesting, *parent.module_nesting] : parent.module_nesting @self_type = self_type @terminated = false @jump_branches = [] @mergeable_changes = @table = table.transform_values { [level, _1] } end
Public Instance Methods
[](name)
click to toggle source
# File irb/type_completion/scope.rb, line 184 def [](name) type = RootScope.type_by_name(name) if type == :const return get_const(nil, nil, name) || Types::NIL if name.include?('::') module_nesting.each do |(nesting, path)| value = get_const nesting, [*path, name] return value if value end return Types::NIL elsif type == :cvar return get_cvar(nil, nil, nil, name) if name.include?('::') nesting, path = module_nesting.first return get_cvar(nesting, path, name) end level, value = @table[name] if level value elsif trace? name @parent[name] elsif type == :ivar self_instance_variable_get name end end
[]=(name, value)
click to toggle source
# File irb/type_completion/scope.rb, line 220 def []=(name, value) type = RootScope.type_by_name(name) if type == :const if name.include?('::') @table[name] = [0, value] else parent_module, parent_path = module_nesting.first set_const parent_module, [*parent_path, name], value end return elsif type == :cvar if name.include?('::') @table[name] = [0, value] else parent_module, parent_path = module_nesting.first set_cvar parent_module, parent_path, name, value end return end variable_level = level_of name, type @table[name] = [variable_level, value] if variable_level end
base_scope()
click to toggle source
# File irb/type_completion/scope.rb, line 278 def base_scope @parent.mutable? ? @parent.base_scope : @parent end
class_variables()
click to toggle source
# File irb/type_completion/scope.rb, line 335 def class_variables cvars = table_class_variables m, = module_nesting.first cvars |= m.class_variables.map(&:to_s) if m.is_a? Module cvars end
conditional(&block)
click to toggle source
# File irb/type_completion/scope.rb, line 359 def conditional(&block) run_branches(block, ->(_s) {}).first || Types::NIL end
constants()
click to toggle source
# File irb/type_completion/scope.rb, line 342 def constants module_nesting.flat_map do |nest,| nest.constants end.map(&:to_s) | table_constants end
get_const(nesting, path, key = nil)
click to toggle source
# File irb/type_completion/scope.rb, line 172 def get_const(nesting, path, key = nil) key ||= [nesting.__id__, path].join('::') _l, value = @table[key] value || @parent.get_const(nesting, path, key) end
get_cvar(nesting, path, name, key = nil)
click to toggle source
# File irb/type_completion/scope.rb, line 178 def get_cvar(nesting, path, name, key = nil) key ||= [name, nesting.__id__, path].join('::') _l, value = @table[key] value || @parent.get_cvar(nesting, path, name, key) end
global_variables()
click to toggle source
# File irb/type_completion/scope.rb, line 247 def global_variables gvar_keys = @table.keys.select do |name| RootScope.type_by_name(name) == :gvar end gvar_keys | @parent.global_variables end
has_own?(name)
click to toggle source
# File irb/type_completion/scope.rb, line 382 def has_own?(name) @table.key? name end
instance_variables()
click to toggle source
# File irb/type_completion/scope.rb, line 288 def instance_variables self_singleton_types = self_type.types.grep(Types::SingletonType) singleton_classes = self_type.types.grep(Types::InstanceType).map(&:klass).select(&:singleton_class?) base_self = base_scope.self_object self_instance_variables = singleton_classes.flat_map do |singleton_class| if singleton_class.respond_to? :attached_object Methods::OBJECT_INSTANCE_VARIABLES_METHOD.bind_call(singleton_class.attached_object).map(&:to_s) elsif singleton_class == Methods::OBJECT_SINGLETON_CLASS_METHOD.bind_call(base_self) Methods::OBJECT_INSTANCE_VARIABLES_METHOD.bind_call(base_self).map(&:to_s) else [] end end [ self_singleton_types.flat_map { _1.module_or_class.instance_variables.map(&:to_s) }, self_instance_variables || [], table_instance_variables ].inject(:|) end
level_of(name, var_type)
click to toggle source
# File irb/type_completion/scope.rb, line 161 def level_of(name, var_type) case var_type when :ivar return level unless @trace_ivar when :gvar return 0 end variable_level, = @table[name] variable_level || parent.level_of(name, var_type) end
local_variables()
click to toggle source
# File irb/type_completion/scope.rb, line 254 def local_variables lvar_keys = @table.keys.select do |name| RootScope.type_by_name(name) == :lvar end lvar_keys |= @parent.local_variables if @trace_lvar lvar_keys end
merge(branches)
click to toggle source
# File irb/type_completion/scope.rb, line 395 def merge(branches) current_level = level merge = {} branches.each do |changes| changes.each do |name, (level, value)| next if current_level < level (merge[name] ||= []) << value end end merge.each do |name, values| values << self[name] unless values.size == branches.size values.compact! self[name] = Types::UnionType[*values.compact] unless values.empty? end end
merge_jumps()
click to toggle source
# File irb/type_completion/scope.rb, line 348 def merge_jumps if terminated? @terminated = false @table = @mergeable_changes merge @jump_branches @terminated = true else merge [*@jump_branches, {}] end end
mutable?(= true)
click to toggle source
# File irb/type_completion/scope.rb, line 127 def mutable? = true def terminated? @terminated end def terminate_with(type, value) return if terminated? store_jump type, value, @mergeable_changes terminate end def store_jump(type, value, changes) return if terminated? if has_own?(type) changes[type] = [level, value] @jump_branches << changes elsif @parent.mutable? @parent.store_jump(type, value, changes) end end def terminate return if terminated? @terminated = true @table = @mergeable_changes.dup end def trace?(name) return false unless @parent type = RootScope.type_by_name(name) type == :ivar ? @trace_ivar : type == :lvar ? @trace_lvar : true end def level_of(name, var_type) case var_type when :ivar return level unless @trace_ivar when :gvar return 0 end variable_level, = @table[name] variable_level || parent.level_of(name, var_type) end def get_const(nesting, path, key = nil) key ||= [nesting.__id__, path].join('::') _l, value = @table[key] value || @parent.get_const(nesting, path, key) end def get_cvar(nesting, path, name, key = nil) key ||= [name, nesting.__id__, path].join('::') _l, value = @table[key] value || @parent.get_cvar(nesting, path, name, key) end def [](name) type = RootScope.type_by_name(name) if type == :const return get_const(nil, nil, name) || Types::NIL if name.include?('::') module_nesting.each do |(nesting, path)| value = get_const nesting, [*path, name] return value if value end return Types::NIL elsif type == :cvar return get_cvar(nil, nil, nil, name) if name.include?('::') nesting, path = module_nesting.first return get_cvar(nesting, path, name) end level, value = @table[name] if level value elsif trace? name @parent[name] elsif type == :ivar self_instance_variable_get name end end def set_const(nesting, path, value) key = [nesting.__id__, path].join('::') @table[key] = [0, value] end def set_cvar(nesting, path, name, value) key = [name, nesting.__id__, path].join('::') @table[key] = [0, value] end def []=(name, value) type = RootScope.type_by_name(name) if type == :const if name.include?('::') @table[name] = [0, value] else parent_module, parent_path = module_nesting.first set_const parent_module, [*parent_path, name], value end return elsif type == :cvar if name.include?('::') @table[name] = [0, value] else parent_module, parent_path = module_nesting.first set_cvar parent_module, parent_path, name, value end return end variable_level = level_of name, type @table[name] = [variable_level, value] if variable_level end def self_type @self_type || @parent.self_type end def global_variables gvar_keys = @table.keys.select do |name| RootScope.type_by_name(name) == :gvar end gvar_keys | @parent.global_variables end def local_variables lvar_keys = @table.keys.select do |name| RootScope.type_by_name(name) == :lvar end lvar_keys |= @parent.local_variables if @trace_lvar lvar_keys end def table_constants constants = module_nesting.flat_map do |mod, path| prefix = [mod.__id__, *path].join('::') + '::' @table.keys.select { _1.start_with? prefix }.map { _1.delete_prefix(prefix).split('::').first } end.uniq constants |= @parent.table_constants if @parent.mutable? constants end def table_module_constants(mod) prefix = "#{mod.__id__}::" constants = @table.keys.select { _1.start_with? prefix }.map { _1.delete_prefix(prefix).split('::').first } constants |= @parent.table_constants if @parent.mutable? constants end def base_scope @parent.mutable? ? @parent.base_scope : @parent end def table_instance_variables ivars = @table.keys.select { RootScope.type_by_name(_1) == :ivar } ivars |= @parent.table_instance_variables if @parent.mutable? && @trace_ivar ivars end def instance_variables self_singleton_types = self_type.types.grep(Types::SingletonType) singleton_classes = self_type.types.grep(Types::InstanceType).map(&:klass).select(&:singleton_class?) base_self = base_scope.self_object self_instance_variables = singleton_classes.flat_map do |singleton_class| if singleton_class.respond_to? :attached_object Methods::OBJECT_INSTANCE_VARIABLES_METHOD.bind_call(singleton_class.attached_object).map(&:to_s) elsif singleton_class == Methods::OBJECT_SINGLETON_CLASS_METHOD.bind_call(base_self) Methods::OBJECT_INSTANCE_VARIABLES_METHOD.bind_call(base_self).map(&:to_s) else [] end end [ self_singleton_types.flat_map { _1.module_or_class.instance_variables.map(&:to_s) }, self_instance_variables || [], table_instance_variables ].inject(:|) end def self_instance_variable_get(name) self_objects = self_type.types.grep(Types::SingletonType).map(&:module_or_class) singleton_classes = self_type.types.grep(Types::InstanceType).map(&:klass).select(&:singleton_class?) base_self = base_scope.self_object singleton_classes.each do |singleton_class| if singleton_class.respond_to? :attached_object self_objects << singleton_class.attached_object elsif singleton_class == base_self.singleton_class self_objects << base_self end end types = self_objects.map do |object| value = begin Methods::OBJECT_INSTANCE_VARIABLE_GET_METHOD.bind_call(object, name) rescue NameError end Types.type_from_object value end Types::UnionType[*types] end def table_class_variables cvars = @table.keys.filter_map { _1.split('::', 2).first if RootScope.type_by_name(_1) == :cvar } cvars |= @parent.table_class_variables if @parent.mutable? cvars end def class_variables cvars = table_class_variables m, = module_nesting.first cvars |= m.class_variables.map(&:to_s) if m.is_a? Module cvars end def constants module_nesting.flat_map do |nest,| nest.constants end.map(&:to_s) | table_constants end def merge_jumps if terminated? @terminated = false @table = @mergeable_changes merge @jump_branches @terminated = true else merge [*@jump_branches, {}] end end def conditional(&block) run_branches(block, ->(_s) {}).first || Types::NIL end def never(&block) block.call Scope.new(self, { BREAK_RESULT => nil, NEXT_RESULT => nil, PATTERNMATCH_BREAK => nil, RETURN_RESULT => nil }) end def run_branches(*blocks) results = [] branches = [] blocks.each do |block| scope = Scope.new self result = block.call scope next if scope.terminated? results << result branches << scope.mergeable_changes end terminate if branches.empty? merge branches results end def has_own?(name) @table.key? name end def update(child_scope) current_level = level child_scope.mergeable_changes.each do |name, (level, value)| self[name] = value if level <= current_level end end protected def merge(branches) current_level = level merge = {} branches.each do |changes| changes.each do |name, (level, value)| next if current_level < level (merge[name] ||= []) << value end end merge.each do |name, values| values << self[name] unless values.size == branches.size values.compact! self[name] = Types::UnionType[*values.compact] unless values.empty? end end end
never(&block)
click to toggle source
# File irb/type_completion/scope.rb, line 363 def never(&block) block.call Scope.new(self, { BREAK_RESULT => nil, NEXT_RESULT => nil, PATTERNMATCH_BREAK => nil, RETURN_RESULT => nil }) end
run_branches(*blocks)
click to toggle source
# File irb/type_completion/scope.rb, line 367 def run_branches(*blocks) results = [] branches = [] blocks.each do |block| scope = Scope.new self result = block.call scope next if scope.terminated? results << result branches << scope.mergeable_changes end terminate if branches.empty? merge branches results end
self_instance_variable_get(name)
click to toggle source
# File irb/type_completion/scope.rb, line 308 def self_instance_variable_get(name) self_objects = self_type.types.grep(Types::SingletonType).map(&:module_or_class) singleton_classes = self_type.types.grep(Types::InstanceType).map(&:klass).select(&:singleton_class?) base_self = base_scope.self_object singleton_classes.each do |singleton_class| if singleton_class.respond_to? :attached_object self_objects << singleton_class.attached_object elsif singleton_class == base_self.singleton_class self_objects << base_self end end types = self_objects.map do |object| value = begin Methods::OBJECT_INSTANCE_VARIABLE_GET_METHOD.bind_call(object, name) rescue NameError end Types.type_from_object value end Types::UnionType[*types] end
self_type()
click to toggle source
# File irb/type_completion/scope.rb, line 243 def self_type @self_type || @parent.self_type end
set_const(nesting, path, value)
click to toggle source
# File irb/type_completion/scope.rb, line 210 def set_const(nesting, path, value) key = [nesting.__id__, path].join('::') @table[key] = [0, value] end
set_cvar(nesting, path, name, value)
click to toggle source
# File irb/type_completion/scope.rb, line 215 def set_cvar(nesting, path, name, value) key = [name, nesting.__id__, path].join('::') @table[key] = [0, value] end
store_jump(type, value, changes)
click to toggle source
# File irb/type_completion/scope.rb, line 139 def store_jump(type, value, changes) return if terminated? if has_own?(type) changes[type] = [level, value] @jump_branches << changes elsif @parent.mutable? @parent.store_jump(type, value, changes) end end
table_class_variables()
click to toggle source
# File irb/type_completion/scope.rb, line 329 def table_class_variables cvars = @table.keys.filter_map { _1.split('::', 2).first if RootScope.type_by_name(_1) == :cvar } cvars |= @parent.table_class_variables if @parent.mutable? cvars end
table_constants()
click to toggle source
# File irb/type_completion/scope.rb, line 262 def table_constants constants = module_nesting.flat_map do |mod, path| prefix = [mod.__id__, *path].join('::') + '::' @table.keys.select { _1.start_with? prefix }.map { _1.delete_prefix(prefix).split('::').first } end.uniq constants |= @parent.table_constants if @parent.mutable? constants end
table_instance_variables()
click to toggle source
# File irb/type_completion/scope.rb, line 282 def table_instance_variables ivars = @table.keys.select { RootScope.type_by_name(_1) == :ivar } ivars |= @parent.table_instance_variables if @parent.mutable? && @trace_ivar ivars end
table_module_constants(mod)
click to toggle source
# File irb/type_completion/scope.rb, line 271 def table_module_constants(mod) prefix = "#{mod.__id__}::" constants = @table.keys.select { _1.start_with? prefix }.map { _1.delete_prefix(prefix).split('::').first } constants |= @parent.table_constants if @parent.mutable? constants end
terminate()
click to toggle source
# File irb/type_completion/scope.rb, line 149 def terminate return if terminated? @terminated = true @table = @mergeable_changes.dup end
terminate_with(type, value)
click to toggle source
# File irb/type_completion/scope.rb, line 133 def terminate_with(type, value) return if terminated? store_jump type, value, @mergeable_changes terminate end
terminated?()
click to toggle source
# File irb/type_completion/scope.rb, line 129 def terminated? @terminated end
trace?(name)
click to toggle source
# File irb/type_completion/scope.rb, line 155 def trace?(name) return false unless @parent type = RootScope.type_by_name(name) type == :ivar ? @trace_ivar : type == :lvar ? @trace_lvar : true end
update(child_scope)
click to toggle source
# File irb/type_completion/scope.rb, line 386 def update(child_scope) current_level = level child_scope.mergeable_changes.each do |name, (level, value)| self[name] = value if level <= current_level end end