From 65c0a38cb6c5b357bdf60f9e977f317cea707b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Thu, 1 Oct 2020 14:42:41 +0200 Subject: [PATCH 1/2] Small refactor in `warn` monkeypatch Only need to set `kw[:uplevel]` when not already set. --- lib/rubygems/core_ext/kernel_warn.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/rubygems/core_ext/kernel_warn.rb b/lib/rubygems/core_ext/kernel_warn.rb index e030ef815c9d..3ed16c8cc7ed 100644 --- a/lib/rubygems/core_ext/kernel_warn.rb +++ b/lib/rubygems/core_ext/kernel_warn.rb @@ -45,10 +45,9 @@ class << self end end end - uplevel = start + kw[:uplevel] = start end - kw[:uplevel] = uplevel original_warn.call(*messages, **kw) } end From 4b0f57f2378abefd6fb82a53449a9e4d81183e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Thu, 1 Oct 2020 14:56:36 +0200 Subject: [PATCH 2/2] Preserve `self` when calling the original Kernel#warn method This fixes infinite recursion when monkey-patching Warning#warn, which is not recommended, but still should behave the same whether RubyGems is loaded or not. --- lib/rubygems/core_ext/kernel_warn.rb | 8 +++--- test/rubygems/test_require.rb | 41 ++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/rubygems/core_ext/kernel_warn.rb b/lib/rubygems/core_ext/kernel_warn.rb index 3ed16c8cc7ed..b468813ce722 100644 --- a/lib/rubygems/core_ext/kernel_warn.rb +++ b/lib/rubygems/core_ext/kernel_warn.rb @@ -6,7 +6,7 @@ module Kernel rubygems_path = "#{__dir__}/" # Frames to be skipped start with this path. - original_warn = method(:warn) + original_warn = instance_method(:warn) remove_method :warn @@ -17,9 +17,9 @@ class << self module_function define_method(:warn) {|*messages, **kw| unless uplevel = kw[:uplevel] if Gem.java_platform? - return original_warn.call(*messages) + return original_warn.bind(self).call(*messages) else - return original_warn.call(*messages, **kw) + return original_warn.bind(self).call(*messages, **kw) end end @@ -48,7 +48,7 @@ class << self kw[:uplevel] = start end - original_warn.call(*messages, **kw) + original_warn.bind(self).call(*messages, **kw) } end end diff --git a/test/rubygems/test_require.rb b/test/rubygems/test_require.rb index 70242a1f3c5e..d65b6c9527de 100644 --- a/test/rubygems/test_require.rb +++ b/test/rubygems/test_require.rb @@ -677,6 +677,47 @@ def test_require_bundler_with_bundler_version end end end + + def test_no_crash_when_overriding_warn_with_warning_module + skip "https://github.com/oracle/truffleruby/issues/2109" if RUBY_ENGINE == "truffleruby" + + Dir.mktmpdir("warn_test") do |dir| + File.write(dir + "/main.rb", "module Warning; def warn(str); super; end; end; warn 'Foo Bar'") + _, err = capture_subprocess_io do + system(*ruby_with_rubygems_in_load_path, "-w", "--disable=gems", "-C", dir, "main.rb") + end + assert_match(/Foo Bar\n$/, err) + _, err = capture_subprocess_io do + system(*ruby_with_rubygems_in_load_path, "-w", "--enable=gems", "-C", dir, "main.rb") + end + assert_match(/Foo Bar\n$/, err) + end + end + + def test_expected_backtrace_location_when_inheriting_from_basic_object_and_including_kernel + Dir.mktmpdir("warn_test") do |dir| + File.write(dir + "/main.rb", "\nrequire 'sub'\n") + File.write(dir + "/sub.rb", <<-'RUBY') + require 'rubygems' + class C < BasicObject + include ::Kernel + def deprecated + warn "This is a deprecated method", uplevel: 2 + end + end + C.new.deprecated + RUBY + + _, err = capture_subprocess_io do + system(*ruby_with_rubygems_in_load_path, "-w", "--enable=gems", "-C", dir, "-I", dir, "main.rb") + end + assert_match(/main\.rb:2: warning: This is a deprecated method$/, err) + _, err = capture_subprocess_io do + system(*ruby_with_rubygems_in_load_path, "-w", "--enable=gems", "-C", dir, "-I", dir, "main.rb") + end + assert_match(/main\.rb:2: warning: This is a deprecated method$/, err) + end + end end private