Description
Problem
While fixing a small regression from my recent StringIO fixes I discovered that a large number of read-only methods will fail if the StringIO is frozen. A few examples:
$ ruby -rstringio -e 's = StringIO.new; p s.lineno; s.freeze; p s.lineno'
0
-e:1:in 'StringIO#lineno': can't modify frozen StringIO: #<StringIO:0x0000000103711ea8> (FrozenError)
from -e:1:in '<main>'
$ ruby -rstringio -e 's = StringIO.new; p s.closed?; s.freeze; p s.closed?'
false
-e:1:in 'StringIO#closed?': can't modify frozen StringIO: #<StringIO:0x00000001008c1e68> (FrozenError)
from -e:1:in '<main>'
$ ruby -rstringio -e 's = StringIO.new; p s.eof?; s.freeze; p s.eof?'
true
-e:1:in 'StringIO#eof?': can't modify frozen StringIO: #<StringIO:0x000000011c171e80> (FrozenError)
from -e:1:in '<main>'
$ ruby -rstringio -e 's = StringIO.new; p s.pos; s.freeze; p s.pos'
0
-e:1:in 'StringIO#pos': can't modify frozen StringIO: #<StringIO:0x0000000105551df0> (FrozenError)
from -e:1:in '<main>'
Cause
This is because the StringIO
macro calls get_strio
which calls rb_io_taint_check
which calls rb_check_frozen
. The StringIO
macro is used in almost every method to access the rb_stringio_t data structure.
A list of methods I believe should be operable when the StringIO is frozen (in stringio.c definition order):
- string (returns underlying String but does not mutate anything)
- lineno
- pos
- closed?/closed_read?/closed_write?
- eof/eof?
- sync
- pid (a dummy method but it writes nothing)
- fileno (dummy)
- pread (by definition does not modify state)
- isatty/tty?
- size/length
- external_encoding
- internal_encoding
In addition, initialize_copy
probably should not require the original StringIO be writable:
$ cx 3.4 ruby -rstringio -e 's = StringIO.new("foo"); s.freeze; p s.dup'
-e:1:in 'StringIO#initialize_copy': can't modify frozen StringIO: #<StringIO:0x0000000102eb1df8> (FrozenError)
from -e:1:in 'Kernel#initialize_dup'
from -e:1:in 'Kernel#dup'
from -e:1:in '<main>'
The data from the original StringIO is unmodified by initialize_copy
, other than the reference-counting ptr->count
(which should not be subject to frozen checks).
Origin
I believe most of these cases are caused by this change from 2011 that added frozen checks to StringIO (the class and the macro).
ruby/ruby@d8d9bac
Fix
Assuming we agree these read-only methods should work with a frozen, I'll modify them in the JRuby extension to do the equivalent of check_strio
which only confirms the ptr
has been initialized. Perhaps a StringIOReadOnly
macro would make sense for CRuby.