ruby-changes:51659
From: nobu <ko1@a...>
Date: Fri, 6 Jul 2018 22:57:05 +0900 (JST)
Subject: [ruby-changes:51659] nobu:r63871 (trunk): const_missing on private constants
nobu 2018-07-06 22:56:58 +0900 (Fri, 06 Jul 2018) New Revision: 63871 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=63871 Log: const_missing on private constants * variable.c (rb_const_search): call #const_missing method on private constants, as well as uninitialized constants. [Feature #14328] Modified files: trunk/spec/ruby/language/constants_spec.rb trunk/test/ruby/test_module.rb trunk/variable.c trunk/vm.c trunk/vm_core.h Index: variable.c =================================================================== --- variable.c (revision 63870) +++ variable.c (revision 63871) @@ -1795,7 +1795,12 @@ rb_const_missing(VALUE klass, VALUE name https://github.com/ruby/ruby/blob/trunk/variable.c#L1795 VALUE rb_mod_const_missing(VALUE klass, VALUE name) { + VALUE ref = GET_EC()->private_const_reference; rb_vm_pop_cfunc_frame(); + if (ref) { + rb_name_err_raise("private constant %2$s::%1$s referenced", + ref, name); + } uninitialized_constant(klass, name); UNREACHABLE; @@ -2363,8 +2368,8 @@ rb_const_search(VALUE klass, ID id, int https://github.com/ruby/ruby/blob/trunk/variable.c#L2368 while ((ce = rb_const_lookup(tmp, id))) { if (visibility && RB_CONST_PRIVATE_P(ce)) { if (BUILTIN_TYPE(tmp) == T_ICLASS) tmp = RBASIC(tmp)->klass; - rb_name_err_raise("private constant %2$s::%1$s referenced", - tmp, ID2SYM(id)); + GET_EC()->private_const_reference = tmp; + return Qundef; } rb_const_warn_if_deprecated(ce, tmp, id); value = ce->value; @@ -2376,7 +2381,7 @@ rb_const_search(VALUE klass, ID id, int https://github.com/ruby/ruby/blob/trunk/variable.c#L2381 continue; } if (exclude && tmp == rb_cObject && klass != rb_cObject) { - return Qundef; + goto not_found; } return value; } @@ -2389,6 +2394,8 @@ rb_const_search(VALUE klass, ID id, int https://github.com/ruby/ruby/blob/trunk/variable.c#L2394 goto retry; } + not_found: + GET_EC()->private_const_reference = 0; return Qundef; } Index: vm_core.h =================================================================== --- vm_core.h (revision 63870) +++ vm_core.h (revision 63871) @@ -836,6 +836,7 @@ typedef struct rb_execution_context_stru https://github.com/ruby/ruby/blob/trunk/vm_core.h#L836 VALUE passed_block_handler; /* for rb_iterate */ const rb_callable_method_entry_t *passed_bmethod_me; /* for bmethod */ enum method_missing_reason method_missing_reason; + VALUE private_const_reference; /* for GC */ struct { Index: spec/ruby/language/constants_spec.rb =================================================================== --- spec/ruby/language/constants_spec.rb (revision 63870) +++ spec/ruby/language/constants_spec.rb (revision 63871) @@ -458,6 +458,18 @@ describe "Module#private_constant marked https://github.com/ruby/ruby/blob/trunk/spec/ruby/language/constants_spec.rb#L458 lambda {mod::Foo}.should raise_error(NameError) end + it "sends #const_missing to the original class or module" do + mod = Module.new + mod.const_set :Foo, true + mod.send :private_constant, :Foo + def mod.const_missing(name) + @const_missing_arg = name + name == :Foo ? name : super + end + + mod::Foo.should == :Foo + end + describe "in a module" do it "cannot be accessed from outside the module" do lambda do Index: vm.c =================================================================== --- vm.c (revision 63870) +++ vm.c (revision 63871) @@ -2418,6 +2418,7 @@ rb_execution_context_mark(const rb_execu https://github.com/ruby/ruby/blob/trunk/vm.c#L2418 rb_mark_tbl(ec->local_storage); RUBY_MARK_UNLESS_NULL(ec->local_storage_recursive_hash); RUBY_MARK_UNLESS_NULL(ec->local_storage_recursive_hash_for_trace); + RUBY_MARK_UNLESS_NULL(ec->private_const_reference); } void rb_fiber_mark_self(rb_fiber_t *fib); Index: test/ruby/test_module.rb =================================================================== --- test/ruby/test_module.rb (revision 63870) +++ test/ruby/test_module.rb (revision 63871) @@ -1422,6 +1422,21 @@ class TestModule < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_module.rb#L1422 RUBY end + def test_private_constant_const_missing + c = Class.new + c.const_set(:FOO, "foo") + c.private_constant(:FOO) + class << c + attr_reader :const_missing_arg + def const_missing(name) + @const_missing_arg = name + name == :FOO ? const_get(:FOO) : super + end + end + assert_equal("foo", c::FOO) + assert_equal(:FOO, c.const_missing_arg) + end + class PrivateClass end private_constant :PrivateClass -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/