summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorusa <usa@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-03-25 14:43:16 +0000
committerusa <usa@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-03-25 14:43:16 +0000
commita14ba6e3d58d6f63f47c0549f60306c4989478ba (patch)
tree0739b8c45e0eadbec011bd3dd4e5d2014dba7504
parentffb1a5d8017a5b331c26f2df985e8cf8fb0bf9e6 (diff)
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_2@58085 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog14
-rw-r--r--lib/forwardable.rb70
-rw-r--r--test/test_forwardable.rb73
-rw-r--r--version.h8
4 files changed, 122 insertions, 43 deletions
diff --git a/ChangeLog b/ChangeLog
index 41eb04b2b5..ff0a0f78f5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+Sat Mar 25 23:35:54 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]
+
+Sat Mar 25 23:35:54 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 Dec 27 20:43:54 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
* time.c (usec2subsecx): fix return type, which is a numeric object but
diff --git a/lib/forwardable.rb b/lib/forwardable.rb
index 602cd8b815..e78b9ed1e0 100644
--- a/lib/forwardable.rb
+++ b/lib/forwardable.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
#
# forwardable.rb -
# $Release Version: 1.1$
@@ -177,33 +178,44 @@ module Forwardable
# q.push 23 #=> NoMethodError
#
def def_instance_delegator(accessor, method, ali = method)
- accessor = accessor.to_s
- if method_defined?(accessor) || private_method_defined?(accessor)
- accessor = "#{accessor}()"
- end
+ gen = Forwardable._delegator_method(self, accessor, method, ali)
- 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
- begin
- module_eval(str, __FILE__, line_no)
- rescue
- instance_eval(str, __FILE__, line_no)
- end
-
+ (Module === self ? self : singleton_class).module_eval(&gen)
end
alias delegate instance_delegate
alias def_delegators def_instance_delegators
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
# SingleForwardable can be used to setup delegation at the object level as well.
@@ -274,23 +286,9 @@ module SingleForwardable
# the method of the same name in _accessor_). If _new_name_ is
# provided, it is used as the name for the delegate method.
def def_single_delegator(accessor, method, ali = method)
- accessor = accessor.to_s
- 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
- }
+ gen = Forwardable._delegator_method(self, accessor, method, ali)
- instance_eval(str, __FILE__, line_no)
+ instance_eval(&gen)
end
alias delegate single_delegate
diff --git a/test/test_forwardable.rb b/test/test_forwardable.rb
index 69372d6936..f678ca4fc7 100644
--- a/test/test_forwardable.rb
+++ b/test/test_forwardable.rb
@@ -144,7 +144,7 @@ class TestForwardable < Test::Unit::TestCase
end
end
- def test_def_single_delegator
+ def test_class_single_delegator
%i[def_delegator def_single_delegator].each do |m|
cls = single_forwardable_class do
__send__ m, :@receiver, :delegated1
@@ -154,7 +154,7 @@ class TestForwardable < Test::Unit::TestCase
end
end
- def test_def_single_delegators
+ def test_class_single_delegators
%i[def_delegators def_single_delegators].each do |m|
cls = single_forwardable_class do
__send__ m, :@receiver, :delegated1, :delegated2
@@ -165,7 +165,7 @@ class TestForwardable < Test::Unit::TestCase
end
end
- def test_single_delegate
+ def test_class_single_delegate
%i[delegate single_delegate].each do |m|
cls = single_forwardable_class do
__send__ m, delegated1: :@receiver, delegated2: :@receiver
@@ -183,6 +183,45 @@ class TestForwardable < Test::Unit::TestCase
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
extend Forwardable
@@ -193,6 +232,27 @@ class TestForwardable < Test::Unit::TestCase
end
end
+ def test_backtrace_adjustment
+ e = assert_raise(NameError) {
+ Foo.new.baz
+ }
+ assert_not_match(/\/forwardable\.rb/, e.backtrace[0])
+ assert_equal(caller(0, 1)[0], Foo.new.c[0])
+ end
+
+ class Foo2 < BasicObject
+ extend ::Forwardable
+
+ def_delegator :bar, :baz
+ end
+
+ def test_basicobject_subclass
+ bug11616 = '[ruby-core:71176] [Bug #11616]'
+ assert_raise_with_message(NameError, /`bar'/, bug11616) {
+ Foo2.new.baz
+ }
+ end
+
private
def forwardable_class(
@@ -226,4 +286,11 @@ class TestForwardable < Test::Unit::TestCase
class_exec(&block)
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
diff --git a/version.h b/version.h
index 0c463b26ee..0b56c5df73 100644
--- a/version.h
+++ b/version.h
@@ -1,10 +1,10 @@
#define RUBY_VERSION "2.2.7"
-#define RUBY_RELEASE_DATE "2017-01-04"
-#define RUBY_PATCHLEVEL 418
+#define RUBY_RELEASE_DATE "2017-03-25"
+#define RUBY_PATCHLEVEL 419
#define RUBY_RELEASE_YEAR 2017
-#define RUBY_RELEASE_MONTH 1
-#define RUBY_RELEASE_DAY 4
+#define RUBY_RELEASE_MONTH 3
+#define RUBY_RELEASE_DAY 25
#include "ruby/version.h"