summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog51
-rw-r--r--class.c16
-rw-r--r--cont.c1
-rw-r--r--eval.c12
-rw-r--r--gc.c7
-rw-r--r--internal.h3
-rw-r--r--method.h15
-rw-r--r--object.c5
-rw-r--r--proc.c4
-rw-r--r--test/ruby/test_refinement.rb39
-rw-r--r--vm.c2
-rw-r--r--vm_eval.c46
-rw-r--r--vm_insnhelper.c92
-rw-r--r--vm_method.c333
14 files changed, 445 insertions, 181 deletions
diff --git a/ChangeLog b/ChangeLog
index 7830b4cfd2..3dd8e9a9bd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,54 @@
+Thu Dec 6 18:23:05 2012 Shugo Maeda <shugo@ruby-lang.org>
+
+ * revised r37993 to avoid SEGV/ILL in tests. In r37993, a method
+ entry with VM_METHOD_TYPE_REFINED holds only the original method
+ definition, so ci->me is set to a method entry allocated in the
+ stack, and it causes SEGV/ILL. In this commit, a method entry
+ with VM_METHOD_TYPE_REFINED holds the whole original method entry.
+ Furthermore, rb_thread_mark() is changed to mark cfp->klass to
+ avoid GC for iclasses created by copy_refinement_iclass().
+
+ * 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_me) 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.
+
+ * vm.c (rb_thread_mark): mark cfp->klass for iclasses created by
+ copy_refinement_iclass().
+
+ * vm.c (Init_VM), cont.c (fiber_init): initialize th->cfp->klass.
+
+ * 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 Dec 6 17:29:03 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
* parse.y (parser_here_document): flush string content between new
diff --git a/class.c b/class.c
index e083bf633d..0aa7561aa6 100644
--- a/class.c
+++ b/class.c
@@ -678,6 +678,13 @@ rb_include_module(VALUE klass, VALUE module)
}
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
include_modules_at(VALUE klass, VALUE c, VALUE module)
{
VALUE p;
@@ -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));
+ 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)
changed = 1;
skip:
@@ -738,7 +752,7 @@ rb_prepend_module(VALUE klass, VALUE module)
RCLASS_SUPER(klass) = origin;
RCLASS_ORIGIN(klass) = origin;
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);
if (changed < 0)
diff --git a/cont.c b/cont.c
index 084ea3edc6..01800a6016 100644
--- a/cont.c
+++ b/cont.c
@@ -1067,6 +1067,7 @@ fiber_init(VALUE fibval, VALUE proc)
th->cfp->ep = th->stack;
*th->cfp->ep = VM_ENVVAL_BLOCK_PTR(0);
th->cfp->self = Qnil;
+ th->cfp->klass = Qnil;
th->cfp->flag = 0;
th->cfp->iseq = 0;
th->cfp->proc = 0;
diff --git a/eval.c b/eval.c
index bcc461e3e4..9d9821a20e 100644
--- a/eval.c
+++ b/eval.c
@@ -1187,6 +1187,14 @@ rb_mod_using(VALUE self, VALUE module)
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
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);
VALUE result = rb_mod_include(argc, argv, module);
NODE *cref;
- ID id_refined_class;
VALUE klass, c;
- CONST_ID(id_refined_class, "__refined_class__");
- klass = rb_attr_get(module, id_refined_class);
+ klass = rb_refinement_module_get_refined_class(module);
while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) {
if (RUBY_VM_NORMAL_ISEQ_P(cfp->iseq) &&
(cref = rb_vm_get_cref(cfp->iseq, cfp->ep)) &&
diff --git a/gc.c b/gc.c
index beaadddb54..4c5e2b48a8 100644
--- a/gc.c
+++ b/gc.c
@@ -2410,6 +2410,7 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
const rb_method_definition_t *def = me->def;
gc_mark(objspace, me->klass);
+ again:
if (!def) return;
switch (def->type) {
case VM_METHOD_TYPE_ISEQ:
@@ -2422,6 +2423,12 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
case VM_METHOD_TYPE_IVAR:
gc_mark(objspace, def->body.attr.location);
break;
+ case VM_METHOD_TYPE_REFINED:
+ if (def->body.orig_me) {
+ def = def->body.orig_me->def;
+ goto again;
+ }
+ break;
default:
break; /* ignore */
}
diff --git a/internal.h b/internal.h
index 8165545813..ced8aead82 100644
--- a/internal.h
+++ b/internal.h
@@ -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_class_name(VALUE x);
+/* eval.c */
+VALUE rb_refinement_module_get_refined_class(VALUE module);
+
/* eval_error.c */
void ruby_error_print(void);
VALUE rb_get_backtrace(VALUE info);
diff --git a/method.h b/method.h
index a87fc5c1ba..b6d20916fb 100644
--- a/method.h
+++ b/method.h
@@ -42,7 +42,8 @@ typedef enum {
VM_METHOD_TYPE_NOTIMPLEMENTED,
VM_METHOD_TYPE_OPTIMIZED, /* Kernel#send, Proc#call, etc */
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;
struct rb_call_info_struct;
@@ -72,6 +73,7 @@ typedef struct rb_method_definition_struct {
OPTIMIZED_METHOD_TYPE_SEND,
OPTIMIZED_METHOD_TYPE_CALL
} optimize_type;
+ struct rb_method_entry_struct *orig_me;
} body;
int alias_count;
} rb_method_definition_t;
@@ -94,9 +96,14 @@ 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);
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_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, VALUE refinements, 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,
+ VALUE *defined_class_ptr);
+rb_method_entry_t *rb_method_entry_with_refinements(VALUE klass, ID id,
+ VALUE *defined_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_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);
diff --git a/object.c b/object.c
index 02bbdb086b..7fdc0e2d02 100644
--- a/object.c
+++ b/object.c
@@ -1338,7 +1338,7 @@ rb_obj_cmp(VALUE obj1, VALUE obj2)
static VALUE
rb_mod_to_s(VALUE klass)
{
- ID id_refined_class, id_defined_at;
+ ID id_defined_at;
VALUE refined_class, defined_at;
if (FL_TEST(klass, FL_SINGLETON)) {
@@ -1358,8 +1358,7 @@ rb_mod_to_s(VALUE klass)
return s;
}
- CONST_ID(id_refined_class, "__refined_class__");
- refined_class = rb_attr_get(klass, id_refined_class);
+ refined_class = rb_refinement_module_get_refined_class(klass);
if (!NIL_P(refined_class)) {
VALUE s = rb_usascii_str_new2("#<refinement:");
diff --git a/proc.c b/proc.c
index 5b13ba9482..9c5f810a79 100644
--- a/proc.c
+++ b/proc.c
@@ -917,7 +917,7 @@ mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope)
rb_method_flag_t flag = NOEX_UNDEF;
again:
- me = rb_method_entry(klass, id, &defined_class);
+ me = rb_method_entry_with_refinements(klass, id, &defined_class);
if (UNDEFINED_METHOD_ENTRY_P(me)) {
ID rmiss = rb_intern("respond_to_missing?");
VALUE sym = ID2SYM(id);
@@ -1683,6 +1683,8 @@ rb_method_entry_arity(const rb_method_entry_t *me)
default:
break;
}
+ case VM_METHOD_TYPE_REFINED:
+ return -1;
}
}
rb_bug("rb_method_entry_arity: invalid method entry type (%d)", def->type);
diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb
index 3ca00ed912..380119fb9b 100644
--- a/test/ruby/test_refinement.rb
+++ b/test/ruby/test_refinement.rb
@@ -379,10 +379,15 @@ class TestRefinement < Test::Unit::TestCase
def test_refine_module_with_overriding
m1 = Module.new {
def foo
- [:m1]
+ super << :m1
end
}
- c = Class.new {
+ c0 = Class.new {
+ def foo
+ [:c0]
+ end
+ }
+ c = Class.new(c0) {
include m1
}
m2 = Module.new {
@@ -393,7 +398,7 @@ class TestRefinement < Test::Unit::TestCase
end
}
obj = c.new
- assert_equal([:m1, :m2], m2.module_eval { obj.foo })
+ assert_equal([:c0, :m1, :m2], m2.module_eval { obj.foo })
end
def test_refine_module_with_double_overriding
@@ -726,7 +731,6 @@ class TestRefinement < Test::Unit::TestCase
end
def test_inline_method_cache
- skip "can't implement efficiently with the current implementation of refinements"
c = InlineMethodCache::C.new
f = Proc.new { c.foo }
assert_equal("original", f.call)
@@ -822,4 +826,31 @@ class TestRefinement < Test::Unit::TestCase
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
diff --git a/vm.c b/vm.c
index b59044d5e4..52f34c332f 100644
--- a/vm.c
+++ b/vm.c
@@ -1686,6 +1686,7 @@ rb_thread_mark(void *ptr)
rb_iseq_t *iseq = cfp->iseq;
rb_gc_mark(cfp->proc);
rb_gc_mark(cfp->self);
+ rb_gc_mark(cfp->klass);
if (iseq) {
rb_gc_mark(RUBY_VM_NORMAL_ISEQ_P(iseq) ? iseq->self : (VALUE)iseq);
}
@@ -2223,6 +2224,7 @@ Init_VM(void)
th->cfp->iseq = iseq;
th->cfp->pc = iseq->iseq_encoded;
th->cfp->self = th->top_self;
+ th->cfp->klass = Qnil;
/*
* The Binding of the top level scope
diff --git a/vm_eval.c b/vm_eval.c
index 50b44b66ca..ea2455c691 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -179,7 +179,14 @@ vm_call0_body(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv)
case VM_METHOD_TYPE_BMETHOD:
return vm_call_bmethod_body(th, ci, argv);
case VM_METHOD_TYPE_ZSUPER:
+ case VM_METHOD_TYPE_REFINED:
{
+ if (ci->me->def->type == VM_METHOD_TYPE_REFINED &&
+ ci->me->def->body.orig_me) {
+ ci->me = ci->me->def->body.orig_me;
+ goto again;
+ }
+
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))) {
@@ -274,8 +281,7 @@ stack_check(void)
}
static inline rb_method_entry_t *
- rb_search_method_entry(VALUE refinements, VALUE recv, ID mid,
- VALUE *defined_class_ptr);
+ rb_search_method_entry(VALUE recv, ID mid, 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);
#define NOEX_OK NOEX_NOSUPER
@@ -296,11 +302,11 @@ static inline int rb_method_call_status(rb_thread_t *th, const rb_method_entry_t
*/
static inline VALUE
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;
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();
int call_status = rb_method_call_status(th, me, scope, self);
@@ -364,7 +370,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, th->cfp->self);
if (call_status != NOEX_OK) {
if (rb_method_basic_definition_p(klass, idMethodMissing)) {
@@ -429,8 +435,7 @@ rb_type_str(enum ruby_value_type type)
}
static inline rb_method_entry_t *
-rb_search_method_entry(VALUE refinements, VALUE recv, ID mid,
- VALUE *defined_class_ptr)
+rb_search_method_entry(VALUE recv, ID mid, VALUE *defined_class_ptr)
{
VALUE klass = CLASS_OF(recv);
@@ -469,8 +474,7 @@ rb_search_method_entry(VALUE refinements, VALUE recv, ID mid,
rb_id2name(mid), type, (void *)recv, flags, klass);
}
}
- return rb_method_entry_get_with_refinements(refinements, klass, mid,
- defined_class_ptr);
+ return rb_method_entry(klass, mid, defined_class_ptr);
}
static inline int
@@ -532,7 +536,7 @@ static inline VALUE
rb_call(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope)
{
rb_thread_t *th = GET_THREAD();
- return rb_call0(recv, mid, argc, argv, scope, th->cfp->self, Qnil);
+ return rb_call0(recv, mid, argc, argv, scope, th->cfp->self);
}
NORETURN(static void raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv,
@@ -785,11 +789,23 @@ rb_funcall_passing_block_with_refinements(VALUE recv, ID mid, int argc,
const VALUE *argv,
VALUE refinements)
{
- rb_thread_t *th = GET_THREAD();
+ VALUE defined_class;
+ rb_method_entry_t *me =
+ rb_search_method_entry(recv, mid, &defined_class);
+ rb_thread_t *th;
+ int call_status;
- PASS_PASSED_BLOCK_TH(th);
- return rb_call0(recv, mid, argc, argv, CALL_PUBLIC, th->cfp->self,
- refinements);
+ if (me && me->def->type == VM_METHOD_TYPE_REFINED) {
+ me = rb_resolve_refined_method(refinements, me, &defined_class);
+ }
+ PASS_PASSED_BLOCK_TH(GET_THREAD());
+ th = GET_THREAD();
+ call_status = rb_method_call_status(th, me, CALL_PUBLIC, th->cfp->self);
+ 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
@@ -823,7 +839,7 @@ send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope)
id = rb_to_id(vid);
}
PASS_PASSED_BLOCK_TH(th);
- return rb_call0(recv, id, argc, argv, scope, self, Qnil);
+ return rb_call0(recv, id, argc, argv, scope, self);
}
/*
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 75da83e525..7d57b792b9 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -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);
}
+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
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);
}
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 = &ci_temp;
@@ -1778,6 +1822,42 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci)
break;
case VM_METHOD_TYPE_UNDEF:
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 *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_me) {
+ ci->me = ci->me->def->body.orig_me;
+ goto normal_method_dispatch;
+ }
+ else {
+ goto zsuper_method_dispatch;
+ }
+ }
}
rb_bug("vm_call_method: unsupported method type (%d)", ci->me->def->type);
}
@@ -1841,6 +1921,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);
}
+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 */
static inline VALUE
@@ -1929,7 +2015,7 @@ vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_inf
/* TODO: use inline cache */
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) {
iseq = iseq->parent_iseq;
@@ -1937,7 +2023,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) {
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);
}
}
diff --git a/vm_method.c b/vm_method.c
index 4b4be139a7..2db6000691 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -4,7 +4,7 @@
#define CACHE_SIZE 0x800
#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
#ifndef NOEX_NOREDEF
@@ -21,7 +21,6 @@ struct cache_entry { /* method hash table. */
VALUE filled_version; /* filled state version */
ID mid; /* method's id */
VALUE klass; /* receiver's class */
- VALUE refinements; /* refinements */
rb_method_entry_t *me;
VALUE defined_class;
};
@@ -148,26 +147,78 @@ rb_sweep_method_entry(void *pvm)
}
}
-void
-rb_free_method_entry(rb_method_entry_t *me)
+static void
+release_method_definition(rb_method_definition_t *def)
{
- rb_method_definition_t *def = me->def;
-
- if (def) {
- if (def->alias_count == 0) {
- xfree(def);
- }
- else if (def->alias_count > 0) {
- def->alias_count--;
+ if (def == 0)
+ return;
+ if (def->alias_count == 0) {
+ if (def->type == VM_METHOD_TYPE_REFINED &&
+ def->body.orig_me) {
+ release_method_definition(def->body.orig_me->def);
+ xfree(def->body.orig_me);
}
- me->def = 0;
+ xfree(def);
}
+ else if (def->alias_count > 0) {
+ def->alias_count--;
+ }
+}
+
+void
+rb_free_method_entry(rb_method_entry_t *me)
+{
+ release_method_definition(me->def);
xfree(me);
}
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_me = ALLOC(rb_method_entry_t);
+ *new_def->body.orig_me = *me;
+ 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 *
rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
@@ -179,6 +230,7 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
#endif
st_table *mtbl;
st_data_t data;
+ int make_refined = 0;
if (NIL_P(klass)) {
klass = rb_cObject;
@@ -201,14 +253,19 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
rklass = klass;
#endif
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__");
- refined_class = rb_ivar_get(klass, id_refined_class);
- rb_redefine_opt_method(refined_class, mid);
+ rb_add_refined_method_entry(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);
/* check re-definition */
@@ -224,6 +281,8 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
}
#endif
rb_vm_check_redefinition_opt_method(old_me, klass);
+ if (old_def->type == VM_METHOD_TYPE_REFINED)
+ make_refined = 1;
if (RTEST(ruby_verbose) &&
type != VM_METHOD_TYPE_UNDEF &&
@@ -276,6 +335,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);
return me;
@@ -343,7 +406,12 @@ rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *opts, rb_method_
int line;
rb_method_entry_t *me = rb_method_entry_make(klass, mid, type, 0, noex);
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_me->def = def;
+ }
+ else {
+ me->def = def;
+ }
def->type = type;
def->original_id = mid;
def->alias_count = 0;
@@ -381,10 +449,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_UNDEF:
break;
+ case VM_METHOD_TYPE_REFINED:
+ def->body.orig_me = (rb_method_entry_t *) opts;
+ break;
default:
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);
}
return me;
@@ -427,86 +498,18 @@ rb_get_alloc_func(VALUE klass)
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*
-search_method_with_refinements(VALUE klass, ID id, VALUE refinements,
- VALUE *defined_class_ptr)
+search_method(VALUE klass, ID id, VALUE *defined_class_ptr)
{
- st_data_t body;
- VALUE iclass, skipped_class = Qnil;
-
- for (body = 0; klass; klass = RCLASS_SUPER(klass)) {
- if (klass != skipped_class) {
- 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)
- *defined_class_ptr = klass;
- return (rb_method_entry_t *)body;
-}
-
-static inline rb_method_entry_t*
-search_method_without_refinements(VALUE klass, ID id, VALUE *defined_class_ptr)
-{
- st_data_t body;
+ rb_method_entry_t *me;
- for (body = 0; klass; klass = RCLASS_SUPER(klass)) {
- if (lookup_method_table(klass, id, &body)) break;
+ for (me = 0; klass; klass = RCLASS_SUPER(klass)) {
+ if ((me = lookup_method_table(klass, id)) != 0) 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);
- }
+ return me;
}
/*
@@ -516,19 +519,17 @@ search_method(VALUE klass, ID id, VALUE refinements, VALUE *defined_class_ptr)
* rb_method_entry() simply.
*/
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;
- rb_method_entry_t *me = search_method(klass, id, refinements,
- &defined_class);
+ rb_method_entry_t *me = search_method(klass, id, &defined_class);
if (ruby_running) {
struct cache_entry *ent;
- ent = cache + EXPR1(klass, refinements, id);
+ ent = cache + EXPR1(klass, id);
ent->filled_version = GET_VM_STATE_VERSION();
ent->klass = klass;
- ent->refinements = refinements;
ent->defined_class = defined_class;
if (UNDEFINED_METHOD_ENTRY_P(me)) {
@@ -548,37 +549,84 @@ rb_method_entry_get_without_cache(VALUE klass, VALUE refinements, ID id,
}
rb_method_entry_t *
-rb_method_entry_get_with_refinements(VALUE refinements, VALUE klass, ID id,
- VALUE *defined_class_ptr)
+rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr)
{
#if OPT_GLOBAL_METHOD_CACHE
struct cache_entry *ent;
- ent = cache + EXPR1(klass, refinements, id);
+ ent = cache + EXPR1(klass, id);
if (ent->filled_version == GET_VM_STATE_VERSION() &&
- ent->mid == id && ent->klass == klass &&
- ent->refinements == refinements) {
+ ent->mid == id && ent->klass == klass) {
if (defined_class_ptr)
*defined_class_ptr = ent->defined_class;
return ent->me;
}
#endif
- return rb_method_entry_get_without_cache(klass, refinements, id,
+ return rb_method_entry_get_without_cache(klass, id, defined_class_ptr);
+}
+
+static rb_method_entry_t *
+get_original_method_entry(VALUE refinements,
+ rb_method_entry_t *me,
+ VALUE *defined_class_ptr)
+{
+ if (me->def->body.orig_me) {
+ return me->def->body.orig_me;
+ }
+ 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,
+ defined_class_ptr);
+ }
+}
+
+rb_method_entry_t *
+rb_resolve_refined_method(VALUE refinements, rb_method_entry_t *me,
+ VALUE *defined_class_ptr)
+{
+ if (me && me->def->type == VM_METHOD_TYPE_REFINED) {
+ VALUE refinement;
+ rb_method_entry_t *tmp_me;
+
+ refinement = find_refinement(refinements, me->klass);
+ if (NIL_P(refinement)) {
+ return get_original_method_entry(refinements, me,
+ 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,
defined_class_ptr);
+ }
+ }
+ else {
+ return me;
+ }
}
rb_method_entry_t *
-rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr)
+rb_method_entry_with_refinements(VALUE klass, ID id,
+ VALUE *defined_class_ptr)
{
- NODE *cref = rb_vm_cref();
- VALUE refinements = Qnil;
+ 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;
- if (cref && !NIL_P(cref->nd_refinements)) {
- refinements = cref->nd_refinements;
+ me = rb_resolve_refined_method(refinements, me, &defined_class);
}
- return rb_method_entry_get_with_refinements(refinements, klass, id,
- defined_class_ptr);
+ if (defined_class_ptr)
+ *defined_class_ptr = defined_class;
+ return me;
}
static void
@@ -677,9 +725,9 @@ rb_export_method(VALUE klass, ID name, rb_method_flag_t noex)
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)) {
- me = search_method(rb_cObject, name, Qnil, &defined_class);
+ me = search_method(rb_cObject, name, &defined_class);
}
if (UNDEFINED_METHOD_ENTRY_P(me)) {
@@ -691,6 +739,9 @@ rb_export_method(VALUE klass, ID name, rb_method_flag_t noex)
if (klass == defined_class) {
me->flag = noex;
+ if (me->def->type == VM_METHOD_TYPE_REFINED) {
+ me->def->body.orig_me->flag = noex;
+ }
}
else {
rb_add_method(klass, name, VM_METHOD_TYPE_ZSUPER, 0, noex);
@@ -701,7 +752,8 @@ rb_export_method(VALUE klass, ID name, rb_method_flag_t noex)
int
rb_method_boundp(VALUE klass, ID id, int ex)
{
- rb_method_entry_t *me = rb_method_entry(klass, id, 0);
+ rb_method_entry_t *me =
+ rb_method_entry_with_refinements(klass, id, 0);
if (me != 0) {
if ((ex & ~NOEX_RESPONDS) &&
@@ -767,9 +819,6 @@ void
rb_undef(VALUE klass, ID id)
{
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)) {
rb_raise(rb_eTypeError, "no class to undef method");
@@ -785,10 +834,7 @@ rb_undef(VALUE klass, ID id)
rb_warn("undefining `%s' may cause serious problems", rb_id2name(id));
}
- if (cref && !NIL_P(cref->nd_refinements)) {
- refinements = cref->nd_refinements;
- }
- me = search_method(klass, id, refinements, 0);
+ me = search_method(klass, id, 0);
if (UNDEFINED_METHOD_ENTRY_P(me)) {
const char *s0 = " class";
@@ -809,11 +855,6 @@ rb_undef(VALUE klass, ID id)
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);
CALL_METHOD_HOOK(klass, undefined, id);
@@ -1035,6 +1076,10 @@ rb_method_entry_eq(const rb_method_entry_t *m1, const rb_method_entry_t *m2)
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 = d1->body.orig_me->def;
+ if (d2 && d2->type == VM_METHOD_TYPE_REFINED)
+ d2 = d2->body.orig_me->def;
if (d1 == d2) return 1;
if (!d1 || !d2) return 0;
if (d1->type != d2->type) {
@@ -1069,6 +1114,7 @@ rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_defini
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);
switch (def->type) {
case VM_METHOD_TYPE_ISEQ:
@@ -1089,6 +1135,14 @@ rb_hash_method_definition(st_index_t hash, const rb_method_definition_t *def)
return hash;
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;
+ }
default:
rb_bug("rb_hash_method_definition: unsupported method type (%d)\n", def->type);
}
@@ -1118,11 +1172,11 @@ rb_alias(VALUE klass, ID name, ID def)
}
again:
- orig_me = search_method(klass, def, Qnil, 0);
+ orig_me = search_method(klass, def, 0);
if (UNDEFINED_METHOD_ENTRY_P(orig_me)) {
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))) {
rb_print_undef(klass, def, 0);
}
@@ -1398,9 +1452,9 @@ rb_mod_modfunc(int argc, VALUE *argv, VALUE module)
id = rb_to_id(argv[i]);
for (;;) {
- me = search_method(m, id, Qnil, 0);
+ me = search_method(m, id, 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)) {
rb_print_undef(module, id, 0);
@@ -1519,21 +1573,6 @@ obj_respond_to_missing(VALUE obj, VALUE mid, VALUE priv)
}
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
Init_eval_method(void)
{
#undef rb_intern