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

ruby-changes:38610

From: ko1 <ko1@a...>
Date: Sun, 31 May 2015 03:45:52 +0900 (JST)
Subject: [ruby-changes:38610] ko1:r50691 (trunk): * method.h: add VM_METHOD_TYPE_ALIAS rb_method_definition_t::type

ko1	2015-05-31 03:45:28 +0900 (Sun, 31 May 2015)

  New Revision: 50691

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=50691

  Log:
    * method.h: add VM_METHOD_TYPE_ALIAS rb_method_definition_t::type
      to fix [Bug #11173].
      Now, inter class/method alias creates new method entry
      VM_METHOD_TYPE_ALIAS, which has an original method entry.
    * vm_insnhelper.c (find_defiend_class_by_owner): added.
      Search corresponding defined_class from owner class/module.
    * vm_method.c (rb_method_entry_get_without_cache): return me->klass
      directly for defined_class.
      Now, no need to check me->klass any more.
    * vm_method.c (method_entry_set0): separated from method_entry_set().
    * vm_method.c (rb_alias): make method entry has VM_METHOD_TYPE_ALIAS.
    * vm_method.c (release_method_definition): support VM_METHOD_TYPE_ALIAS.
    * vm_method.c (rb_hash_method_definition): ditto.
    * vm_method.c (rb_method_definition_eq): ditto.
    * vm_method.c (release_method_definition): ditto.
    * vm_insnhelper.c (vm_call_method): ditto.
    * vm_insnhelper.c (vm_method_cfunc_entry): ditto.
    * vm_eval.c (vm_call0_body): ditto.
    * gc.c (mark_method_entry): ditto.
    * proc.c (method_def_iseq): ditto.
    * proc.c (method_cref): ditto.
    * proc.c (rb_method_entry_min_max_arity): ditto.
    * test/ruby/test_alias.rb: add tests.
    * test/ruby/test_module.rb: fix a test to catch up current behavior.

  Modified files:
    trunk/ChangeLog
    trunk/gc.c
    trunk/method.h
    trunk/proc.c
    trunk/test/ruby/test_alias.rb
    trunk/test/ruby/test_module.rb
    trunk/vm_eval.c
    trunk/vm_insnhelper.c
    trunk/vm_method.c
Index: method.h
===================================================================
--- method.h	(revision 50690)
+++ method.h	(revision 50691)
@@ -51,6 +51,7 @@ typedef enum { https://github.com/ruby/ruby/blob/trunk/method.h#L51
     VM_METHOD_TYPE_IVAR,
     VM_METHOD_TYPE_BMETHOD,
     VM_METHOD_TYPE_ZSUPER,
+    VM_METHOD_TYPE_ALIAS,
     VM_METHOD_TYPE_UNDEF,
     VM_METHOD_TYPE_NOTIMPLEMENTED,
     VM_METHOD_TYPE_OPTIMIZED, /* Kernel#send, Proc#call, etc */
@@ -71,6 +72,10 @@ typedef struct rb_method_attr_struct { https://github.com/ruby/ruby/blob/trunk/method.h#L72
     const VALUE location;
 } rb_method_attr_t;
 
+typedef struct rb_method_alias_struct {
+    const struct rb_method_entry_struct *original_me; /* original_me->klass is original owner */
+} rb_method_alias_t;
+
 typedef struct rb_iseq_struct rb_iseq_t;
 
 typedef struct rb_method_definition_struct {
@@ -85,6 +90,7 @@ typedef struct rb_method_definition_stru https://github.com/ruby/ruby/blob/trunk/method.h#L90
 	} iseq_body;
 	rb_method_cfunc_t cfunc;
 	rb_method_attr_t attr;
+	rb_method_alias_t alias;
 	const VALUE proc;                 /* should be mark */
 	enum method_optimized_type {
 	    OPTIMIZED_METHOD_TYPE_SEND,
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 50690)
+++ ChangeLog	(revision 50691)
@@ -1,3 +1,49 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Sun May 31 03:36:42 2015  Koichi Sasada  <ko1@a...>
+
+	* method.h: add VM_METHOD_TYPE_ALIAS rb_method_definition_t::type
+	  to fix [Bug #11173].
+
+	  Now, inter class/method alias creates new method entry
+	  VM_METHOD_TYPE_ALIAS, which has an original method entry.
+
+	* vm_insnhelper.c (find_defiend_class_by_owner): added.
+	  Search corresponding defined_class from owner class/module.
+
+	* vm_method.c (rb_method_entry_get_without_cache): return me->klass
+	  directly for defined_class.
+
+	  Now, no need to check me->klass any more.
+
+	* vm_method.c (method_entry_set0): separated from method_entry_set().
+
+	* vm_method.c (rb_alias): make method entry has VM_METHOD_TYPE_ALIAS.
+
+	* vm_method.c (release_method_definition): support VM_METHOD_TYPE_ALIAS.
+
+	* vm_method.c (rb_hash_method_definition): ditto.
+
+	* vm_method.c (rb_method_definition_eq): ditto.
+
+	* vm_method.c (release_method_definition): ditto.
+
+	* vm_insnhelper.c (vm_call_method): ditto.
+
+	* vm_insnhelper.c (vm_method_cfunc_entry): ditto.
+
+	* vm_eval.c (vm_call0_body): ditto.
+
+	* gc.c (mark_method_entry): ditto.
+
+	* proc.c (method_def_iseq): ditto.
+
+	* proc.c (method_cref): ditto.
+
+	* proc.c (rb_method_entry_min_max_arity): ditto.
+
+	* test/ruby/test_alias.rb: add tests.
+
+	* test/ruby/test_module.rb: fix a test to catch up current behavior.
+
 Sun May 31 03:34:25 2015  Koichi Sasada  <ko1@a...>
 
 	* vm_method.c (rb_unlink_method_entry): make it static.
Index: vm_eval.c
===================================================================
--- vm_eval.c	(revision 50690)
+++ vm_eval.c	(revision 50691)
@@ -217,6 +217,12 @@ vm_call0_body(rb_thread_t* th, rb_call_i https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L217
 	    if (!ci->me->def) return Qnil;
 	    goto again;
 	}
+      case VM_METHOD_TYPE_ALIAS:
+	{
+	    ci->me = ci->me->def->body.alias.original_me;
+	    ci->defined_class = find_defiend_class_by_owner(ci->defined_class, ci->me->klass);
+	    goto again;
+	}
       case VM_METHOD_TYPE_MISSING:
 	{
 	    VALUE new_args = rb_ary_new4(ci->argc, argv);
Index: proc.c
===================================================================
--- proc.c	(revision 50690)
+++ proc.c	(revision 50691)
@@ -2066,6 +2066,8 @@ rb_method_entry_min_max_arity(const rb_m https://github.com/ruby/ruby/blob/trunk/proc.c#L2066
 	return *max = 1;
       case VM_METHOD_TYPE_IVAR:
 	return *max = 0;
+      case VM_METHOD_TYPE_ALIAS:
+	return rb_method_entry_min_max_arity(def->body.alias.original_me, max);
       case VM_METHOD_TYPE_BMETHOD:
 	return rb_proc_min_max_arity(def->body.proc, max);
       case VM_METHOD_TYPE_ISEQ: {
@@ -2204,13 +2206,24 @@ static const rb_iseq_t * https://github.com/ruby/ruby/blob/trunk/proc.c#L2206
 method_def_iseq(const rb_method_definition_t *def)
 {
     switch (def->type) {
-      case VM_METHOD_TYPE_BMETHOD:
-	return get_proc_iseq(def->body.proc, 0);
       case VM_METHOD_TYPE_ISEQ:
 	return def->body.iseq_body.iseq;
-      default:
-	return NULL;
+      case VM_METHOD_TYPE_BMETHOD:
+	return get_proc_iseq(def->body.proc, 0);
+      case VM_METHOD_TYPE_ALIAS:
+	return method_def_iseq(def->body.alias.original_me->def);
+      case VM_METHOD_TYPE_CFUNC:
+      case VM_METHOD_TYPE_ATTRSET:
+      case VM_METHOD_TYPE_IVAR:
+      case VM_METHOD_TYPE_ZSUPER:
+      case VM_METHOD_TYPE_UNDEF:
+      case VM_METHOD_TYPE_NOTIMPLEMENTED:
+      case VM_METHOD_TYPE_OPTIMIZED:
+      case VM_METHOD_TYPE_MISSING:
+      case VM_METHOD_TYPE_REFINED:
+	break;
     }
+    return NULL;
 }
 
 const rb_iseq_t *
@@ -2224,9 +2237,13 @@ method_cref(VALUE method) https://github.com/ruby/ruby/blob/trunk/proc.c#L2237
 {
     const rb_method_definition_t *def = method_def(method);
 
+  again:
     switch (def->type) {
       case VM_METHOD_TYPE_ISEQ:
 	return def->body.iseq_body.cref;
+      case VM_METHOD_TYPE_ALIAS:
+	def = def->body.alias.original_me->def;
+	goto again;
       default:
 	return NULL;
     }
Index: vm_method.c
===================================================================
--- vm_method.c	(revision 50690)
+++ vm_method.c	(revision 50691)
@@ -178,13 +178,20 @@ rb_sweep_method_entry(void *pvm) https://github.com/ruby/ruby/blob/trunk/vm_method.c#L178
 static void
 release_method_definition(rb_method_definition_t *def)
 {
-    if (def == 0)
-	return;
+    if (def == 0) return;
+
     if (def->alias_count == 0) {
-	if (def->type == VM_METHOD_TYPE_REFINED &&
-	    def->body.orig_me) {
-	    rb_free_method_entry(def->body.orig_me);
+	switch (def->type) {
+	  case VM_METHOD_TYPE_REFINED:
+	    if (def->body.orig_me) rb_free_method_entry(def->body.orig_me);
+	    break;
+	  case VM_METHOD_TYPE_ALIAS:
+	    if (!def->body.alias.original_me) rb_free_method_entry(def->body.alias.original_me);
+	    break;
+	  default:
+	    break;
 	}
+
 	xfree(def);
     }
     else if (def->alias_count > 0) {
@@ -537,13 +544,20 @@ rb_add_method_iseq(VALUE klass, ID mid, https://github.com/ruby/ruby/blob/trunk/vm_method.c#L544
 }
 
 static rb_method_entry_t *
+method_entry_set0(VALUE klass, ID mid, rb_method_type_t type,
+		  rb_method_definition_t *def, rb_method_flag_t noex, VALUE defined_class)
+{
+    rb_method_entry_t *newme = rb_method_entry_make(klass, mid, type, def, noex, defined_class);
+    method_added(klass, mid);
+    return newme;
+}
+
+static rb_method_entry_t *
 method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *me,
 		 rb_method_flag_t noex, VALUE defined_class)
 {
     rb_method_type_t type = me->def ? me->def->type : VM_METHOD_TYPE_UNDEF;
-    rb_method_entry_t *newme = rb_method_entry_make(klass, mid, type, me->def, noex, defined_class);
-    method_added(klass, mid);
-    return newme;
+    return method_entry_set0(klass, mid, type, me->def, noex, defined_class);
 }
 
 rb_method_entry_t *
@@ -613,16 +627,6 @@ rb_method_entry_get_without_cache(VALUE https://github.com/ruby/ruby/blob/trunk/vm_method.c#L627
     VALUE defined_class;
     rb_method_entry_t *me = search_method(klass, id, &defined_class);
 
-    if (me && me->klass) {
-	switch (BUILTIN_TYPE(me->klass)) {
-	  case T_CLASS:
-	    if (RBASIC(klass)->flags & FL_SINGLETON) break;
-	    /* fall through */
-	  case T_ICLASS:
-	    defined_class = me->klass;
-	}
-    }
-
     if (ruby_running) {
 	if (OPT_GLOBAL_METHOD_CACHE) {
 	    struct cache_entry *ent;
@@ -1205,18 +1209,38 @@ rb_method_entry_eq(const rb_method_entry https://github.com/ruby/ruby/blob/trunk/vm_method.c#L1209
     return rb_method_definition_eq(m1->def, m2->def);
 }
 
+static const rb_method_definition_t *
+original_method_definition(const rb_method_definition_t *def)
+{
+  again:
+    if (def) {
+	switch (def->type) {
+	  case VM_METHOD_TYPE_REFINED:
+	    if (def->body.orig_me) {
+		def = def->body.orig_me->def;
+		goto again;
+	    }
+	    break;
+	  case VM_METHOD_TYPE_ALIAS:
+	    def = def->body.alias.original_me->def;
+	    goto again;
+	  default:
+	    break;
+	}
+    }
+    return def;
+}
+
 static int
 rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2)
 {
-    if (d1 && d1->type == VM_METHOD_TYPE_REFINED && d1->body.orig_me)
-	d1 = d1->body.orig_me->def;
-    if (d2 && d2->type == VM_METHOD_TYPE_REFINED && d2->body.orig_me)
-	d2 = d2->body.orig_me->def;
+    d1 = original_method_definition(d1);
+    d2 = original_method_definition(d2);
+
     if (d1 == d2) return 1;
     if (!d1 || !d2) return 0;
-    if (d1->type != d2->type) {
-	return 0;
-    }
+    if (d1->type != d2->type) return 0;
+
     switch (d1->type) {
       case VM_METHOD_TYPE_ISEQ:
 	return d1->body.iseq_body.iseq == d2->body.iseq_body.iseq;
@@ -1237,17 +1261,21 @@ rb_method_definition_eq(const rb_method_ https://github.com/ruby/ruby/blob/trunk/vm_method.c#L1261
 	return 1;
       case VM_METHOD_TYPE_OPTIMIZED:
 	return d1->body.optimize_type == d2->body.optimize_type;
-      default:
-	rb_bug("rb_method_entry_eq: unsupported method type (%d)\n", d1->type);
-	return 0;
+      case VM_METHOD_TYPE_REFINED:
+      case VM_METHOD_TYPE_ALIAS:
+	break;
     }
+    rb_bug("rb_method_definition_eq: unsupported type: %d\n", d1->type);
 }
 
 static st_index_t
 rb_hash_method_definition(st_index_t hash, const rb_method_definition_t *def)
 {
-  again:
     hash = rb_hash_uint(hash, def->type);
+    def = original_method_definition(def);
+
+    if (!def) return hash;
+
     switch (def->type) {
       case VM_METHOD_TYPE_ISEQ:
 	return rb_hash_uint(hash, (st_index_t)def->body.iseq_body.iseq);
@@ -1268,18 +1296,11 @@ rb_hash_method_definition(st_index_t has https://github.com/ruby/ruby/blob/trunk/vm_method.c#L1296
       case VM_METHOD_TYPE_OPTIMIZED:
 	return rb_hash_uint(hash, def->body.optimize_type);
       case VM_METHOD_TYPE_REFINED:
-	if (def->body.orig_me) {
-	    def = def->body.orig_me->def;
-	    goto again;
-	}
-	else {
-	    return hash;
+      case VM_METHOD_TYPE_ALIAS:
+	break; /* unreachable */
 	}
-      default:
 	rb_bug("rb_hash_method_definition: unsupported method type (%d)\n", def->type);
     }
-    return hash;
-}
 
 st_index_t
 rb_hash_method_entry(st_index_t hash, const rb_method_entry_t *me)
@@ -1310,26 +1331,53 @@ rb_alias(VALUE klass, ID name, ID def) https://github.com/ruby/ruby/blob/trunk/vm_method.c#L1331
     if (UNDEFINED_METHOD_ENTRY_P(orig_me) ||
 	UNDEFINED_REFINED_METHOD_P(orig_me->def)) {
 	if ((!RB_TYPE_P(klass, T_MODULE)) ||
-	    (orig_me = search_method(rb_cObject, def, 0),
+	    (orig_me = search_method(rb_cObject, def, &defined_class),
 	     UNDEFINED_METHOD_ENTRY_P(orig_me))) {
 	    rb_print_undef(klass, def, 0);
 	}
     }
+
     if (orig_me->def->type == VM_METHOD_TYPE_ZSUPER) {
 	klass = RCLASS_SUPER(klass);
 	def = orig_me->def->original_id;
 	flag = orig_me->flag;
 	goto again;
     }
+
+    if (flag == NOEX_UNDEF) flag = orig_me->flag;
+
+    if (defined_class != target_klass) { /* inter class/module alias */
+	VALUE real_owner;
+	rb_method_entry_t *new_orig_me;
+	rb_method_definition_t *def;
+
     if (RB_TYPE_P(defined_class, T_ICLASS)) {
-	VALUE real_class = RBASIC_CLASS(defined_class);
-	if (real_class && RCLASS_ORIGIN(real_class) == defined_class)
-	    defined_class = real_class;
+	    defined_class = real_owner = RBASIC_CLASS(defined_class);
+	}
+	else {
+	    real_owner = defined_class;
     }
 
-    if (flag == NOEX_UNDEF) flag = orig_me->flag;
+	/* make ne me */
+	new_orig_me = ALLOC(rb_method_entry_t);
+	*new_orig_me = *orig_me;
+	new_orig_me->called_id = name;
+
+	/* make alias def */
+	def = ALLOC(rb_method_definition_t);
+	def->type = VM_METHOD_TYPE_ALIAS;
+	def->original_id = orig_me->called_id;
+	def->alias_count = -1; /* will be increment at method_entry_set0() */
+	def->body.alias.original_me = new_orig_me;
+	if (new_orig_me->def) new_orig_me->def->alias_count++;
+
+	/* make copy */
+	method_entry_set0(target_klass, name, VM_METHOD_TYPE_ALIAS, def, flag, defined_class);
+    }
+    else {
     method_entry_set(target_klass, name, orig_me, flag, defined_class);
 }
+}
 
 /*
  *  call-seq:
Index: gc.c
===================================================================
--- gc.c	(revision 50690)
+++ gc.c	(revision 50691)
@@ -3946,21 +3946,29 @@ mark_method_entry(rb_objspace_t *objspac https://github.com/ruby/ruby/blob/trunk/gc.c#L3946
 	gc_mark(objspace, def->body.iseq_body.iseq->self);
 	gc_mark(objspace, (VALUE)def->body.iseq_body.cref);
 	break;
-      case VM_METHOD_TYPE_BMETHOD:
-	gc_mark(objspace, def->body.proc);
-	break;
       case VM_METHOD_TYPE_ATTRSET:
       case VM_METHOD_TYPE_IVAR:
 	gc_mark(objspace, def->body.attr.location);
 	break;
+      case VM_METHOD_TYPE_BMETHOD:
+	gc_mark(objspace, def->body.proc);
+	break;
+      case VM_METHOD_TYPE_ALIAS:
+	mark_method_entry(objspace, def->body.alias.original_me);
+	return;
       case VM_METHOD_TYPE_REFINED:
 	if (def->body.orig_me) {
 	    def = def->body.orig_me->def;
 	    goto again;
 	}
 	break;
-      default:
-	break; /* ignore */
+      case VM_METHOD_TYPE_CFUNC:
+      case VM_METHOD_TYPE_ZSUPER:
+      case VM_METHOD_TYPE_MISSING:
+      case VM_METHOD_TYPE_OPTIMIZED:
+      case VM_METHOD_TYPE_UNDEF:
+      case VM_METHOD_TYPE_NOTIMPLEMENTED:
+	break;
     }
 }
 
Index: vm_insnhelper.c
===================================================================
--- vm_insnhelper.c	(revision 50690)
+++ vm_insnhelper.c	(revision 50691)
@@ -1382,6 +1382,7 @@ vm_method_cfunc_entry(const rb_method_en https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1382
 	METHOD_BUG(OPTIMIZED);
 	METHOD_BUG(MISSING);
 	METHOD_BUG(REFINED);
+	METHOD_BUG(ALIAS);
 # undef METHOD_BUG
       default:
 	rb_bug("wrong method type: %d", me->def->type);
@@ -1700,6 +1701,25 @@ current_method_entry(rb_thread_t *th, rb https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1701
     return cfp;
 }
 
+static VALUE
+find_defiend_class_by_owner(VALUE current_class, VALUE target_owner)
+{
+    VALUE klass = current_class;
+
+    /* for prepended Module, then start from cover class */
+    if (RB_TYPE_P(klass, T_ICLASS) && FL_TEST(klass, RICLASS_IS_ORIGIN)) klass = RBASIC_CLASS(klass);
+
+    while (RTEST(klass)) {
+	VALUE owner = RB_TYPE_P(klass, T_ICLASS) ? RBASIC_CLASS(klass) : klass;
+	if (owner == target_owner) {
+	    return klass;
+	}
+	klass = RCLASS_SUPER(klass);
+    }
+
+    return current_class; /* maybe module function */
+}
+
 static
 #ifdef _MSC_VER
 __forceinline
@@ -1771,6 +1791,11 @@ vm_call_method(rb_thread_t *th, rb_contr https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1791
 		    goto start_method_dispatch;
 		}
 	      }
+	      case VM_METHOD_TYPE_ALIAS: {
+		ci->me = ci->me->def->body.alias.original_me;
+		ci->defined_class = find_defiend_class_by_owner(ci->defined_class, ci->me->klass /* owner */);
+		goto normal_method_dispatch;
+	      }
 	      case VM_METHOD_TYPE_OPTIMIZED:{
 		switch (ci->me->def->body.optimize_type) {
 		  case OPTIMIZED_METHOD_TYPE_SEND:
Index: test/ruby/test_module.rb
===================================================================
--- test/ruby/test_module.rb	(revision 50690)
+++ test/ruby/test_module.rb	(revision 50691)
@@ -2042,7 +2042,7 @@ class TestModule < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_module.rb#L2042
 
       A.prepend InspectIsShallow
 
-      expect = "#<Method: A(Object)#inspect(shallow_inspect)>"
+      expect = "#<Method: A(ShallowInspect)#inspect(shallow_inspect)>"
       assert_equal expect, A.new.method(:inspect).inspect, "#{bug_10282}"
     RUBY
   end
Index: test/ruby/test_alias.rb
===================================================================
--- test/ruby/test_alias.rb	(revision 50690)
+++ test/ruby/test_alias.rb	(revision 50691)
@@ -193,4 +193,13 @@ class TestAlias < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_alias.rb#L193
       assert_equal(o.to_s, o.orig_to_s, bug)
     end;
   end
+
+  class C0; def foo; end; end
+  class C1 < C0; alias bar foo; end
+
+  def test_alias_method_equation
+    obj = C1.new
+    assert_equal(obj.method(:bar), obj.method(:foo))
+    assert_equal(obj.method(:foo), obj.method(:bar))
+  end
 end

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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