Project

General

Profile

Actions

Bug #21151

open

IO and StringIO raise FrozenError even for read-only methods

Added by headius (Charles Nutter) 2 days ago. Updated 1 day ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:121136]

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>'

@kou (Kouhei Sutou) 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 those from the StringIO list above that currently raise. For example lineno uses GetOpenFile and raises, 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 (Yukihiro Matsumoto) 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.

Actions

Also available in: Atom PDF

Like0
Like0Like0