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