* 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/trunk@20747 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
13aaff3e9f
commit
3a5c0bbcb5
20
ChangeLog
20
ChangeLog
@ -1,3 +1,23 @@
|
|||||||
|
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>
|
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.
|
* id.h, template/id.h.tmpl (ruby_method_ids): sees YYTOKENTYPE too.
|
||||||
|
63
class.c
63
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
|
VALUE
|
||||||
rb_make_metaclass(VALUE obj, VALUE super)
|
rb_make_metaclass(VALUE obj, VALUE super)
|
||||||
{
|
{
|
||||||
if (BUILTIN_TYPE(obj) == T_CLASS && FL_TEST(obj, FL_SINGLETON)) {
|
if (BUILTIN_TYPE(obj) == T_CLASS && FL_TEST(obj, FL_SINGLETON)) { /* obj is a metaclass */
|
||||||
VALUE metaclass, meta_of_super;
|
return make_metametaclass(obj);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
VALUE metasuper;
|
VALUE metasuper;
|
||||||
@ -844,6 +862,11 @@ rb_singleton_class(VALUE obj)
|
|||||||
else {
|
else {
|
||||||
klass = rb_make_metaclass(obj, RBASIC(obj)->klass);
|
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)) {
|
if (OBJ_TAINTED(obj)) {
|
||||||
OBJ_TAINT(klass);
|
OBJ_TAINT(klass);
|
||||||
}
|
}
|
||||||
|
169
test/ruby/test_metaclass.rb
Normal file
169
test/ruby/test_metaclass.rb
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user