[rubygems/rubygems] Ensure that loading multiple gemspecs with legacy YAML class references does not warn

Before this, you would get constant redefinition warnings on Psych::DefaultKey

Additionally, ensure the retries wont continue infinitely in the case of the ArgumentError not being caused by Marshal trying to load the undefined classes

https://github.com/rubygems/rubygems/commit/919e8c2de4
This commit is contained in:
Samuel Giddins 2023-08-17 15:38:53 -07:00 committed by git
parent f69f775f24
commit 1935433f5f
2 changed files with 32 additions and 2 deletions

View File

@ -1302,10 +1302,20 @@ class Gem::Specification < Gem::BasicSpecification
Gem.load_yaml
yaml_set = false
retry_count = 0
array = begin
Marshal.load str
rescue ArgumentError => e
# Avoid an infinite retry loop when the argument error has nothing to do
# with the classes not being defined.
# 1 retry each allowed in case all 3 of
# - YAML
# - YAML::Syck::DefaultKey
# - YAML::Private type
# need to be defined
raise if retry_count >= 3
#
# Some very old marshaled specs included references to `YAML::PrivateType`
# and `YAML::Syck::DefaultKey` constants due to bugs in the old emitter
@ -1323,11 +1333,12 @@ class Gem::Specification < Gem::BasicSpecification
if message.include?("YAML::Syck::")
YAML.const_set "Syck", YAML unless YAML.const_defined?(:Syck)
YAML::Syck.const_set "DefaultKey", Class.new if message.include?("YAML::Syck::DefaultKey")
elsif message.include?("YAML::PrivateType")
YAML::Syck.const_set "DefaultKey", Class.new if message.include?("YAML::Syck::DefaultKey") && !YAML::Syck.const_defined?(:DefaultKey)
elsif message.include?("YAML::PrivateType") && !YAML.const_defined?(:PrivateType)
YAML.const_set "PrivateType", Class.new
end
retry_count += 1
retry
ensure
Object.__send__(:remove_const, "YAML") if yaml_set

View File

@ -1094,6 +1094,25 @@ dependencies: []
assert_equal(yaml_defined, Object.const_defined?("YAML"))
end
def test_handles_dependencies_with_other_syck_requirements_argument_error
yaml_defined = Object.const_defined?("YAML")
data = Marshal.dump(Gem::Specification.new do |s|
v = Gem::Version.allocate
v.instance_variable_set :@version, "YAML::Syck::DefaultKey"
s.instance_variable_set :@version, v
end)
assert_raises(ArgumentError) { Marshal.load(data) }
out, err = capture_output do
assert_raises(ArgumentError) { Marshal.load(data) }
end
assert_empty out
assert_empty err
assert_equal(yaml_defined, Object.const_defined?("YAML"))
end
def test_initialize
spec = Gem::Specification.new do |s|
s.name = "blah"