Permalink
Fetching contributors…
Cannot retrieve contributors at this time
794 lines (620 sloc) 19.6 KB
##
# Some terminology notes:
#
# [Encloser] The Class or Module inside which this one is defined or, in the
# event we are at top-level, Object.
#
# [Direct superclass] Whatever is next in the chain of superclass invocations.
# This may be either an included Module, a Class or nil.
#
# [Superclass] The real semantic superclass and thus only applies to Class
# objects.
class Module
attr_reader :constant_table
attr_writer :method_table
private :included
def self.nesting
scope = Rubinius::LexicalScope.of_sender
nesting = []
while scope and scope.module != Object
nesting << scope.module
scope = scope.parent
end
nesting
end
def initialize(&block)
# The method and constants tables are created and setup
# by Class and Module allocate.
module_eval(&block) if block
end
private :initialize
def const_get(name, inherit=true)
Rubinius::Type.const_get self, name, inherit
end
def const_defined?(name, inherit=true)
c = Rubinius::Type.const_get self, name, inherit, false
return c.equal?(undefined) ? false : true
end
def attr(*attributes)
vis = Rubinius::VariableScope.of_sender.method_visibility
if attributes.size == 2 && (attributes.last.is_a?(TrueClass) || attributes.last.is_a?(FalseClass))
name = attributes.first
Rubinius.add_reader name, self, vis
Rubinius.add_writer name, self, vis if attributes.last
else
attributes.each do |name|
if name.is_a?(Symbol) || (name.respond_to?(:to_str) && name.to_str.is_a?(String) && !name.to_str.empty?)
Rubinius.add_reader(name, self, vis)
else
raise TypeError, "#{name.inspect} is not a symbol"
end
end
end
return nil
end
private :attr
# Install a new Autoload object into the constants table
# See core/autoload.rb
def autoload(name, path)
path = Rubinius::Type.coerce_to_path(path)
raise ArgumentError, "empty file name" if path.empty?
name = Rubinius::Type.coerce_to_constant_name name
Rubinius.check_frozen
if entry = @constant_table.lookup(name)
if entry.constant.kind_of? Autoload
# If there is already an Autoload here, just change the path to
# autoload!
entry.constant.set_path(path)
else
# Trying to register an autoload for a constant that already exists,
# ignore the request entirely.
end
return
end
autoload_constant = Autoload.new(name, self, path)
constant_table.store(name, autoload_constant, :public)
if self == Kernel
Object.singleton_class.constant_table.store(name, autoload_constant, :public)
end
Rubinius.inc_global_serial
return nil
end
def constants(all=undefined)
tbl = Rubinius::LookupTable.new
@constant_table.each do |name, constant, visibility|
tbl[name] = true unless visibility == :private
end
if all
current = self.direct_superclass
while current and current != Object
current.constant_table.each do |name, constant, visibility|
tbl[name] = true unless visibility == :private
end
current = current.direct_superclass
end
end
# special case: Module.constants returns Object's constants
if self.equal?(Module) && undefined.equal?(all)
Object.constant_table.each do |name, constant, visibility|
tbl[name] = true unless visibility == :private
end
end
tbl.keys
end
#--
# These have to be aliases, not methods that call instance eval, because we
# need to pull in the binding of the person that calls them, not the
# intermediate binding.
#++
def module_eval(string=undefined, filename="(eval)", line=1, &prc)
# we have a custom version with the prc, rather than using instance_exec
# so that we can setup the LexicalScope properly.
if prc
unless undefined.equal?(string)
raise ArgumentError, "cannot pass both string and proc"
end
# Return a copy of the BlockEnvironment with the receiver set to self
env = prc.block
lexical_scope = env.repoint_scope self
return env.call_under(self, lexical_scope, true, self)
elsif undefined.equal?(string)
raise ArgumentError, 'block not supplied'
end
string = StringValue(string)
filename = StringValue(filename)
# The constantscope of a module_eval CM is the receiver of module_eval
cs = Rubinius::LexicalScope.new self, Rubinius::LexicalScope.of_sender
binding = Binding.setup(Rubinius::VariableScope.of_sender,
Rubinius::CompiledCode.of_sender,
cs)
c = Rubinius::ToolSets::Runtime::Compiler
be = c.construct_block string, binding, filename, line
be.call_under self, cs, true, self
end
alias_method :class_eval, :module_eval
def private_constant(*names)
names = names.map(&:to_sym)
unknown_constants = names - @constant_table.keys
if unknown_constants.size > 0
raise NameError, "#{unknown_constants.size > 1 ? 'Constants' : 'Constant'} #{unknown_constants.map{|e| "#{name}::#{e}"}.join(', ')} undefined"
end
names.each do |name|
entry = @constant_table.lookup(name)
entry.visibility = :private
end
end
def public_constant(*names)
names = names.map(&:to_sym)
unknown_constants = names - @constant_table.keys
if unknown_constants.size > 0
raise NameError, "#{unknown_constants.size > 1 ? 'Constants' : 'Constant'} #{unknown_constants.map{|e| "#{name}::#{e}"}.join(', ')} undefined"
end
names.each do |name|
entry = @constant_table.lookup(name)
entry.visibility = :public
end
end
##
# Returns an UnboundMethod corresponding to the given name. The name will be
# searched for in this Module as well as any included Modules or
# superclasses. The UnboundMethod is populated with the method name and the
# Module that the method was located in.
#
# Raises a TypeError if the given name.to_sym fails and a NameError if the
# name cannot be located.
def instance_method(name)
name = Rubinius::Type.coerce_to_symbol name
mod = self
while mod
if mod == mod.origin && entry = mod.method_table.lookup(name)
break if entry.visibility == :undef
if meth = entry.get_method
if meth.kind_of? Rubinius::Alias
meth = meth.original_exec
end
mod = mod.module if mod.class == Rubinius::IncludedModule
return UnboundMethod.new(mod, meth, self, name)
end
end
mod = mod.direct_superclass
end
raise NameError.new("undefined method `#{name}' for #{self}", name)
end
def public_instance_method(name)
name = Rubinius::Type.coerce_to_symbol name
mod = self
while mod
if mod == mod.origin && entry = mod.method_table.lookup(name)
vis = entry.visibility
break if vis == :undef
if meth = entry.method
if meth.kind_of? Rubinius::Alias
meth = meth.original_exec
end
mod = mod.module if mod.class == Rubinius::IncludedModule
if vis == :public
return UnboundMethod.new(mod, meth, self, name)
else
raise NameError.new("method `#{name}' for module `#{self}' is #{vis}", name)
end
end
end
mod = mod.direct_superclass
end
raise NameError.new("undefined method `#{name}' for #{self}", name)
end
def visibility_for_aliased_method(new_name, current_name_visibility)
special_methods = [
:initialize,
:initialize_copy,
:initialize_clone,
:initialize_dup,
:respond_to_missing?
]
if special_methods.include?(new_name)
:private
else
current_name_visibility
end
end
private :visibility_for_aliased_method
def verify_class_variable_name(name)
if name.kind_of? Symbol
return name if name.is_cvar?
raise NameError, "#{name} is not an allowed class variable name"
end
name = StringValue(name).to_sym
unless name.is_cvar?
raise NameError, "#{name} is not an allowed class variable name"
end
name
end
private :verify_class_variable_name
def class_variable_set(name, val)
Rubinius.primitive :module_cvar_set
class_variable_set verify_class_variable_name(name), val
end
def class_variable_get(name)
Rubinius.primitive :module_cvar_get
class_variable_get verify_class_variable_name(name)
end
def class_variable_defined?(name)
Rubinius.primitive :module_cvar_defined
class_variable_defined? verify_class_variable_name(name)
end
def remove_class_variable(name)
Rubinius.primitive :module_cvar_remove
remove_class_variable verify_class_variable_name(name)
end
def __class_variables__
Rubinius.primitive :module_class_variables
raise PrimitiveFailure, "Module#__class_variables__ primitive failed"
end
alias_method :class_variables, :__class_variables__
def lookup_method(sym, check_object_too=true, trim_im=true)
mod = self
while mod
if mod == mod.origin && entry = mod.method_table.lookup(sym.to_sym)
mod = mod.module if trim_im and mod.kind_of? Rubinius::IncludedModule
return [mod, entry]
end
mod = mod.direct_superclass
end
# Optionally also search Object (and everything included in Object);
# this lets a module alias methods on Object or Kernel.
if check_object_too and instance_of?(Module)
return Object.lookup_method(sym, true, trim_im)
end
end
def ancestors
out = []
Rubinius::Type.each_ancestor(self) { |mod| out << mod }
return out
end
def public_method_defined?(sym)
sym = Rubinius::Type.coerce_to_reflection_name(sym)
mod, meth = lookup_method(sym, false)
meth ? meth.public? : false
end
def private_method_defined?(sym)
sym = Rubinius::Type.coerce_to_reflection_name(sym)
mod, meth = lookup_method(sym, false)
meth ? meth.private? : false
end
def protected_method_defined?(sym)
sym = Rubinius::Type.coerce_to_reflection_name(sym)
mod, meth = lookup_method(sym, false)
meth ? meth.protected? : false
end
def method_defined?(sym)
sym = Rubinius::Type.coerce_to_symbol(sym)
mod, meth = lookup_method(sym, false)
meth ? meth.public? || meth.protected? : false
end
def instance_methods(all=true)
ary = []
if all
table = Rubinius::LookupTable.new
mod = self
while mod
mod.method_table.each do |name, obj, vis|
unless table.key?(name)
table[name] = true
ary << name if vis == :public or vis == :protected
end
end
mod = mod.direct_superclass
end
else
method_table.each do |name, obj, vis|
ary << name if vis == :public or vis == :protected
end
end
ary
end
def public_instance_methods(all=true)
filter_methods(:public, all)
end
def private_instance_methods(all=true)
filter_methods(:private, all)
end
def protected_instance_methods(all=true)
filter_methods(:protected, all)
end
def filter_methods(filter, all)
ary = []
if all and self.direct_superclass
table = Rubinius::LookupTable.new
mod = self
while mod
mod.method_table.each do |name, obj, vis|
unless table.key?(name)
table[name] = true
ary << name if vis == filter
end
end
mod = mod.direct_superclass
end
else
method_table.each do |name, obj, vis|
ary << name if vis == filter
end
end
ary
end
private :filter_methods
def define_method(name, meth = undefined, &prc)
if undefined.equal?(meth) and !block_given?
raise ArgumentError, "tried to create Proc object without a block"
end
meth = prc if undefined.equal?(meth)
name = Rubinius::Type.coerce_to_symbol name
if meth.respond_to?(:for_define_method)
code, scope = meth.for_define_method(name, self.class)
else
raise TypeError, "wrong argument type #{meth.class} (expected Proc/Method)"
end
Rubinius.add_method name, code, self, scope, 0, :public
name
end
private :define_method
def thunk_method(name, value)
thunk = Rubinius::Thunk.new(value)
name = Rubinius::Type.coerce_to_symbol name
Rubinius.add_method name, thunk, self, nil, 0, :public
name
end
def extend_object(obj)
include_into Rubinius::Type.object_singleton_class(obj)
end
private :extend_object
def include?(mod)
if !mod.kind_of?(Module) or mod.kind_of?(Class)
raise TypeError, "wrong argument type #{mod.class} (expected Module)"
end
return false if self.equal?(mod)
Rubinius::Type.each_ancestor(self) { |m| return true if mod.equal?(m) }
false
end
def included_modules
out = []
sup = direct_superclass
while sup
if sup.class == Rubinius::IncludedModule
out << sup.module
end
sup = sup.direct_superclass
end
out
end
def set_visibility(meth, vis, where=nil)
name = Rubinius::Type.coerce_to_symbol(meth)
Rubinius::VM.reset_method_cache self, name
vis = vis.to_sym
if entry = @method_table.lookup(name)
entry.visibility = vis
elsif lookup_method(name)
@method_table.store name, nil, nil, nil, 0, vis
else
raise NameError.new("Unknown #{where}method '#{name}' to make #{vis.to_s} (#{self})", name)
end
return name
end
def set_class_visibility(meth, vis)
Rubinius::Type.object_singleton_class(self).set_visibility meth, vis, "class "
end
private :set_class_visibility
def module_exec(*args, &prc)
raise LocalJumpError, "Missing block" unless block_given?
env = prc.block
lexical_scope = env.repoint_scope self
return env.call_under(self, lexical_scope, true, *args)
end
alias_method :class_exec, :module_exec
##
# Return the named constant enclosed in this Module.
#
# Included Modules and, for Class objects, superclasses are also searched.
# Modules will in addition look in Object. The name is attempted to convert
# using #to_str. If the constant is not found, #const_missing is called
# with the name.
def const_missing(name)
unless self == Object
mod_name = "#{Rubinius::Type.module_name self}::"
end
case Rubinius.constant_missing_reason
when :private
msg = "Private constant: #{mod_name}#{name}"
else
msg = "Missing or uninitialized constant: #{mod_name}#{name}"
end
raise NameError.new(msg, name)
end
def <(other)
unless other.kind_of? Module
raise TypeError, "compared with non class/module"
end
# We're not an ancestor of ourself
return false if self.equal? other
Rubinius::Type.each_ancestor(self) { |mod| return true if mod.equal?(other) }
Rubinius::Type.each_ancestor(other) { |mod| return false if mod.equal?(self) }
nil
end
def <=(other)
equal?(other) || (self < other)
end
def >(other)
unless other.kind_of? Module
raise TypeError, "compared with non class/module"
end
other < self
end
def >=(other)
equal?(other) || (self > other)
end
def <=>(other)
return 0 if self.equal? other
return nil unless other.kind_of? Module
lt = self < other
return nil if lt.nil?
lt ? -1 : 1
end
def ===(inst)
Rubinius::Type.object_kind_of?(inst, self)
end
# Is an autoload trigger defined for the given path?
def autoload?(name)
name = name.to_sym
entry = constant_table.lookup(name)
return unless entry
trigger = entry.constant
return unless trigger && trigger.kind_of?(Autoload)
trigger.path
end
def remove_const(name)
unless name.kind_of? Symbol
name = StringValue name
unless Rubinius::CType.isupper name.getbyte(0)
raise NameError, "constant names must begin with a capital letter: #{name}"
end
end
sym = name.to_sym
unless constant_table.has_name?(sym)
mod_name = Rubinius::Type.module_name self
raise NameError, "Missing or uninitialized constant: #{mod_name}::#{name}"
end
val = constant_table.delete(sym)
Rubinius.inc_global_serial
# Silly API compact. Shield Autoload instances
return nil if Rubinius::Type.object_kind_of?(val, Autoload)
val
end
private :remove_const
def extended(name)
end
private :extended
def method_added(name)
end
private :method_added
def method_removed(name)
end
private :method_removed
def method_undefined(name)
end
private :method_undefined
def initialize_copy(other)
# If the method table is already different, we already
# initialized this module.
unless @method_table == other.method_table
raise TypeError, "already initialized module"
end
@method_table = other.method_table.dup
sc_s = Rubinius::Type.object_singleton_class self
sc_o = Rubinius::Type.object_singleton_class other
sc_s.method_table = sc_o.method_table.dup
sc_s.superclass = sc_o.direct_superclass
@constant_table = Rubinius::ConstantTable.new
other.constant_table.each do |name, val|
if val.kind_of? Autoload
val = Autoload.new(val.name, self, val.path)
end
@constant_table.store(name, val, :public)
end
if other == other.origin
@origin = self
else
@origin = other.origin.dup
end
self
end
private :initialize_copy
def add_ivars(code)
case code
when Rubinius::CompiledCode
new_ivars = code.literals.select { |l| l.kind_of?(Symbol) and l.is_ivar? }
return if new_ivars.empty?
if @seen_ivars
new_ivars.each do |x|
unless @seen_ivars.include?(x)
@seen_ivars << x
end
end
else
@seen_ivars = new_ivars
end
when Rubinius::AccessVariable
if @seen_ivars
unless @seen_ivars.include?(code.name)
@seen_ivars << code.name
end
else
@seen_ivars = [code.name]
end
else
raise "Unknown type of method to learn ivars - #{code.class}"
end
end
def dynamic_method(name, file="(dynamic)", line=1)
g = Rubinius::ToolSets::Runtime::Generator.new
g.name = name.to_sym
g.file = file.to_sym
g.set_line Integer(line)
yield g
g.close
g.use_detected
g.encode
code = g.package Rubinius::CompiledCode
scope = Rubinius::LexicalScope.new(self, Rubinius::LexicalScope.new(Object))
code.scope = scope
Rubinius.add_method name, code, self, scope, 0, :public
return code
end
def freeze
@method_table.freeze
@constant_table.freeze
super
end
def origin= origin
@origin = origin
end
def prepended(mod); end
private :prepended
def prepend(*modules)
modules.reverse_each do |mod|
if !mod.kind_of?(Module) or mod.kind_of?(Class)
raise TypeError, "wrong argument type #{mod.class} (expected Module)"
end
Rubinius.privately do
mod.prepend_features self
end
Rubinius.privately do
mod.prepended self
end
end
self
end
def prepend_features(klass)
unless klass.kind_of? Module
raise TypeError, "invalid argument class #{klass.class}, expected Module"
end
if kind_of? Class
raise TypeError, "invalid receiver class #{__class__}, expected Module"
end
if klass == klass.origin
im = Rubinius::IncludedModule.new(klass)
im.superclass = klass.direct_superclass
klass.superclass = im
klass.origin = im
klass.method_table.each do |meth, obj, vis|
Rubinius::VM.reset_method_cache klass, meth
end
end
Rubinius::Type.include_modules_from(self, klass)
Rubinius::Type.infect(klass, self)
self
end
private :prepend_features
def singleton_class?
!!Rubinius::Type.singleton_class_object(self)
end
end