summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--test/ruby/test_variable.rb14
-rw-r--r--variable.c70
3 files changed, 77 insertions, 16 deletions
diff --git a/ChangeLog b/ChangeLog
index 0160994ac2..3685ac3057 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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;
}