зеркало из https://github.com/github/ruby.git
* 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50691 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
Родитель
255c16b598
Коммит
f1d4e8b3b3
46
ChangeLog
46
ChangeLog
|
@ -1,3 +1,49 @@
|
|||
Sun May 31 03:36:42 2015 Koichi Sasada <ko1@atdot.net>
|
||||
|
||||
* 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@atdot.net>
|
||||
|
||||
* vm_method.c (rb_unlink_method_entry): make it static.
|
||||
|
|
18
gc.c
18
gc.c
|
@ -3946,21 +3946,29 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
6
method.h
6
method.h
|
@ -51,6 +51,7 @@ typedef enum {
|
|||
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 {
|
|||
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_struct {
|
|||
} 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,
|
||||
|
|
25
proc.c
25
proc.c
|
@ -2066,6 +2066,8 @@ rb_method_entry_min_max_arity(const rb_method_entry_t *me, int *max)
|
|||
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 *
|
|||
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)
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -193,4 +193,13 @@ class TestAlias < Test::Unit::TestCase
|
|||
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
|
||||
|
|
|
@ -2042,7 +2042,7 @@ class TestModule < Test::Unit::TestCase
|
|||
|
||||
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
|
||||
|
|
|
@ -217,6 +217,12 @@ vm_call0_body(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv)
|
|||
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);
|
||||
|
|
|
@ -1382,6 +1382,7 @@ vm_method_cfunc_entry(const rb_method_entry_t *me)
|
|||
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_control_frame_t *cfp)
|
|||
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_control_frame_t *cfp, rb_call_info_t *ci)
|
|||
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:
|
||||
|
|
136
vm_method.c
136
vm_method.c
|
@ -178,13 +178,20 @@ rb_sweep_method_entry(void *pvm)
|
|||
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) {
|
||||
|
@ -536,14 +543,21 @@ rb_add_method_iseq(VALUE klass, ID mid, rb_iseq_t *iseq, rb_cref_t *cref, rb_met
|
|||
rb_add_method0(klass, mid, VM_METHOD_TYPE_ISEQ, iseq, noex, cref);
|
||||
}
|
||||
|
||||
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 klass, ID id,
|
|||
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_t *m1, const rb_method_entry_t *m2)
|
|||
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_definition_t *d1, const rb_method_defini
|
|||
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 hash, const rb_method_definition_t *def)
|
|||
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;
|
||||
case VM_METHOD_TYPE_ALIAS:
|
||||
break; /* unreachable */
|
||||
}
|
||||
else {
|
||||
return hash;
|
||||
}
|
||||
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)
|
|||
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 (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;
|
||||
}
|
||||
|
||||
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)) {
|
||||
defined_class = real_owner = RBASIC_CLASS(defined_class);
|
||||
}
|
||||
else {
|
||||
real_owner = defined_class;
|
||||
}
|
||||
|
||||
/* 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:
|
||||
|
|
Загрузка…
Ссылка в новой задаче