summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorU.Nakamura <usa@ruby-lang.org>2023-07-25 20:29:45 +0900
committerU.Nakamura <usa@ruby-lang.org>2023-07-25 20:29:45 +0900
commit3799270ec27b1267cd9923dae00bcb3955f7e061 (patch)
treea09f9c752513589363cf906e4c0dcddb461049d7
parente7c94d9d1d2bdbf396c489d1dc653c771f59bb92 (diff)
merge revision(s) bd786e78969f9d4a8699376ceafe10934b6ad533: [Backport #19084]
Fix mutation on shared strings. (#7837) --- io_buffer.c | 19 ++++++++++++------- test/ruby/test_io_buffer.rb | 4 ---- 2 files changed, 12 insertions(+), 11 deletions(-)
-rw-r--r--io_buffer.c39
-rw-r--r--version.h2
2 files changed, 23 insertions, 18 deletions
diff --git a/io_buffer.c b/io_buffer.c
index 5951f54fc4..4b806906f2 100644
--- a/io_buffer.c
+++ b/io_buffer.c
@@ -284,37 +284,40 @@ rb_io_buffer_type_allocate(VALUE self)
return instance;
}
-static VALUE
-io_buffer_for_make_instance(VALUE klass, VALUE string)
+static VALUE io_buffer_for_make_instance(VALUE klass, VALUE string, enum rb_io_buffer_flags flags)
{
VALUE instance = rb_io_buffer_type_allocate(klass);
struct rb_io_buffer *data = NULL;
TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
- enum rb_io_buffer_flags flags = RB_IO_BUFFER_EXTERNAL;
+ flags |= RB_IO_BUFFER_EXTERNAL;
if (RB_OBJ_FROZEN(string))
flags |= RB_IO_BUFFER_READONLY;
+ if (!(flags & RB_IO_BUFFER_READONLY))
+ rb_str_modify(string);
+
io_buffer_initialize(data, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);
return instance;
}
struct io_buffer_for_yield_instance_arguments {
- VALUE klass;
- VALUE string;
- VALUE instance;
+ VALUE klass;
+ VALUE string;
+ VALUE instance;
+ enum rb_io_buffer_flags flags;
};
static VALUE
io_buffer_for_yield_instance(VALUE _arguments) {
struct io_buffer_for_yield_instance_arguments *arguments = (struct io_buffer_for_yield_instance_arguments *)_arguments;
- rb_str_locktmp(arguments->string);
+ arguments->instance = io_buffer_for_make_instance(arguments->klass, arguments->string, arguments->flags);
- arguments->instance = io_buffer_for_make_instance(arguments->klass, arguments->string);
+ rb_str_locktmp(arguments->string);
return rb_yield(arguments->instance);
}
@@ -348,7 +351,8 @@ io_buffer_for_yield_instance_ensure(VALUE _arguments)
* collector, the source string will be locked and cannot be modified.
*
* If the string is frozen, it will create a read-only buffer which cannot be
- * modified.
+ * modified. If the string is shared, it may trigger a copy-on-write when
+ * using the block form.
*
* string = 'test'
* buffer = IO::Buffer.for(string)
@@ -376,17 +380,18 @@ rb_io_buffer_type_for(VALUE klass, VALUE string)
// If the string is frozen, both code paths are okay.
// If the string is not frozen, if a block is not given, it must be frozen.
if (rb_block_given_p()) {
- struct io_buffer_for_yield_instance_arguments arguments = {
- .klass = klass,
- .string = string,
- .instance = Qnil,
- };
+ struct io_buffer_for_yield_instance_arguments arguments = {
+ .klass = klass,
+ .string = string,
+ .instance = Qnil,
+ .flags = 0,
+ };
return rb_ensure(io_buffer_for_yield_instance, (VALUE)&arguments, io_buffer_for_yield_instance_ensure, (VALUE)&arguments);
} else {
- // This internally returns the source string if it's already frozen.
- string = rb_str_tmp_frozen_acquire(string);
- return io_buffer_for_make_instance(klass, string);
+ // This internally returns the source string if it's already frozen.
+ string = rb_str_tmp_frozen_acquire(string);
+ return io_buffer_for_make_instance(klass, string, RB_IO_BUFFER_READONLY);
}
}
diff --git a/version.h b/version.h
index 3e6dccb31b..9841804328 100644
--- a/version.h
+++ b/version.h
@@ -11,7 +11,7 @@
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
#define RUBY_VERSION_TEENY 4
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
-#define RUBY_PATCHLEVEL 234
+#define RUBY_PATCHLEVEL 235
#define RUBY_RELEASE_YEAR 2023
#define RUBY_RELEASE_MONTH 7