Skip to content

Commit a2ec866

Browse files
committed
* 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@30714 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
1 parent f0483c4 commit a2ec866

File tree

5 files changed

+58
-8
lines changed

5 files changed

+58
-8
lines changed

ChangeLog

+12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
Sat Jan 29 01:24:57 2011 Yusuke Endoh <mame@tsg.ne.jp>
2+
3+
* compile.c (NODE_CLASS, NODE_MODULE), insns.def (defineclass): raise
4+
an exception when "class Foo::Bar" is evaluated and Foo::Bar is
5+
private. To implement this, define_type of "defineclass" is added
6+
so that the instruction can distinguish whether the class definition
7+
is scoped (class Foo::Bar) or not (class Bar).
8+
9+
* test/ruby/test_class.rb (test_redefine_private_class),
10+
test/ruby/test_module.rb
11+
(test_define_module_under_private_constant): add tests for above.
12+
113
Sat Jan 29 01:19:17 2011 Yusuke Endoh <mame@tsg.ne.jp>
214

315
* constant.h, variable.c: to ensure compatibility, rb_const_get_* must

compile.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -4680,10 +4680,10 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
46804680
node->nd_body,
46814681
rb_sprintf("<class:%s>", rb_id2name(node->nd_cpath->nd_mid)),
46824682
ISEQ_TYPE_CLASS, nd_line(node));
4683-
compile_cpath(ret, iseq, node->nd_cpath);
4683+
VALUE noscope = compile_cpath(ret, iseq, node->nd_cpath);
46844684
COMPILE(ret, "super", node->nd_super);
46854685
ADD_INSN3(ret, nd_line(node), defineclass,
4686-
ID2SYM(node->nd_cpath->nd_mid), iseqval, INT2FIX(0));
4686+
ID2SYM(node->nd_cpath->nd_mid), iseqval, INT2FIX(noscope ? 3 : 0));
46874687

46884688
if (poped) {
46894689
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)
46964696
rb_sprintf("<module:%s>", rb_id2name(node->nd_cpath->nd_mid)),
46974697
ISEQ_TYPE_CLASS, nd_line(node));
46984698

4699-
compile_cpath(ret, iseq, node->nd_cpath);
4699+
VALUE noscope = compile_cpath(ret, iseq, node->nd_cpath);
47004700
ADD_INSN (ret, nd_line(node), putnil); /* dummy */
47014701
ADD_INSN3(ret, nd_line(node), defineclass,
4702-
ID2SYM(node->nd_cpath->nd_mid), iseqval, INT2FIX(2));
4702+
ID2SYM(node->nd_cpath->nd_mid), iseqval, INT2FIX(noscope ? 5 : 2));
47034703
if (poped) {
47044704
ADD_INSN(ret, nd_line(node), pop);
47054705
}

insns.def

+6-4
Original file line numberDiff line numberDiff line change
@@ -894,7 +894,8 @@ defineclass
894894
VALUE klass;
895895

896896
switch ((int)define_type) {
897-
case 0:
897+
case 0: /* scoped: class Foo::Bar */
898+
case 3: /* no scope: class Bar */
898899
/* val is dummy. classdef returns class scope value */
899900

900901
if (super == Qnil) {
@@ -907,7 +908,7 @@ defineclass
907908
rb_autoload_load(cbase, id);
908909
if (rb_const_defined_at(cbase, id)) {
909910
/* already exist */
910-
klass = rb_const_get_at(cbase, id);
911+
klass = define_type == 0 ? rb_public_const_get(cbase, id) : rb_const_get_at(cbase, id);
911912
if (TYPE(klass) != T_CLASS) {
912913
rb_raise(rb_eTypeError, "%s is not a class", rb_id2name(id));
913914
}
@@ -935,15 +936,16 @@ defineclass
935936
/* super is dummy */
936937
klass = rb_singleton_class(cbase);
937938
break;
938-
case 2:
939+
case 2: /* scoped: module Foo::Bar or module ::Bar */
940+
case 5: /* no scope: module Bar */
939941
/* val is dummy. classdef returns class scope value */
940942
/* super is dummy */
941943

942944
vm_check_if_namespace(cbase);
943945

944946
/* find klass */
945947
if (rb_const_defined_at(cbase, id)) {
946-
klass = rb_const_get_at(cbase, id);
948+
klass = define_type == 2 ? rb_public_const_get(cbase, id) : rb_const_get_at(cbase, id);
947949
/* already exist */
948950
if (TYPE(klass) != T_MODULE) {
949951
rb_raise(rb_eTypeError, "%s is not a module", rb_id2name(id));

test/ruby/test_class.rb

+18
Original file line numberDiff line numberDiff line change
@@ -240,4 +240,22 @@ def foo
240240
def test_nested_class_removal
241241
assert_normal_exit('File.__send__(:remove_const, :Stat); at_exit{File.stat(".")}; GC.start')
242242
end
243+
244+
class PrivateClass
245+
end
246+
private_constant :PrivateClass
247+
248+
def test_redefine_private_class
249+
assert_raise(NameError) do
250+
eval("class ::TestClass::PrivateClass; end")
251+
end
252+
eval <<-END
253+
class ::TestClass
254+
class PrivateClass
255+
def foo; 42; end
256+
end
257+
end
258+
END
259+
assert_equal(42, PrivateClass.new.foo)
260+
end
243261
end

test/ruby/test_module.rb

+18
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,24 @@ def test_private_constant
947947
c.private_constant(:FOO)
948948
assert_raise(NameError) { c::FOO }
949949
assert_equal("foo", c.class_eval("FOO"))
950+
assert_equal("foo", c.const_get("FOO"))
951+
end
952+
953+
class PrivateClass
954+
end
955+
private_constant :PrivateClass
956+
957+
def test_define_module_under_private_constant
958+
assert_raise(NameError) do
959+
eval %q{class TestModule::PrivateClass; end}
960+
end
961+
assert_raise(NameError) do
962+
eval %q{module TestModule::PrivateClass::TestModule; end}
963+
end
964+
eval %q{class PrivateClass; end}
965+
eval %q{module PrivateClass::TestModule; end}
966+
assert_instance_of(Module, PrivateClass::TestModule)
967+
PrivateClass.class_eval { remove_const(:TestModule) }
950968
end
951969

952970
def test_public_constant

0 commit comments

Comments
 (0)