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]
This commit is contained in:
Jeremy Evans 2022-03-09 14:57:49 -08:00
Родитель f3b58e1d38
Коммит 173a6b6a80
2 изменённых файлов: 69 добавлений и 50 удалений

108
proc.c
Просмотреть файл

@ -2165,61 +2165,14 @@ rb_mod_public_instance_method(VALUE mod, VALUE vid)
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)
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
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);
}
/*

Просмотреть файл

@ -323,6 +323,17 @@ class TestMethod < Test::Unit::TestCase
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) {