2012-08-02 07:34:19 -04:00
|
|
|
require 'test/unit'
|
2012-11-10 23:45:31 -05:00
|
|
|
require_relative 'envutil'
|
2012-08-02 07:34:19 -04:00
|
|
|
|
2012-12-06 09:31:43 -05:00
|
|
|
require "refinement"
|
|
|
|
|
2012-08-02 07:34:19 -04:00
|
|
|
class TestRefinement < Test::Unit::TestCase
|
|
|
|
class Foo
|
|
|
|
def x
|
|
|
|
return "Foo#x"
|
|
|
|
end
|
|
|
|
|
|
|
|
def y
|
|
|
|
return "Foo#y"
|
|
|
|
end
|
|
|
|
|
|
|
|
def call_x
|
|
|
|
return x
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module FooExt
|
|
|
|
refine Foo do
|
|
|
|
def x
|
|
|
|
return "FooExt#x"
|
|
|
|
end
|
|
|
|
|
|
|
|
def y
|
|
|
|
return "FooExt#y " + super
|
|
|
|
end
|
|
|
|
|
|
|
|
def z
|
|
|
|
return "FooExt#z"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module FooExt2
|
|
|
|
refine Foo do
|
|
|
|
def x
|
|
|
|
return "FooExt2#x"
|
|
|
|
end
|
|
|
|
|
|
|
|
def y
|
|
|
|
return "FooExt2#y " + super
|
|
|
|
end
|
|
|
|
|
|
|
|
def z
|
|
|
|
return "FooExt2#z"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class FooSub < Foo
|
|
|
|
def x
|
|
|
|
return "FooSub#x"
|
|
|
|
end
|
|
|
|
|
|
|
|
def y
|
|
|
|
return "FooSub#y " + super
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class FooExtClient
|
|
|
|
using FooExt
|
|
|
|
|
|
|
|
def self.invoke_x_on(foo)
|
|
|
|
return foo.x
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.invoke_y_on(foo)
|
|
|
|
return foo.y
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.invoke_z_on(foo)
|
|
|
|
return foo.z
|
|
|
|
end
|
|
|
|
|
2012-11-02 20:09:26 -04:00
|
|
|
def self.send_z_on(foo)
|
|
|
|
return foo.send(:z)
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.method_z(foo)
|
|
|
|
return foo.method(:z)
|
|
|
|
end
|
|
|
|
|
2012-08-02 07:34:19 -04:00
|
|
|
def self.invoke_call_x_on(foo)
|
|
|
|
return foo.call_x
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class FooExtClient2
|
|
|
|
using FooExt
|
|
|
|
using FooExt2
|
|
|
|
|
|
|
|
def self.invoke_y_on(foo)
|
|
|
|
return foo.y
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_override
|
|
|
|
foo = Foo.new
|
|
|
|
assert_equal("Foo#x", foo.x)
|
|
|
|
assert_equal("FooExt#x", FooExtClient.invoke_x_on(foo))
|
|
|
|
assert_equal("Foo#x", foo.x)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_super
|
|
|
|
foo = Foo.new
|
|
|
|
assert_equal("Foo#y", foo.y)
|
|
|
|
assert_equal("FooExt#y Foo#y", FooExtClient.invoke_y_on(foo))
|
|
|
|
assert_equal("Foo#y", foo.y)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_super_chain
|
|
|
|
foo = Foo.new
|
|
|
|
assert_equal("Foo#y", foo.y)
|
|
|
|
assert_equal("FooExt2#y FooExt#y Foo#y", FooExtClient2.invoke_y_on(foo))
|
|
|
|
assert_equal("Foo#y", foo.y)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_new_method
|
|
|
|
foo = Foo.new
|
|
|
|
assert_raise(NoMethodError) { foo.z }
|
|
|
|
assert_equal("FooExt#z", FooExtClient.invoke_z_on(foo))
|
|
|
|
assert_raise(NoMethodError) { foo.z }
|
|
|
|
end
|
|
|
|
|
2012-11-02 20:09:26 -04:00
|
|
|
def test_new_method_by_send
|
|
|
|
foo = Foo.new
|
|
|
|
assert_raise(NoMethodError) { foo.send(:z) }
|
|
|
|
assert_equal("FooExt#z", FooExtClient.send_z_on(foo))
|
|
|
|
assert_raise(NoMethodError) { foo.send(:z) }
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_new_method_by_method_object
|
|
|
|
foo = Foo.new
|
|
|
|
assert_raise(NoMethodError) { foo.send(:z) }
|
|
|
|
assert_equal("FooExt#z", FooExtClient.method_z(foo).call)
|
|
|
|
assert_raise(NoMethodError) { foo.send(:z) }
|
|
|
|
end
|
|
|
|
|
2012-08-02 07:34:19 -04:00
|
|
|
def test_no_local_rebinding
|
|
|
|
foo = Foo.new
|
|
|
|
assert_equal("Foo#x", foo.call_x)
|
|
|
|
assert_equal("Foo#x", FooExtClient.invoke_call_x_on(foo))
|
|
|
|
assert_equal("Foo#x", foo.call_x)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_subclass_is_prior
|
|
|
|
sub = FooSub.new
|
|
|
|
assert_equal("FooSub#x", sub.x)
|
|
|
|
assert_equal("FooSub#x", FooExtClient.invoke_x_on(sub))
|
|
|
|
assert_equal("FooSub#x", sub.x)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_super_in_subclass
|
|
|
|
sub = FooSub.new
|
|
|
|
assert_equal("FooSub#y Foo#y", sub.y)
|
|
|
|
# not "FooSub#y FooExt#y Foo#y"
|
|
|
|
assert_equal("FooSub#y Foo#y", FooExtClient.invoke_y_on(sub))
|
|
|
|
assert_equal("FooSub#y Foo#y", sub.y)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_new_method_on_subclass
|
|
|
|
sub = FooSub.new
|
|
|
|
assert_raise(NoMethodError) { sub.z }
|
|
|
|
assert_equal("FooExt#z", FooExtClient.invoke_z_on(sub))
|
|
|
|
assert_raise(NoMethodError) { sub.z }
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_module_eval
|
|
|
|
foo = Foo.new
|
|
|
|
assert_equal("Foo#x", foo.x)
|
|
|
|
assert_equal("FooExt#x", FooExt.module_eval { foo.x })
|
|
|
|
assert_equal("Foo#x", foo.x)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_instance_eval
|
|
|
|
foo = Foo.new
|
|
|
|
ext_client = FooExtClient.new
|
|
|
|
assert_equal("Foo#x", foo.x)
|
|
|
|
assert_equal("FooExt#x", ext_client.instance_eval { foo.x })
|
|
|
|
assert_equal("Foo#x", foo.x)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_override_builtin_method
|
|
|
|
m = Module.new {
|
|
|
|
refine Fixnum do
|
|
|
|
def /(other) quo(other) end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
assert_equal(0, 1 / 2)
|
|
|
|
assert_equal(Rational(1, 2), m.module_eval { 1 / 2 })
|
|
|
|
assert_equal(0, 1 / 2)
|
|
|
|
end
|
|
|
|
|
2012-11-06 23:09:51 -05:00
|
|
|
def test_override_builtin_method_with_method_added
|
|
|
|
m = Module.new {
|
|
|
|
refine Fixnum do
|
|
|
|
def self.method_added(*args); end
|
|
|
|
def +(other) "overriden" end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
assert_equal(3, 1 + 2)
|
|
|
|
assert_equal("overriden", m.module_eval { 1 + 2 })
|
|
|
|
assert_equal(3, 1 + 2)
|
|
|
|
end
|
|
|
|
|
2012-08-02 07:34:19 -04:00
|
|
|
def test_return_value_of_refine
|
|
|
|
mod = nil
|
|
|
|
result = nil
|
|
|
|
m = Module.new {
|
|
|
|
result = refine(Object) {
|
|
|
|
mod = self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert_equal mod, result
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_refine_same_class_twice
|
|
|
|
result1 = nil
|
|
|
|
result2 = nil
|
|
|
|
result3 = nil
|
|
|
|
m = Module.new {
|
|
|
|
result1 = refine(Fixnum) {
|
|
|
|
def foo; return "foo" end
|
|
|
|
}
|
|
|
|
result2 = refine(Fixnum) {
|
|
|
|
def bar; return "bar" end
|
|
|
|
}
|
|
|
|
result3 = refine(String) {
|
|
|
|
def baz; return "baz" end
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert_equal("foo", m.module_eval { 1.foo })
|
|
|
|
assert_equal("bar", m.module_eval { 1.bar })
|
|
|
|
assert_equal(result1, result2)
|
|
|
|
assert_not_equal(result1, result3)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_respond_to?
|
|
|
|
m = Module.new {
|
|
|
|
refine Fixnum do
|
|
|
|
def foo; "foo"; end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
assert_equal(false, 1.respond_to?(:foo))
|
|
|
|
assert_equal(true, m.module_eval { 1.respond_to?(:foo) })
|
|
|
|
assert_equal(false, 1.respond_to?(:foo))
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_builtin_method_no_local_rebinding
|
|
|
|
m = Module.new {
|
|
|
|
refine String do
|
|
|
|
def <=>(other) return 0 end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
assert_equal(false, m.module_eval { "1" >= "2" })
|
|
|
|
|
|
|
|
m2 = Module.new {
|
|
|
|
refine Array do
|
|
|
|
def each
|
|
|
|
super do |i|
|
|
|
|
yield 2 * i
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
a = [1, 2, 3]
|
|
|
|
assert_equal(1, m2.module_eval { a.min })
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_module_inclusion
|
|
|
|
m1 = Module.new {
|
|
|
|
def foo
|
|
|
|
"m1#foo"
|
|
|
|
end
|
|
|
|
|
|
|
|
def bar
|
|
|
|
"m1#bar"
|
|
|
|
end
|
|
|
|
}
|
|
|
|
m2 = Module.new {
|
|
|
|
def bar
|
|
|
|
"m2#bar"
|
|
|
|
end
|
|
|
|
|
|
|
|
def baz
|
|
|
|
"m2#baz"
|
|
|
|
end
|
|
|
|
}
|
|
|
|
m3 = Module.new {
|
|
|
|
def baz
|
|
|
|
"m3#baz"
|
|
|
|
end
|
|
|
|
}
|
|
|
|
include_proc = Proc.new {
|
|
|
|
include m3, m2
|
|
|
|
}
|
|
|
|
m = Module.new {
|
|
|
|
refine String do
|
|
|
|
include m1
|
|
|
|
module_eval(&include_proc)
|
|
|
|
|
|
|
|
def call_foo
|
|
|
|
foo
|
|
|
|
end
|
|
|
|
|
|
|
|
def call_bar
|
|
|
|
bar
|
|
|
|
end
|
|
|
|
|
|
|
|
def call_baz
|
|
|
|
baz
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.call_foo(s)
|
|
|
|
s.foo
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.call_bar(s)
|
|
|
|
s.bar
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.call_baz(s)
|
|
|
|
s.baz
|
|
|
|
end
|
|
|
|
}
|
|
|
|
assert_equal("m1#foo", m.module_eval { "abc".foo })
|
|
|
|
assert_equal("m2#bar", m.module_eval { "abc".bar })
|
|
|
|
assert_equal("m3#baz", m.module_eval { "abc".baz })
|
|
|
|
assert_equal("m1#foo", m.module_eval { "abc".call_foo })
|
|
|
|
assert_equal("m2#bar", m.module_eval { "abc".call_bar })
|
|
|
|
assert_equal("m3#baz", m.module_eval { "abc".call_baz })
|
|
|
|
assert_equal("m1#foo", m.call_foo("abc"))
|
|
|
|
assert_equal("m2#bar", m.call_bar("abc"))
|
|
|
|
assert_equal("m3#baz", m.call_baz("abc"))
|
|
|
|
end
|
2012-08-06 22:44:24 -04:00
|
|
|
|
|
|
|
def test_refine_prepended_class
|
|
|
|
m1 = Module.new {
|
|
|
|
def foo
|
|
|
|
super << :m1
|
|
|
|
end
|
|
|
|
}
|
|
|
|
c = Class.new {
|
|
|
|
prepend m1
|
|
|
|
|
|
|
|
def foo
|
|
|
|
[:c]
|
|
|
|
end
|
|
|
|
}
|
|
|
|
m2 = Module.new {
|
|
|
|
refine c do
|
|
|
|
def foo
|
|
|
|
super << :m2
|
|
|
|
end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
obj = c.new
|
|
|
|
assert_equal([:c, :m1, :m2], m2.module_eval { obj.foo })
|
|
|
|
end
|
2012-08-02 07:34:19 -04:00
|
|
|
|
2012-09-27 05:53:24 -04:00
|
|
|
def test_refine_module_without_overriding
|
|
|
|
m1 = Module.new
|
|
|
|
c = Class.new {
|
|
|
|
include m1
|
|
|
|
}
|
|
|
|
m2 = Module.new {
|
|
|
|
refine m1 do
|
|
|
|
def foo
|
|
|
|
:m2
|
|
|
|
end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
obj = c.new
|
|
|
|
assert_equal(:m2, m2.module_eval { obj.foo })
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_refine_module_with_overriding
|
|
|
|
m1 = Module.new {
|
|
|
|
def foo
|
* 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.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38236 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-06 08:08:41 -05:00
|
|
|
super << :m1
|
2012-09-27 05:53:24 -04:00
|
|
|
end
|
|
|
|
}
|
* 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.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38236 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-06 08:08:41 -05:00
|
|
|
c0 = Class.new {
|
|
|
|
def foo
|
|
|
|
[:c0]
|
|
|
|
end
|
|
|
|
}
|
|
|
|
c = Class.new(c0) {
|
2012-09-27 05:53:24 -04:00
|
|
|
include m1
|
|
|
|
}
|
|
|
|
m2 = Module.new {
|
|
|
|
refine m1 do
|
|
|
|
def foo
|
|
|
|
super << :m2
|
|
|
|
end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
obj = c.new
|
* 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.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38236 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-06 08:08:41 -05:00
|
|
|
assert_equal([:c0, :m1, :m2], m2.module_eval { obj.foo })
|
2012-09-27 05:53:24 -04:00
|
|
|
end
|
|
|
|
|
2012-09-28 22:21:27 -04:00
|
|
|
def test_refine_module_with_double_overriding
|
|
|
|
m1 = Module.new {
|
|
|
|
def foo
|
|
|
|
[:m1]
|
|
|
|
end
|
|
|
|
}
|
|
|
|
c = Class.new {
|
|
|
|
include m1
|
|
|
|
}
|
|
|
|
m2 = Module.new {
|
|
|
|
refine m1 do
|
|
|
|
def foo
|
|
|
|
super << :m2
|
|
|
|
end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
m3 = Module.new {
|
|
|
|
using m2
|
|
|
|
refine m1 do
|
|
|
|
def foo
|
|
|
|
super << :m3
|
|
|
|
end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
obj = c.new
|
|
|
|
assert_equal([:m1, :m2, :m3], m3.module_eval { obj.foo })
|
|
|
|
end
|
|
|
|
|
2012-09-28 02:48:20 -04:00
|
|
|
def test_refine_module_and_call_superclass_method
|
|
|
|
m1 = Module.new
|
|
|
|
c1 = Class.new {
|
|
|
|
def foo
|
|
|
|
"c1#foo"
|
|
|
|
end
|
|
|
|
}
|
|
|
|
c2 = Class.new(c1) {
|
|
|
|
include m1
|
|
|
|
}
|
|
|
|
m2 = Module.new {
|
|
|
|
refine m1 do
|
|
|
|
end
|
|
|
|
}
|
|
|
|
obj = c2.new
|
|
|
|
assert_equal("c1#foo", m2.module_eval { obj.foo })
|
|
|
|
end
|
|
|
|
|
2012-09-27 05:53:24 -04:00
|
|
|
def test_refine_neither_class_nor_module
|
|
|
|
assert_raise(TypeError) do
|
|
|
|
Module.new {
|
|
|
|
refine Object.new do
|
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
|
|
|
assert_raise(TypeError) do
|
|
|
|
Module.new {
|
|
|
|
refine 123 do
|
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
|
|
|
assert_raise(TypeError) do
|
|
|
|
Module.new {
|
|
|
|
refine "foo" do
|
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
2012-09-28 22:21:27 -04:00
|
|
|
|
|
|
|
def test_refine_in_class_and_class_eval
|
|
|
|
c = Class.new {
|
|
|
|
refine Fixnum do
|
|
|
|
def foo
|
|
|
|
"c"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
assert_equal("c", c.class_eval { 123.foo })
|
|
|
|
end
|
|
|
|
|
2012-11-11 01:14:16 -05:00
|
|
|
def test_main_using
|
2012-12-06 09:31:43 -05:00
|
|
|
assert_in_out_err([], <<-INPUT, %w(:C :M), /Refinements are experimental/)
|
|
|
|
require "refinement"
|
|
|
|
|
2012-11-11 01:14:16 -05:00
|
|
|
class C
|
|
|
|
def foo
|
|
|
|
:C
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module M
|
|
|
|
refine C do
|
|
|
|
def foo
|
|
|
|
:M
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
c = C.new
|
|
|
|
p c.foo
|
|
|
|
using M
|
|
|
|
p c.foo
|
|
|
|
INPUT
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_no_kernel_using
|
|
|
|
assert_raise(NoMethodError) do
|
|
|
|
using Module.new
|
2012-09-28 22:21:27 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_module_using_class
|
|
|
|
c = Class.new
|
|
|
|
m = Module.new
|
|
|
|
assert_raise(TypeError) do
|
|
|
|
m.module_eval do
|
|
|
|
using c
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2012-10-08 10:56:08 -04:00
|
|
|
|
|
|
|
def test_refinements_empty
|
|
|
|
m = Module.new
|
|
|
|
assert(m.refinements.empty?)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_refinements_one
|
|
|
|
c = Class.new
|
|
|
|
c_ext = nil
|
|
|
|
m = Module.new {
|
|
|
|
refine c do
|
|
|
|
c_ext = self
|
|
|
|
end
|
|
|
|
}
|
|
|
|
assert_equal({c => c_ext}, m.refinements)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_refinements_two
|
|
|
|
c1 = Class.new
|
|
|
|
c1_ext = nil
|
|
|
|
c2 = Class.new
|
|
|
|
c2_ext = nil
|
|
|
|
m = Module.new {
|
|
|
|
refine c1 do
|
|
|
|
c1_ext = self
|
|
|
|
end
|
|
|
|
|
|
|
|
refine c2 do
|
|
|
|
c2_ext = self
|
|
|
|
end
|
|
|
|
}
|
|
|
|
assert_equal({c1 => c1_ext, c2 => c2_ext}, m.refinements)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_refinements_duplicate_refine
|
|
|
|
c = Class.new
|
|
|
|
c_ext = nil
|
|
|
|
m = Module.new {
|
|
|
|
refine c do
|
|
|
|
c_ext = self
|
|
|
|
end
|
|
|
|
refine c do
|
|
|
|
end
|
|
|
|
}
|
|
|
|
assert_equal({c => c_ext}, m.refinements)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_refinements_no_recursion
|
|
|
|
c1 = Class.new
|
|
|
|
c1_ext = nil
|
|
|
|
m1 = Module.new {
|
|
|
|
refine c1 do
|
|
|
|
c1_ext = self
|
|
|
|
end
|
|
|
|
}
|
|
|
|
c2 = Class.new
|
|
|
|
c2_ext = nil
|
|
|
|
m2 = Module.new {
|
|
|
|
using m1
|
|
|
|
refine c2 do
|
|
|
|
c2_ext = self
|
|
|
|
end
|
|
|
|
}
|
|
|
|
assert_equal({c2 => c2_ext}, m2.refinements)
|
|
|
|
end
|
2012-10-30 10:58:47 -04:00
|
|
|
|
|
|
|
def test_refine_without_block
|
|
|
|
c1 = Class.new
|
|
|
|
e = assert_raise(ArgumentError) {
|
|
|
|
Module.new do
|
|
|
|
refine c1
|
|
|
|
end
|
|
|
|
}
|
|
|
|
assert_equal("no block given", e.message)
|
|
|
|
end
|
2012-11-01 01:45:28 -04:00
|
|
|
|
|
|
|
module IndirectUsing
|
|
|
|
class C
|
|
|
|
end
|
|
|
|
|
|
|
|
module M1
|
|
|
|
refine C do
|
|
|
|
def m1
|
|
|
|
:m1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module M2
|
|
|
|
refine C do
|
|
|
|
def m2
|
|
|
|
:m2
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module M3
|
|
|
|
using M1
|
|
|
|
using M2
|
|
|
|
end
|
|
|
|
|
|
|
|
module M
|
|
|
|
using M3
|
|
|
|
|
|
|
|
def self.call_m1
|
|
|
|
C.new.m1
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.call_m2
|
|
|
|
C.new.m2
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_indirect_using
|
|
|
|
assert_equal(:m1, IndirectUsing::M.call_m1)
|
|
|
|
assert_equal(:m2, IndirectUsing::M.call_m2)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_indirect_using_module_eval
|
|
|
|
c = Class.new
|
|
|
|
m1 = Module.new {
|
|
|
|
refine c do
|
|
|
|
def m1
|
|
|
|
:m1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
m2 = Module.new {
|
|
|
|
refine c do
|
|
|
|
def m2
|
|
|
|
:m2
|
|
|
|
end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
m3 = Module.new {
|
|
|
|
using m1
|
|
|
|
using m2
|
|
|
|
}
|
|
|
|
m = Module.new {
|
|
|
|
using m3
|
|
|
|
}
|
|
|
|
assert_equal(:m1, m.module_eval { c.new.m1 })
|
|
|
|
assert_equal(:m2, m.module_eval { c.new.m2 })
|
|
|
|
end
|
2012-11-02 01:48:29 -04:00
|
|
|
|
|
|
|
module SymbolToProc
|
|
|
|
class C
|
|
|
|
end
|
|
|
|
|
|
|
|
module M
|
|
|
|
refine C do
|
|
|
|
def foo
|
|
|
|
"foo"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.call_foo
|
|
|
|
c = C.new
|
|
|
|
:foo.to_proc.call(c)
|
|
|
|
end
|
2012-11-02 20:09:26 -04:00
|
|
|
|
|
|
|
def self.foo_proc
|
|
|
|
:foo.to_proc
|
|
|
|
end
|
2012-11-02 01:48:29 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_symbol_to_proc
|
|
|
|
assert_equal("foo", SymbolToProc::M.call_foo)
|
2012-11-02 20:09:26 -04:00
|
|
|
assert_equal("foo", SymbolToProc::M.foo_proc.call(SymbolToProc::C.new))
|
2012-11-02 01:48:29 -04:00
|
|
|
end
|
2012-11-02 04:53:06 -04:00
|
|
|
|
|
|
|
module Inspect
|
|
|
|
module M
|
|
|
|
refine Fixnum do
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_inspect
|
|
|
|
assert_equal("#<refinement:Fixnum@TestRefinement::Inspect::M>",
|
|
|
|
Inspect::M.refinements[Fixnum].inspect)
|
|
|
|
|
|
|
|
c = Class.new
|
|
|
|
m = Module.new {
|
|
|
|
refine String do
|
|
|
|
end
|
|
|
|
refine c do
|
|
|
|
end
|
|
|
|
}
|
|
|
|
assert_equal("#<refinement:String@#{m.inspect}>",
|
|
|
|
m.refinements[String].inspect)
|
|
|
|
assert_equal("#<refinement:#{c.inspect}@#{m.inspect}>",
|
|
|
|
m.refinements[c].inspect)
|
|
|
|
end
|
2012-11-10 21:42:04 -05:00
|
|
|
|
|
|
|
module InlineMethodCache
|
|
|
|
class C
|
|
|
|
def foo
|
|
|
|
"original"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module M
|
|
|
|
refine C do
|
|
|
|
def foo
|
|
|
|
"refined"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_inline_method_cache
|
|
|
|
c = InlineMethodCache::C.new
|
|
|
|
f = Proc.new { c.foo }
|
|
|
|
assert_equal("original", f.call)
|
|
|
|
assert_equal("refined", InlineMethodCache::M.module_eval(&f))
|
|
|
|
assert_equal("original", f.call)
|
|
|
|
end
|
2012-11-10 23:45:31 -05:00
|
|
|
|
|
|
|
module UsingMethodCache
|
|
|
|
class C
|
|
|
|
def foo
|
|
|
|
"original"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module M1
|
|
|
|
refine C do
|
|
|
|
def foo
|
|
|
|
"M1"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module M2
|
|
|
|
refine C do
|
|
|
|
def foo
|
|
|
|
"M2"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module M
|
|
|
|
c = C.new
|
|
|
|
ORIGINAL_FOO = c.foo
|
|
|
|
using M1
|
|
|
|
c.foo
|
|
|
|
using M2
|
|
|
|
M2_FOO = c.foo
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_using_method_cache
|
|
|
|
assert_equal("original", UsingMethodCache::M::ORIGINAL_FOO)
|
|
|
|
assert_equal("M2", UsingMethodCache::M::M2_FOO)
|
|
|
|
|
2012-12-06 09:31:43 -05:00
|
|
|
assert_in_out_err([], <<-INPUT, %w(:M1 :M2), /Refinements are experimental/)
|
|
|
|
require "refinement"
|
|
|
|
|
2012-11-10 23:45:31 -05:00
|
|
|
class C
|
|
|
|
def foo
|
|
|
|
"original"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module M1
|
|
|
|
refine C do
|
|
|
|
def foo
|
|
|
|
:M1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module M2
|
|
|
|
refine C do
|
|
|
|
def foo
|
|
|
|
:M2
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
c = C.new
|
|
|
|
using M1
|
|
|
|
p c.foo
|
|
|
|
using M2
|
|
|
|
p c.foo
|
|
|
|
INPUT
|
|
|
|
end
|
2012-11-13 04:05:18 -05:00
|
|
|
|
|
|
|
def test_circular_using_is_not_allowed
|
|
|
|
a = Module.new
|
|
|
|
b = Module.new
|
|
|
|
|
|
|
|
assert_raise ArgumentError do
|
|
|
|
a.module_eval do
|
|
|
|
using a
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
b.module_eval do
|
|
|
|
using a
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_raise ArgumentError do
|
|
|
|
a.module_eval do
|
|
|
|
using b
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
* 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.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38236 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-06 08:08:41 -05:00
|
|
|
|
|
|
|
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
|
2012-09-27 05:53:24 -04:00
|
|
|
end
|