ruby-changes:71541
From: Jeremy <ko1@a...>
Date: Wed, 30 Mar 2022 04:10:26 +0900 (JST)
Subject: [ruby-changes:71541] 173a6b6a80 (master): Make define_singleton_method always define a public method
https://git.ruby-lang.org/ruby.git/commit/?id=173a6b6a80 From 173a6b6a802d80b8cf200308fd3653832b700b1c Mon Sep 17 00:00:00 2001 From: Jeremy Evans <code@j...> Date: Wed, 9 Mar 2022 14:57:49 -0800 Subject: Make define_singleton_method always define a public method In very unlikely cases, it could previously define a non-public method starting in Ruby 2.1. Fixes [Bug #18561] --- proc.c | 108 +++++++++++++++++++++++++---------------------- test/ruby/test_method.rb | 11 +++++ 2 files changed, 69 insertions(+), 50 deletions(-) diff --git a/proc.c b/proc.c index 93b4013c31..4f43c56006 100644 --- a/proc.c +++ b/proc.c @@ -2165,61 +2165,14 @@ rb_mod_public_instance_method(VALUE mod, VALUE vid) https://github.com/ruby/ruby/blob/trunk/proc.c#L2165 return mnew_unbound(mod, id, rb_cUnboundMethod, TRUE); } -/* - * call-seq: - * define_method(symbol, method) -> symbol - * define_method(symbol) { block } -> symbol - * - * Defines an instance method in the receiver. The _method_ - * parameter can be a +Proc+, a +Method+ or an +UnboundMethod+ object. - * If a block is specified, it is used as the method body. - * If a block or the _method_ parameter has parameters, - * they're used as method parameters. - * This block is evaluated using #instance_eval. - * - * class A - * def fred - * puts "In Fred" - * end - * def create_method(name, &block) - * self.class.define_method(name, &block) - * end - * define_method(:wilma) { puts "Charge it!" } - * define_method(:flint) {|name| puts "I'm #{name}!"} - * end - * class B < A - * define_method(:barney, instance_method(:fred)) - * end - * a = B.new - * a.barney - * a.wilma - * a.flint('Dino') - * a.create_method(:betty) { p self } - * a.betty - * - * <em>produces:</em> - * - * In Fred - * Charge it! - * I'm Dino! - * #<B:0x401b39e8> - */ - static VALUE -rb_mod_define_method(int argc, VALUE *argv, VALUE mod) +rb_mod_define_method_with_visibility(int argc, VALUE *argv, VALUE mod, const struct rb_scope_visi_struct* scope_visi) { ID id; VALUE body; VALUE name; - const rb_cref_t *cref = rb_vm_cref_in_context(mod, mod); - const rb_scope_visibility_t default_scope_visi = {METHOD_VISI_PUBLIC, FALSE}; - const rb_scope_visibility_t *scope_visi = &default_scope_visi; int is_method = FALSE; - if (cref) { - scope_visi = CREF_SCOPE_VISI(cref); - } - rb_check_arity(argc, 1, 2); name = argv[0]; id = rb_check_id(&name); @@ -2280,12 +2233,66 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod) https://github.com/ruby/ruby/blob/trunk/proc.c#L2233 return ID2SYM(id); } +/* + * call-seq: + * define_method(symbol, method) -> symbol + * define_method(symbol) { block } -> symbol + * + * Defines an instance method in the receiver. The _method_ + * parameter can be a +Proc+, a +Method+ or an +UnboundMethod+ object. + * If a block is specified, it is used as the method body. + * If a block or the _method_ parameter has parameters, + * they're used as method parameters. + * This block is evaluated using #instance_eval. + * + * class A + * def fred + * puts "In Fred" + * end + * def create_method(name, &block) + * self.class.define_method(name, &block) + * end + * define_method(:wilma) { puts "Charge it!" } + * define_method(:flint) {|name| puts "I'm #{name}!"} + * end + * class B < A + * define_method(:barney, instance_method(:fred)) + * end + * a = B.new + * a.barney + * a.wilma + * a.flint('Dino') + * a.create_method(:betty) { p self } + * a.betty + * + * <em>produces:</em> + * + * In Fred + * Charge it! + * I'm Dino! + * #<B:0x401b39e8> + */ + +static VALUE +rb_mod_define_method(int argc, VALUE *argv, VALUE mod) +{ + const rb_cref_t *cref = rb_vm_cref_in_context(mod, mod); + const rb_scope_visibility_t default_scope_visi = {METHOD_VISI_PUBLIC, FALSE}; + const rb_scope_visibility_t *scope_visi = &default_scope_visi; + + if (cref) { + scope_visi = CREF_SCOPE_VISI(cref); + } + + return rb_mod_define_method_with_visibility(argc, argv, mod, scope_visi); +} + /* * call-seq: * define_singleton_method(symbol, method) -> symbol * define_singleton_method(symbol) { block } -> symbol * - * Defines a singleton method in the receiver. The _method_ + * Defines a public singleton method in the receiver. The _method_ * parameter can be a +Proc+, a +Method+ or an +UnboundMethod+ object. * If a block is specified, it is used as the method body. * If a block or a method has parameters, they're used as method parameters. @@ -2315,8 +2322,9 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/proc.c#L2322 rb_obj_define_method(int argc, VALUE *argv, VALUE obj) { VALUE klass = rb_singleton_class(obj); + const rb_scope_visibility_t scope_visi = {METHOD_VISI_PUBLIC, FALSE}; - return rb_mod_define_method(argc, argv, klass); + return rb_mod_define_method_with_visibility(argc, argv, klass, &scope_visi); } /* diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index caf8bebd35..83e499913a 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -323,6 +323,17 @@ class TestMethod < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_method.rb#L323 assert_equal(:foo, o.foo) end + PUBLIC_SINGLETON_TEST = Object.new + class << PUBLIC_SINGLETON_TEST + private + PUBLIC_SINGLETON_TEST.define_singleton_method(:dsm){} + def PUBLIC_SINGLETON_TEST.def; end + end + def test_define_singleton_method_public + assert_equal(true, PUBLIC_SINGLETON_TEST.method(:dsm).public?) + assert_equal(true, PUBLIC_SINGLETON_TEST.method(:def).public?) + end + def test_define_singleton_method_no_proc o = Object.new assert_raise(ArgumentError) { -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/