diff --git a/load.c b/load.c index b85a247c18..1c1fe1afa1 100644 --- a/load.c +++ b/load.c @@ -447,6 +447,11 @@ get_loaded_features_index(vm_ns_t *vm_ns) VALUE previous_realpath_map = rb_hash_dup(realpath_map); rb_hash_clear(realpaths); rb_hash_clear(realpath_map); + + /* We have to make a copy of features here because the StringValue call + * below could call a Ruby method, which could modify $LOADED_FEATURES + * and cause it to be corrupt. */ + features = rb_ary_resurrect(features); for (i = 0; i < RARRAY_LEN(features); i++) { VALUE entry, as_str; as_str = entry = rb_ary_entry(features, i); @@ -456,6 +461,10 @@ get_loaded_features_index(vm_ns_t *vm_ns) rb_ary_store(features, i, as_str); features_index_add(vm_ns, as_str, INT2FIX(i)); } + /* The user modified $LOADED_FEATURES, so we should restore the changes. */ + if (!rb_ary_shared_with_p(features, CURRENT_NS_LOADED_FEATURES(vm_ns))) { + rb_ary_replace(CURRENT_NS_LOADED_FEATURES(vm_ns), features); + } reset_loaded_features_snapshot(vm_ns); features = CURRENT_NS_LOADED_FEATURES_SNAPSHOT(vm_ns); diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb index 13e7076391..3b6cc1178d 100644 --- a/test/ruby/test_require.rb +++ b/test/ruby/test_require.rb @@ -840,6 +840,30 @@ class TestRequire < Test::Unit::TestCase p :ok end; } + + # [Bug #21567] + assert_separately(%w[-rtempfile], "#{<<~"begin;"}\n#{<<~"end;"}") + begin; + class MyString + def initialize(path) + @path = path + end + + def to_str + $LOADED_FEATURES.clear + @path + end + + def to_path = @path + end + + def create_ruby_file = Tempfile.create(["test", ".rb"]).path + + require MyString.new(create_ruby_file) + $LOADED_FEATURES.unshift(create_ruby_file) + $LOADED_FEATURES << MyString.new(create_ruby_file) + require create_ruby_file + end; end def test_loading_fifo_threading_raise