[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]