mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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:
parent
f3b58e1d38
commit
173a6b6a80
Notes:
git
2022-03-30 04:10:34 +09:00
2 changed files with 69 additions and 50 deletions
108
proc.c
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) {
|
||||
|
|
Loading…
Reference in a new issue