mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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
This commit is contained in:
parent
2210709b79
commit
2a4c87dc31
5 changed files with 117 additions and 15 deletions
1
method.h
1
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_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(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_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);
|
const rb_method_entry_t *rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
|
||||||
RUBY_SYMBOL_EXPORT_BEGIN
|
RUBY_SYMBOL_EXPORT_BEGIN
|
||||||
|
|
8
proc.c
8
proc.c
|
@ -1413,7 +1413,7 @@ mnew_internal(const rb_method_entry_t *me, VALUE klass, VALUE iclass,
|
||||||
if (me->defined_class) {
|
if (me->defined_class) {
|
||||||
VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->defined_class));
|
VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->defined_class));
|
||||||
id = me->def->original_id;
|
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 {
|
else {
|
||||||
VALUE klass = RCLASS_SUPER(me->owner);
|
VALUE klass = RCLASS_SUPER(me->owner);
|
||||||
|
@ -1448,10 +1448,10 @@ mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope)
|
||||||
VALUE iclass = Qnil;
|
VALUE iclass = Qnil;
|
||||||
|
|
||||||
if (obj == Qundef) { /* UnboundMethod */
|
if (obj == Qundef) { /* UnboundMethod */
|
||||||
me = rb_method_entry_without_refinements(klass, id, &iclass);
|
me = rb_method_entry_with_refinements(klass, id, &iclass);
|
||||||
}
|
}
|
||||||
else {
|
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);
|
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));
|
super_class = RCLASS_SUPER(RCLASS_ORIGIN(iclass));
|
||||||
mid = data->me->called_id;
|
mid = data->me->called_id;
|
||||||
if (!super_class) return Qnil;
|
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;
|
if (!me) return Qnil;
|
||||||
return mnew_internal(me, me->owner, iclass, data->recv, mid, rb_obj_class(method), FALSE, FALSE);
|
return mnew_internal(me, me->owner, iclass, data->recv, mid, rb_obj_class(method), FALSE, FALSE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -526,6 +526,7 @@ describe "Module#refine" do
|
||||||
result.should == "hello from refinement"
|
result.should == "hello from refinement"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ruby_version_is "" ... "2.7" do
|
||||||
it "is not honored by Kernel#method" do
|
it "is not honored by Kernel#method" do
|
||||||
klass = Class.new
|
klass = Class.new
|
||||||
refinement = Module.new do
|
refinement = Module.new do
|
||||||
|
@ -541,6 +542,63 @@ describe "Module#refine" do
|
||||||
end
|
end
|
||||||
}.should raise_error(NameError, /undefined method `foo'/)
|
}.should raise_error(NameError, /undefined method `foo'/)
|
||||||
end
|
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.new.method(:foo).class
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
ruby_version_is "" ... "2.6" do
|
||||||
it "is not honored by Kernel#respond_to?" do
|
it "is not honored by Kernel#respond_to?" do
|
||||||
|
|
|
@ -118,6 +118,10 @@ class TestRefinement < Test::Unit::TestCase
|
||||||
return foo.method(:z)
|
return foo.method(:z)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.instance_method_z(foo)
|
||||||
|
return foo.class.instance_method(:z)
|
||||||
|
end
|
||||||
|
|
||||||
def self.invoke_call_x_on(foo)
|
def self.invoke_call_x_on(foo)
|
||||||
return foo.call_x
|
return foo.call_x
|
||||||
end
|
end
|
||||||
|
@ -213,11 +217,44 @@ class TestRefinement < Test::Unit::TestCase
|
||||||
assert_raise(NoMethodError) { FooExtClient.public_send_b_on(foo) }
|
assert_raise(NoMethodError) { FooExtClient.public_send_b_on(foo) }
|
||||||
end
|
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
|
foo = Foo.new
|
||||||
assert_raise(NameError) { foo.method(:z) }
|
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_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
|
end
|
||||||
|
|
||||||
def test_no_local_rebinding
|
def test_no_local_rebinding
|
||||||
|
|
|
@ -899,6 +899,12 @@ method_entry_resolve_refinement(VALUE klass, ID id, int with_refinement, VALUE *
|
||||||
return me;
|
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 *
|
MJIT_FUNC_EXPORTED const rb_callable_method_entry_t *
|
||||||
rb_callable_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class_ptr)
|
rb_callable_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class_ptr)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue