1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

merge revision(s) 53383,55366: [Backport #12478]

* lib/forwardable.rb (def_instance_delegator): adjust backtrace of
	  method body by tail call optimization.  adjusting the delegated
	  target is still done by deleting backtrace.

	* lib/forwardable.rb (def_single_delegator): ditto.

	* lib/forwardable.rb (Forwardable._delegator_method): extract
	  method generator and deal with non-module objects.
	  [ruby-dev:49656] [Bug #12478]


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_3@57570 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nagachika 2017-02-07 17:18:46 +00:00
parent 161f08ba0d
commit 1034b6d23d
4 changed files with 99 additions and 40 deletions

View file

@ -1,3 +1,17 @@
Wed Feb 8 02:17:02 2017 Nobuyoshi Nakada <nobu@ruby-lang.org>
* lib/forwardable.rb (Forwardable._delegator_method): extract
method generator and deal with non-module objects.
[ruby-dev:49656] [Bug #12478]
Wed Feb 8 02:17:02 2017 Nobuyoshi Nakada <nobu@ruby-lang.org>
* lib/forwardable.rb (def_instance_delegator): adjust backtrace of
method body by tail call optimization. adjusting the delegated
target is still done by deleting backtrace.
* lib/forwardable.rb (def_single_delegator): ditto.
Tue Jan 17 03:51:48 2017 Nobuyoshi Nakada <nobu@ruby-lang.org> Tue Jan 17 03:51:48 2017 Nobuyoshi Nakada <nobu@ruby-lang.org>
* compile.c (setup_args): duplicate splatting array if more * compile.c (setup_args): duplicate splatting array if more

View file

@ -178,33 +178,44 @@ module Forwardable
# q.push 23 #=> NoMethodError # q.push 23 #=> NoMethodError
# #
def def_instance_delegator(accessor, method, ali = method) def def_instance_delegator(accessor, method, ali = method)
accessor = accessor.to_s gen = Forwardable._delegator_method(self, accessor, method, ali)
if method_defined?(accessor) || private_method_defined?(accessor)
accessor = "#{accessor}()"
end
line_no = __LINE__; str = %{
def #{ali}(*args, &block)
begin
#{accessor}.__send__(:#{method}, *args, &block)
rescue ::Exception
$@.delete_if{|s| ::Forwardable::FILE_REGEXP =~ s} unless ::Forwardable::debug
::Kernel::raise
end
end
}
# If it's not a class or module, it's an instance # If it's not a class or module, it's an instance
begin (Module === self ? self : singleton_class).module_eval(&gen)
module_eval(str, __FILE__, line_no)
rescue
instance_eval(str, __FILE__, line_no)
end
end end
alias delegate instance_delegate alias delegate instance_delegate
alias def_delegators def_instance_delegators alias def_delegators def_instance_delegators
alias def_delegator def_instance_delegator alias def_delegator def_instance_delegator
def self._delegator_method(obj, accessor, method, ali)
accessor = accessor.to_s unless Symbol === accessor
if Module === obj ?
obj.method_defined?(accessor) || obj.private_method_defined?(accessor) :
obj.respond_to?(accessor, true)
accessor = "#{accessor}()"
end
line_no = __LINE__+1; str = "#{<<-"begin;"}\n#{<<-"end;"}"
begin;
proc do
def #{ali}(*args, &block)
begin
#{accessor}
ensure
$@.delete_if {|s| ::Forwardable::FILE_REGEXP =~ s} if $@ and !::Forwardable::debug
end.__send__ :#{method}, *args, &block
end
end
end;
RubyVM::InstructionSequence
.compile(str, __FILE__, __FILE__, line_no,
trace_instruction: false,
tailcall_optimization: true)
.eval
end
end end
# SingleForwardable can be used to setup delegation at the object level as well. # SingleForwardable can be used to setup delegation at the object level as well.
@ -275,23 +286,9 @@ module SingleForwardable
# the method of the same name in _accessor_). If _new_name_ is # the method of the same name in _accessor_). If _new_name_ is
# provided, it is used as the name for the delegate method. # provided, it is used as the name for the delegate method.
def def_single_delegator(accessor, method, ali = method) def def_single_delegator(accessor, method, ali = method)
accessor = accessor.to_s gen = Forwardable._delegator_method(self, accessor, method, ali)
if method_defined?(accessor) || private_method_defined?(accessor)
accessor = "#{accessor}()"
end
line_no = __LINE__; str = %{ instance_eval(&gen)
def #{ali}(*args, &block)
begin
#{accessor}.__send__(:#{method}, *args, &block)
rescue ::Exception
$@.delete_if{|s| ::Forwardable::FILE_REGEXP =~ s} unless ::Forwardable::debug
::Kernel::raise
end
end
}
instance_eval(str, __FILE__, line_no)
end end
alias delegate single_delegate alias delegate single_delegate

View file

@ -144,7 +144,7 @@ class TestForwardable < Test::Unit::TestCase
end end
end end
def test_def_single_delegator def test_class_single_delegator
%i[def_delegator def_single_delegator].each do |m| %i[def_delegator def_single_delegator].each do |m|
cls = single_forwardable_class do cls = single_forwardable_class do
__send__ m, :@receiver, :delegated1 __send__ m, :@receiver, :delegated1
@ -154,7 +154,7 @@ class TestForwardable < Test::Unit::TestCase
end end
end end
def test_def_single_delegators def test_class_single_delegators
%i[def_delegators def_single_delegators].each do |m| %i[def_delegators def_single_delegators].each do |m|
cls = single_forwardable_class do cls = single_forwardable_class do
__send__ m, :@receiver, :delegated1, :delegated2 __send__ m, :@receiver, :delegated1, :delegated2
@ -165,7 +165,7 @@ class TestForwardable < Test::Unit::TestCase
end end
end end
def test_single_delegate def test_class_single_delegate
%i[delegate single_delegate].each do |m| %i[delegate single_delegate].each do |m|
cls = single_forwardable_class do cls = single_forwardable_class do
__send__ m, delegated1: :@receiver, delegated2: :@receiver __send__ m, delegated1: :@receiver, delegated2: :@receiver
@ -183,10 +183,50 @@ class TestForwardable < Test::Unit::TestCase
end end
end end
def test_obj_single_delegator
%i[def_delegator def_single_delegator].each do |m|
obj = single_forwardable_object do
__send__ m, :@receiver, :delegated1
end
assert_same RETURNED1, obj.delegated1
end
end
def test_obj_single_delegators
%i[def_delegators def_single_delegators].each do |m|
obj = single_forwardable_object do
__send__ m, :@receiver, :delegated1, :delegated2
end
assert_same RETURNED1, obj.delegated1
assert_same RETURNED2, obj.delegated2
end
end
def test_obj_single_delegate
%i[delegate single_delegate].each do |m|
obj = single_forwardable_object do
__send__ m, delegated1: :@receiver, delegated2: :@receiver
end
assert_same RETURNED1, obj.delegated1
assert_same RETURNED2, obj.delegated2
obj = single_forwardable_object do
__send__ m, %i[delegated1 delegated2] => :@receiver
end
assert_same RETURNED1, obj.delegated1
assert_same RETURNED2, obj.delegated2
end
end
class Foo class Foo
extend Forwardable extend Forwardable
def_delegator :bar, :baz def_delegator :bar, :baz
def_delegator :caller, :itself, :c
class Exception class Exception
end end
@ -197,6 +237,7 @@ class TestForwardable < Test::Unit::TestCase
Foo.new.baz Foo.new.baz
} }
assert_not_match(/\/forwardable\.rb/, e.backtrace[0]) assert_not_match(/\/forwardable\.rb/, e.backtrace[0])
assert_equal(caller(0, 1)[0], Foo.new.c[0])
end end
class Foo2 < BasicObject class Foo2 < BasicObject
@ -245,4 +286,11 @@ class TestForwardable < Test::Unit::TestCase
class_exec(&block) class_exec(&block)
end end
end end
def single_forwardable_object(&block)
obj = Object.new.extend SingleForwardable
obj.instance_variable_set(:@receiver, RECEIVER)
obj.instance_eval(&block)
obj
end
end end

View file

@ -1,6 +1,6 @@
#define RUBY_VERSION "2.3.3" #define RUBY_VERSION "2.3.3"
#define RUBY_RELEASE_DATE "2017-02-08" #define RUBY_RELEASE_DATE "2017-02-08"
#define RUBY_PATCHLEVEL 242 #define RUBY_PATCHLEVEL 243
#define RUBY_RELEASE_YEAR 2017 #define RUBY_RELEASE_YEAR 2017
#define RUBY_RELEASE_MONTH 2 #define RUBY_RELEASE_MONTH 2