* vm_method.c (rb_method_entry_make): add a method entry with
VM_METHOD_TYPE_REFINED to the class refined by the refinement if the target module is a refinement. When a method entry with VM_METHOD_TYPE_UNDEF is invoked by vm_call_method(), a method with the same name is searched in refinements. If such a method is found, the method is invoked. Otherwise, the original method in the refined class (rb_method_definition_t::body.orig_def) is invoked. This change is made to simplify the normal method lookup and to improve the performance of normal method calls. * vm_method.c (EXPR1, search_method, rb_method_entry), vm_eval.c (rb_call0, rb_search_method_entry): do not use refinements for method lookup. * vm_insnhelper.c (vm_call_method): search methods in refinements if ci->me is VM_METHOD_TYPE_REFINED. If the method is called by super (i.e., ci->call == vm_call_super_method), skip the same method entry as the current method to avoid infinite call of the same method. * class.c (include_modules_at): add a refined method entry for each method defined in a module included in a refinement. * class.c (rb_prepend_module): set an empty table to RCLASS_M_TBL(klass) to add refined method entries, because refinements should have priority over prepended modules. * proc.c (mnew): use rb_method_entry_with_refinements() to get a refined method. * test/ruby/test_refinement.rb (test_inline_method_cache): do not skip the test because it should pass successfully. * test/ruby/test_refinement.rb (test_redefine_refined_method): new test for the case a refined method is redefined. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37993 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
bda0aca2af
commit
421314cf4e
38
ChangeLog
38
ChangeLog
@ -1,3 +1,41 @@
|
|||||||
|
Thu Nov 29 21:42:16 2012 Shugo Maeda <shugo@ruby-lang.org>
|
||||||
|
|
||||||
|
* vm_method.c (rb_method_entry_make): add a method entry with
|
||||||
|
VM_METHOD_TYPE_REFINED to the class refined by the refinement if
|
||||||
|
the target module is a refinement. When a method entry with
|
||||||
|
VM_METHOD_TYPE_UNDEF is invoked by vm_call_method(), a method with
|
||||||
|
the same name is searched in refinements. If such a method is
|
||||||
|
found, the method is invoked. Otherwise, the original method in
|
||||||
|
the refined class (rb_method_definition_t::body.orig_def) is
|
||||||
|
invoked. This change is made to simplify the normal method lookup
|
||||||
|
and to improve the performance of normal method calls.
|
||||||
|
|
||||||
|
* vm_method.c (EXPR1, search_method, rb_method_entry),
|
||||||
|
vm_eval.c (rb_call0, rb_search_method_entry): do not use
|
||||||
|
refinements for method lookup.
|
||||||
|
|
||||||
|
* vm_insnhelper.c (vm_call_method): search methods in refinements if
|
||||||
|
ci->me is VM_METHOD_TYPE_REFINED. If the method is called by
|
||||||
|
super (i.e., ci->call == vm_call_super_method), skip the same
|
||||||
|
method entry as the current method to avoid infinite call of the
|
||||||
|
same method.
|
||||||
|
|
||||||
|
* class.c (include_modules_at): add a refined method entry for each
|
||||||
|
method defined in a module included in a refinement.
|
||||||
|
|
||||||
|
* class.c (rb_prepend_module): set an empty table to
|
||||||
|
RCLASS_M_TBL(klass) to add refined method entries, because
|
||||||
|
refinements should have priority over prepended modules.
|
||||||
|
|
||||||
|
* proc.c (mnew): use rb_method_entry_with_refinements() to get
|
||||||
|
a refined method.
|
||||||
|
|
||||||
|
* test/ruby/test_refinement.rb (test_inline_method_cache): do not skip
|
||||||
|
the test because it should pass successfully.
|
||||||
|
|
||||||
|
* test/ruby/test_refinement.rb (test_redefine_refined_method): new
|
||||||
|
test for the case a refined method is redefined.
|
||||||
|
|
||||||
Thu Nov 29 17:45:10 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
Thu Nov 29 17:45:10 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
* variable.c (rb_const_set): show namespace in warning messages.
|
* variable.c (rb_const_set): show namespace in warning messages.
|
||||||
|
16
class.c
16
class.c
@ -677,6 +677,13 @@ rb_include_module(VALUE klass, VALUE module)
|
|||||||
if (changed) rb_clear_cache();
|
if (changed) rb_clear_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
add_refined_method_entry_i(st_data_t key, st_data_t value, st_data_t data)
|
||||||
|
{
|
||||||
|
rb_add_refined_method_entry((VALUE) data, (ID) key);
|
||||||
|
return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
include_modules_at(VALUE klass, VALUE c, VALUE module)
|
include_modules_at(VALUE klass, VALUE c, VALUE module)
|
||||||
{
|
{
|
||||||
@ -707,6 +714,13 @@ include_modules_at(VALUE klass, VALUE c, VALUE module)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
c = RCLASS_SUPER(c) = rb_include_class_new(module, RCLASS_SUPER(c));
|
c = RCLASS_SUPER(c) = rb_include_class_new(module, RCLASS_SUPER(c));
|
||||||
|
if (FL_TEST(klass, RMODULE_IS_REFINEMENT)) {
|
||||||
|
VALUE refined_class =
|
||||||
|
rb_refinement_module_get_refined_class(klass);
|
||||||
|
|
||||||
|
st_foreach(RMODULE_M_TBL(module), add_refined_method_entry_i,
|
||||||
|
(st_data_t) refined_class);
|
||||||
|
}
|
||||||
if (RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries)
|
if (RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries)
|
||||||
changed = 1;
|
changed = 1;
|
||||||
skip:
|
skip:
|
||||||
@ -738,7 +752,7 @@ rb_prepend_module(VALUE klass, VALUE module)
|
|||||||
RCLASS_SUPER(klass) = origin;
|
RCLASS_SUPER(klass) = origin;
|
||||||
RCLASS_ORIGIN(klass) = origin;
|
RCLASS_ORIGIN(klass) = origin;
|
||||||
RCLASS_M_TBL(origin) = RCLASS_M_TBL(klass);
|
RCLASS_M_TBL(origin) = RCLASS_M_TBL(klass);
|
||||||
RCLASS_M_TBL(klass) = 0;
|
RCLASS_M_TBL(klass) = st_init_numtable();
|
||||||
}
|
}
|
||||||
changed = include_modules_at(klass, klass, module);
|
changed = include_modules_at(klass, klass, module);
|
||||||
if (changed < 0)
|
if (changed < 0)
|
||||||
|
12
eval.c
12
eval.c
@ -1187,6 +1187,14 @@ rb_mod_using(VALUE self, VALUE module)
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VALUE rb_refinement_module_get_refined_class(VALUE module)
|
||||||
|
{
|
||||||
|
ID id_refined_class;
|
||||||
|
|
||||||
|
CONST_ID(id_refined_class, "__refined_class__");
|
||||||
|
return rb_attr_get(module, id_refined_class);
|
||||||
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
refinement_module_include(int argc, VALUE *argv, VALUE module)
|
refinement_module_include(int argc, VALUE *argv, VALUE module)
|
||||||
{
|
{
|
||||||
@ -1195,11 +1203,9 @@ refinement_module_include(int argc, VALUE *argv, VALUE module)
|
|||||||
rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(th);
|
rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(th);
|
||||||
VALUE result = rb_mod_include(argc, argv, module);
|
VALUE result = rb_mod_include(argc, argv, module);
|
||||||
NODE *cref;
|
NODE *cref;
|
||||||
ID id_refined_class;
|
|
||||||
VALUE klass, c;
|
VALUE klass, c;
|
||||||
|
|
||||||
CONST_ID(id_refined_class, "__refined_class__");
|
klass = rb_refinement_module_get_refined_class(module);
|
||||||
klass = rb_attr_get(module, id_refined_class);
|
|
||||||
while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) {
|
while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) {
|
||||||
if (RUBY_VM_NORMAL_ISEQ_P(cfp->iseq) &&
|
if (RUBY_VM_NORMAL_ISEQ_P(cfp->iseq) &&
|
||||||
(cref = rb_vm_get_cref(cfp->iseq, cfp->ep)) &&
|
(cref = rb_vm_get_cref(cfp->iseq, cfp->ep)) &&
|
||||||
|
4
gc.c
4
gc.c
@ -2432,6 +2432,7 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
|
|||||||
const rb_method_definition_t *def = me->def;
|
const rb_method_definition_t *def = me->def;
|
||||||
|
|
||||||
gc_mark(objspace, me->klass);
|
gc_mark(objspace, me->klass);
|
||||||
|
again:
|
||||||
if (!def) return;
|
if (!def) return;
|
||||||
switch (def->type) {
|
switch (def->type) {
|
||||||
case VM_METHOD_TYPE_ISEQ:
|
case VM_METHOD_TYPE_ISEQ:
|
||||||
@ -2444,6 +2445,9 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
|
|||||||
case VM_METHOD_TYPE_IVAR:
|
case VM_METHOD_TYPE_IVAR:
|
||||||
gc_mark(objspace, def->body.attr.location);
|
gc_mark(objspace, def->body.attr.location);
|
||||||
break;
|
break;
|
||||||
|
case VM_METHOD_TYPE_REFINED:
|
||||||
|
def = def->body.orig_def;
|
||||||
|
goto again;
|
||||||
default:
|
default:
|
||||||
break; /* ignore */
|
break; /* ignore */
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,9 @@ NORETURN(void rb_async_bug_errno(const char *,int));
|
|||||||
const char *rb_builtin_type_name(int t);
|
const char *rb_builtin_type_name(int t);
|
||||||
const char *rb_builtin_class_name(VALUE x);
|
const char *rb_builtin_class_name(VALUE x);
|
||||||
|
|
||||||
|
/* eval.c */
|
||||||
|
VALUE rb_refinement_module_get_refined_class(VALUE module);
|
||||||
|
|
||||||
/* eval_error.c */
|
/* eval_error.c */
|
||||||
void ruby_error_print(void);
|
void ruby_error_print(void);
|
||||||
VALUE rb_get_backtrace(VALUE info);
|
VALUE rb_get_backtrace(VALUE info);
|
||||||
|
15
method.h
15
method.h
@ -42,7 +42,8 @@ typedef enum {
|
|||||||
VM_METHOD_TYPE_NOTIMPLEMENTED,
|
VM_METHOD_TYPE_NOTIMPLEMENTED,
|
||||||
VM_METHOD_TYPE_OPTIMIZED, /* Kernel#send, Proc#call, etc */
|
VM_METHOD_TYPE_OPTIMIZED, /* Kernel#send, Proc#call, etc */
|
||||||
VM_METHOD_TYPE_MISSING, /* wrapper for method_missing(id) */
|
VM_METHOD_TYPE_MISSING, /* wrapper for method_missing(id) */
|
||||||
VM_METHOD_TYPE_CFUNC_FRAMELESS
|
VM_METHOD_TYPE_CFUNC_FRAMELESS,
|
||||||
|
VM_METHOD_TYPE_REFINED,
|
||||||
} rb_method_type_t;
|
} rb_method_type_t;
|
||||||
|
|
||||||
struct rb_call_info_struct;
|
struct rb_call_info_struct;
|
||||||
@ -72,6 +73,7 @@ typedef struct rb_method_definition_struct {
|
|||||||
OPTIMIZED_METHOD_TYPE_SEND,
|
OPTIMIZED_METHOD_TYPE_SEND,
|
||||||
OPTIMIZED_METHOD_TYPE_CALL
|
OPTIMIZED_METHOD_TYPE_CALL
|
||||||
} optimize_type;
|
} optimize_type;
|
||||||
|
struct rb_method_definition_struct *orig_def;
|
||||||
} body;
|
} body;
|
||||||
int alias_count;
|
int alias_count;
|
||||||
} rb_method_definition_t;
|
} rb_method_definition_t;
|
||||||
@ -94,9 +96,16 @@ struct unlinked_method_entry_list_entry {
|
|||||||
void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_flag_t noex);
|
void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_flag_t noex);
|
||||||
rb_method_entry_t *rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_flag_t noex);
|
rb_method_entry_t *rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_flag_t noex);
|
||||||
rb_method_entry_t *rb_method_entry(VALUE klass, ID id, VALUE *define_class_ptr);
|
rb_method_entry_t *rb_method_entry(VALUE klass, ID id, VALUE *define_class_ptr);
|
||||||
|
void rb_add_refined_method_entry(VALUE refined_class, ID mid);
|
||||||
|
rb_method_entry_t *rb_resolve_refined_method(VALUE refinements,
|
||||||
|
rb_method_entry_t *me,
|
||||||
|
rb_method_entry_t *me_buf,
|
||||||
|
VALUE *defined_class_ptr);
|
||||||
|
rb_method_entry_t *rb_method_entry_with_refinements(VALUE klass, ID id,
|
||||||
|
rb_method_entry_t *me_buf,
|
||||||
|
VALUE *defined_class_ptr);
|
||||||
|
|
||||||
rb_method_entry_t *rb_method_entry_get_with_refinements(VALUE refinements, VALUE klass, ID id, VALUE *define_class_ptr);
|
rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, ID id, VALUE *define_class_ptr);
|
||||||
rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, VALUE refinements, ID id, VALUE *define_class_ptr);
|
|
||||||
rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_flag_t noex);
|
rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_flag_t noex);
|
||||||
|
|
||||||
int rb_method_entry_arity(const rb_method_entry_t *me);
|
int rb_method_entry_arity(const rb_method_entry_t *me);
|
||||||
|
5
object.c
5
object.c
@ -1337,7 +1337,7 @@ rb_obj_cmp(VALUE obj1, VALUE obj2)
|
|||||||
static VALUE
|
static VALUE
|
||||||
rb_mod_to_s(VALUE klass)
|
rb_mod_to_s(VALUE klass)
|
||||||
{
|
{
|
||||||
ID id_refined_class, id_defined_at;
|
ID id_defined_at;
|
||||||
VALUE refined_class, defined_at;
|
VALUE refined_class, defined_at;
|
||||||
|
|
||||||
if (FL_TEST(klass, FL_SINGLETON)) {
|
if (FL_TEST(klass, FL_SINGLETON)) {
|
||||||
@ -1357,8 +1357,7 @@ rb_mod_to_s(VALUE klass)
|
|||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
CONST_ID(id_refined_class, "__refined_class__");
|
refined_class = rb_refinement_module_get_refined_class(klass);
|
||||||
refined_class = rb_attr_get(klass, id_refined_class);
|
|
||||||
if (!NIL_P(refined_class)) {
|
if (!NIL_P(refined_class)) {
|
||||||
VALUE s = rb_usascii_str_new2("#<refinement:");
|
VALUE s = rb_usascii_str_new2("#<refinement:");
|
||||||
|
|
||||||
|
4
proc.c
4
proc.c
@ -917,7 +917,7 @@ mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope)
|
|||||||
rb_method_flag_t flag = NOEX_UNDEF;
|
rb_method_flag_t flag = NOEX_UNDEF;
|
||||||
|
|
||||||
again:
|
again:
|
||||||
me = rb_method_entry(klass, id, &defined_class);
|
me = rb_method_entry_with_refinements(klass, id, &meb, &defined_class);
|
||||||
if (UNDEFINED_METHOD_ENTRY_P(me)) {
|
if (UNDEFINED_METHOD_ENTRY_P(me)) {
|
||||||
ID rmiss = rb_intern("respond_to_missing?");
|
ID rmiss = rb_intern("respond_to_missing?");
|
||||||
VALUE sym = ID2SYM(id);
|
VALUE sym = ID2SYM(id);
|
||||||
@ -1682,6 +1682,8 @@ rb_method_entry_arity(const rb_method_entry_t *me)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case VM_METHOD_TYPE_REFINED:
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rb_bug("rb_method_entry_arity: invalid method entry type (%d)", def->type);
|
rb_bug("rb_method_entry_arity: invalid method entry type (%d)", def->type);
|
||||||
|
@ -726,7 +726,6 @@ class TestRefinement < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_inline_method_cache
|
def test_inline_method_cache
|
||||||
skip "can't implement efficiently with the current implementation of refinements"
|
|
||||||
c = InlineMethodCache::C.new
|
c = InlineMethodCache::C.new
|
||||||
f = Proc.new { c.foo }
|
f = Proc.new { c.foo }
|
||||||
assert_equal("original", f.call)
|
assert_equal("original", f.call)
|
||||||
@ -822,4 +821,31 @@ class TestRefinement < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module RedifineRefinedMethod
|
||||||
|
class C
|
||||||
|
def foo
|
||||||
|
"original"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module M
|
||||||
|
refine C do
|
||||||
|
def foo
|
||||||
|
"refined"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class C
|
||||||
|
def foo
|
||||||
|
"redefined"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_redefine_refined_method
|
||||||
|
c = RedifineRefinedMethod::C.new
|
||||||
|
assert_equal("refined", RedifineRefinedMethod::M.module_eval { c.foo })
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
49
vm_eval.c
49
vm_eval.c
@ -179,7 +179,17 @@ vm_call0_body(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv)
|
|||||||
case VM_METHOD_TYPE_BMETHOD:
|
case VM_METHOD_TYPE_BMETHOD:
|
||||||
return vm_call_bmethod_body(th, ci, argv);
|
return vm_call_bmethod_body(th, ci, argv);
|
||||||
case VM_METHOD_TYPE_ZSUPER:
|
case VM_METHOD_TYPE_ZSUPER:
|
||||||
|
case VM_METHOD_TYPE_REFINED:
|
||||||
{
|
{
|
||||||
|
if (ci->me->def->type == VM_METHOD_TYPE_REFINED &&
|
||||||
|
ci->me->def->body.orig_def) {
|
||||||
|
rb_method_entry_t orig_me;
|
||||||
|
orig_me = *ci->me;
|
||||||
|
orig_me.def = ci->me->def->body.orig_def;
|
||||||
|
ci->me = &orig_me;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
ci->defined_class = RCLASS_SUPER(ci->defined_class);
|
ci->defined_class = RCLASS_SUPER(ci->defined_class);
|
||||||
|
|
||||||
if (!ci->defined_class || !(ci->me = rb_method_entry(ci->defined_class, ci->mid, &ci->defined_class))) {
|
if (!ci->defined_class || !(ci->me = rb_method_entry(ci->defined_class, ci->mid, &ci->defined_class))) {
|
||||||
@ -274,8 +284,7 @@ stack_check(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline rb_method_entry_t *
|
static inline rb_method_entry_t *
|
||||||
rb_search_method_entry(VALUE refinements, VALUE recv, ID mid,
|
rb_search_method_entry(VALUE recv, ID mid, VALUE *defined_class_ptr);
|
||||||
VALUE *defined_class_ptr);
|
|
||||||
static inline int rb_method_call_status(rb_thread_t *th, const rb_method_entry_t *me, call_type scope, VALUE self);
|
static inline int rb_method_call_status(rb_thread_t *th, const rb_method_entry_t *me, call_type scope, VALUE self);
|
||||||
#define NOEX_OK NOEX_NOSUPER
|
#define NOEX_OK NOEX_NOSUPER
|
||||||
|
|
||||||
@ -295,11 +304,11 @@ static inline int rb_method_call_status(rb_thread_t *th, const rb_method_entry_t
|
|||||||
*/
|
*/
|
||||||
static inline VALUE
|
static inline VALUE
|
||||||
rb_call0(VALUE recv, ID mid, int argc, const VALUE *argv,
|
rb_call0(VALUE recv, ID mid, int argc, const VALUE *argv,
|
||||||
call_type scope, VALUE self, VALUE refinements)
|
call_type scope, VALUE self)
|
||||||
{
|
{
|
||||||
VALUE defined_class;
|
VALUE defined_class;
|
||||||
rb_method_entry_t *me =
|
rb_method_entry_t *me =
|
||||||
rb_search_method_entry(refinements, recv, mid, &defined_class);
|
rb_search_method_entry(recv, mid, &defined_class);
|
||||||
rb_thread_t *th = GET_THREAD();
|
rb_thread_t *th = GET_THREAD();
|
||||||
int call_status = rb_method_call_status(th, me, scope, self);
|
int call_status = rb_method_call_status(th, me, scope, self);
|
||||||
|
|
||||||
@ -363,7 +372,7 @@ check_funcall(VALUE recv, ID mid, int argc, VALUE *argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
me = rb_search_method_entry(Qnil, recv, mid, &defined_class);
|
me = rb_search_method_entry(recv, mid, &defined_class);
|
||||||
call_status = rb_method_call_status(th, me, CALL_FCALL, Qundef);
|
call_status = rb_method_call_status(th, me, CALL_FCALL, Qundef);
|
||||||
if (call_status != NOEX_OK) {
|
if (call_status != NOEX_OK) {
|
||||||
if (rb_method_basic_definition_p(klass, idMethodMissing)) {
|
if (rb_method_basic_definition_p(klass, idMethodMissing)) {
|
||||||
@ -428,8 +437,7 @@ rb_type_str(enum ruby_value_type type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline rb_method_entry_t *
|
static inline rb_method_entry_t *
|
||||||
rb_search_method_entry(VALUE refinements, VALUE recv, ID mid,
|
rb_search_method_entry(VALUE recv, ID mid, VALUE *defined_class_ptr)
|
||||||
VALUE *defined_class_ptr)
|
|
||||||
{
|
{
|
||||||
VALUE klass = CLASS_OF(recv);
|
VALUE klass = CLASS_OF(recv);
|
||||||
|
|
||||||
@ -468,8 +476,7 @@ rb_search_method_entry(VALUE refinements, VALUE recv, ID mid,
|
|||||||
rb_id2name(mid), type, (void *)recv, flags, klass);
|
rb_id2name(mid), type, (void *)recv, flags, klass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rb_method_entry_get_with_refinements(refinements, klass, mid,
|
return rb_method_entry(klass, mid, defined_class_ptr);
|
||||||
defined_class_ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
@ -533,7 +540,7 @@ rb_method_call_status(rb_thread_t *th, const rb_method_entry_t *me, call_type sc
|
|||||||
static inline VALUE
|
static inline VALUE
|
||||||
rb_call(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope)
|
rb_call(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope)
|
||||||
{
|
{
|
||||||
return rb_call0(recv, mid, argc, argv, scope, Qundef, Qnil);
|
return rb_call0(recv, mid, argc, argv, scope, Qundef);
|
||||||
}
|
}
|
||||||
|
|
||||||
NORETURN(static void raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv,
|
NORETURN(static void raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv,
|
||||||
@ -786,10 +793,24 @@ rb_funcall_passing_block_with_refinements(VALUE recv, ID mid, int argc,
|
|||||||
const VALUE *argv,
|
const VALUE *argv,
|
||||||
VALUE refinements)
|
VALUE refinements)
|
||||||
{
|
{
|
||||||
PASS_PASSED_BLOCK_TH(GET_THREAD());
|
VALUE defined_class;
|
||||||
|
rb_method_entry_t meb, *me =
|
||||||
|
rb_search_method_entry(recv, mid, &defined_class);
|
||||||
|
rb_thread_t *th;
|
||||||
|
int call_status;
|
||||||
|
|
||||||
return rb_call0(recv, mid, argc, argv, CALL_PUBLIC, Qundef,
|
if (me && me->def->type == VM_METHOD_TYPE_REFINED) {
|
||||||
refinements);
|
me = rb_resolve_refined_method(refinements, me, &meb,
|
||||||
|
&defined_class);
|
||||||
|
}
|
||||||
|
PASS_PASSED_BLOCK_TH(GET_THREAD());
|
||||||
|
th = GET_THREAD();
|
||||||
|
call_status = rb_method_call_status(th, me, CALL_PUBLIC, Qundef);
|
||||||
|
if (call_status != NOEX_OK) {
|
||||||
|
return method_missing(recv, mid, argc, argv, call_status);
|
||||||
|
}
|
||||||
|
stack_check();
|
||||||
|
return vm_call0(th, recv, mid, argc, argv, me, defined_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
@ -816,7 +837,7 @@ send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope)
|
|||||||
id = rb_to_id(vid);
|
id = rb_to_id(vid);
|
||||||
}
|
}
|
||||||
PASS_PASSED_BLOCK_TH(th);
|
PASS_PASSED_BLOCK_TH(th);
|
||||||
return rb_call0(recv, id, argc, argv, scope, self, Qnil);
|
return rb_call0(recv, id, argc, argv, scope, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1694,6 +1694,47 @@ vm_call_method_missing(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_inf
|
|||||||
return vm_call_method(th, reg_cfp, &ci_entry);
|
return vm_call_method(th, reg_cfp, &ci_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
copy_refinement_iclass(VALUE iclass, VALUE superclass)
|
||||||
|
{
|
||||||
|
VALUE result, c;
|
||||||
|
|
||||||
|
Check_Type(iclass, T_ICLASS);
|
||||||
|
c = result = rb_include_class_new(RBASIC(iclass)->klass, superclass);
|
||||||
|
RCLASS_REFINED_CLASS(c) = RCLASS_REFINED_CLASS(iclass);
|
||||||
|
iclass = RCLASS_SUPER(iclass);
|
||||||
|
while (iclass && BUILTIN_TYPE(iclass) == T_ICLASS) {
|
||||||
|
c = RCLASS_SUPER(c) = rb_include_class_new(RBASIC(iclass)->klass,
|
||||||
|
RCLASS_SUPER(c));
|
||||||
|
RCLASS_REFINED_CLASS(c) = RCLASS_REFINED_CLASS(iclass);
|
||||||
|
iclass = RCLASS_SUPER(iclass);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
find_refinement(VALUE refinements, VALUE klass)
|
||||||
|
{
|
||||||
|
VALUE refinement;
|
||||||
|
|
||||||
|
if (NIL_P(refinements)) {
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
refinement = rb_hash_lookup(refinements, klass);
|
||||||
|
if (NIL_P(refinement) &&
|
||||||
|
BUILTIN_TYPE(klass) == T_ICLASS) {
|
||||||
|
refinement = rb_hash_lookup(refinements,
|
||||||
|
RBASIC(klass)->klass);
|
||||||
|
if (!NIL_P(refinement)) {
|
||||||
|
refinement = copy_refinement_iclass(refinement, klass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return refinement;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2);
|
||||||
|
static VALUE vm_call_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci);
|
||||||
|
|
||||||
static inline VALUE
|
static inline VALUE
|
||||||
vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci)
|
vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci)
|
||||||
{
|
{
|
||||||
@ -1735,7 +1776,10 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci)
|
|||||||
return vm_call_bmethod(th, cfp, ci);
|
return vm_call_bmethod(th, cfp, ci);
|
||||||
}
|
}
|
||||||
case VM_METHOD_TYPE_ZSUPER:{
|
case VM_METHOD_TYPE_ZSUPER:{
|
||||||
VALUE klass = RCLASS_SUPER(ci->me->klass);
|
VALUE klass;
|
||||||
|
|
||||||
|
zsuper_method_dispatch:
|
||||||
|
klass = RCLASS_SUPER(ci->me->klass);
|
||||||
ci_temp = *ci;
|
ci_temp = *ci;
|
||||||
ci = &ci_temp;
|
ci = &ci_temp;
|
||||||
|
|
||||||
@ -1778,6 +1822,44 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci)
|
|||||||
break;
|
break;
|
||||||
case VM_METHOD_TYPE_UNDEF:
|
case VM_METHOD_TYPE_UNDEF:
|
||||||
break;
|
break;
|
||||||
|
case VM_METHOD_TYPE_REFINED:{
|
||||||
|
NODE *cref = rb_vm_get_cref(cfp->iseq, cfp->ep);
|
||||||
|
VALUE refinements = cref ? cref->nd_refinements : Qnil;
|
||||||
|
VALUE refinement, defined_class;
|
||||||
|
rb_method_entry_t orig_me, *me;
|
||||||
|
ci_temp = *ci;
|
||||||
|
ci = &ci_temp;
|
||||||
|
|
||||||
|
refinement = find_refinement(refinements,
|
||||||
|
ci->defined_class);
|
||||||
|
if (NIL_P(refinement)) {
|
||||||
|
goto no_refinement_dispatch;
|
||||||
|
}
|
||||||
|
me = rb_method_entry(refinement, ci->mid, &defined_class);
|
||||||
|
if (me) {
|
||||||
|
if (ci->call == vm_call_super_method &&
|
||||||
|
cfp->me &&
|
||||||
|
rb_method_definition_eq(me->def, cfp->me->def)) {
|
||||||
|
goto no_refinement_dispatch;
|
||||||
|
}
|
||||||
|
ci->me = me;
|
||||||
|
ci->defined_class = defined_class;
|
||||||
|
if (me->def->type != VM_METHOD_TYPE_REFINED) {
|
||||||
|
goto normal_method_dispatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
no_refinement_dispatch:
|
||||||
|
if (ci->me->def->body.orig_def) {
|
||||||
|
orig_me = *ci->me;
|
||||||
|
orig_me.def = ci->me->def->body.orig_def;
|
||||||
|
ci->me = &orig_me;
|
||||||
|
goto normal_method_dispatch;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
goto zsuper_method_dispatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rb_bug("vm_call_method: unsupported method type (%d)", ci->me->def->type);
|
rb_bug("vm_call_method: unsupported method type (%d)", ci->me->def->type);
|
||||||
}
|
}
|
||||||
@ -1841,6 +1923,12 @@ vm_call_general(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci
|
|||||||
return vm_call_method(th, reg_cfp, ci);
|
return vm_call_method(th, reg_cfp, ci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
vm_call_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci)
|
||||||
|
{
|
||||||
|
return vm_call_method(th, reg_cfp, ci);
|
||||||
|
}
|
||||||
|
|
||||||
/* super */
|
/* super */
|
||||||
|
|
||||||
static inline VALUE
|
static inline VALUE
|
||||||
@ -1929,7 +2017,7 @@ vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_inf
|
|||||||
|
|
||||||
/* TODO: use inline cache */
|
/* TODO: use inline cache */
|
||||||
ci->me = rb_method_entry(ci->klass, ci->mid, &ci->defined_class);
|
ci->me = rb_method_entry(ci->klass, ci->mid, &ci->defined_class);
|
||||||
ci->call = vm_call_general;
|
ci->call = vm_call_super_method;
|
||||||
|
|
||||||
while (iseq && !iseq->klass) {
|
while (iseq && !iseq->klass) {
|
||||||
iseq = iseq->parent_iseq;
|
iseq = iseq->parent_iseq;
|
||||||
@ -1937,7 +2025,7 @@ vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_inf
|
|||||||
|
|
||||||
if (ci->me && ci->me->def->type == VM_METHOD_TYPE_ISEQ && ci->me->def->body.iseq == iseq) {
|
if (ci->me && ci->me->def->type == VM_METHOD_TYPE_ISEQ && ci->me->def->body.iseq == iseq) {
|
||||||
ci->klass = RCLASS_SUPER(ci->defined_class);
|
ci->klass = RCLASS_SUPER(ci->defined_class);
|
||||||
ci->me = rb_method_entry_get_with_refinements(Qnil, ci->klass, ci->mid, &ci->defined_class);
|
ci->me = rb_method_entry(ci->klass, ci->mid, &ci->defined_class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
308
vm_method.c
308
vm_method.c
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#define CACHE_SIZE 0x800
|
#define CACHE_SIZE 0x800
|
||||||
#define CACHE_MASK 0x7ff
|
#define CACHE_MASK 0x7ff
|
||||||
#define EXPR1(c,o,m) ((((c)>>3)^((o)>>3)^(m))&CACHE_MASK)
|
#define EXPR1(c,m) ((((c)>>3)^(m))&CACHE_MASK)
|
||||||
|
|
||||||
#define NOEX_NOREDEF 0
|
#define NOEX_NOREDEF 0
|
||||||
#ifndef NOEX_NOREDEF
|
#ifndef NOEX_NOREDEF
|
||||||
@ -21,7 +21,6 @@ struct cache_entry { /* method hash table. */
|
|||||||
VALUE filled_version; /* filled state version */
|
VALUE filled_version; /* filled state version */
|
||||||
ID mid; /* method's id */
|
ID mid; /* method's id */
|
||||||
VALUE klass; /* receiver's class */
|
VALUE klass; /* receiver's class */
|
||||||
VALUE refinements; /* refinements */
|
|
||||||
rb_method_entry_t *me;
|
rb_method_entry_t *me;
|
||||||
VALUE defined_class;
|
VALUE defined_class;
|
||||||
};
|
};
|
||||||
@ -155,6 +154,12 @@ rb_free_method_entry(rb_method_entry_t *me)
|
|||||||
|
|
||||||
if (def) {
|
if (def) {
|
||||||
if (def->alias_count == 0) {
|
if (def->alias_count == 0) {
|
||||||
|
if (def->type == VM_METHOD_TYPE_REFINED) {
|
||||||
|
def->body.orig_def->alias_count--;
|
||||||
|
if (def->body.orig_def->alias_count == 0) {
|
||||||
|
xfree(def->body.orig_def);
|
||||||
|
}
|
||||||
|
}
|
||||||
xfree(def);
|
xfree(def);
|
||||||
}
|
}
|
||||||
else if (def->alias_count > 0) {
|
else if (def->alias_count > 0) {
|
||||||
@ -167,7 +172,50 @@ rb_free_method_entry(rb_method_entry_t *me)
|
|||||||
|
|
||||||
static int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2);
|
static int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2);
|
||||||
|
|
||||||
void rb_redefine_opt_method(VALUE, ID);
|
static inline rb_method_entry_t *
|
||||||
|
lookup_method_table(VALUE klass, ID id)
|
||||||
|
{
|
||||||
|
st_data_t body;
|
||||||
|
st_table *m_tbl = RCLASS_M_TBL(klass);
|
||||||
|
if (st_lookup(m_tbl, id, &body)) {
|
||||||
|
return (rb_method_entry_t *) body;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
make_method_entry_refined(rb_method_entry_t *me)
|
||||||
|
{
|
||||||
|
rb_method_definition_t *new_def;
|
||||||
|
|
||||||
|
if (me->def && me->def->type == VM_METHOD_TYPE_REFINED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
new_def = ALLOC(rb_method_definition_t);
|
||||||
|
new_def->type = VM_METHOD_TYPE_REFINED;
|
||||||
|
new_def->original_id = me->called_id;
|
||||||
|
new_def->alias_count = 0;
|
||||||
|
new_def->body.orig_def = me->def;
|
||||||
|
rb_vm_check_redefinition_opt_method(me, me->klass);
|
||||||
|
if (me->def) me->def->alias_count++;
|
||||||
|
me->def = new_def;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rb_add_refined_method_entry(VALUE refined_class, ID mid)
|
||||||
|
{
|
||||||
|
rb_method_entry_t *me = lookup_method_table(refined_class, mid);
|
||||||
|
|
||||||
|
if (me) {
|
||||||
|
make_method_entry_refined(me);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rb_add_method(refined_class, mid, VM_METHOD_TYPE_REFINED, 0,
|
||||||
|
NOEX_PUBLIC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static rb_method_entry_t *
|
static rb_method_entry_t *
|
||||||
rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
|
rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
|
||||||
@ -179,6 +227,7 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
|
|||||||
#endif
|
#endif
|
||||||
st_table *mtbl;
|
st_table *mtbl;
|
||||||
st_data_t data;
|
st_data_t data;
|
||||||
|
int make_refined = 0;
|
||||||
|
|
||||||
if (NIL_P(klass)) {
|
if (NIL_P(klass)) {
|
||||||
klass = rb_cObject;
|
klass = rb_cObject;
|
||||||
@ -199,14 +248,19 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
|
|||||||
rklass = klass;
|
rklass = klass;
|
||||||
#endif
|
#endif
|
||||||
if (FL_TEST(klass, RMODULE_IS_REFINEMENT)) {
|
if (FL_TEST(klass, RMODULE_IS_REFINEMENT)) {
|
||||||
ID id_refined_class;
|
VALUE refined_class =
|
||||||
VALUE refined_class;
|
rb_refinement_module_get_refined_class(klass);
|
||||||
|
|
||||||
CONST_ID(id_refined_class, "__refined_class__");
|
rb_add_refined_method_entry(refined_class, mid);
|
||||||
refined_class = rb_ivar_get(klass, id_refined_class);
|
}
|
||||||
rb_redefine_opt_method(refined_class, mid);
|
if (type == VM_METHOD_TYPE_REFINED) {
|
||||||
|
rb_method_entry_t *old_me =
|
||||||
|
lookup_method_table(RCLASS_ORIGIN(klass), mid);
|
||||||
|
if (old_me) rb_vm_check_redefinition_opt_method(old_me, klass);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
klass = RCLASS_ORIGIN(klass);
|
||||||
}
|
}
|
||||||
klass = RCLASS_ORIGIN(klass);
|
|
||||||
mtbl = RCLASS_M_TBL(klass);
|
mtbl = RCLASS_M_TBL(klass);
|
||||||
|
|
||||||
/* check re-definition */
|
/* check re-definition */
|
||||||
@ -222,6 +276,8 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
rb_vm_check_redefinition_opt_method(old_me, klass);
|
rb_vm_check_redefinition_opt_method(old_me, klass);
|
||||||
|
if (old_def->type == VM_METHOD_TYPE_REFINED)
|
||||||
|
make_refined = 1;
|
||||||
|
|
||||||
if (RTEST(ruby_verbose) &&
|
if (RTEST(ruby_verbose) &&
|
||||||
type != VM_METHOD_TYPE_UNDEF &&
|
type != VM_METHOD_TYPE_UNDEF &&
|
||||||
@ -274,6 +330,10 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (make_refined) {
|
||||||
|
make_method_entry_refined(me);
|
||||||
|
}
|
||||||
|
|
||||||
st_insert(mtbl, mid, (st_data_t) me);
|
st_insert(mtbl, mid, (st_data_t) me);
|
||||||
|
|
||||||
return me;
|
return me;
|
||||||
@ -341,7 +401,12 @@ rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *opts, rb_method_
|
|||||||
int line;
|
int line;
|
||||||
rb_method_entry_t *me = rb_method_entry_make(klass, mid, type, 0, noex);
|
rb_method_entry_t *me = rb_method_entry_make(klass, mid, type, 0, noex);
|
||||||
rb_method_definition_t *def = ALLOC(rb_method_definition_t);
|
rb_method_definition_t *def = ALLOC(rb_method_definition_t);
|
||||||
me->def = def;
|
if (me->def && me->def->type == VM_METHOD_TYPE_REFINED) {
|
||||||
|
me->def->body.orig_def = def;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
me->def = def;
|
||||||
|
}
|
||||||
def->type = type;
|
def->type = type;
|
||||||
def->original_id = mid;
|
def->original_id = mid;
|
||||||
def->alias_count = 0;
|
def->alias_count = 0;
|
||||||
@ -379,10 +444,13 @@ rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *opts, rb_method_
|
|||||||
case VM_METHOD_TYPE_ZSUPER:
|
case VM_METHOD_TYPE_ZSUPER:
|
||||||
case VM_METHOD_TYPE_UNDEF:
|
case VM_METHOD_TYPE_UNDEF:
|
||||||
break;
|
break;
|
||||||
|
case VM_METHOD_TYPE_REFINED:
|
||||||
|
def->body.orig_def = (rb_method_definition_t *) opts;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
rb_bug("rb_add_method: unsupported method type (%d)\n", type);
|
rb_bug("rb_add_method: unsupported method type (%d)\n", type);
|
||||||
}
|
}
|
||||||
if (type != VM_METHOD_TYPE_UNDEF) {
|
if (type != VM_METHOD_TYPE_UNDEF && type != VM_METHOD_TYPE_REFINED) {
|
||||||
method_added(klass, mid);
|
method_added(klass, mid);
|
||||||
}
|
}
|
||||||
return me;
|
return me;
|
||||||
@ -425,86 +493,18 @@ rb_get_alloc_func(VALUE klass)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
|
||||||
copy_refinement_iclass(VALUE iclass, VALUE superclass)
|
|
||||||
{
|
|
||||||
VALUE result, c;
|
|
||||||
|
|
||||||
Check_Type(iclass, T_ICLASS);
|
|
||||||
c = result = rb_include_class_new(RBASIC(iclass)->klass, superclass);
|
|
||||||
RCLASS_REFINED_CLASS(c) = RCLASS_REFINED_CLASS(iclass);
|
|
||||||
iclass = RCLASS_SUPER(iclass);
|
|
||||||
while (iclass && BUILTIN_TYPE(iclass) == T_ICLASS) {
|
|
||||||
c = RCLASS_SUPER(c) = rb_include_class_new(RBASIC(iclass)->klass,
|
|
||||||
RCLASS_SUPER(c));
|
|
||||||
RCLASS_REFINED_CLASS(c) = RCLASS_REFINED_CLASS(iclass);
|
|
||||||
iclass = RCLASS_SUPER(iclass);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
|
||||||
lookup_method_table(VALUE klass, ID id, st_data_t *body)
|
|
||||||
{
|
|
||||||
st_table *m_tbl = RCLASS_M_TBL(klass);
|
|
||||||
if (!m_tbl) {
|
|
||||||
m_tbl = RCLASS_M_TBL(RCLASS_ORIGIN(RBASIC(klass)->klass));
|
|
||||||
}
|
|
||||||
return st_lookup(m_tbl, id, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline rb_method_entry_t*
|
static inline rb_method_entry_t*
|
||||||
search_method_with_refinements(VALUE klass, ID id, VALUE refinements,
|
search_method(VALUE klass, ID id, VALUE *defined_class_ptr)
|
||||||
VALUE *defined_class_ptr)
|
|
||||||
{
|
{
|
||||||
st_data_t body;
|
rb_method_entry_t *me;
|
||||||
VALUE iclass, skipped_class = Qnil;
|
|
||||||
|
|
||||||
for (body = 0; klass; klass = RCLASS_SUPER(klass)) {
|
for (me = 0; klass; klass = RCLASS_SUPER(klass)) {
|
||||||
if (klass != skipped_class) {
|
if ((me = lookup_method_table(klass, id)) != 0) break;
|
||||||
iclass = rb_hash_lookup(refinements, klass);
|
|
||||||
if (NIL_P(iclass) && BUILTIN_TYPE(klass) == T_ICLASS) {
|
|
||||||
iclass = rb_hash_lookup(refinements, RBASIC(klass)->klass);
|
|
||||||
if (!NIL_P(iclass))
|
|
||||||
iclass = copy_refinement_iclass(iclass, klass);
|
|
||||||
}
|
|
||||||
if (!NIL_P(iclass)) {
|
|
||||||
skipped_class = klass;
|
|
||||||
klass = iclass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lookup_method_table(klass, id, &body)) break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defined_class_ptr)
|
if (defined_class_ptr)
|
||||||
*defined_class_ptr = klass;
|
*defined_class_ptr = klass;
|
||||||
return (rb_method_entry_t *)body;
|
return me;
|
||||||
}
|
|
||||||
|
|
||||||
static inline rb_method_entry_t*
|
|
||||||
search_method_without_refinements(VALUE klass, ID id, VALUE *defined_class_ptr)
|
|
||||||
{
|
|
||||||
st_data_t body;
|
|
||||||
|
|
||||||
for (body = 0; klass; klass = RCLASS_SUPER(klass)) {
|
|
||||||
if (lookup_method_table(klass, id, &body)) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defined_class_ptr)
|
|
||||||
*defined_class_ptr = klass;
|
|
||||||
return (rb_method_entry_t *)body;
|
|
||||||
}
|
|
||||||
|
|
||||||
static rb_method_entry_t*
|
|
||||||
search_method(VALUE klass, ID id, VALUE refinements, VALUE *defined_class_ptr)
|
|
||||||
{
|
|
||||||
if (NIL_P(refinements)) {
|
|
||||||
return search_method_without_refinements(klass, id, defined_class_ptr);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return search_method_with_refinements(klass, id, refinements,
|
|
||||||
defined_class_ptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -514,19 +514,17 @@ search_method(VALUE klass, ID id, VALUE refinements, VALUE *defined_class_ptr)
|
|||||||
* rb_method_entry() simply.
|
* rb_method_entry() simply.
|
||||||
*/
|
*/
|
||||||
rb_method_entry_t *
|
rb_method_entry_t *
|
||||||
rb_method_entry_get_without_cache(VALUE klass, VALUE refinements, ID id,
|
rb_method_entry_get_without_cache(VALUE klass, ID id,
|
||||||
VALUE *defined_class_ptr)
|
VALUE *defined_class_ptr)
|
||||||
{
|
{
|
||||||
VALUE defined_class;
|
VALUE defined_class;
|
||||||
rb_method_entry_t *me = search_method(klass, id, refinements,
|
rb_method_entry_t *me = search_method(klass, id, &defined_class);
|
||||||
&defined_class);
|
|
||||||
|
|
||||||
if (ruby_running) {
|
if (ruby_running) {
|
||||||
struct cache_entry *ent;
|
struct cache_entry *ent;
|
||||||
ent = cache + EXPR1(klass, refinements, id);
|
ent = cache + EXPR1(klass, id);
|
||||||
ent->filled_version = GET_VM_STATE_VERSION();
|
ent->filled_version = GET_VM_STATE_VERSION();
|
||||||
ent->klass = klass;
|
ent->klass = klass;
|
||||||
ent->refinements = refinements;
|
|
||||||
ent->defined_class = defined_class;
|
ent->defined_class = defined_class;
|
||||||
|
|
||||||
if (UNDEFINED_METHOD_ENTRY_P(me)) {
|
if (UNDEFINED_METHOD_ENTRY_P(me)) {
|
||||||
@ -546,37 +544,89 @@ rb_method_entry_get_without_cache(VALUE klass, VALUE refinements, ID id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
rb_method_entry_t *
|
rb_method_entry_t *
|
||||||
rb_method_entry_get_with_refinements(VALUE refinements, VALUE klass, ID id,
|
rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr)
|
||||||
VALUE *defined_class_ptr)
|
|
||||||
{
|
{
|
||||||
#if OPT_GLOBAL_METHOD_CACHE
|
#if OPT_GLOBAL_METHOD_CACHE
|
||||||
struct cache_entry *ent;
|
struct cache_entry *ent;
|
||||||
|
|
||||||
ent = cache + EXPR1(klass, refinements, id);
|
ent = cache + EXPR1(klass, id);
|
||||||
if (ent->filled_version == GET_VM_STATE_VERSION() &&
|
if (ent->filled_version == GET_VM_STATE_VERSION() &&
|
||||||
ent->mid == id && ent->klass == klass &&
|
ent->mid == id && ent->klass == klass) {
|
||||||
ent->refinements == refinements) {
|
|
||||||
if (defined_class_ptr)
|
if (defined_class_ptr)
|
||||||
*defined_class_ptr = ent->defined_class;
|
*defined_class_ptr = ent->defined_class;
|
||||||
return ent->me;
|
return ent->me;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return rb_method_entry_get_without_cache(klass, refinements, id,
|
return rb_method_entry_get_without_cache(klass, id, defined_class_ptr);
|
||||||
defined_class_ptr);
|
}
|
||||||
|
|
||||||
|
static rb_method_entry_t *
|
||||||
|
get_original_method_entry(VALUE refinements,
|
||||||
|
rb_method_entry_t *me, rb_method_entry_t *me_buf,
|
||||||
|
VALUE *defined_class_ptr)
|
||||||
|
{
|
||||||
|
if (me->def->body.orig_def) {
|
||||||
|
*me_buf = *me;
|
||||||
|
me_buf->def = me->def->body.orig_def;
|
||||||
|
return me_buf;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rb_method_entry_t *tmp_me;
|
||||||
|
tmp_me = rb_method_entry(RCLASS_SUPER(me->klass), me->called_id,
|
||||||
|
defined_class_ptr);
|
||||||
|
return rb_resolve_refined_method(refinements, tmp_me, me_buf,
|
||||||
|
defined_class_ptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rb_method_entry_t *
|
rb_method_entry_t *
|
||||||
rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr)
|
rb_resolve_refined_method(VALUE refinements, rb_method_entry_t *me,
|
||||||
|
rb_method_entry_t *me_buf,
|
||||||
|
VALUE *defined_class_ptr)
|
||||||
{
|
{
|
||||||
NODE *cref = rb_vm_cref();
|
if (me && me->def->type == VM_METHOD_TYPE_REFINED) {
|
||||||
VALUE refinements = Qnil;
|
VALUE refinement;
|
||||||
|
rb_method_entry_t *tmp_me;
|
||||||
|
|
||||||
if (cref && !NIL_P(cref->nd_refinements)) {
|
refinement = find_refinement(refinements, me->klass);
|
||||||
refinements = cref->nd_refinements;
|
if (NIL_P(refinement)) {
|
||||||
|
return get_original_method_entry(refinements, me, me_buf,
|
||||||
|
defined_class_ptr);
|
||||||
|
}
|
||||||
|
tmp_me = rb_method_entry(refinement, me->called_id,
|
||||||
|
defined_class_ptr);
|
||||||
|
if (tmp_me && tmp_me->def->type != VM_METHOD_TYPE_REFINED) {
|
||||||
|
return tmp_me;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return get_original_method_entry(refinements, me, me_buf,
|
||||||
|
defined_class_ptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return rb_method_entry_get_with_refinements(refinements, klass, id,
|
else {
|
||||||
defined_class_ptr);
|
return me;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_method_entry_t *
|
||||||
|
rb_method_entry_with_refinements(VALUE klass, ID id,
|
||||||
|
rb_method_entry_t *me_buf,
|
||||||
|
VALUE *defined_class_ptr)
|
||||||
|
{
|
||||||
|
VALUE defined_class;
|
||||||
|
rb_method_entry_t *me = rb_method_entry(klass, id, &defined_class);
|
||||||
|
|
||||||
|
if (me && me->def->type == VM_METHOD_TYPE_REFINED) {
|
||||||
|
NODE *cref = rb_vm_cref();
|
||||||
|
VALUE refinements = cref ? cref->nd_refinements : Qnil;
|
||||||
|
|
||||||
|
me = rb_resolve_refined_method(refinements, me, me_buf,
|
||||||
|
&defined_class);
|
||||||
|
}
|
||||||
|
if (defined_class_ptr)
|
||||||
|
*defined_class_ptr = defined_class;
|
||||||
|
return me;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -675,9 +725,9 @@ rb_export_method(VALUE klass, ID name, rb_method_flag_t noex)
|
|||||||
rb_secure(4);
|
rb_secure(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
me = search_method(klass, name, Qnil, &defined_class);
|
me = search_method(klass, name, &defined_class);
|
||||||
if (!me && RB_TYPE_P(klass, T_MODULE)) {
|
if (!me && RB_TYPE_P(klass, T_MODULE)) {
|
||||||
me = search_method(rb_cObject, name, Qnil, &defined_class);
|
me = search_method(rb_cObject, name, &defined_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UNDEFINED_METHOD_ENTRY_P(me)) {
|
if (UNDEFINED_METHOD_ENTRY_P(me)) {
|
||||||
@ -699,7 +749,9 @@ rb_export_method(VALUE klass, ID name, rb_method_flag_t noex)
|
|||||||
int
|
int
|
||||||
rb_method_boundp(VALUE klass, ID id, int ex)
|
rb_method_boundp(VALUE klass, ID id, int ex)
|
||||||
{
|
{
|
||||||
rb_method_entry_t *me = rb_method_entry(klass, id, 0);
|
rb_method_entry_t meb;
|
||||||
|
rb_method_entry_t *me =
|
||||||
|
rb_method_entry_with_refinements(klass, id, &meb, 0);
|
||||||
|
|
||||||
if (me != 0) {
|
if (me != 0) {
|
||||||
if ((ex & ~NOEX_RESPONDS) &&
|
if ((ex & ~NOEX_RESPONDS) &&
|
||||||
@ -765,9 +817,6 @@ void
|
|||||||
rb_undef(VALUE klass, ID id)
|
rb_undef(VALUE klass, ID id)
|
||||||
{
|
{
|
||||||
rb_method_entry_t *me;
|
rb_method_entry_t *me;
|
||||||
NODE *cref = rb_vm_cref();
|
|
||||||
VALUE refinements = Qnil;
|
|
||||||
void rb_using_refinement(NODE *cref, VALUE klass, VALUE module);
|
|
||||||
|
|
||||||
if (NIL_P(klass)) {
|
if (NIL_P(klass)) {
|
||||||
rb_raise(rb_eTypeError, "no class to undef method");
|
rb_raise(rb_eTypeError, "no class to undef method");
|
||||||
@ -783,10 +832,7 @@ rb_undef(VALUE klass, ID id)
|
|||||||
rb_warn("undefining `%s' may cause serious problems", rb_id2name(id));
|
rb_warn("undefining `%s' may cause serious problems", rb_id2name(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cref && !NIL_P(cref->nd_refinements)) {
|
me = search_method(klass, id, 0);
|
||||||
refinements = cref->nd_refinements;
|
|
||||||
}
|
|
||||||
me = search_method(klass, id, refinements, 0);
|
|
||||||
|
|
||||||
if (UNDEFINED_METHOD_ENTRY_P(me)) {
|
if (UNDEFINED_METHOD_ENTRY_P(me)) {
|
||||||
const char *s0 = " class";
|
const char *s0 = " class";
|
||||||
@ -807,11 +853,6 @@ rb_undef(VALUE klass, ID id)
|
|||||||
rb_id2name(id), s0, rb_class2name(c));
|
rb_id2name(id), s0, rb_class2name(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!RTEST(rb_class_inherited_p(klass, me->klass))) {
|
|
||||||
VALUE mod = rb_module_new();
|
|
||||||
rb_using_refinement(cref, klass, mod);
|
|
||||||
klass = mod;
|
|
||||||
}
|
|
||||||
rb_add_method(klass, id, VM_METHOD_TYPE_UNDEF, 0, NOEX_PUBLIC);
|
rb_add_method(klass, id, VM_METHOD_TYPE_UNDEF, 0, NOEX_PUBLIC);
|
||||||
|
|
||||||
CALL_METHOD_HOOK(klass, undefined, id);
|
CALL_METHOD_HOOK(klass, undefined, id);
|
||||||
@ -1033,6 +1074,10 @@ rb_method_entry_eq(const rb_method_entry_t *m1, const rb_method_entry_t *m2)
|
|||||||
static int
|
static int
|
||||||
rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2)
|
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 = d1->body.orig_def;
|
||||||
|
if (d2 && d2->type == VM_METHOD_TYPE_REFINED)
|
||||||
|
d2 = d2->body.orig_def;
|
||||||
if (d1 == d2) return 1;
|
if (d1 == d2) return 1;
|
||||||
if (!d1 || !d2) return 0;
|
if (!d1 || !d2) return 0;
|
||||||
if (d1->type != d2->type) {
|
if (d1->type != d2->type) {
|
||||||
@ -1067,6 +1112,7 @@ rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_defini
|
|||||||
static st_index_t
|
static st_index_t
|
||||||
rb_hash_method_definition(st_index_t hash, const rb_method_definition_t *def)
|
rb_hash_method_definition(st_index_t hash, const rb_method_definition_t *def)
|
||||||
{
|
{
|
||||||
|
again:
|
||||||
hash = rb_hash_uint(hash, def->type);
|
hash = rb_hash_uint(hash, def->type);
|
||||||
switch (def->type) {
|
switch (def->type) {
|
||||||
case VM_METHOD_TYPE_ISEQ:
|
case VM_METHOD_TYPE_ISEQ:
|
||||||
@ -1087,6 +1133,9 @@ rb_hash_method_definition(st_index_t hash, const rb_method_definition_t *def)
|
|||||||
return hash;
|
return hash;
|
||||||
case VM_METHOD_TYPE_OPTIMIZED:
|
case VM_METHOD_TYPE_OPTIMIZED:
|
||||||
return rb_hash_uint(hash, def->body.optimize_type);
|
return rb_hash_uint(hash, def->body.optimize_type);
|
||||||
|
case VM_METHOD_TYPE_REFINED:
|
||||||
|
def = def->body.orig_def;
|
||||||
|
goto again;
|
||||||
default:
|
default:
|
||||||
rb_bug("rb_hash_method_definition: unsupported method type (%d)\n", def->type);
|
rb_bug("rb_hash_method_definition: unsupported method type (%d)\n", def->type);
|
||||||
}
|
}
|
||||||
@ -1116,11 +1165,11 @@ rb_alias(VALUE klass, ID name, ID def)
|
|||||||
}
|
}
|
||||||
|
|
||||||
again:
|
again:
|
||||||
orig_me = search_method(klass, def, Qnil, 0);
|
orig_me = search_method(klass, def, 0);
|
||||||
|
|
||||||
if (UNDEFINED_METHOD_ENTRY_P(orig_me)) {
|
if (UNDEFINED_METHOD_ENTRY_P(orig_me)) {
|
||||||
if ((!RB_TYPE_P(klass, T_MODULE)) ||
|
if ((!RB_TYPE_P(klass, T_MODULE)) ||
|
||||||
(orig_me = search_method(rb_cObject, def, Qnil, 0),
|
(orig_me = search_method(rb_cObject, def, 0),
|
||||||
UNDEFINED_METHOD_ENTRY_P(orig_me))) {
|
UNDEFINED_METHOD_ENTRY_P(orig_me))) {
|
||||||
rb_print_undef(klass, def, 0);
|
rb_print_undef(klass, def, 0);
|
||||||
}
|
}
|
||||||
@ -1396,9 +1445,9 @@ rb_mod_modfunc(int argc, VALUE *argv, VALUE module)
|
|||||||
|
|
||||||
id = rb_to_id(argv[i]);
|
id = rb_to_id(argv[i]);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
me = search_method(m, id, Qnil, 0);
|
me = search_method(m, id, 0);
|
||||||
if (me == 0) {
|
if (me == 0) {
|
||||||
me = search_method(rb_cObject, id, Qnil, 0);
|
me = search_method(rb_cObject, id, 0);
|
||||||
}
|
}
|
||||||
if (UNDEFINED_METHOD_ENTRY_P(me)) {
|
if (UNDEFINED_METHOD_ENTRY_P(me)) {
|
||||||
rb_print_undef(module, id, 0);
|
rb_print_undef(module, id, 0);
|
||||||
@ -1511,21 +1560,6 @@ obj_respond_to_missing(VALUE obj, VALUE mid, VALUE priv)
|
|||||||
return Qfalse;
|
return Qfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
rb_redefine_opt_method(VALUE klass, ID mid)
|
|
||||||
{
|
|
||||||
st_data_t data;
|
|
||||||
rb_method_entry_t *me = 0;
|
|
||||||
VALUE origin = RCLASS_ORIGIN(klass);
|
|
||||||
|
|
||||||
if (!st_lookup(RCLASS_M_TBL(origin), mid, &data) ||
|
|
||||||
!(me = (rb_method_entry_t *)data) ||
|
|
||||||
(!me->def || me->def->type == VM_METHOD_TYPE_UNDEF)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rb_vm_check_redefinition_opt_method(me, origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Init_eval_method(void)
|
Init_eval_method(void)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user