diff --git a/ChangeLog b/ChangeLog index 7c61963cef..933fccb0cd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +Mon Dec 15 14:56:59 2008 Yuki Sonoda (Yugui) + + * test/ruby/test_metaclass.rb: new test case for metaclass hierarchy. + + * class.c (make_metametaclass): new function. extracted from + rb_make_metaclass. + + * class.c (rb_make_metaclass): uses make_metametaclass when called for a + metaclass. + + * class.c (rb_singleton_class): creates a meta^(n+2)-class in + addition to a meta^(n+1)-class when called for a meta^(n)-class. + This is because the returned meta^(n+1) class must acts as an instance of + Class, metaclass of Class, ..., meta^(n+1)-class of Class, + Module, metaclass of Module, ..., meta^(n+1)-class of Module, + Object, metaclass of Object, ..., meta^(n+2)-class of Object, + BasicObject, metaclass of BasicObject, ..., meta^(n+2)-class of + and BasicObject even when Class, Module, Object or BasicObject has + not have its meta^(i)-class yet. + Mon Dec 15 15:13:22 2008 Nobuyoshi Nakada * id.h, template/id.h.tmpl (ruby_method_ids): sees YYTOKENTYPE too. diff --git a/class.c b/class.c index 066fc07443..89becccb54 100644 --- a/class.c +++ b/class.c @@ -186,29 +186,47 @@ rb_singleton_class_attached(VALUE klass, VALUE obj) } } + +static VALUE +make_metametaclass(VALUE metaclass) +{ + VALUE metametaclass, super_of_metaclass; + + if (RBASIC(metaclass)->klass == metaclass) { /* for meta^(n)-class of Class */ + metametaclass = rb_class_boot(Qnil); + RBASIC(metametaclass)->klass = metametaclass; + } + else { + metametaclass = rb_class_boot(Qnil); + RBASIC(metametaclass)->klass = + (RBASIC(RBASIC(metaclass)->klass)->klass == RBASIC(metaclass)->klass) + ? make_metametaclass(RBASIC(metaclass)->klass) + : RBASIC(RBASIC(metaclass)->klass)->klass; + } + + FL_SET(metametaclass, FL_SINGLETON); + rb_singleton_class_attached(metametaclass, metaclass); + RBASIC(metaclass)->klass = metametaclass; + + super_of_metaclass = RCLASS_SUPER(metaclass); + while (FL_TEST(super_of_metaclass, T_ICLASS)) { + super_of_metaclass = RCLASS_SUPER(super_of_metaclass); + } + RCLASS_SUPER(metametaclass) = + rb_iv_get(RBASIC(super_of_metaclass)->klass, "__attached__") == super_of_metaclass + ? RBASIC(super_of_metaclass)->klass + : make_metametaclass(super_of_metaclass); + OBJ_INFECT(metametaclass, RCLASS_SUPER(metametaclass)); + + return metametaclass; +} + + VALUE rb_make_metaclass(VALUE obj, VALUE super) { - if (BUILTIN_TYPE(obj) == T_CLASS && FL_TEST(obj, FL_SINGLETON)) { - VALUE metaclass, meta_of_super; - if (RBASIC(obj)->klass == obj) { /* for meta^(n)-class of Class */ - metaclass = rb_class_boot(obj); - RBASIC(metaclass)->klass = metaclass; - } - else { - metaclass = rb_class_boot(super); - RBASIC(metaclass)->klass = rb_singleton_class(RBASIC(obj)->klass); - } - FL_SET(metaclass, FL_SINGLETON); - rb_singleton_class_attached(metaclass, obj); - RBASIC(obj)->klass = metaclass; - - meta_of_super = RCLASS(obj)->ptr->super; - while (FL_TEST(meta_of_super, T_ICLASS)) { - meta_of_super = RCLASS(meta_of_super)->ptr->super; - } - RCLASS(metaclass)->ptr->super = rb_singleton_class(meta_of_super); - return metaclass; + if (BUILTIN_TYPE(obj) == T_CLASS && FL_TEST(obj, FL_SINGLETON)) { /* obj is a metaclass */ + return make_metametaclass(obj); } else { VALUE metasuper; @@ -844,6 +862,11 @@ rb_singleton_class(VALUE obj) else { klass = rb_make_metaclass(obj, RBASIC(obj)->klass); } + + if (BUILTIN_TYPE(obj) == T_CLASS) { + if (rb_iv_get(RBASIC(klass)->klass, "__attached__") != klass) + make_metametaclass(klass); + } if (OBJ_TAINTED(obj)) { OBJ_TAINT(klass); } diff --git a/test/ruby/test_metaclass.rb b/test/ruby/test_metaclass.rb new file mode 100644 index 0000000000..d531cb9dcb --- /dev/null +++ b/test/ruby/test_metaclass.rb @@ -0,0 +1,169 @@ +require 'test/unit' +require_relative 'envutil' +require '~/dev/metaclass_util/metaclass_util' + +class TestMetaclass < Test::Unit::TestCase + class Foo; end + class Bar < Foo; end + class Baz; end + + def setup + Object.class_eval do + def method_o; end + end + Module.class_eval do + def method_m; end + end + Class.class_eval do + def method_c; end + end + end + def teardown + Object.class_eval do + remove_method :method_o rescue nil + end + Module.class_eval do + remove_method :method_m rescue nil + end + Class.class_eval do + remove_method :method_c rescue nil + end + Object.class_eval do + class << self + remove_method :class_method_o rescue nil + end + end + Module.class_eval do + class << self + remove_method :class_method_m rescue nil + end + end + Class.class_eval do + class << self + remove_method :class_method_c rescue nil + end + end + Object.class_eval do + class << self + class << self + remove_method :metaclass_method_o rescue nil + end + end + end + Module.class_eval do + class << self + class << self + remove_method :metaclass_method_m rescue nil + end + end + end + Class.class_eval do + class << self + class << self + remove_method :metaclass_method_c rescue nil + end + end + end + end + + def test_metaclass + class << Object + def class_method_o; end + end + class << Foo + def class_method_f; end + end + class << Baz + def class_method_b; end + end + assert_nothing_raised{ Bar.method_o } + assert_nothing_raised{ Bar.method_m } + assert_nothing_raised{ Bar.method_c } + assert_nothing_raised{ Bar.class_method_o } + assert_nothing_raised{ Bar.class_method_f } + assert_raise(NoMethodError){ Bar.class_method_b } + + class << Module + def class_method_m; end + end + class << Class + def class_method_c; end + end + class << Object + class << self + def metaclass_method_o; end + end + end + class << Foo + class << self + def metaclass_method_f; end + end + end + class << Baz + class << self + def metaclass_method_b; end + end + end + metaclass_of_bar = class << Bar; self end + assert_nothing_raised{ metaclass_of_bar.method_o } + assert_nothing_raised{ metaclass_of_bar.method_m } + assert_nothing_raised{ metaclass_of_bar.method_c } + assert_nothing_raised{ metaclass_of_bar.class_method_o } + assert_raise(NoMethodError){ metaclass_of_bar.class_method_f } + assert_raise(NoMethodError){ metaclass_of_bar.class_method_b } + assert_nothing_raised{ metaclass_of_bar.class_method_m } + assert_nothing_raised{ metaclass_of_bar.class_method_c } + assert_nothing_raised{ metaclass_of_bar.metaclass_method_o } + assert_nothing_raised{ metaclass_of_bar.metaclass_method_f } + assert_raise(NoMethodError){ metaclass_of_bar.metaclass_method_b } + + class << Module + class << self + def metaclass_method_m; end + end + end + class << Class + class << self + def metaclass_method_c; end + end + end + class << Object + class << self + class << self + def metametaclass_method_o; end + end + end + end + class << Foo + class << self + class << self + def metametaclass_method_f; end + end + end + end + class << Baz + class << self + class << self + def metametaclass_method_b; end + end + end + end + metametaclass_of_bar = class << metaclass_of_bar; self end + assert_nothing_raised{ metametaclass_of_bar.method_o } + assert_nothing_raised{ metametaclass_of_bar.method_m } + assert_nothing_raised{ metametaclass_of_bar.method_c } + assert_nothing_raised{ metametaclass_of_bar.class_method_o } + assert_raise(NoMethodError){ metametaclass_of_bar.class_method_f } + assert_raise(NoMethodError){ metametaclass_of_bar.class_method_b } + assert_nothing_raised{ metametaclass_of_bar.class_method_m } + assert_nothing_raised{ metametaclass_of_bar.class_method_c } + assert_nothing_raised{ metametaclass_of_bar.metaclass_method_o } + assert_raise(NoMethodError){ metametaclass_of_bar.metaclass_method_f } + assert_raise(NoMethodError){ metametaclass_of_bar.metaclass_method_b } + assert_nothing_raised{ metametaclass_of_bar.metaclass_method_m } + assert_nothing_raised{ metametaclass_of_bar.metaclass_method_c } + assert_nothing_raised{ metametaclass_of_bar.metametaclass_method_o } + assert_nothing_raised{ metametaclass_of_bar.metametaclass_method_f } + assert_raise(NoMethodError){ metametaclass_of_bar.metaclass_method_b } + end +end