1 # depends on: class.rb proc.rb autoload.rb
4 # Some terminology notes:
6 # [Encloser] The Class or Module inside which this one is defined or, in the
7 # event we are at top-level, Object.
9 # [Direct superclass] Whatever is next in the chain of superclass invocations.
10 # This may be either an included Module, a Class or nil.
12 # [Superclass] The real semantic superclass and thus only applies to Class
17 ivar_as_index :__ivars__ => 0, :method_table => 1, :method_cache => 2, :name => 3, :constants => 4, :encloser => 5, :superclass => 6
19 def method_table ; @method_table ; end
20 def method_cache ; @method_cache ; end
21 def constants_table ; @constants ; end
22 def encloser ; @encloser ; end
24 def constants_table=(c) ; @constants = c ; end
25 def method_table=(m) ; @method_table = m ; end
28 mod = MethodContext.current.sender.receiver
29 unless mod.kind_of? Module
30 mod = MethodContext.current.sender.method_module
33 while mod != Object && mod.kind_of?(Module)
40 def initialize(&block)
41 _eval_under(self, &block) if block
45 # HACK: This should work after after the bootstrap is loaded,
46 # but it seems to blow things up, so it's only used after
47 # core is loaded. I think it's because the bootstrap Class#new
48 # doesn't use a privileged send.
51 def __method_added__cv(name)
52 if name == :initialize
56 method_added(name) if self.respond_to? :method_added
59 def verify_class_variable_name(name)
60 name = name.kind_of?(Symbol) ? name.to_s : StringValue(name)
61 unless name[0..1] == '@@' and name[2].toupper.between?(?A, ?Z) or name[2] == ?_
62 raise NameError, "#{name} is not an allowed class variable name"
66 private :verify_class_variable_name
68 def class_variables_table
69 @class_variables ||= Hash.new
71 private :class_variables_table
73 def class_variable_set(name, val)
74 name = verify_class_variable_name name
76 current = direct_superclass
78 if current.__kind_of__ MetaClass
79 vars = current.attached_instance.send :class_variables_table
80 elsif current.__kind_of__ IncludedModule
81 vars = current.module.send :class_variables_table
83 vars = current.send :class_variables_table
85 return vars[name] = val if vars.key? name
86 current = current.direct_superclass
89 if self.__kind_of__ MetaClass
90 table = self.attached_instance.send :class_variables_table
92 table = class_variables_table
97 def class_variable_get(name)
98 name = verify_class_variable_name name
102 if current.__kind_of__ MetaClass
103 vars = current.attached_instance.send :class_variables_table
104 elsif current.__kind_of__ IncludedModule
105 vars = current.module.send :class_variables_table
107 vars = current.send :class_variables_table
109 return vars[name] if vars.key? name
110 current = current.direct_superclass
113 # Try to print something useful for anonymous modules and metaclasses
114 module_name = self.name || self.inspect
115 raise NameError, "uninitialized class variable #{name} in #{module_name}"
118 def class_variable_defined?(name)
119 name = verify_class_variable_name name
123 if current.__kind_of__ IncludedModule
124 vars = current.module.send :class_variables_table
126 vars = current.send :class_variables_table
128 return true if vars.key? name
129 current = current.direct_superclass
134 def class_variables(symbols = false)
136 ancestors.map do |mod|
137 names.concat mod.send(:class_variables_table).keys
139 names = names.map { |name| name.to_s } unless symbols
144 @name ? @name.to_s : ""
148 @name ? @name.to_s : super
151 alias_method :inspect, :to_s
153 def find_method_in_hierarchy(sym)
157 if method = mod.method_table[sym.to_sym]
161 mod = mod.direct_superclass
164 # Always also search Object (and everything included in Object).
165 # This lets a module alias methods on Kernel.
166 if instance_of?(Module) and self != Kernel
167 return Object.find_method_in_hierarchy(sym)
172 if self.class == MetaClass
177 sup = direct_superclass()
179 if sup.class == IncludedModule
181 elsif sup.class != MetaClass
184 sup = sup.direct_superclass()
191 mod = direct_superclass()
194 mod = mod.direct_superclass()
200 # Create a wrapper to a function in a C-linked library that
201 # exists somewhere in the system. If a specific library is
202 # not given, the function is assumed to exist in the running
203 # process, the Rubinius executable. The process contains many
204 # linked libraries in addition to Rubinius' codebase, libc of
205 # course the most prominent on the system side. The wrapper
206 # method is added to the Module as a singleton method or a
209 # The function is specified like a declaration: the first
210 # argument is the type symbol for the return type (see FFI
211 # documentation for types), the second argument is the name
212 # of the function and the third argument is an Array of the
213 # types of the function's arguments. Currently at most 6
214 # arguments can be given.
216 # # If you want to wrap this function:
217 # int foobar(double arg_one, const char* some_string);
219 # # The arguments to #attach_foreign look like this:
220 # :int, 'foobar', [:double, :string]
222 # If the function is from an external library such as, say,
223 # libpcre, libcurl etc. you can give the name or path of
224 # the library. The fourth argument is an option hash and
225 # the library name should be given in the +:from+ key of
226 # the hash. The name may (and for portable code, should)
227 # omit the file extension. If the extension is present,
228 # it must be the correct one for the runtime platform.
229 # The library is searched for in the system library paths
230 # but if necessary, the full absolute or relative path can
233 # By default, the new method's name is the same as the
234 # function it wraps but in some cases it is desirable to
235 # change this. You can specify the method name in the +:as+
236 # key of the option hash.
237 def attach_foreign(ret_type, name, arg_types, opts = {})
240 if lib and !lib.chomp! ".#{Rubinius::LIBSUFFIX}"
241 lib.chomp! ".#{Rubinius::ALT_LIBSUFFIX}" rescue nil # .defined? is broken anyway
244 func = FFI.create_function lib, name.to_s, arg_types, ret_type
245 metaclass.method_table[(opts[:as] || name).to_sym] = func
248 def find_class_method_in_hierarchy(sym)
249 self.metaclass.find_method_in_hierarchy(sym)
252 def alias_method_cv(new_name, current_name)
253 new_name = normalize_name(new_name)
254 current_name = normalize_name(current_name)
255 meth = find_method_in_hierarchy(current_name)
256 # We valid +meth+ because all hell can break loose if method_table has unexpected
259 if meth.kind_of? Tuple
262 if !meth[0].kind_of?(Symbol) or
263 not (meth[1].kind_of?(CompiledMethod) or meth[1].kind_of?(AccessVarMethod))
264 raise TypeError, "Invalid object found in method_table while attempting to alias '#{current_name}'"
268 # REFACTOR: pull up a common superclass and test against that
269 unless meth.kind_of?(CompiledMethod) or meth.kind_of?(AccessVarMethod) or meth.kind_of?(DelegatedMethod) then
270 raise TypeError, "Invalid object found in method_table while attempting to alias '#{current_name}' #{meth.inspect}"
273 method_table[new_name] = meth
274 Rubinius::VM.reset_method_cache(new_name)
276 if self.kind_of? MetaClass
277 raise NameError, "Unable to find '#{current_name}' for object #{self.attached_instance.inspect}"
279 thing = self.kind_of?(Class) ? "class" : "module"
280 raise NameError, "undefined method `#{current_name}' for #{thing} `#{self.name}'"
285 def remote_alias(new_name, mod, current_name)
286 cm = mod.find_method_in_hierarchy(current_name)
288 raise NameError, "Unable to find method '#{current_name}' under #{mod}"
297 if meth.primitive and meth.primitive > 0
298 raise NameError, "Unable to remote alias primitive method '#{current_name}'"
301 method_table[new_name] = cm
302 Rubinius::VM.reset_method_cache(new_name)
307 def undef_method(*names)
309 name = normalize_name(name)
310 # Will raise a NameError if the method doesn't exist.
311 instance_method(name)
312 method_table[name] = false
313 Rubinius::VM.reset_method_cache(name)
315 method_undefined(name) if respond_to? :method_undefined
321 def remove_method(*names)
323 name = normalize_name(name)
324 # Will raise a NameError if the method doesn't exist.
325 instance_method(name)
326 unless self.method_table[name]
327 raise NameError, "method `#{name}' not defined in #{self.name}"
329 method_table.delete name
330 Rubinius::VM.reset_method_cache(name)
332 method_removed(name) if respond_to? :method_removed
338 def public_method_defined?(sym)
339 sym = StringValue(sym) unless sym.is_a? Symbol
340 m = find_method_in_hierarchy sym
341 m &&= Tuple[:public, m] unless m.is_a? Tuple
342 m ? m.first == :public : false
345 def private_method_defined?(sym)
346 sym = StringValue(sym) unless sym.is_a? Symbol
347 m = find_method_in_hierarchy sym
348 m &&= Tuple[:public, m] unless m.is_a? Tuple
349 m ? m.first == :private : false
352 def protected_method_defined?(sym)
353 sym = StringValue(sym) unless sym.is_a? Symbol
354 m = find_method_in_hierarchy sym
355 m &&= Tuple[:public, m] unless m.is_a? Tuple
356 m ? m.first == :protected : false
359 def method_defined?(sym)
360 sym = normalize_name(sym)
361 m = find_method_in_hierarchy sym
362 m &&= Tuple[:public, m] unless m.is_a? Tuple
363 m ? [:public,:protected].include?(m.first) : false
367 # Returns an UnboundMethod corresponding to the given name. The name will be
368 # searched for in this Module as well as any included Modules or
369 # superclasses. The UnboundMethod is populated with the method name and the
370 # Module that the method was located in.
372 # Raises a TypeError if the given name.to_sym fails and a NameError if the
373 # name cannot be located.
375 def instance_method(name)
376 name = Type.coerce_to name, Symbol, :to_sym
378 mod, cmethod = __find_method(name)
380 # We want to show the real module
381 mod = mod.module if mod.class == IncludedModule
382 return UnboundMethod.new(mod, cmethod, self) if cmethod
384 raise NameError, "Undefined method `#{name}' for #{self}"
387 def instance_methods(all=true)
388 filter_methods(:public_names, all) | filter_methods(:protected_names, all)
391 def public_instance_methods(all=true)
392 filter_methods(:public_names, all)
395 def private_instance_methods(all=true)
396 filter_methods(:private_names, all)
399 def protected_instance_methods(all=true)
400 filter_methods(:protected_names, all)
403 def filter_methods(filter, all)
404 names = method_table.__send__(filter)
405 unless all or self.is_a?(MetaClass) or self.is_a?(IncludedModule)
406 return names.map { |name| name.to_s }
409 excludes = method_table.map { |name, meth| meth == false ? name : nil }
410 undefed = excludes.compact
412 sup = direct_superclass
415 names |= sup.method_table.__send__(filter)
417 excludes = method_table.map { |name, meth| meth == false ? name : nil }
418 undefed += excludes.compact
420 sup = sup.direct_superclass
423 (names - undefed).map { |name| name.to_s }
425 # private :filter_methods
427 def define_method(name, meth = nil, &prc)
430 if meth.kind_of?(Proc)
431 block_env = meth.block
432 cm = DelegatedMethod.build(:call_on_instance, block_env, true)
433 elsif meth.kind_of?(Method)
434 cm = DelegatedMethod.build(:call, meth, false)
435 elsif meth.kind_of?(UnboundMethod)
436 cm = DelegatedMethod.build(:call_on_instance, meth, true)
438 raise TypeError, "wrong argument type #{meth.class} (expected Proc/Method)"
441 self.method_table[name.to_sym] = cm
442 Rubinius::VM.reset_method_cache(name.to_sym)
446 def extend_object(obj)
447 append_features obj.metaclass
451 # Don't call this include, otherwise it will shadow the bootstrap version
452 # while core loads (a violation of the core/bootstrap boundry)
455 def include_cv(*modules)
456 modules.reverse_each do |mod|
457 if !mod.kind_of?(Module) or mod.kind_of?(Class)
458 raise TypeError, "wrong argument type #{mod.class} (expected Module)"
461 next if ancestors.include? mod
463 mod.send(:append_features, self)
464 mod.send(:included, self)
469 # Called when this Module is being included in another Module.
470 # This may be overridden for custom behaviour, but the default
471 # is to add constants, instance methods and module variables
472 # of this Module and all Modules that this one includes to the
473 # includer Module, which is passed in as the parameter +other+.
477 def append_features_cv(other)
478 hierarchy = other.ancestors
480 superclass_chain.reverse_each do |ancestor|
481 if ancestor.instance_of? IncludedModule and not hierarchy.include? ancestor.module
482 IncludedModule.new(ancestor.module).attach_to other
486 IncludedModule.new(self).attach_to other
490 if !mod.kind_of?(Module) or mod.kind_of?(Class)
491 raise TypeError, "wrong argument type #{mod.class} (expected Module)"
493 ancestors.include? mod
498 sup = direct_superclass
501 if sup.class == IncludedModule
505 sup = sup.direct_superclass
511 def set_visibility(meth, vis, where = nil)
512 name = normalize_name(meth)
515 if entry = method_table[name] then
516 if entry.kind_of?(Tuple) then
520 entry = Tuple[vis, entry.dup]
522 method_table[name] = entry
523 elsif find_method_in_hierarchy(name) then
524 method_table[name] = Tuple[vis, nil]
526 raise NoMethodError, "Unknown #{where}method '#{name}' to make #{vis.to_s} (#{self})"
529 Rubinius::VM.reset_method_cache name
534 def set_class_visibility(meth, vis)
535 metaclass.set_visibility meth, vis, "class "
539 # As with include_cv above, don't call this private.
542 def private_cv(*args)
544 MethodContext.current.sender.method_scope = :private
548 args.each { |meth| set_visibility(meth, :private) }
553 MethodContext.current.sender.method_scope = :protected
557 args.each { |meth| set_visibility(meth, :protected) }
562 MethodContext.current.sender.method_scope = nil
566 args.each { |meth| set_visibility(meth, :public) }
569 def private_class_method(*args)
571 set_class_visibility(meth, :private)
576 def public_class_method(*args)
578 set_class_visibility(meth, :public)
583 def module_exec(*args, &prc)
584 instance_exec(*args, &prc)
586 alias_method :class_exec, :module_exec
588 def module_function_cv(*args)
590 ctx = MethodContext.current.sender
591 block_env = ctx.env if ctx.kind_of?(BlockContext)
592 # Set the method_scope in the home context if this is an eval
593 ctx = block_env.home_block if block_env and block_env.from_eval?
594 ctx.method_scope = :module
598 method_name = normalize_name meth
599 method = find_method_in_hierarchy(method_name)
600 mc.method_table[method_name] = method.dup
601 mc.set_visibility method_name, :public
602 set_visibility method_name, :private
610 constants = self.constants_table.keys
611 current = self.direct_superclass
613 while current != nil && current != Object
614 constants += current.constants_table.keys
615 current = current.direct_superclass
618 constants.map { |c| c.to_s }
621 def const_defined?(name)
622 name = normalize_const_name(name)
626 return true if current.constants_table.has_key?(name)
627 current = current.direct_superclass
633 # Check if a full constant path is defined, e.g. SomeModule::Something
634 def const_path_defined?(name)
635 # Start at Object if this is a fully-qualified path
636 if name[0,2] == "::" then
638 pieces = name[2,(name.length - 2)].split("::")
641 pieces = name.split("::")
646 while current and not defined
648 defined = pieces.all? do |piece|
649 if const.is_a?(Module) and const.constants_table.key?(piece)
650 const = const.constants_table[piece]
654 current = current.direct_superclass
659 def const_set(name, value)
660 if value.is_a? Module
661 value.set_name_if_necessary(name, self)
663 constants_table[normalize_const_name(name)] = value
669 # \_\_const_set__ is emitted by the compiler for const assignment in
672 def __const_set__(name, value)
673 if constants_table[normalize_const_name(name)]
674 warn "already initialized constant #{name}"
676 return const_set(name, value)
680 # Return the named constant enclosed in this Module.
682 # Included Modules and, for Class objects, superclasses are also searched.
683 # Modules will in addition look in Object. The name is attempted to convert
684 # using #to_str. If the constant is not found, #const_missing is called
688 recursive_const_get(name)
691 def const_lookup(name)
694 parts = String(name).split '::'
695 parts.each do |part| mod = mod.const_get part end
700 def const_missing(name)
701 raise NameError, "Missing or uninitialized constant: #{name}"
704 def attr_reader_cv(*names)
706 method_symbol = reader_method_symbol(name)
707 access_method = AccessVarMethod.get_ivar(attribute_symbol(name), normalize_name(name))
708 method_table[method_symbol] = access_method
714 def attr_writer_cv(*names)
716 method_symbol = writer_method_symbol(name)
717 access_method = AccessVarMethod.set_ivar(attribute_symbol(name), normalize_name(name))
718 method_table[method_symbol] = access_method
724 def attr_accessor_cv(*names)
732 def attr(name,writeable=false)
734 attr_writer(name) if writeable
739 unless other.kind_of? Module
740 raise TypeError, "compared with non class/module"
742 return false if self.equal? other
743 ancestors.index(other) && true
747 return true if self.equal? other
749 return false if lt.nil? && other < self
754 unless other.kind_of? Module
755 raise TypeError, "compared with non class/module"
761 unless other.kind_of? Module
762 raise TypeError, "compared with non class/module"
764 return true if self.equal? other
766 return false if gt.nil? && other > self
771 return 0 if self.equal? other
772 return nil unless other.kind_of? Module
775 other < self ? 1 : nil
782 return true if inst.kind_of? self
783 # TODO: check if inst is extended by self
784 # inst.metaclass < self & true rescue false
788 def set_name_if_necessary(name, mod)
789 return unless @name.nil?
791 while mod and mod != Object
792 parts.unshift mod.name
795 @name = parts.join("::").to_sym
799 # Move the core versions in place now that everything is loaded.
802 def self.after_loaded # :nodoc:
803 alias_method :__method_added__, :__method_added__cv
804 alias_method :alias_method, :alias_method_cv
805 alias_method :module_function, :module_function_cv
806 alias_method :include, :include_cv
807 alias_method :private, :private_cv
808 alias_method :append_features, :append_features_cv
809 alias_method :attr_reader, :attr_reader_cv
810 alias_method :attr_writer, :attr_writer_cv
811 alias_method :attr_accessor, :attr_accessor_cv
813 alias_method :attach_function, :attach_function_cv
815 private :alias_method
818 def autoload(name, path)
819 name = normalize_const_name(name)
820 raise ArgumentError, "empty file name" if path.empty?
821 trigger = Autoload.new(name, self, path)
822 constants_table[name] = trigger
828 return unless constants_table.key?(name)
829 trigger = constants_table[name]
830 return unless trigger.kind_of?(Autoload)
831 trigger.original_path
834 def remove_const(name)
836 const_missing(name) unless constants_table.has_key?(sym)
837 constants_table.delete(sym)
840 private :remove_const
847 def method_added(name)
850 private :method_added
852 # See #const_get for documentation.
853 def recursive_const_get(name, missing=true)
854 name = normalize_const_name(name)
856 current, constant = self, nil
859 constant = current.constants_table[name]
860 constant = constant.call if constant.kind_of?(Autoload)
861 return constant if constant
862 current = current.direct_superclass
865 if self.kind_of?(Module) and not self.kind_of?(Class)
866 constant = Object.constants_table[name]
867 constant = constant.call if constant.kind_of?(Autoload)
868 return constant if constant
871 return nil unless missing
876 private :recursive_const_get
878 def normalize_name(name)
880 if name.respond_to?(:to_sym)
881 warn 'do not use Fixnums as Symbols' if name.kind_of?(Fixnum)
882 sym_name = name.to_sym
883 elsif name.respond_to?(:to_str)
884 sym_name = StringValue(name).to_sym
886 raise TypeError, "#{name} is not a symbol" unless sym_name
891 private :normalize_name
893 def normalize_const_name(name)
894 name = normalize_name(name)
895 raise NameError, "wrong constant name #{name}" unless valid_const_name?(name)
899 private :normalize_const_name
902 # Modified to fit definition at:
903 # http://docs.huihoo.com/ruby/ruby-man-1.4/syntax.html#variable
906 def valid_const_name?(name)
907 name.to_s =~ /^((::)?[A-Z]\w*)+$/ ? true : false
910 private :valid_const_name?
912 def attribute_symbol(name)
913 "@#{normalize_name(name)}".to_sym
916 private :attribute_symbol
918 def reader_method_symbol(name)
922 private :reader_method_symbol
924 def writer_method_symbol(name)
925 "#{normalize_name(name)}=".to_sym
928 private :writer_method_symbol