mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* fix the behavior when a module is included into a refinement.
This change is a little tricky, so it might be better to prohibit module inclusion to refinements. * include/ruby/ruby.h (RMODULE_INCLUDED_INTO_REFINEMENT): new flag to represent that a module (iclass) is included into a refinement. * class.c (include_modules_at): set RMODULE_INCLUDED_INTO_REFINEMENT if klass is a refinement. * eval.c (rb_mod_refine): set the superclass of a refinement to the refined class for super. * eval.c (rb_using_refinement): skip the above superclass (the refined class) when creating iclasses for refinements. Otherwise, `using Refinement1; using Refinement2' creates iclasses: <Refinement2> -> <RefinedClass> -> <Refinement1> -> RefinedClass, where <Module> is an iclass for Module, so RefinedClass is searched before Refinement1. The correct iclasses should be <Refinement2> -> <Refinement1> -> RefinedClass. * vm_insnhelper.c (vm_search_normal_superclass): if klass is an iclass for a refinement, use the refinement's superclass instead of the iclass's superclass. Otherwise, multiple refinements are searched by super. For example, if a refinement Refinement2 includes a module M (i.e., Refinement2 -> <M> -> RefinedClass, and if refinements iclasses are <Refinement2> -> <M>' -> <Refinement1> -> RefinedClass, then super in <Refinement2> should use Refinement2's superclass <M> instead of <Refinement2>'s superclass <M>'. * vm_insnhelper.c (vm_search_super_method): do not raise a NotImplementError if current_defind_class is a module included into a refinement. Because of the change of vm_search_normal_superclass(), the receiver might not be an instance of the module('s iclass). * test/ruby/test_refinement.rb: related test. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38298 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
fa7c4ab408
commit
7ef16d224a
6 changed files with 132 additions and 7 deletions
41
ChangeLog
41
ChangeLog
|
@ -1,3 +1,44 @@
|
||||||
|
Tue Dec 11 00:26:58 2012 Shugo Maeda <shugo@ruby-lang.org>
|
||||||
|
|
||||||
|
* fix the behavior when a module is included into a refinement.
|
||||||
|
This change is a little tricky, so it might be better to prohibit
|
||||||
|
module inclusion to refinements.
|
||||||
|
|
||||||
|
* include/ruby/ruby.h (RMODULE_INCLUDED_INTO_REFINEMENT): new flag
|
||||||
|
to represent that a module (iclass) is included into a refinement.
|
||||||
|
|
||||||
|
* class.c (include_modules_at): set RMODULE_INCLUDED_INTO_REFINEMENT
|
||||||
|
if klass is a refinement.
|
||||||
|
|
||||||
|
* eval.c (rb_mod_refine): set the superclass of a refinement to the
|
||||||
|
refined class for super.
|
||||||
|
|
||||||
|
* eval.c (rb_using_refinement): skip the above superclass (the
|
||||||
|
refined class) when creating iclasses for refinements. Otherwise,
|
||||||
|
`using Refinement1; using Refinement2' creates iclasses:
|
||||||
|
<Refinement2> -> <RefinedClass> -> <Refinement1> -> RefinedClass,
|
||||||
|
where <Module> is an iclass for Module, so RefinedClass is
|
||||||
|
searched before Refinement1. The correct iclasses should be
|
||||||
|
<Refinement2> -> <Refinement1> -> RefinedClass.
|
||||||
|
|
||||||
|
* vm_insnhelper.c (vm_search_normal_superclass): if klass is an
|
||||||
|
iclass for a refinement, use the refinement's superclass instead
|
||||||
|
of the iclass's superclass. Otherwise, multiple refinements are
|
||||||
|
searched by super. For example, if a refinement Refinement2
|
||||||
|
includes a module M (i.e., Refinement2 -> <M> -> RefinedClass,
|
||||||
|
and if refinements iclasses are <Refinement2> -> <M>' ->
|
||||||
|
<Refinement1> -> RefinedClass, then super in <Refinement2> should
|
||||||
|
use Refinement2's superclass <M> instead of <Refinement2>'s
|
||||||
|
superclass <M>'.
|
||||||
|
|
||||||
|
* vm_insnhelper.c (vm_search_super_method): do not raise a
|
||||||
|
NotImplementError if current_defind_class is a module included
|
||||||
|
into a refinement. Because of the change of
|
||||||
|
vm_search_normal_superclass(), the receiver might not be an
|
||||||
|
instance of the module('s iclass).
|
||||||
|
|
||||||
|
* test/ruby/test_refinement.rb: related test.
|
||||||
|
|
||||||
Mon Dec 10 18:35:25 2012 Shugo Maeda <shugo@ruby-lang.org>
|
Mon Dec 10 18:35:25 2012 Shugo Maeda <shugo@ruby-lang.org>
|
||||||
|
|
||||||
* vm_method.c (rb_method_entry_without_refinements): use
|
* vm_method.c (rb_method_entry_without_refinements): use
|
||||||
|
|
1
class.c
1
class.c
|
@ -720,6 +720,7 @@ include_modules_at(VALUE klass, VALUE c, VALUE module)
|
||||||
|
|
||||||
st_foreach(RMODULE_M_TBL(module), add_refined_method_entry_i,
|
st_foreach(RMODULE_M_TBL(module), add_refined_method_entry_i,
|
||||||
(st_data_t) refined_class);
|
(st_data_t) refined_class);
|
||||||
|
FL_SET(c, RMODULE_INCLUDED_INTO_REFINEMENT);
|
||||||
}
|
}
|
||||||
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;
|
||||||
|
|
3
eval.c
3
eval.c
|
@ -1079,7 +1079,7 @@ rb_using_refinement(NODE *cref, VALUE klass, VALUE module)
|
||||||
c = iclass = rb_include_class_new(module, superclass);
|
c = iclass = rb_include_class_new(module, superclass);
|
||||||
RCLASS_REFINED_CLASS(c) = klass;
|
RCLASS_REFINED_CLASS(c) = klass;
|
||||||
module = RCLASS_SUPER(module);
|
module = RCLASS_SUPER(module);
|
||||||
while (module) {
|
while (module && module != klass) {
|
||||||
FL_SET(module, RMODULE_IS_OVERLAID);
|
FL_SET(module, RMODULE_IS_OVERLAID);
|
||||||
c = RCLASS_SUPER(c) = rb_include_class_new(module, RCLASS_SUPER(c));
|
c = RCLASS_SUPER(c) = rb_include_class_new(module, RCLASS_SUPER(c));
|
||||||
RCLASS_REFINED_CLASS(c) = klass;
|
RCLASS_REFINED_CLASS(c) = klass;
|
||||||
|
@ -1193,6 +1193,7 @@ rb_mod_refine(VALUE module, VALUE klass)
|
||||||
refinement = rb_hash_lookup(refinements, klass);
|
refinement = rb_hash_lookup(refinements, klass);
|
||||||
if (NIL_P(refinement)) {
|
if (NIL_P(refinement)) {
|
||||||
refinement = rb_module_new();
|
refinement = rb_module_new();
|
||||||
|
RCLASS_SUPER(refinement) = klass;
|
||||||
FL_SET(refinement, RMODULE_IS_REFINEMENT);
|
FL_SET(refinement, RMODULE_IS_REFINEMENT);
|
||||||
CONST_ID(id_refined_class, "__refined_class__");
|
CONST_ID(id_refined_class, "__refined_class__");
|
||||||
rb_ivar_set(refinement, id_refined_class, klass);
|
rb_ivar_set(refinement, id_refined_class, klass);
|
||||||
|
|
|
@ -737,6 +737,7 @@ struct RClass {
|
||||||
#define RMODULE_SUPER(m) RCLASS_SUPER(m)
|
#define RMODULE_SUPER(m) RCLASS_SUPER(m)
|
||||||
#define RMODULE_IS_OVERLAID FL_USER2
|
#define RMODULE_IS_OVERLAID FL_USER2
|
||||||
#define RMODULE_IS_REFINEMENT FL_USER3
|
#define RMODULE_IS_REFINEMENT FL_USER3
|
||||||
|
#define RMODULE_INCLUDED_INTO_REFINEMENT FL_USER4
|
||||||
|
|
||||||
struct RFloat {
|
struct RFloat {
|
||||||
struct RBasic basic;
|
struct RBasic basic;
|
||||||
|
|
|
@ -13,6 +13,10 @@ class TestRefinement < Test::Unit::TestCase
|
||||||
return "Foo#y"
|
return "Foo#y"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def a
|
||||||
|
return "Foo#a"
|
||||||
|
end
|
||||||
|
|
||||||
def call_x
|
def call_x
|
||||||
return x
|
return x
|
||||||
end
|
end
|
||||||
|
@ -31,6 +35,10 @@ class TestRefinement < Test::Unit::TestCase
|
||||||
def z
|
def z
|
||||||
return "FooExt#z"
|
return "FooExt#z"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def a
|
||||||
|
return "FooExt#a"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -98,6 +106,10 @@ class TestRefinement < Test::Unit::TestCase
|
||||||
def self.invoke_y_on(foo)
|
def self.invoke_y_on(foo)
|
||||||
return foo.y
|
return foo.y
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.invoke_a_on(foo)
|
||||||
|
return foo.a
|
||||||
|
end
|
||||||
end
|
end
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
@ -122,6 +134,13 @@ class TestRefinement < Test::Unit::TestCase
|
||||||
assert_equal("Foo#y", foo.y)
|
assert_equal("Foo#y", foo.y)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_using_same_class_refinements
|
||||||
|
foo = Foo.new
|
||||||
|
assert_equal("Foo#a", foo.a)
|
||||||
|
assert_equal("FooExt#a", FooExtClient2.invoke_a_on(foo))
|
||||||
|
assert_equal("Foo#a", foo.a)
|
||||||
|
end
|
||||||
|
|
||||||
def test_new_method
|
def test_new_method
|
||||||
foo = Foo.new
|
foo = Foo.new
|
||||||
assert_raise(NoMethodError) { foo.z }
|
assert_raise(NoMethodError) { foo.z }
|
||||||
|
@ -610,6 +629,69 @@ class TestRefinement < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module IncludeIntoRefinement
|
||||||
|
class C
|
||||||
|
def bar
|
||||||
|
return "C#bar"
|
||||||
|
end
|
||||||
|
|
||||||
|
def baz
|
||||||
|
return "C#baz"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Mixin
|
||||||
|
def foo
|
||||||
|
return "Mixin#foo"
|
||||||
|
end
|
||||||
|
|
||||||
|
def bar
|
||||||
|
return super << " Mixin#bar"
|
||||||
|
end
|
||||||
|
|
||||||
|
def baz
|
||||||
|
return super << " Mixin#baz"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module M
|
||||||
|
refine C do
|
||||||
|
include Mixin
|
||||||
|
|
||||||
|
def baz
|
||||||
|
return super << " M#baz"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
eval <<-EOF, TOPLEVEL_BINDING
|
||||||
|
using TestRefinement::IncludeIntoRefinement::M
|
||||||
|
|
||||||
|
module TestRefinement::IncludeIntoRefinement::User
|
||||||
|
def self.invoke_foo_on(x)
|
||||||
|
x.foo
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.invoke_bar_on(x)
|
||||||
|
x.bar
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.invoke_baz_on(x)
|
||||||
|
x.baz
|
||||||
|
end
|
||||||
|
end
|
||||||
|
EOF
|
||||||
|
|
||||||
|
def test_include_into_refinement
|
||||||
|
x = IncludeIntoRefinement::C.new
|
||||||
|
assert_equal("Mixin#foo", IncludeIntoRefinement::User.invoke_foo_on(x))
|
||||||
|
assert_equal("C#bar Mixin#bar",
|
||||||
|
IncludeIntoRefinement::User.invoke_bar_on(x))
|
||||||
|
assert_equal("C#baz Mixin#baz M#baz",
|
||||||
|
IncludeIntoRefinement::User.invoke_baz_on(x))
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def eval_using(mod, s)
|
def eval_using(mod, s)
|
||||||
|
|
|
@ -1866,12 +1866,10 @@ vm_search_normal_superclass(VALUE klass)
|
||||||
{
|
{
|
||||||
if (BUILTIN_TYPE(klass) == T_ICLASS &&
|
if (BUILTIN_TYPE(klass) == T_ICLASS &&
|
||||||
FL_TEST(RBASIC(klass)->klass, RMODULE_IS_REFINEMENT)) {
|
FL_TEST(RBASIC(klass)->klass, RMODULE_IS_REFINEMENT)) {
|
||||||
return rb_refinement_module_get_refined_class(RBASIC(klass)->klass);
|
klass = RBASIC(klass)->klass;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
klass = RCLASS_ORIGIN(klass);
|
klass = RCLASS_ORIGIN(klass);
|
||||||
return RCLASS_SUPER(klass);
|
return RCLASS_SUPER(klass);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1945,7 +1943,8 @@ vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_inf
|
||||||
current_defind_class = RCLASS_REFINED_CLASS(current_defind_class);
|
current_defind_class = RCLASS_REFINED_CLASS(current_defind_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rb_obj_is_kind_of(ci->recv, current_defind_class)) {
|
if (!FL_TEST(current_defind_class, RMODULE_INCLUDED_INTO_REFINEMENT) &&
|
||||||
|
!rb_obj_is_kind_of(ci->recv, current_defind_class)) {
|
||||||
rb_raise(rb_eNotImpError, "super from singleton method that is defined to multiple classes is not supported; this will be fixed in 2.0.0 or later");
|
rb_raise(rb_eNotImpError, "super from singleton method that is defined to multiple classes is not supported; this will be fixed in 2.0.0 or later");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue