summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornagachika <nagachika@ruby-lang.org>2025-03-29 16:49:59 +0900
committernagachika <nagachika@ruby-lang.org>2025-03-29 16:49:59 +0900
commitaac5c546cd35ff0aeab120e3724fbb1296892ae3 (patch)
tree574cc6fa8d3ef7458b11540d3981eae80421a98d
parent770060f7662698ca74bc28428919a688b8fe06d2 (diff)
merge revision(s) f69ad0e810e1fdc18dc12f77bbecfa49999ef3bf: [Backport #21094]
[Bug #21094] Update nested module names when setting temporary name
-rw-r--r--spec/ruby/core/module/set_temporary_name_spec.rb45
-rw-r--r--test/ruby/test_module.rb9
-rw-r--r--variable.c85
-rw-r--r--version.h2
4 files changed, 137 insertions, 4 deletions
diff --git a/spec/ruby/core/module/set_temporary_name_spec.rb b/spec/ruby/core/module/set_temporary_name_spec.rb
index f5886a3398..9a4d027aad 100644
--- a/spec/ruby/core/module/set_temporary_name_spec.rb
+++ b/spec/ruby/core/module/set_temporary_name_spec.rb
@@ -64,5 +64,50 @@ ruby_version_is "3.3" do
m::M = m::N
m::M.name.should =~ /\A#<Module:0x\h+>::M\z/m
end
+
+ it "can reassign a temporary name repeatedly" do
+ m = Module.new
+
+ m.set_temporary_name("fake_name")
+ m.name.should == "fake_name"
+
+ m.set_temporary_name("fake_name_2")
+ m.name.should == "fake_name_2"
+ end
+
+ ruby_bug "#21094", ""..."3.5" do
+ it "also updates a name of a nested module" do
+ m = Module.new
+ m::N = Module.new
+ m::N.name.should =~ /\A#<Module:0x\h+>::N\z/
+
+ m.set_temporary_name "m"
+ m::N.name.should == "m::N"
+
+ m.set_temporary_name nil
+ m::N.name.should == nil
+ end
+ end
+
+ it "keeps temporary name when assigned in an anonymous module" do
+ outer = Module.new
+ m = Module.new
+ m.set_temporary_name "m"
+ m.name.should == "m"
+ outer::M = m
+ m.name.should == "m"
+ m.inspect.should == "m"
+ end
+
+ it "keeps temporary name when assigned in an anonymous module and nested before" do
+ outer = Module.new
+ m = Module.new
+ outer::A = m
+ m.set_temporary_name "m"
+ m.name.should == "m"
+ outer::M = m
+ m.name.should == "m"
+ m.inspect.should == "m"
+ end
end
end
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index f2accd0c59..7de50c4c79 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -3367,13 +3367,22 @@ class TestModule < Test::Unit::TestCase
m::N.set_temporary_name(nil)
assert_nil(m::N.name)
+ m::N.const_set(:O, Module.new)
+ m.const_set(:Recursive, m)
+ m::N.const_set(:Recursive, m)
+ m.const_set(:A, 42)
+
m.set_temporary_name(name = "fake_name")
name.upcase!
assert_equal("fake_name", m.name)
assert_raise(FrozenError) {m.name.upcase!}
+ assert_equal("fake_name::N", m::N.name)
+ assert_equal("fake_name::N::O", m::N::O.name)
m.set_temporary_name(nil)
assert_nil m.name
+ assert_nil m::N.name
+ assert_nil m::N::O.name
assert_raise_with_message(ArgumentError, "empty class/module name") do
m.set_temporary_name("")
diff --git a/variable.c b/variable.c
index ea73dd00dc..f8cf7d735e 100644
--- a/variable.c
+++ b/variable.c
@@ -157,6 +157,80 @@ is_constant_path(VALUE name)
return true;
}
+struct sub_temporary_name_args {
+ VALUE names;
+ ID last;
+};
+
+static VALUE build_const_path(VALUE head, ID tail);
+static void set_sub_temporary_name_foreach(VALUE mod, struct sub_temporary_name_args *args, VALUE name);
+
+static VALUE
+set_sub_temporary_name_recursive(VALUE mod, VALUE data, int recursive)
+{
+ if (recursive) return Qfalse;
+
+ struct sub_temporary_name_args *args = (void *)data;
+ VALUE name = 0;
+ if (args->names) {
+ name = build_const_path(rb_ary_last(0, 0, args->names), args->last);
+ }
+ set_sub_temporary_name_foreach(mod, args, name);
+ return Qtrue;
+}
+
+static VALUE
+set_sub_temporary_name_topmost(VALUE mod, VALUE data, int recursive)
+{
+ if (recursive) return Qfalse;
+
+ struct sub_temporary_name_args *args = (void *)data;
+ VALUE name = args->names;
+ if (name) {
+ args->names = rb_ary_hidden_new(0);
+ }
+ set_sub_temporary_name_foreach(mod, args, name);
+ return Qtrue;
+}
+
+static enum rb_id_table_iterator_result
+set_sub_temporary_name_i(ID id, VALUE val, void *data)
+{
+ val = ((rb_const_entry_t *)val)->value;
+ if (rb_namespace_p(val) && !RCLASS_EXT(val)->permanent_classpath) {
+ VALUE arg = (VALUE)data;
+ struct sub_temporary_name_args *args = data;
+ args->last = id;
+ rb_exec_recursive_paired(set_sub_temporary_name_recursive, val, arg, arg);
+ }
+ return ID_TABLE_CONTINUE;
+}
+
+static void
+set_sub_temporary_name_foreach(VALUE mod, struct sub_temporary_name_args *args, VALUE name)
+{
+ RCLASS_SET_CLASSPATH(mod, name, FALSE);
+ struct rb_id_table *tbl = RCLASS_CONST_TBL(mod);
+ if (!tbl) return;
+ if (!name) {
+ rb_id_table_foreach(tbl, set_sub_temporary_name_i, args);
+ }
+ else {
+ long names_len = RARRAY_LEN(args->names); // paranoiac check?
+ rb_ary_push(args->names, name);
+ rb_id_table_foreach(tbl, set_sub_temporary_name_i, args);
+ rb_ary_set_len(args->names, names_len);
+ }
+}
+
+static void
+set_sub_temporary_name(VALUE mod, VALUE name)
+{
+ struct sub_temporary_name_args args = {name};
+ VALUE arg = (VALUE)&args;
+ rb_exec_recursive_paired(set_sub_temporary_name_topmost, mod, arg, arg);
+}
+
/*
* call-seq:
* mod.set_temporary_name(string) -> self
@@ -215,8 +289,11 @@ rb_mod_set_temporary_name(VALUE mod, VALUE name)
if (NIL_P(name)) {
// Set the temporary classpath to NULL (anonymous):
- RCLASS_SET_CLASSPATH(mod, 0, FALSE);
- } else {
+ RB_VM_LOCK_ENTER();
+ set_sub_temporary_name(mod, 0);
+ RB_VM_LOCK_LEAVE();
+ }
+ else {
// Ensure the name is a string:
StringValue(name);
@@ -231,7 +308,9 @@ rb_mod_set_temporary_name(VALUE mod, VALUE name)
name = rb_str_new_frozen(name);
// Set the temporary classpath to the given name:
- RCLASS_SET_CLASSPATH(mod, name, FALSE);
+ RB_VM_LOCK_ENTER();
+ set_sub_temporary_name(mod, name);
+ RB_VM_LOCK_LEAVE();
}
return mod;
diff --git a/version.h b/version.h
index 7d9016b449..b9f9eee68d 100644
--- a/version.h
+++ b/version.h
@@ -11,7 +11,7 @@
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
#define RUBY_VERSION_TEENY 7
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
-#define RUBY_PATCHLEVEL 137
+#define RUBY_PATCHLEVEL 138
#include "ruby/version.h"
#include "ruby/internal/abi.h"