summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Williams <samuel.williams@oriontransfer.co.nz>2023-05-22 12:58:17 +0900
committerGitHub <noreply@github.com>2023-05-22 12:58:17 +0900
commitbd786e78969f9d4a8699376ceafe10934b6ad533 (patch)
treef4d73aea61935283a755d9a0d9a940e47dbb1fc9
parent8fef1373be84cc3bea9df72d52f63d10c87cfaf5 (diff)
Fix mutation on shared strings. (#7837)
Notes
Notes: Merged-By: ioquatix <samuel@codeotaku.com>
-rw-r--r--io_buffer.c19
-rw-r--r--test/ruby/test_io_buffer.rb4
2 files changed, 12 insertions, 11 deletions
diff --git a/io_buffer.c b/io_buffer.c
index 5ffb79bc0c..04838f0033 100644
--- a/io_buffer.c
+++ b/io_buffer.c
@@ -300,19 +300,21 @@ 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 *buffer = NULL;
TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- 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(buffer, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);
return instance;
@@ -322,6 +324,7 @@ struct io_buffer_for_yield_instance_arguments {
VALUE klass;
VALUE string;
VALUE instance;
+ enum rb_io_buffer_flags flags;
};
static VALUE
@@ -329,9 +332,9 @@ 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);
}
@@ -365,7 +368,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)
@@ -397,6 +401,7 @@ rb_io_buffer_type_for(VALUE klass, VALUE string)
.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);
@@ -404,7 +409,7 @@ rb_io_buffer_type_for(VALUE klass, VALUE string)
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);
+ return io_buffer_for_make_instance(klass, string, RB_IO_BUFFER_READONLY);
}
}
diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb
index e29400d76e..b9d52d83ce 100644
--- a/test/ruby/test_io_buffer.rb
+++ b/test/ruby/test_io_buffer.rb
@@ -362,10 +362,6 @@ class TestIOBuffer < Test::Unit::TestCase
end
def test_read
- # This is currently a bug in IO:Buffer [#19084] which affects extended
- # strings. On 32 bit machines, the example below becomes extended, so
- # we omit this test until the bug is fixed.
- omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1
io = Tempfile.new
io.write("Hello World")
io.seek(0)