Project

General

Profile

Bug #21151

Updated by headius (Charles Nutter) 2 days ago

Problem 
 -------- 

 While fixing a small [regression](https://github.com/ruby/stringio/issues/119) from my recent StringIO [fixes](https://github.com/ruby/stringio/issues/116) 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>' 
 ``` 

 @kou pointed out that IO also raises for at least `lineno` and suggested I re-open the issue here. I still believe these read-only operations should be allowed on frozen IO or StringIO. 

 Cause in StringIO 
 ----------------- 

 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 to be writable: 

 ``` 
 $ 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). 

 Cause in IO 
 ----------- 

 The `GetOpenFile` macro calls `RB_IO_POINTER` macro which calls `rb_io_taint_check` which calls `rb_check_frozen`. 

 Methods in IO that should work on a frozen IO include any of the above StringIO that currently raise. For example `lineno` uses `GetOpenFile`, but `fileno` does not and does not raise. 

 There's clearly some inconsistency here we can clean up. 

 Origin 
 ------ 

 I believe most of the StringIO cases are caused by this change from 2011 that added frozen checks to StringIO (the class and the macro): https://github.com/ruby/ruby/commit/d8d9bac5c8b071135e50ad3f21c8a9b6a9c06e54 

 In IO, this behavior dates way back to 2000 by @matz himself: https://github.com/ruby/ruby/commit/087c83d7ceed6893afff93066937fb570ae4a115 

 Notes 
 ----- 

 Originally filed as https://github.com/ruby/stringio/issues/120. 

 I have started to fix the StringIO cases for JRuby in https://github.com/ruby/stringio/pull/122. I could probably fix the C version of StringIO as well, but I'm a little more unsure about how to fix IO.

Back