From ef45a57801a2ae8621b0cde59f11159f89f0a8dc Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Wed, 31 Jul 2019 17:03:11 -0700 Subject: [PATCH] Make attr* methods define public methods if self in caller is not same as receiver Previously, attr* methods could be private even if not in the private section of a class/module block. This uses the same approach that ruby started using for define_method in 1fc33199736f316dd71d0c551edbf514528ddde6. Fixes [Bug #4537] --- test/ruby/test_module.rb | 26 ++++++++++++++++++++++++++ vm.c | 2 +- vm_method.c | 4 +++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index 37045ad0d9..cf50db3374 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -705,6 +705,32 @@ class TestModule < Test::Unit::TestCase assert_equal(false, o.respond_to?(:bar=)) end + def test_attr_public_at_toplevel + s = Object.new + TOPLEVEL_BINDING.eval(<<-END).call(s.singleton_class) + proc do |c| + c.send(:attr_accessor, :x) + c.send(:attr, :y) + c.send(:attr_reader, :z) + c.send(:attr_writer, :w) + end + END + assert_nil s.x + s.x = 1 + assert_equal 1, s.x + + assert_nil s.y + s.instance_variable_set(:@y, 2) + assert_equal 2, s.y + + assert_nil s.z + s.instance_variable_set(:@z, 3) + assert_equal 3, s.z + + s.w = 4 + assert_equal 4, s.instance_variable_get(:@w) + end + def test_const_get_evaled c1 = Class.new c2 = Class.new(c1) diff --git a/vm.c b/vm.c index 593540a32e..b4e2e3c5e6 100644 --- a/vm.c +++ b/vm.c @@ -1383,7 +1383,7 @@ rb_vm_cref_in_context(VALUE self, VALUE cbase) const rb_execution_context_t *ec = GET_EC(); const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp); const rb_cref_t *cref; - if (cfp->self != self) return NULL; + if (!cfp || cfp->self != self) return NULL; if (!vm_env_cref_by_cref(cfp->ep)) return NULL; cref = vm_get_cref(cfp->ep); if (CREF_CLASS(cref) != cbase) return NULL; diff --git a/vm_method.c b/vm_method.c index 35634d275c..da891229f8 100644 --- a/vm_method.c +++ b/vm_method.c @@ -1135,14 +1135,16 @@ rb_scope_module_func_set(void) vm_cref_set_visibility(METHOD_VISI_PRIVATE, TRUE); } +const rb_cref_t *rb_vm_cref_in_context(VALUE self, VALUE cbase); void rb_attr(VALUE klass, ID id, int read, int write, int ex) { ID attriv; rb_method_visibility_t visi; const rb_execution_context_t *ec = GET_EC(); + const rb_cref_t *cref = rb_vm_cref_in_context(klass, klass); - if (!ex) { + if (!ex || !cref) { visi = METHOD_VISI_PUBLIC; } else {