ruby-changes:37957
From: nobu <ko1@a...>
Date: Fri, 20 Mar 2015 18:41:18 +0900 (JST)
Subject: [ruby-changes:37957] nobu:r50038 (trunk): proc.c: respond_to_missing? at Method
nobu 2015-03-20 18:41:06 +0900 (Fri, 20 Mar 2015) New Revision: 50038 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=50038 Log: 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] Modified files: trunk/ChangeLog trunk/proc.c trunk/test/-ext-/symbol/test_inadvertent_creation.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 50037) +++ ChangeLog (revision 50038) @@ -1,3 +1,11 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Fri Mar 20 18:41:03 2015 Nobuyoshi Nakada <nobu@r...> + + * 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@g...> * .travis.yml: enabled email notification. Index: proc.c =================================================================== --- proc.c (revision 50037) +++ proc.c (revision 50038) @@ -1134,29 +1134,68 @@ rb_obj_is_method(VALUE m) https://github.com/ruby/ruby/blob/trunk/proc.c#L1134 } } +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 mnew_internal(rb_method_entry_t *me, VALUE defined_class, VALUE klass, VALUE obj, ID id, VALUE mclass, int scope, int error) { - VALUE method; + struct METHOD *data; VALUE rclass = klass; + VALUE method; ID rid = id; - struct METHOD *data; rb_method_definition_t *def = 0; rb_method_flag_t flag = NOEX_UNDEF; again: if (UNDEFINED_METHOD_ENTRY_P(me)) { - ID rmiss = idRespond_to_missing; - VALUE sym = ID2SYM(id); - - 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 (respond_to_missing_p(klass, obj, ID2SYM(id), scope)) { + return mnew_missing(rclass, klass, obj, id, rid, mclass); } if (!error) return Qnil; rb_print_undef(klass, id, 0); @@ -1183,7 +1222,6 @@ mnew_internal(rb_method_entry_t *me, VAL https://github.com/ruby/ruby/blob/trunk/proc.c#L1222 rclass = RCLASS_SUPER(rclass); } - gen_method: method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data); data->recv = obj; @@ -1191,24 +1229,7 @@ mnew_internal(rb_method_entry_t *me, VAL https://github.com/ruby/ruby/blob/trunk/proc.c#L1229 data->defined_class = defined_class; data->id = rid; data->me = ALLOC(rb_method_entry_t); - if (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->me = *me; data->ume = ALLOC(struct unlinked_method_entry_list_entry); data->me->def->alias_count++; @@ -1440,6 +1461,23 @@ rb_method_name_error(VALUE klass, VALUE https://github.com/ruby/ruby/blob/trunk/proc.c#L1461 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: * obj.method(sym) -> method @@ -1471,11 +1509,7 @@ rb_method_name_error(VALUE klass, VALUE https://github.com/ruby/ruby/blob/trunk/proc.c#L1509 VALUE rb_obj_method(VALUE obj, VALUE vid) { - ID id = rb_check_id(&vid); - if (!id) { - rb_method_name_error(CLASS_OF(obj), vid); - } - return mnew(CLASS_OF(obj), obj, id, rb_cMethod, FALSE); + return obj_method(obj, vid, FALSE); } /* @@ -1488,11 +1522,7 @@ rb_obj_method(VALUE obj, VALUE vid) https://github.com/ruby/ruby/blob/trunk/proc.c#L1522 VALUE rb_obj_public_method(VALUE obj, VALUE vid) { - ID id = rb_check_id(&vid); - if (!id) { - rb_method_name_error(CLASS_OF(obj), vid); - } - return mnew(CLASS_OF(obj), obj, id, rb_cMethod, TRUE); + return obj_method(obj, vid, TRUE); } /* @@ -1526,6 +1556,11 @@ rb_obj_singleton_method(VALUE obj, VALUE https://github.com/ruby/ruby/blob/trunk/proc.c#L1556 VALUE klass; ID id = rb_check_id(&vid); 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"'", QUOTE(vid), obj); } Index: test/-ext-/symbol/test_inadvertent_creation.rb =================================================================== --- test/-ext-/symbol/test_inadvertent_creation.rb (revision 50037) +++ test/-ext-/symbol/test_inadvertent_creation.rb (revision 50038) @@ -82,6 +82,28 @@ module Test_Symbol https://github.com/ruby/ruby/blob/trunk/test/-ext-/symbol/test_inadvertent_creation.rb#L82 assert_not_interned_false(c, :class_variable_defined?, noninterned_name("@@"), feature5072) 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]' def test_undefined_instance_variable -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/