ruby-changes:54718
From: nobu <ko1@a...>
Date: Mon, 28 Jan 2019 20:45:28 +0900 (JST)
Subject: [ruby-changes:54718] nobu:r66935 (trunk): Add refinements support to method/instance_method.
nobu 2019-01-28 20:45:21 +0900 (Mon, 28 Jan 2019) New Revision: 66935 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=66935 Log: Add refinements support to method/instance_method. [Fix GH-2034] From: manga_osyo <manga.osyo@g...> Modified files: trunk/method.h trunk/proc.c trunk/spec/ruby/core/module/refine_spec.rb trunk/test/ruby/test_refinement.rb trunk/vm_method.c Index: test/ruby/test_refinement.rb =================================================================== --- test/ruby/test_refinement.rb (revision 66934) +++ test/ruby/test_refinement.rb (revision 66935) @@ -118,6 +118,10 @@ class TestRefinement < Test::Unit::TestC https://github.com/ruby/ruby/blob/trunk/test/ruby/test_refinement.rb#L118 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::TestC https://github.com/ruby/ruby/blob/trunk/test/ruby/test_refinement.rb#L217 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 Index: proc.c =================================================================== --- proc.c (revision 66934) +++ proc.c (revision 66935) @@ -1413,7 +1413,7 @@ mnew_internal(const rb_method_entry_t *m https://github.com/ruby/ruby/blob/trunk/proc.c#L1413 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, VALU https://github.com/ruby/ruby/blob/trunk/proc.c#L1448 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) https://github.com/ruby/ruby/blob/trunk/proc.c#L2766 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); } Index: method.h =================================================================== --- method.h (revision 66934) +++ method.h (revision 66935) @@ -196,6 +196,7 @@ rb_method_entry_t *rb_method_entry_creat https://github.com/ruby/ruby/blob/trunk/method.h#L196 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 Index: vm_method.c =================================================================== --- vm_method.c (revision 66934) +++ vm_method.c (revision 66935) @@ -899,6 +899,12 @@ method_entry_resolve_refinement(VALUE kl https://github.com/ruby/ruby/blob/trunk/vm_method.c#L899 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) { Index: spec/ruby/core/module/refine_spec.rb =================================================================== --- spec/ruby/core/module/refine_spec.rb (revision 66934) +++ spec/ruby/core/module/refine_spec.rb (revision 66935) @@ -526,20 +526,78 @@ describe "Module#refine" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/module/refine_spec.rb#L526 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 + 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 - klass.new.method(:foo) + result = klass.instance_method(:foo).class end - }.should raise_error(NameError, /undefined method `foo'/) + + result.should == UnboundMethod + end end ruby_version_is "" ... "2.6" do -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/