mirror of
https://github.com/mistergibson/gxg-web-client.git
synced 2026-08-15 11:34:29 -07:00
236 lines
5.9 KiB
Ruby
236 lines
5.9 KiB
Ruby
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
|