From de1fbd182e265da9ef5893d8de50afbdadb99c7a Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 24 Nov 2025 08:48:19 +0100 Subject: [PATCH] Simpler and faster check for the delegation fastpath Fix: https://github.com/ruby/forwardable/issues/35 [Bug #21708] Trying to compile code to check if a method can use the delegation fastpath is a bit wasteful and cause `RUPYOPT=-d` to be full of misleading errors. It's simpler and faster to use a simple regexp to do the same check. --- forwardable.gemspec | 2 +- lib/forwardable.rb | 34 ++++++++++++++-------------------- lib/forwardable/impl.rb | 17 ----------------- 3 files changed, 15 insertions(+), 38 deletions(-) delete mode 100644 lib/forwardable/impl.rb diff --git a/forwardable.gemspec b/forwardable.gemspec index 9ad59c5..1b539bc 100644 --- a/forwardable.gemspec +++ b/forwardable.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| spec.licenses = ["Ruby", "BSD-2-Clause"] spec.required_ruby_version = '>= 2.4.0' - spec.files = ["forwardable.gemspec", "lib/forwardable.rb", "lib/forwardable/impl.rb"] + spec.files = ["forwardable.gemspec", "lib/forwardable.rb"] spec.bindir = "exe" spec.executables = [] spec.require_paths = ["lib"] diff --git a/lib/forwardable.rb b/lib/forwardable.rb index 76267c2..040f467 100644 --- a/lib/forwardable.rb +++ b/lib/forwardable.rb @@ -109,8 +109,6 @@ # +delegate.rb+. # module Forwardable - require 'forwardable/impl' - # Version of +forwardable.rb+ VERSION = "1.3.3" VERSION.freeze @@ -206,37 +204,33 @@ def self._delegator_method(obj, accessor, method, ali) if Module === obj ? obj.method_defined?(accessor) || obj.private_method_defined?(accessor) : obj.respond_to?(accessor, true) - accessor = "#{accessor}()" + accessor = "(#{accessor}())" end args = RUBY_VERSION >= '2.7' ? '...' : '*args, &block' method_call = ".__send__(:#{method}, #{args})" - if _valid_method?(method) + if method.match?(/\A[_a-zA-Z]\w*[?!]?\z/) loc, = caller_locations(2,1) pre = "_ =" mesg = "#{Module === obj ? obj : obj.class}\##{ali} at #{loc.path}:#{loc.lineno} forwarding to private method " - method_call = "#{<<-"begin;"}\n#{<<-"end;".chomp}" - begin; - unless defined? _.#{method} - ::Kernel.warn #{mesg.dump}"\#{_.class}"'##{method}', uplevel: 1 - _#{method_call} - else - _.#{method}(#{args}) - end - end; + method_call = <<~RUBY.chomp + if defined?(_.#{method}) + _.#{method}(#{args}) + else + ::Kernel.warn #{mesg.dump}"\#{_.class}"'##{method}', uplevel: 1 + _#{method_call} + end + RUBY end - _compile_method("#{<<-"begin;"}\n#{<<-"end;"}", __FILE__, __LINE__+1) - begin; + eval(<<~RUBY, nil, __FILE__, __LINE__ + 1) proc do def #{ali}(#{args}) - #{pre} - begin - #{accessor} - end#{method_call} + #{pre}#{accessor} + #{method_call} end end - end; + RUBY end end diff --git a/lib/forwardable/impl.rb b/lib/forwardable/impl.rb deleted file mode 100644 index 0322c13..0000000 --- a/lib/forwardable/impl.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Forwardable - # :stopdoc: - - def self._valid_method?(method) - catch {|tag| - eval("BEGIN{throw tag}; ().#{method}", binding, __FILE__, __LINE__) - } - rescue SyntaxError - false - else - true - end - - def self._compile_method(src, file, line) - eval(src, nil, file, line) - end -end