From 241ca7bfff26b1f624ef64212b68d76efcb78f9d Mon Sep 17 00:00:00 2001 From: nobu Date: Sat, 14 Dec 2013 02:25:58 +0000 Subject: object.c: nested path const_defined? * object.c (rb_mod_const_defined): support nested class path as well as const_get. [Feature #7414] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@44194 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 5 +++ object.c | 88 ++++++++++++++++++++++++++++++++++++++++++------ test/ruby/test_module.rb | 20 +++++++++++ 3 files changed, 103 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 48c4203e74..5ee650b19c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Sat Dec 14 11:25:56 2013 Nobuyoshi Nakada + + * object.c (rb_mod_const_defined): support nested class path as + well as const_get. [Feature #7414] + Sat Dec 14 01:31:52 2013 Nobuyoshi Nakada * eval.c (rb_rescue2): reuse tags pushed for body proc to protect diff --git a/object.c b/object.c index 9c84fe5931..9b8ca7400b 100644 --- a/object.c +++ b/object.c @@ -2213,6 +2213,8 @@ static VALUE rb_mod_const_defined(int argc, VALUE *argv, VALUE mod) { VALUE name, recur; + rb_encoding *enc; + const char *pbeg, *p, *path, *pend; ID id; if (argc == 1) { @@ -2222,20 +2224,86 @@ rb_mod_const_defined(int argc, VALUE *argv, VALUE mod) else { rb_scan_args(argc, argv, "11", &name, &recur); } - if (!(id = rb_check_id(&name))) { - if (rb_is_const_name(name)) { - return Qfalse; + + if (SYMBOL_P(name)) { + id = SYM2ID(name); + if (!rb_is_const_id(id)) goto wrong_id; + return RTEST(recur) ? rb_const_defined(mod, id) : rb_const_defined_at(mod, id); + } + + path = StringValuePtr(name); + enc = rb_enc_get(name); + + if (!rb_enc_asciicompat(enc)) { + rb_raise(rb_eArgError, "invalid class path encoding (non ASCII)"); + } + + pbeg = p = path; + pend = path + RSTRING_LEN(name); + + if (p >= pend || !*p) { + wrong_name: + rb_raise(rb_eNameError, "wrong constant name %"PRIsVALUE, + QUOTE(name)); + } + + if (p + 2 < pend && p[0] == ':' && p[1] == ':') { + mod = rb_cObject; + p += 2; + pbeg = p; + } + + while (p < pend) { + VALUE part; + long len, beglen; + + while (p < pend && *p != ':') p++; + + if (pbeg == p) goto wrong_name; + + id = rb_check_id_cstr(pbeg, len = p-pbeg, enc); + beglen = pbeg-path; + + if (p < pend && p[0] == ':') { + if (p + 2 >= pend || p[1] != ':') goto wrong_name; + p += 2; + pbeg = p; + } + + if (!id) { + if (!ISUPPER(*pbeg) || !rb_enc_symname2_p(pbeg, len, enc)) { + part = rb_str_subseq(name, beglen, len); + rb_name_error_str(part, "wrong constant name %"PRIsVALUE, + QUOTE(part)); + } + else { + return Qfalse; + } + } + if (!rb_is_const_id(id)) { + wrong_id: + rb_name_error(id, "wrong constant name %"PRIsVALUE, + QUOTE_ID(id)); + } + if (RTEST(recur)) { + if (!rb_const_defined(mod, id)) + return Qfalse; + mod = rb_const_get(mod, id); } else { - rb_name_error_str(name, "wrong constant name %"PRIsVALUE, - QUOTE(name)); + if (!rb_const_defined_at(mod, id)) + return Qfalse; + mod = rb_const_get_at(mod, id); + } + recur = Qfalse; + + if (p < pend && !RB_TYPE_P(mod, T_MODULE) && !RB_TYPE_P(mod, T_CLASS)) { + rb_raise(rb_eTypeError, "%"PRIsVALUE" does not refer to class/module", + QUOTE(name)); } } - if (!rb_is_const_id(id)) { - rb_name_error(id, "wrong constant name %"PRIsVALUE, - QUOTE_ID(id)); - } - return RTEST(recur) ? rb_const_defined(mod, id) : rb_const_defined_at(mod, id); + + return Qtrue; } /* diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index 26dbf9a588..5b5e04b0da 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -303,6 +303,26 @@ class TestModule < Test::Unit::TestCase end end + def test_nested_defined + assert_send([Object, :const_defined?, [self.class.name, 'Other'].join('::')]) + assert_send([self.class, :const_defined?, 'User::USER']) + assert_not_send([self.class, :const_defined?, 'User::Foo']) + end + + def test_nested_defined_symbol + const = [self.class, Other].join('::').to_sym + assert_raise(NameError) {Object.const_defined?(const)} + + const = [User, 'USER'].join('::').to_sym + assert_raise(NameError) {self.class.const_defined?(const)} + end + + def test_nested_defined_bad_class + assert_raise(TypeError) do + self.class.const_defined?('User::USER::Foo') + end + end + def test_const_set assert_not_operator(Other, :const_defined?, :KOALA) Other.const_set(:KOALA, 99) -- cgit v1.2.3