core/variable_scope.rb
module Rubinius
class VariableScope
# To handle Module#private, protected
attr_accessor :method_visibility
attr_reader :parent
attr_reader :block
attr_reader :module
attr_reader :self
attr_reader :name
# CompiledCode this scope is for.
#
attr_reader :method
def self.of_sender
Rubinius.primitive :variable_scope_of_sender
raise PrimitiveFailure, "Rubinius::VariableScope.of_sender primitive failed"
end
def self.current
Rubinius.primitive :variable_scope_current
raise PrimitiveFailure, "Rubinius::VariableScope.current primitive failed"
end
def self.allocate
Rubinius.primitive :variable_scope_allocate
raise PrimitiveFailure, "Rubinius::VariableScope.allocate primitive failed"
end
def self.synthesize(method, module_, parent, self_, block, locals)
Rubinius.primitive :variable_scope_synthesize
raise PrimitiveFailure, "Rubinius::VariableScope.synthesize primitive failed"
end
def set_local(slot, obj)
Rubinius.primitive :variable_scope_set_local
raise PrimitiveFailure, "Rubinius::VariableScope#set_local primitive failed"
end
def locals
Rubinius.primitive :variable_scope_locals
raise PrimitiveFailure, "Rubinius::VariableScope#locals primitive failed"
end
def top_level_visibility?
Rubinius.primitive :variable_scope_top_level_visibility
raise PrimitiveFailure, "Rubinius::VariableScope#top_level_visibility primitive failed"
end
def script?
Rubinius.primitive :variable_scope_script
raise PrimitiveFailure, "Rubinius::VariableScope#script primitive failed"
end
def locked?
Rubinius.primitive :variable_scope_locked
raise PrimitiveFailure, "Rubinius::VariableScope#locked primitive failed"
end
def locked!
Rubinius.primitive :variable_scope_set_locked
raise PrimitiveFailure, "Rubinius::VariableScope#set_locked primitive failed"
end
def self=(obj)
@self = obj
end
def has_dynamic_locals?
@dynamic_locals
end
def dynamic_locals
@dynamic_locals ||= Rubinius::LookupTable.new
end
def set_eval_local(name, val)
scope = @parent
while scope
if scope.dynamic_locals.key? name
scope.dynamic_locals[name] = val
return val
end
scope = scope.parent
end
# Otherwise, put it in the highest non-eval scope
if @parent
scope = @parent
while scope.parent and scope.method.for_eval?
scope = scope.parent
end
scope.dynamic_locals[name] = val
else
# This happens when we run some confused compiled code.
# Typically when we use eval to create the code and then
# reuse it in a non-eval context.
dynamic_locals[name] = val
end
end
def get_eval_local(name)
scope = self
while scope
if scope.has_dynamic_locals? && scope.dynamic_locals.key?(name)
return scope.dynamic_locals[name]
end
scope = scope.parent
end
nil
end
def eval_local_defined?(name, search=true)
if search
scope = self
while scope
return true if scope.dynamic_locals.key? name
scope = scope.parent
end
else
return true if @dynamic_locals and @dynamic_locals.key? name
end
return false
end
def local_defined?(name)
vars = self
while vars
return true if vars.method.local_names.include? name
return true if vars.eval_local_defined?(name, false)
vars = vars.parent
end
false
end
def local_layout
vars = self
out = []
while vars
meth = vars.method.local_names.to_a
eloc = vars.dynamic_locals.keys.map { |x| x.to_s }.sort
out << [meth, eloc]
vars = vars.parent
end
out
end
# Returns the names of local variables available in this scope
#
def local_variables
locals = []
scope = self
# Ascend up through all applicable blocks to get all vars.
while scope
if scope.method.local_names
scope.method.local_names.each do |name|
locals << name
end
end
if dyn = scope.dynamic_locals
dyn.keys.each do |name|
locals << name unless locals.include?(name)
end
end
scope = scope.parent
end
locals
end
def exitted?
@exitted
end
def super_method_defined?
# Minor HACK __block__ as the name is ambigious. Need a better
# way to detect that a VS is for a define_method scope.
#
# This code is so that if this VS is for a block, we walk up
# to the VS for the actual method. It's complicated by the fact
# that there may be a VS for a define_method in there, so we need
# to stop looking if we hit one.
if @parent and @method.name == :__block__
vars = self
while vars.method.name == :__block__ and nxt = vars.parent
vars = nxt
end
return vars.super_method_defined?
end
if sup = @module.direct_superclass
# This will probably break for define_method
return sup.lookup_method(@method.name) != nil
end
return false
end
def method_visibility
return @method_visibility if @method_visibility
# if this scope is for a script, and there is no method_visibility
# already set, then we default to private.
#
# This is so that a script body has it's visibility default to private.
return :private if script?
return nil if top_level_visibility?
return @parent.method_visibility if @parent
# The default, let the caller sort it out.
return nil
end
def inspect
"#<#{self.class.name}:0x#{self.object_id.to_s(16)} module=#{@module} method=#{@method.inspect}>"
end
end
end