Fix mutation on shared strings. (#7837)

This commit is contained in:
Samuel Williams 2023-05-22 12:58:17 +09:00 коммит произвёл GitHub
Родитель 8fef1373be
Коммит bd786e7896
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 12 добавлений и 11 удалений

Просмотреть файл

@ -300,19 +300,21 @@ rb_io_buffer_type_allocate(VALUE self)
return instance; return instance;
} }
static VALUE static VALUE io_buffer_for_make_instance(VALUE klass, VALUE string, enum rb_io_buffer_flags flags)
io_buffer_for_make_instance(VALUE klass, VALUE string)
{ {
VALUE instance = rb_io_buffer_type_allocate(klass); VALUE instance = rb_io_buffer_type_allocate(klass);
struct rb_io_buffer *buffer = NULL; struct rb_io_buffer *buffer = NULL;
TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, buffer); 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)) if (RB_OBJ_FROZEN(string))
flags |= RB_IO_BUFFER_READONLY; 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); io_buffer_initialize(buffer, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);
return instance; return instance;
@ -322,6 +324,7 @@ struct io_buffer_for_yield_instance_arguments {
VALUE klass; VALUE klass;
VALUE string; VALUE string;
VALUE instance; VALUE instance;
enum rb_io_buffer_flags flags;
}; };
static VALUE 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; 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); 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. * 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 * 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' * string = 'test'
* buffer = IO::Buffer.for(string) * buffer = IO::Buffer.for(string)
@ -397,6 +401,7 @@ rb_io_buffer_type_for(VALUE klass, VALUE string)
.klass = klass, .klass = klass,
.string = string, .string = string,
.instance = Qnil, .instance = Qnil,
.flags = 0,
}; };
return rb_ensure(io_buffer_for_yield_instance, (VALUE)&arguments, io_buffer_for_yield_instance_ensure, (VALUE)&arguments); 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 { else {
// This internally returns the source string if it's already frozen. // This internally returns the source string if it's already frozen.
string = rb_str_tmp_frozen_acquire(string); 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);
} }
} }

Просмотреть файл

@ -362,10 +362,6 @@ class TestIOBuffer < Test::Unit::TestCase
end end
def test_read 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 = Tempfile.new
io.write("Hello World") io.write("Hello World")
io.seek(0) io.seek(0)