From d8e0d24feadff5b19af914cbdce9076133c23cca Mon Sep 17 00:00:00 2001 From: dearblue Date: Mon, 13 Apr 2026 21:31:56 +0900 Subject: [PATCH 1/2] Add `mrb_str_dup_frozen()` --- include/mruby/string.h | 10 ++++++++++ src/string.c | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/mruby/string.h b/include/mruby/string.h index b88259452..22683b89a 100644 --- a/include/mruby/string.h +++ b/include/mruby/string.h @@ -342,6 +342,16 @@ MRB_API const char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *str); */ MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str); +/** + * Returns a frozen string object. + * The string will be duplicated and frozen if it is not already frozen. + * + * @param mrb The current mruby state. + * @param str An original Ruby string. + * @return [mrb_value] Ruby frozen string. + */ +MRB_API mrb_value mrb_str_dup_frozen(mrb_state *mrb, mrb_value str); + /** * Returns a symbol from a passed in Ruby string. * diff --git a/src/string.c b/src/string.c index d490ec310..9f886b95f 100644 --- a/src/string.c +++ b/src/string.c @@ -1358,6 +1358,16 @@ mrb_str_dup(mrb_state *mrb, mrb_value str) return str_replace(mrb, dup, s); } +MRB_API mrb_value +mrb_str_dup_frozen(mrb_state *mrb, mrb_value str) +{ + if (!mrb_frozen_p(mrb_basic_ptr(str))) { + str = mrb_str_dup(mrb, str); + mrb_basic_ptr(str)->frozen = TRUE; + } + return str; +} + enum str_convert_range { /* `beg` and `len` are byte unit in `0 ... str.bytesize` */ STR_BYTE_RANGE_CORRECTED = 1, From 9ad94a50e7ac7027ceb43ec0cdbe5fa980b038bd Mon Sep 17 00:00:00 2001 From: dearblue Date: Mon, 13 Apr 2026 21:33:23 +0900 Subject: [PATCH 2/2] Supplement to #6781 `mrb_str_dup()` always duplicates string objects in an unfrozen state, and the class is also set. Therefore, it can be observed and modified from the Ruby side using the `ObjectSpace.each_object` method. By using `mrb_str_dup_frozen()`, unnecessary duplication can be avoided, and modifications to the string can also be prevented. --- mrbgems/mruby-sprintf/src/sprintf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c index c7fbf4397..6cbef79a4 100644 --- a/mrbgems/mruby-sprintf/src/sprintf.c +++ b/mrbgems/mruby-sprintf/src/sprintf.c @@ -386,7 +386,7 @@ mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fm buffer, so this is O(1); String#replace on the original goes through str_replace which decrements the shared refcount, leaving our copy's buffer intact. */ - fmt = mrb_str_dup(mrb, fmt); + fmt = mrb_str_dup_frozen(mrb, fmt); p = RSTRING_PTR(fmt); end = p + RSTRING_LEN(fmt); blen = 0;