summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog12
-rw-r--r--compile.c8
-rw-r--r--insns.def10
-rw-r--r--test/ruby/test_class.rb18
-rw-r--r--test/ruby/test_module.rb18
5 files changed, 58 insertions, 8 deletions
diff --git a/ChangeLog b/ChangeLog
index b7829c5ef7..cbd3607497 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+Sat Jan 29 01:24:57 2011 Yusuke Endoh <mame@tsg.ne.jp>
+
+ * compile.c (NODE_CLASS, NODE_MODULE), insns.def (defineclass): raise
+ an exception when "class Foo::Bar" is evaluated and Foo::Bar is
+ private. To implement this, define_type of "defineclass" is added
+ so that the instruction can distinguish whether the class definition
+ is scoped (class Foo::Bar) or not (class Bar).
+
+ * test/ruby/test_class.rb (test_redefine_private_class),
+ test/ruby/test_module.rb
+ (test_define_module_under_private_constant): add tests for above.
+
Sat Jan 29 01:19:17 2011 Yusuke Endoh <mame@tsg.ne.jp>
* constant.h, variable.c: to ensure compatibility, rb_const_get_* must
diff --git a/compile.c b/compile.c
index 99487d8e44..459a3999ff 100644
--- a/compile.c
+++ b/compile.c
@@ -4680,10 +4680,10 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
node->nd_body,
rb_sprintf("<class:%s>", rb_id2name(node->nd_cpath->nd_mid)),
ISEQ_TYPE_CLASS, nd_line(node));
- compile_cpath(ret, iseq, node->nd_cpath);
+ VALUE noscope = compile_cpath(ret, iseq, node->nd_cpath);
COMPILE(ret, "super", node->nd_super);
ADD_INSN3(ret, nd_line(node), defineclass,
- ID2SYM(node->nd_cpath->nd_mid), iseqval, INT2FIX(0));
+ ID2SYM(node->nd_cpath->nd_mid), iseqval, INT2FIX(noscope ? 3 : 0));
if (poped) {
ADD_INSN(ret, nd_line(node), pop);
@@ -4696,10 +4696,10 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
rb_sprintf("<module:%s>", rb_id2name(node->nd_cpath->nd_mid)),
ISEQ_TYPE_CLASS, nd_line(node));
- compile_cpath(ret, iseq, node->nd_cpath);
+ VALUE noscope = compile_cpath(ret, iseq, node->nd_cpath);
ADD_INSN (ret, nd_line(node), putnil); /* dummy */
ADD_INSN3(ret, nd_line(node), defineclass,
- ID2SYM(node->nd_cpath->nd_mid), iseqval, INT2FIX(2));
+ ID2SYM(node->nd_cpath->nd_mid), iseqval, INT2FIX(noscope ? 5 : 2));
if (poped) {
ADD_INSN(ret, nd_line(node), pop);
}
diff --git a/insns.def b/insns.def
index e45609b880..72b0f27cc6 100644
--- a/insns.def
+++ b/insns.def
@@ -894,7 +894,8 @@ defineclass
VALUE klass;
switch ((int)define_type) {
- case 0:
+ case 0: /* scoped: class Foo::Bar */
+ case 3: /* no scope: class Bar */
/* val is dummy. classdef returns class scope value */
if (super == Qnil) {
@@ -907,7 +908,7 @@ defineclass
rb_autoload_load(cbase, id);
if (rb_const_defined_at(cbase, id)) {
/* already exist */
- klass = rb_const_get_at(cbase, id);
+ klass = define_type == 0 ? rb_public_const_get(cbase, id) : rb_const_get_at(cbase, id);
if (TYPE(klass) != T_CLASS) {
rb_raise(rb_eTypeError, "%s is not a class", rb_id2name(id));
}
@@ -935,7 +936,8 @@ defineclass
/* super is dummy */
klass = rb_singleton_class(cbase);
break;
- case 2:
+ case 2: /* scoped: module Foo::Bar or module ::Bar */
+ case 5: /* no scope: module Bar */
/* val is dummy. classdef returns class scope value */
/* super is dummy */
@@ -943,7 +945,7 @@ defineclass
/* find klass */
if (rb_const_defined_at(cbase, id)) {
- klass = rb_const_get_at(cbase, id);
+ klass = define_type == 2 ? rb_public_const_get(cbase, id) : rb_const_get_at(cbase, id);
/* already exist */
if (TYPE(klass) != T_MODULE) {
rb_raise(rb_eTypeError, "%s is not a module", rb_id2name(id));
diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb
index 3f18294e0f..55940a8891 100644
--- a/test/ruby/test_class.rb
+++ b/test/ruby/test_class.rb
@@ -240,4 +240,22 @@ class TestClass < Test::Unit::TestCase
def test_nested_class_removal
assert_normal_exit('File.__send__(:remove_const, :Stat); at_exit{File.stat(".")}; GC.start')
end
+
+ class PrivateClass
+ end
+ private_constant :PrivateClass
+
+ def test_redefine_private_class
+ assert_raise(NameError) do
+ eval("class ::TestClass::PrivateClass; end")
+ end
+ eval <<-END
+ class ::TestClass
+ class PrivateClass
+ def foo; 42; end
+ end
+ end
+ END
+ assert_equal(42, PrivateClass.new.foo)
+ end
end
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index 87a4905e3c..3306c0db74 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -947,6 +947,24 @@ class TestModule < Test::Unit::TestCase
c.private_constant(:FOO)
assert_raise(NameError) { c::FOO }
assert_equal("foo", c.class_eval("FOO"))
+ assert_equal("foo", c.const_get("FOO"))
+ end
+
+ class PrivateClass
+ end
+ private_constant :PrivateClass
+
+ def test_define_module_under_private_constant
+ assert_raise(NameError) do
+ eval %q{class TestModule::PrivateClass; end}
+ end
+ assert_raise(NameError) do
+ eval %q{module TestModule::PrivateClass::TestModule; end}
+ end
+ eval %q{class PrivateClass; end}
+ eval %q{module PrivateClass::TestModule; end}
+ assert_instance_of(Module, PrivateClass::TestModule)
+ PrivateClass.class_eval { remove_const(:TestModule) }
end
def test_public_constant