diff options
author | matz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2007-10-03 06:48:06 +0000 |
---|---|---|
committer | matz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2007-10-03 06:48:06 +0000 |
commit | 91a079cc86b01699d11481bdd8f9636df334a404 (patch) | |
tree | 1bd507ac5bcb0c59f0ef86ce8a8cc53e583fcaae | |
parent | ee2ac58e4de6a63cc83bbef13b6a25718f5b8f0f (diff) |
* variable.c (rb_cvar_set): check whether class variable is
defined in superclasses. root classes have higher priority.
removes lower class variable entry from IV_TBL (if it's defined
in classes, not modules).
* variable.c (rb_cvar_get): ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@13604 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | test/ruby/test_variable.rb | 14 | ||||
-rw-r--r-- | variable.c | 70 |
3 files changed, 77 insertions, 16 deletions
@@ -1,3 +1,12 @@ +Wed Oct 3 15:43:15 2007 Yukihiro Matsumoto <matz@ruby-lang.org> + + * variable.c (rb_cvar_set): check whether class variable is + defined in superclasses. root classes have higher priority. + removes lower class variable entry from IV_TBL (if it's defined + in classes, not modules). + + * variable.c (rb_cvar_get): ditto. + Wed Oct 3 10:06:53 2007 Nobuyoshi Nakada <nobu@ruby-lang.org> * ruby.c (ruby_process_options): push frame with program name. diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb index 32e2030a23..cd04de5c3a 100644 --- a/test/ruby/test_variable.rb +++ b/test/ruby/test_variable.rb @@ -25,7 +25,7 @@ class TestVariable < Test::Unit::TestCase end class Titans < Gods - @@rule = "Cronus" # do not affect @@rule in Gods + @@rule = "Cronus" # modifies @@rule in Gods include Olympians def ruler4 @@rule @@ -44,13 +44,13 @@ class TestVariable < Test::Unit::TestCase $_ = foobar assert_equal(foobar, $_) - assert_equal("Uranus", Gods.new.ruler0) - assert_equal("Uranus", Gods.ruler1) - assert_equal("Uranus", Gods.ruler2) - assert_equal("Uranus", Titans.ruler1) - assert_equal("Uranus", Titans.ruler2) + assert_equal("Cronus", Gods.new.ruler0) + assert_equal("Cronus", Gods.ruler1) + assert_equal("Cronus", Gods.ruler2) + assert_equal("Cronus", Titans.ruler1) + assert_equal("Cronus", Titans.ruler2) atlas = Titans.new - assert_equal("Uranus", atlas.ruler0) + assert_equal("Cronus", atlas.ruler0) assert_equal("Zeus", atlas.ruler3) assert_equal("Cronus", atlas.ruler4) end diff --git a/variable.c b/variable.c index 5f15fbc74d..a0f68f2073 100644 --- a/variable.c +++ b/variable.c @@ -1701,15 +1701,53 @@ rb_define_global_const(const char *name, VALUE val) rb_define_const(rb_cObject, name, val); } +static VALUE +original_module(c) + VALUE c; +{ + if (TYPE(c) == T_ICLASS) + return RBASIC(c)->klass; + return c; +} + void rb_cvar_set(VALUE klass, ID id, VALUE val) { - mod_av_set(klass, id, val, Qfalse); + VALUE tmp; + VALUE front = 0, target = 0; + + tmp = klass; + while (tmp) { + if (RCLASS_IV_TBL(tmp) && st_lookup(RCLASS_IV_TBL(tmp),id,0)) { + if (!front) front = tmp; + target = tmp; + } + tmp = RCLASS_SUPER(tmp); + } + if (target) { + if (front && target != front) { + ID did = id; + + if (RTEST(ruby_verbose)) { + rb_warning("class variable %s of %s is overtaken by %s", + rb_id2name(id), rb_class2name(original_module(front)), + rb_class2name(original_module(target))); + } + if (BUILTIN_TYPE(front) == T_CLASS) { + st_delete(RCLASS_IV_TBL(front),&did,0); + } + } + } + else { + target = klass; + } + + mod_av_set(target, id, val, Qfalse); } #define CVAR_LOOKUP(v,r) do {\ if (RCLASS_IV_TBL(klass) && st_lookup(RCLASS_IV_TBL(klass),id,(v))) {\ - return (r);\ + r;\ }\ if (FL_TEST(klass, FL_SINGLETON) ) {\ VALUE obj = rb_iv_get(klass, "__attached__");\ @@ -1728,7 +1766,7 @@ rb_cvar_set(VALUE klass, ID id, VALUE val) }\ while (klass) {\ if (RCLASS_IV_TBL(klass) && st_lookup(RCLASS_IV_TBL(klass),id,(v))) {\ - return (r);\ + r;\ }\ klass = RCLASS_SUPER(klass);\ }\ @@ -1737,20 +1775,34 @@ rb_cvar_set(VALUE klass, ID id, VALUE val) VALUE rb_cvar_get(VALUE klass, ID id) { - VALUE value, tmp; + VALUE value, tmp, front = 0, target = 0; tmp = klass; - CVAR_LOOKUP(&value, value); - rb_name_error(id,"uninitialized class variable %s in %s", - rb_id2name(id), rb_class2name(tmp)); - return Qnil; /* not reached */ + CVAR_LOOKUP(&value, {if (!front) front = klass; target = klass;}); + if (!target) { + rb_name_error(id,"uninitialized class variable %s in %s", + rb_id2name(id), rb_class2name(tmp)); + } + if (front && target != front) { + ID did = id; + + if (RTEST(ruby_verbose)) { + rb_warning("class variable %s of %s is overtaken by %s", + rb_id2name(id), rb_class2name(original_module(front)), + rb_class2name(original_module(target))); + } + if (BUILTIN_TYPE(front) == T_CLASS) { + st_delete(RCLASS_IV_TBL(front),&did,0); + } + } + return value; } VALUE rb_cvar_defined(VALUE klass, ID id) { if (!klass) return Qfalse; - CVAR_LOOKUP(0,Qtrue); + CVAR_LOOKUP(0,return Qtrue); return Qfalse; } |