summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2019-01-28 11:45:21 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2019-01-28 11:45:21 +0000
commit2a4c87dc31b3774b8f8f2ab7b74f6d864f41a804 (patch)
tree7ebced0774e05f5a00f78f93d94e15c5d7b202e1
parent2210709b79222ecd2d9758f7316fdbd7c068ea84 (diff)
Add refinements support to method/instance_method.
[Fix GH-2034] From: manga_osyo <manga.osyo@gmail.com> git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66935 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--method.h1
-rw-r--r--proc.c8
-rw-r--r--spec/ruby/core/module/refine_spec.rb74
-rw-r--r--test/ruby/test_refinement.rb41
-rw-r--r--vm_method.c6
5 files changed, 116 insertions, 14 deletions
diff --git a/method.h b/method.h
index 771e77e889..fdf234c8b4 100644
--- a/method.h
+++ b/method.h
@@ -196,6 +196,7 @@ rb_method_entry_t *rb_method_entry_create(ID called_id, VALUE klass, rb_method_v
const rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id);
const rb_method_entry_t *rb_method_entry(VALUE klass, ID id);
+const rb_method_entry_t *rb_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class);
const rb_method_entry_t *rb_method_entry_without_refinements(VALUE klass, ID id, VALUE *defined_class);
const rb_method_entry_t *rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
RUBY_SYMBOL_EXPORT_BEGIN
diff --git a/proc.c b/proc.c
index 6241bf32ec..76728f280f 100644
--- a/proc.c
+++ b/proc.c
@@ -1413,7 +1413,7 @@ mnew_internal(const rb_method_entry_t *me, VALUE klass, VALUE iclass,
if (me->defined_class) {
VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->defined_class));
id = me->def->original_id;
- me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(klass, id, &iclass);
+ me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass);
}
else {
VALUE klass = RCLASS_SUPER(me->owner);
@@ -1448,10 +1448,10 @@ mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope)
VALUE iclass = Qnil;
if (obj == Qundef) { /* UnboundMethod */
- me = rb_method_entry_without_refinements(klass, id, &iclass);
+ me = rb_method_entry_with_refinements(klass, id, &iclass);
}
else {
- me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(klass, id, &iclass);
+ me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass);
}
return mnew_from_me(me, klass, iclass, obj, id, mclass, scope);
}
@@ -2766,7 +2766,7 @@ method_super_method(VALUE method)
super_class = RCLASS_SUPER(RCLASS_ORIGIN(iclass));
mid = data->me->called_id;
if (!super_class) return Qnil;
- me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(super_class, mid, &iclass);
+ me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(super_class, mid, &iclass);
if (!me) return Qnil;
return mnew_internal(me, me->owner, iclass, data->recv, mid, rb_obj_class(method), FALSE, FALSE);
}
diff --git a/spec/ruby/core/module/refine_spec.rb b/spec/ruby/core/module/refine_spec.rb
index 662d49205d..42b2b64331 100644
--- a/spec/ruby/core/module/refine_spec.rb
+++ b/spec/ruby/core/module/refine_spec.rb
@@ -526,20 +526,78 @@ describe "Module#refine" do
result.should == "hello from refinement"
end
- it "is not honored by Kernel#method" do
- klass = Class.new
- refinement = Module.new do
- refine klass do
- def foo; end
+ ruby_version_is "" ... "2.7" do
+ it "is not honored by Kernel#method" do
+ klass = Class.new
+ refinement = Module.new do
+ refine klass do
+ def foo; end
+ end
end
+
+ -> {
+ Module.new do
+ using refinement
+ klass.new.method(:foo)
+ end
+ }.should raise_error(NameError, /undefined method `foo'/)
end
+ end
- -> {
+ ruby_version_is "2.7" do
+ it "is honored by Kernel#method" do
+ klass = Class.new
+ refinement = Module.new do
+ refine klass do
+ def foo; end
+ end
+ end
+
+ result = nil
Module.new do
using refinement
- klass.new.method(:foo)
+ result = klass.new.method(:foo).class
end
- }.should raise_error(NameError, /undefined method `foo'/)
+
+ result.should == Method
+ end
+ end
+
+ ruby_version_is "" ... "2.7" do
+ it "is not honored by Kernel#instance_method" do
+ klass = Class.new
+ refinement = Module.new do
+ refine klass do
+ def foo; end
+ end
+ end
+
+ -> {
+ Module.new do
+ using refinement
+ klass.instance_method(:foo)
+ end
+ }.should raise_error(NameError, /undefined method `foo'/)
+ end
+ end
+
+ ruby_version_is "2.7" do
+ it "is honored by Kernel#method" do
+ klass = Class.new
+ refinement = Module.new do
+ refine klass do
+ def foo; end
+ end
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = klass.instance_method(:foo).class
+ end
+
+ result.should == UnboundMethod
+ end
end
ruby_version_is "" ... "2.6" do
diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb
index 5c06266d6e..ba571670cf 100644
--- a/test/ruby/test_refinement.rb
+++ b/test/ruby/test_refinement.rb
@@ -118,6 +118,10 @@ class TestRefinement < Test::Unit::TestCase
return foo.method(:z)
end
+ def self.instance_method_z(foo)
+ return foo.class.instance_method(:z)
+ end
+
def self.invoke_call_x_on(foo)
return foo.call_x
end
@@ -213,11 +217,44 @@ class TestRefinement < Test::Unit::TestCase
assert_raise(NoMethodError) { FooExtClient.public_send_b_on(foo) }
end
- def test_method_should_not_use_refinements
+ module MethodIntegerPowEx
+ refine Integer do
+ def pow(*)
+ :refine_pow
+ end
+ end
+ end
+ def test_method_should_use_refinements
foo = Foo.new
assert_raise(NameError) { foo.method(:z) }
- assert_raise(NameError) { FooExtClient.method_z(foo) }
+ assert_equal("FooExt#z", FooExtClient.method_z(foo).call)
assert_raise(NameError) { foo.method(:z) }
+ assert_equal(8, eval(<<~EOS, Sandbox::BINDING))
+ meth = 2.method(:pow)
+ using MethodIntegerPowEx
+ meth.call(3)
+ EOS
+ assert_equal(:refine_pow, eval_using(MethodIntegerPowEx, "2.pow(3)"))
+ end
+
+ module InstanceMethodIntegerPowEx
+ refine Integer do
+ def abs
+ :refine_abs
+ end
+ end
+ end
+ def test_instance_method_should_use_refinements
+ foo = Foo.new
+ assert_raise(NameError) { Foo.instance_method(:z) }
+ assert_equal("FooExt#z", FooExtClient.instance_method_z(foo).bind(foo).call)
+ assert_raise(NameError) { Foo.instance_method(:z) }
+ assert_equal(4, eval(<<~EOS, Sandbox::BINDING))
+ meth = Integer.instance_method(:abs)
+ using InstanceMethodIntegerPowEx
+ meth.bind(-4).call
+ EOS
+ assert_equal(:refine_abs, eval_using(InstanceMethodIntegerPowEx, "Integer.instance_method(:abs).bind(-4).call"))
end
def test_no_local_rebinding
diff --git a/vm_method.c b/vm_method.c
index 3333ea16b4..76fdb2069f 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -899,6 +899,12 @@ method_entry_resolve_refinement(VALUE klass, ID id, int with_refinement, VALUE *
return me;
}
+const rb_method_entry_t *
+rb_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class_ptr)
+{
+ return method_entry_resolve_refinement(klass, id, TRUE, defined_class_ptr);
+}
+
MJIT_FUNC_EXPORTED const rb_callable_method_entry_t *
rb_callable_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class_ptr)
{