summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoryugui <yugui@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-12-17 06:15:46 +0000
committeryugui <yugui@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-12-17 06:15:46 +0000
commit85d944ed648c40eb91e96741d9209a50d9d44729 (patch)
tree66e03eb52c1adc1d574cce554f26a141cba2cae9
parent2463b42e86af4c962a903e48ff7dfd9a05ec7b30 (diff)
merges r20747 and r20802 from trunk into ruby_1_9_1.
* 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_9_1@20814 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog25
-rw-r--r--class.c63
-rw-r--r--test/ruby/test_metaclass.rb167
3 files changed, 235 insertions, 20 deletions
diff --git a/ChangeLog b/ChangeLog
index 832c38002d..02fdaa61e4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+Wed Dec 17 09:50:19 2008 Yuki Sonoda (Yugui) <yugui@yugui.jp>
+
+ * test/ruby/test_metaclass.rb: removed codes for my debugging.
+ sorry. [ruby-dev:37470]
+
+Mon Dec 15 14:56:59 2008 Yuki Sonoda (Yugui) <yugui@yugui.jp>
+
+ * 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 <nobu@ruby-lang.org>
* 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..6386a02dfa
--- /dev/null
+++ b/test/ruby/test_metaclass.rb
@@ -0,0 +1,167 @@
+require 'test/unit'
+
+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