mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
proc.c: respond_to_missing? at Method
* proc.c (respond_to_missing_p): check if the receiver responds to the given method by respond_to_missing?. * proc.c (mnew_missing): create Method object for method_missing. [ruby-core:68564] [Bug #10985] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50038 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
0e18d13594
commit
c8a28a5a1b
3 changed files with 107 additions and 42 deletions
|
@ -1,3 +1,11 @@
|
||||||
|
Fri Mar 20 18:41:03 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
|
* proc.c (respond_to_missing_p): check if the receiver responds to
|
||||||
|
the given method by respond_to_missing?.
|
||||||
|
|
||||||
|
* proc.c (mnew_missing): create Method object for method_missing.
|
||||||
|
[ruby-core:68564] [Bug #10985]
|
||||||
|
|
||||||
Fri Mar 20 17:43:18 2015 SHIBATA Hiroshi <shibata.hiroshi@gmail.com>
|
Fri Mar 20 17:43:18 2015 SHIBATA Hiroshi <shibata.hiroshi@gmail.com>
|
||||||
|
|
||||||
* .travis.yml: enabled email notification.
|
* .travis.yml: enabled email notification.
|
||||||
|
|
117
proc.c
117
proc.c
|
@ -1134,29 +1134,68 @@ rb_obj_is_method(VALUE m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
respond_to_missing_p(VALUE klass, VALUE obj, VALUE sym, int scope)
|
||||||
|
{
|
||||||
|
/* TODO: merge with obj_respond_to() */
|
||||||
|
ID rmiss = idRespond_to_missing;
|
||||||
|
|
||||||
|
if (obj == Qundef) return 0;
|
||||||
|
if (rb_method_basic_definition_p(klass, rmiss)) return 0;
|
||||||
|
return RTEST(rb_funcall(obj, rmiss, 2, sym, scope ? Qfalse : Qtrue));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
mnew_missing(VALUE rclass, VALUE klass, VALUE obj, ID id, ID rid, VALUE mclass)
|
||||||
|
{
|
||||||
|
struct METHOD *data;
|
||||||
|
VALUE method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
|
||||||
|
rb_method_entry_t *me;
|
||||||
|
rb_method_definition_t *def;
|
||||||
|
|
||||||
|
data->recv = obj;
|
||||||
|
data->rclass = rclass;
|
||||||
|
data->defined_class = klass;
|
||||||
|
data->id = rid;
|
||||||
|
|
||||||
|
me = ALLOC(rb_method_entry_t);
|
||||||
|
data->me = me;
|
||||||
|
me->flag = 0;
|
||||||
|
me->mark = 0;
|
||||||
|
me->called_id = id;
|
||||||
|
me->klass = klass;
|
||||||
|
me->def = 0;
|
||||||
|
|
||||||
|
def = ALLOC(rb_method_definition_t);
|
||||||
|
me->def = def;
|
||||||
|
def->type = VM_METHOD_TYPE_MISSING;
|
||||||
|
def->original_id = id;
|
||||||
|
def->alias_count = 0;
|
||||||
|
|
||||||
|
data->ume = ALLOC(struct unlinked_method_entry_list_entry);
|
||||||
|
data->me->def->alias_count++;
|
||||||
|
|
||||||
|
OBJ_INFECT(method, klass);
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
mnew_internal(rb_method_entry_t *me, VALUE defined_class, VALUE klass,
|
mnew_internal(rb_method_entry_t *me, VALUE defined_class, VALUE klass,
|
||||||
VALUE obj, ID id, VALUE mclass, int scope, int error)
|
VALUE obj, ID id, VALUE mclass, int scope, int error)
|
||||||
{
|
{
|
||||||
VALUE method;
|
|
||||||
VALUE rclass = klass;
|
|
||||||
ID rid = id;
|
|
||||||
struct METHOD *data;
|
struct METHOD *data;
|
||||||
|
VALUE rclass = klass;
|
||||||
|
VALUE method;
|
||||||
|
ID rid = id;
|
||||||
rb_method_definition_t *def = 0;
|
rb_method_definition_t *def = 0;
|
||||||
rb_method_flag_t flag = NOEX_UNDEF;
|
rb_method_flag_t flag = NOEX_UNDEF;
|
||||||
|
|
||||||
again:
|
again:
|
||||||
if (UNDEFINED_METHOD_ENTRY_P(me)) {
|
if (UNDEFINED_METHOD_ENTRY_P(me)) {
|
||||||
ID rmiss = idRespond_to_missing;
|
if (respond_to_missing_p(klass, obj, ID2SYM(id), scope)) {
|
||||||
VALUE sym = ID2SYM(id);
|
return mnew_missing(rclass, klass, obj, id, rid, mclass);
|
||||||
|
|
||||||
if (obj != Qundef && !rb_method_basic_definition_p(klass, rmiss)) {
|
|
||||||
if (RTEST(rb_funcall(obj, rmiss, 2, sym, scope ? Qfalse : Qtrue))) {
|
|
||||||
me = 0;
|
|
||||||
defined_class = klass;
|
|
||||||
|
|
||||||
goto gen_method;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!error) return Qnil;
|
if (!error) return Qnil;
|
||||||
rb_print_undef(klass, id, 0);
|
rb_print_undef(klass, id, 0);
|
||||||
|
@ -1183,7 +1222,6 @@ mnew_internal(rb_method_entry_t *me, VALUE defined_class, VALUE klass,
|
||||||
rclass = RCLASS_SUPER(rclass);
|
rclass = RCLASS_SUPER(rclass);
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_method:
|
|
||||||
method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
|
method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
|
||||||
|
|
||||||
data->recv = obj;
|
data->recv = obj;
|
||||||
|
@ -1191,24 +1229,7 @@ mnew_internal(rb_method_entry_t *me, VALUE defined_class, VALUE klass,
|
||||||
data->defined_class = defined_class;
|
data->defined_class = defined_class;
|
||||||
data->id = rid;
|
data->id = rid;
|
||||||
data->me = ALLOC(rb_method_entry_t);
|
data->me = ALLOC(rb_method_entry_t);
|
||||||
if (me) {
|
|
||||||
*data->me = *me;
|
*data->me = *me;
|
||||||
}
|
|
||||||
else {
|
|
||||||
me = data->me;
|
|
||||||
me->flag = 0;
|
|
||||||
me->mark = 0;
|
|
||||||
me->called_id = id;
|
|
||||||
me->klass = klass;
|
|
||||||
me->def = 0;
|
|
||||||
|
|
||||||
def = ALLOC(rb_method_definition_t);
|
|
||||||
me->def = def;
|
|
||||||
|
|
||||||
def->type = VM_METHOD_TYPE_MISSING;
|
|
||||||
def->original_id = id;
|
|
||||||
def->alias_count = 0;
|
|
||||||
}
|
|
||||||
data->ume = ALLOC(struct unlinked_method_entry_list_entry);
|
data->ume = ALLOC(struct unlinked_method_entry_list_entry);
|
||||||
data->me->def->alias_count++;
|
data->me->def->alias_count++;
|
||||||
|
|
||||||
|
@ -1440,6 +1461,23 @@ rb_method_name_error(VALUE klass, VALUE str)
|
||||||
QUOTE(str), s0, rb_class_name(c));
|
QUOTE(str), s0, rb_class_name(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
obj_method(VALUE obj, VALUE vid, int scope)
|
||||||
|
{
|
||||||
|
ID id = rb_check_id(&vid);
|
||||||
|
const VALUE klass = CLASS_OF(obj);
|
||||||
|
const VALUE mclass = rb_cMethod;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
if (respond_to_missing_p(klass, obj, vid, scope)) {
|
||||||
|
id = rb_intern_str(vid);
|
||||||
|
return mnew_missing(klass, klass, obj, id, id, mclass);
|
||||||
|
}
|
||||||
|
rb_method_name_error(klass, vid);
|
||||||
|
}
|
||||||
|
return mnew(klass, obj, id, mclass, scope);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* obj.method(sym) -> method
|
* obj.method(sym) -> method
|
||||||
|
@ -1471,11 +1509,7 @@ rb_method_name_error(VALUE klass, VALUE str)
|
||||||
VALUE
|
VALUE
|
||||||
rb_obj_method(VALUE obj, VALUE vid)
|
rb_obj_method(VALUE obj, VALUE vid)
|
||||||
{
|
{
|
||||||
ID id = rb_check_id(&vid);
|
return obj_method(obj, vid, FALSE);
|
||||||
if (!id) {
|
|
||||||
rb_method_name_error(CLASS_OF(obj), vid);
|
|
||||||
}
|
|
||||||
return mnew(CLASS_OF(obj), obj, id, rb_cMethod, FALSE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1488,11 +1522,7 @@ rb_obj_method(VALUE obj, VALUE vid)
|
||||||
VALUE
|
VALUE
|
||||||
rb_obj_public_method(VALUE obj, VALUE vid)
|
rb_obj_public_method(VALUE obj, VALUE vid)
|
||||||
{
|
{
|
||||||
ID id = rb_check_id(&vid);
|
return obj_method(obj, vid, TRUE);
|
||||||
if (!id) {
|
|
||||||
rb_method_name_error(CLASS_OF(obj), vid);
|
|
||||||
}
|
|
||||||
return mnew(CLASS_OF(obj), obj, id, rb_cMethod, TRUE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1526,6 +1556,11 @@ rb_obj_singleton_method(VALUE obj, VALUE vid)
|
||||||
VALUE klass;
|
VALUE klass;
|
||||||
ID id = rb_check_id(&vid);
|
ID id = rb_check_id(&vid);
|
||||||
if (!id) {
|
if (!id) {
|
||||||
|
if (!NIL_P(klass = rb_singleton_class_get(obj)) &&
|
||||||
|
respond_to_missing_p(klass, obj, vid, FALSE)) {
|
||||||
|
id = rb_intern_str(vid);
|
||||||
|
return mnew_missing(klass, klass, obj, id, id, rb_cMethod);
|
||||||
|
}
|
||||||
rb_name_error_str(vid, "undefined singleton method `%"PRIsVALUE"' for `%"PRIsVALUE"'",
|
rb_name_error_str(vid, "undefined singleton method `%"PRIsVALUE"' for `%"PRIsVALUE"'",
|
||||||
QUOTE(vid), obj);
|
QUOTE(vid), obj);
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,28 @@ module Test_Symbol
|
||||||
assert_not_interned_false(c, :class_variable_defined?, noninterned_name("@@"), feature5072)
|
assert_not_interned_false(c, :class_variable_defined?, noninterned_name("@@"), feature5072)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_missing_method
|
||||||
|
bug10985 = '[ruby-core:68564] [Bug #10985]'
|
||||||
|
m = nil
|
||||||
|
c = Class.new do
|
||||||
|
def self.respond_to_missing?(*)
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
s = noninterned_name
|
||||||
|
assert_nothing_raised(NameError, bug10985) {m = c.method(s)}
|
||||||
|
assert_raise_with_message(NoMethodError, /#{s}/) {m.call}
|
||||||
|
|
||||||
|
s = noninterned_name
|
||||||
|
assert_nothing_raised(NameError, bug10985) {m = c.public_method(s.to_sym)}
|
||||||
|
assert_raise_with_message(NoMethodError, /#{s}/) {m.call}
|
||||||
|
|
||||||
|
s = noninterned_name
|
||||||
|
assert_nothing_raised(NameError, bug10985) {m = c.singleton_method(s.to_sym)}
|
||||||
|
assert_raise_with_message(NoMethodError, /#{s}/) {m.call}
|
||||||
|
end
|
||||||
|
|
||||||
Feature5079 = '[ruby-core:38404]'
|
Feature5079 = '[ruby-core:38404]'
|
||||||
|
|
||||||
def test_undefined_instance_variable
|
def test_undefined_instance_variable
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue