Skip to content

Commit 17ee957

Browse files
Eagerly defrost chilled strings
[Feature #20390] Co-authored-by: Jean Boussier <[email protected]>
1 parent 779f713 commit 17ee957

File tree

2 files changed

+41
-4
lines changed

2 files changed

+41
-4
lines changed

ext/stringio/stringio.c

+20-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
static const char *const
1616
STRINGIO_VERSION = "3.1.1";
1717

18+
#include <stdbool.h>
19+
1820
#include "ruby.h"
1921
#include "ruby/io.h"
2022
#include "ruby/encoding.h"
@@ -49,6 +51,13 @@ static long strio_write(VALUE self, VALUE str);
4951
#define IS_STRIO(obj) (rb_typeddata_is_kind_of((obj), &strio_data_type))
5052
#define error_inval(msg) (rb_syserr_fail(EINVAL, msg))
5153
#define get_enc(ptr) ((ptr)->enc ? (ptr)->enc : !NIL_P((ptr)->string) ? rb_enc_get((ptr)->string) : NULL)
54+
#ifndef HAVE_RB_STR_CHILLED_P
55+
static bool
56+
rb_str_chilled_p(VALUE str)
57+
{
58+
return false;
59+
}
60+
#endif
5261

5362
static struct StringIO *
5463
strio_alloc(void)
@@ -166,8 +175,14 @@ writable(VALUE strio)
166175
static void
167176
check_modifiable(struct StringIO *ptr)
168177
{
169-
if (OBJ_FROZEN(ptr->string)) {
170-
rb_raise(rb_eIOError, "not modifiable string");
178+
if (NIL_P(ptr->string)) {
179+
/* Null device StringIO */
180+
}
181+
else if (rb_str_chilled_p(ptr->string)) {
182+
rb_str_modify(ptr->string);
183+
}
184+
else if (OBJ_FROZEN_RAW(ptr->string)) {
185+
rb_raise(rb_eIOError, "not modifiable string");
171186
}
172187
}
173188

@@ -287,7 +302,8 @@ strio_init(int argc, VALUE *argv, struct StringIO *ptr, VALUE self)
287302
else if (!argc) {
288303
string = rb_enc_str_new("", 0, rb_default_external_encoding());
289304
}
290-
if (!NIL_P(string) && OBJ_FROZEN_RAW(string)) {
305+
306+
if (!NIL_P(string) && OBJ_FROZEN_RAW(string) && !rb_str_chilled_p(string)) {
291307
if (ptr->flags & FMODE_WRITABLE) {
292308
rb_syserr_fail(EACCES, 0);
293309
}
@@ -481,7 +497,7 @@ strio_set_string(VALUE self, VALUE string)
481497
rb_io_taint_check(self);
482498
ptr->flags &= ~FMODE_READWRITE;
483499
StringValue(string);
484-
ptr->flags = OBJ_FROZEN(string) ? FMODE_READABLE : FMODE_READWRITE;
500+
ptr->flags = OBJ_FROZEN(string) && !rb_str_chilled_p(string) ? FMODE_READABLE : FMODE_READWRITE;
485501
ptr->pos = 0;
486502
ptr->lineno = 0;
487503
RB_OBJ_WRITE(self, &ptr->string, string);

test/stringio/test_stringio.rb

+21
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,27 @@ def test_coderange_after_overwrite
978978
assert_predicate(s.string, :ascii_only?)
979979
end
980980

981+
if eval(%{ "test".frozen? && !"test".equal?("test") }) # Ruby 3.4+ chilled strings
982+
def test_chilled_string
983+
chilled_string = eval(%{""})
984+
io = StringIO.new(chilled_string)
985+
assert_warning(/literal string will be frozen/) { io << "test" }
986+
assert_equal("test", io.string)
987+
assert_same(chilled_string, io.string)
988+
end
989+
990+
def test_chilled_string_string_set
991+
io = StringIO.new
992+
chilled_string = eval(%{""})
993+
io.string = chilled_string
994+
assert_warning(/literal string will be frozen/) { io << "test" }
995+
assert_equal("test", io.string)
996+
assert_same(chilled_string, io.string)
997+
end
998+
end
999+
1000+
private
1001+
9811002
def assert_string(content, encoding, str, mesg = nil)
9821003
assert_equal([content, encoding], [str, str.encoding], mesg)
9831004
end

0 commit comments

Comments
 (0)