From 2bbb856c268bf0e8b6d9aa081ca4616f198fdbf4 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 20 Feb 2025 10:28:12 -0600 Subject: [PATCH 1/4] Don't require the string be modifiable to seek seek only modifies the StringIO pos, and should work when the underlying String is frozen. Fixes #119 --- ext/java/org/jruby/ext/stringio/StringIO.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ext/java/org/jruby/ext/stringio/StringIO.java b/ext/java/org/jruby/ext/stringio/StringIO.java index 532f771..9d2a20a 100644 --- a/ext/java/org/jruby/ext/stringio/StringIO.java +++ b/ext/java/org/jruby/ext/stringio/StringIO.java @@ -1356,8 +1356,6 @@ public IRubyObject seek(ThreadContext context, IRubyObject arg0, IRubyObject arg } private RubyFixnum seekCommon(ThreadContext context, int argc, IRubyObject arg0, IRubyObject arg1) { - checkModifiable(); - Ruby runtime = context.runtime; IRubyObject whence = context.nil; @@ -1367,10 +1365,10 @@ private RubyFixnum seekCommon(ThreadContext context, int argc, IRubyObject arg0, whence = arg1; } - checkOpen(); - StringIOData ptr = this.getPtr(); + checkOpen(); + boolean locked = lock(context, ptr); try { switch (whence.isNil() ? 0 : RubyNumeric.num2int(whence)) { From b946d46757bde901a819d02d1565f41b36a789ad Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 20 Feb 2025 10:28:49 -0600 Subject: [PATCH 2/4] Add test for seek with frozen String See #119 --- test/stringio/test_stringio.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/stringio/test_stringio.rb b/test/stringio/test_stringio.rb index 8d236e5..b3ea342 100644 --- a/test/stringio/test_stringio.rb +++ b/test/stringio/test_stringio.rb @@ -483,6 +483,11 @@ def test_seek f.close unless f.closed? end + def test_seek_frozen_string + f = StringIO.new(-"1234") + assert_nothing_raised { f.seek(0) } + end + def test_each_byte f = StringIO.new("1234") a = [] From 7bb821ac43276c0d1b7f002da7f046253aded451 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 20 Feb 2025 10:55:09 -0600 Subject: [PATCH 3/4] Add tests for read-only methods on frozen StringIO See #120 --- test/stringio/test_stringio.rb | 98 ++++++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 15 deletions(-) diff --git a/test/stringio/test_stringio.rb b/test/stringio/test_stringio.rb index b3ea342..fcb7409 100644 --- a/test/stringio/test_stringio.rb +++ b/test/stringio/test_stringio.rb @@ -330,6 +330,9 @@ def test_open def test_isatty assert_equal(false, StringIO.new("").isatty) + assert_equal(false, StringIO.new("").tty?) + assert_nothing_raised { StringIO.new("").freeze.isatty} + assert_nothing_raised { StringIO.new("").freeze.tty?} end def test_fsync @@ -339,6 +342,7 @@ def test_fsync def test_sync assert_equal(true, StringIO.new("").sync) assert_equal(false, StringIO.new("").sync = false) + assert_nothing_raised { StringIO.new("").freeze.sync} end def test_set_fcntl @@ -391,8 +395,8 @@ def test_closed assert_equal(false, f.closed?) f.close assert_equal(true, f.closed?) - ensure - f.close unless f.closed? + f.freeze + assert_nothing_raised { f.closed? } end def test_closed_read @@ -402,8 +406,8 @@ def test_closed_read assert_equal(false, f.closed_read?) f.close_read assert_equal(true, f.closed_read?) - ensure - f.close unless f.closed? + f.freeze + assert_nothing_raised { f.closed_read? } end def test_closed_write @@ -413,8 +417,8 @@ def test_closed_write assert_equal(false, f.closed_write?) f.close_write assert_equal(true, f.closed_write?) - ensure - f.close unless f.closed? + f.freeze + assert_nothing_raised { f.closed_write? } end def test_dup @@ -440,8 +444,8 @@ def test_lineno f.lineno = 1000 assert_equal([1000, "baz\n"], [f.lineno, f.gets]) assert_equal([1001, nil], [f.lineno, f.gets]) - ensure - f.close unless f.closed? + f.freeze + assert_nothing_raised { f.lineno } end def test_pos @@ -454,8 +458,8 @@ def test_pos assert_equal([4, "bar\n"], [f.pos, f.gets]) assert_equal([8, "baz\n"], [f.pos, f.gets]) assert_equal([12, nil], [f.pos, f.gets]) - ensure - f.close unless f.closed? + f.freeze + assert_nothing_raised { f.pos } end def test_reopen @@ -479,13 +483,10 @@ def test_seek assert_raise(Errno::EINVAL) { f.seek(1, 3) } f.close assert_raise(IOError) { f.seek(0) } - ensure - f.close unless f.closed? - end - - def test_seek_frozen_string f = StringIO.new(-"1234") assert_nothing_raised { f.seek(0) } + ensure + f.close unless f.closed? end def test_each_byte @@ -808,11 +809,17 @@ def test_pread buf = "stale".b assert_equal "stale".b, StringIO.new("").pread(0, 0, buf) assert_equal "stale".b, buf + + assert_nothing_raised { StringIO.new("pread").freeze.pread(3, 0)} end def test_size f = StringIO.new("1234") assert_equal(4, f.size) + assert_equal(4, f.length) + f.freeze + assert_nothing_raised { f.size } + assert_nothing_raised { f.length } end # This test is should in ruby/test_method.rb @@ -1055,6 +1062,67 @@ def test_chilled_string_string_set end end + def test_eof + f = StringIO.new + assert_equal(true, f.eof) + assert_equal(true, f.eof?) + f.ungetc("1234") + assert_equal(false, f.eof) + assert_equal(false, f.eof?) + f.freeze + assert_nothing_raised { f.eof } + assert_nothing_raised { f.eof? } + end + + def test_pid + f = StringIO.new + assert_equal(nil, f.pid) + f.freeze + assert_nothing_raised { f.pid } + end + + def test_fileno + f = StringIO.new + assert_equal(nil, f.fileno) + f.freeze + assert_nothing_raised { f.fileno } + end + + def test_external_encoding + f = StringIO.new + assert_equal(Encoding.find("external"), f.external_encoding) + f = StringIO.new("1234".encode("UTF-16BE")) + assert_equal(Encoding::UTF_16BE, f.external_encoding) + f.freeze + assert_nothing_raised { f.external_encoding } + end + + def test_string + f = StringIO.new + assert_equal("", f.string) + f = StringIO.new("1234") + assert_equal("1234", f.string) + f.freeze + assert_nothing_raised { f.string } + end + + def test_initialize_copy + f = StringIO.new("1234") + f.read(1) + f2 = f.dup + assert_equal(1, f2.pos) + f.read(1) + assert_equal(2, f2.pos) + f.ungetc("56") + assert_equal(0, f2.pos) + assert_equal("5634", f2.string) + f.freeze + assert_nothing_raised do + f2 = f.dup + assert_true(f2.frozen?) + end + end + private def assert_string(content, encoding, str, mesg = nil) From 38303a4e7ef28f20ba1fc549968fd8aa881af00b Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 20 Feb 2025 11:09:37 -0600 Subject: [PATCH 4/4] Allow read-only methods to work with frozen StringIO This fixes the JRuby extension to allow read-only methods to work against a frozen StringIO, as described in #119. --- ext/java/org/jruby/ext/stringio/StringIO.java | 132 ++++++++++-------- 1 file changed, 71 insertions(+), 61 deletions(-) diff --git a/ext/java/org/jruby/ext/stringio/StringIO.java b/ext/java/org/jruby/ext/stringio/StringIO.java index 9d2a20a..3352e00 100644 --- a/ext/java/org/jruby/ext/stringio/StringIO.java +++ b/ext/java/org/jruby/ext/stringio/StringIO.java @@ -94,9 +94,19 @@ static class StringIOData { private StringIOData ptr; // MRI: get_strio, StringIO macro - private StringIOData getPtr() { + private StringIOData getPtrForWrite() { // equivalent to rb_io_taint_check without tainting checkFrozen(); + + checkInitialized(); + + return ptr; + } + + // MRI: get_strio, StringIO macro + private StringIOData getPtrForRead() { + checkInitialized(); + return ptr; } @@ -142,7 +152,7 @@ public static RubyClass createStringIOClass(final Ruby runtime) { // mri: get_enc public Encoding getEncoding() { - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForRead(); Encoding enc = ptr.enc; if (enc != null) { return enc; @@ -157,7 +167,7 @@ public Encoding getEncoding() { } public void setEncoding(Encoding enc) { - getPtr().enc = enc; + getPtrForWrite().enc = enc; } @JRubyMethod(name = "new", rest = true, meta = true) @@ -257,7 +267,7 @@ private static IRubyObject yieldOrReturn(ThreadContext context, Block block, Str try { val = block.yield(context, strio); } finally { - strio.getPtr().string = null; + strio.getPtrForWrite().string = null; strio.flags &= ~STRIO_READWRITE; } } @@ -271,7 +281,7 @@ protected StringIO(Ruby runtime, RubyClass klass) { @JRubyMethod(visibility = PRIVATE, keywords = true) public IRubyObject initialize(ThreadContext context) { - if (getPtr() == null) { + if (ptr == null) { ptr = new StringIOData(); } @@ -283,7 +293,7 @@ public IRubyObject initialize(ThreadContext context) { @JRubyMethod(visibility = PRIVATE, keywords = true) public IRubyObject initialize(ThreadContext context, IRubyObject arg0) { - if (getPtr() == null) { + if (ptr == null) { ptr = new StringIOData(); } @@ -295,7 +305,7 @@ public IRubyObject initialize(ThreadContext context, IRubyObject arg0) { @JRubyMethod(visibility = PRIVATE, keywords = true) public IRubyObject initialize(ThreadContext context, IRubyObject arg0, IRubyObject arg1) { - if (getPtr() == null) { + if (ptr == null) { ptr = new StringIOData(); } @@ -307,7 +317,7 @@ public IRubyObject initialize(ThreadContext context, IRubyObject arg0, IRubyObje @JRubyMethod(visibility = PRIVATE, keywords = true) public IRubyObject initialize(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) { - if (getPtr() == null) { + if (ptr == null) { ptr = new StringIOData(); } @@ -323,7 +333,7 @@ private void strioInit(ThreadContext context, int argc, IRubyObject arg0, IRubyO IRubyObject string = context.nil; IRubyObject vmode = context.nil; - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); boolean locked = lock(context, ptr); try { @@ -411,15 +421,15 @@ public IRubyObject initialize_copy(ThreadContext context, IRubyObject other) { if (this == otherIO) return this; - ptr = otherIO.getPtr(); - flags = flags & ~STRIO_READWRITE | otherIO.flags & STRIO_READWRITE; + ptr = otherIO.getPtrForRead(); + flags = otherIO.flags & (STRIO_READWRITE | FROZEN_F); return this; } @JRubyMethod public IRubyObject binmode(ThreadContext context) { - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); ptr.enc = EncodingUtils.ascii8bitEncoding(context.runtime); if (writable()) ptr.string.setEncoding(ptr.enc); @@ -478,7 +488,7 @@ public IRubyObject closed_p() { public IRubyObject close_read(ThreadContext context) { // ~ checkReadable() : checkInitialized(); - if ( (getPtr().flags & OpenFile.READABLE) == 0 ) { + if ( (getPtrForWrite().flags & OpenFile.READABLE) == 0 ) { throw context.runtime.newIOError("not opened for reading"); } int flags = this.flags; @@ -498,7 +508,7 @@ public IRubyObject closed_read_p() { public IRubyObject close_write(ThreadContext context) { // ~ checkWritable() : checkInitialized(); - if ( (getPtr().flags & OpenFile.WRITABLE) == 0 ) { + if ( (getPtrForWrite().flags & OpenFile.WRITABLE) == 0 ) { throw context.runtime.newIOError("not opened for writing"); } int flags = this.flags; @@ -615,7 +625,7 @@ public IRubyObject each_byte(ThreadContext context, Block block) { if (!block.isGiven()) return enumeratorize(runtime, this, "each_byte"); checkReadable(); - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); boolean locked = lock(context, ptr); try { @@ -649,13 +659,13 @@ public IRubyObject each_char(final ThreadContext context, final Block block) { @JRubyMethod(name = {"eof", "eof?"}) public IRubyObject eof(ThreadContext context) { checkReadable(); - StringIOData ptr = getPtr(); + StringIOData ptr = this.getPtrForRead(); if (ptr.pos < ptr.string.size()) return context.fals; return context.tru; } private boolean isEndOfString() { - StringIOData ptr = getPtr(); + StringIOData ptr = getPtrForWrite(); return ptr.string == null || ptr.pos >= ptr.string.size(); } @@ -665,7 +675,7 @@ public IRubyObject getc(ThreadContext context) { if (isEndOfString()) return context.nil; - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); boolean locked = lock(context, ptr); try { @@ -688,7 +698,7 @@ public IRubyObject getbyte(ThreadContext context) { if (isEndOfString()) return context.nil; int c; - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); boolean locked = lock(context, ptr); try { c = ptr.string.getByteList().get(ptr.pos++) & 0xFF; @@ -702,7 +712,7 @@ public IRubyObject getbyte(ThreadContext context) { // MRI: strio_substr // must be called under lock private RubyString strioSubstr(Ruby runtime, int pos, int len, Encoding enc) { - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForRead(); final RubyString string = ptr.string; int rlen = string.size() - pos; @@ -753,25 +763,25 @@ private static int bm_search(byte[] little, int lstart, int llen, byte[] big, in @JRubyMethod(name = "gets", writes = FrameField.LASTLINE) public IRubyObject gets(ThreadContext context) { - if (getPtr().string == null) return context.nil; + if (getPtrForWrite().string == null) return context.nil; return Getline.getlineCall(context, GETLINE, this, getEncoding()); } @JRubyMethod(name = "gets", writes = FrameField.LASTLINE) public IRubyObject gets(ThreadContext context, IRubyObject arg0) { - if (getPtr().string == null) return context.nil; + if (getPtrForWrite().string == null) return context.nil; return Getline.getlineCall(context, GETLINE, this, getEncoding(), arg0); } @JRubyMethod(name = "gets", writes = FrameField.LASTLINE) public IRubyObject gets(ThreadContext context, IRubyObject arg0, IRubyObject arg1) { - if (getPtr().string == null) return context.nil; + if (getPtrForWrite().string == null) return context.nil; return Getline.getlineCall(context, GETLINE, this, getEncoding(), arg0, arg1); } @JRubyMethod(name = "gets", writes = FrameField.LASTLINE) public IRubyObject gets(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) { - if (getPtr().string == null) return context.nil; + if (getPtrForWrite().string == null) return context.nil; return Getline.getlineCall(context, GETLINE, this, getEncoding(), arg0, arg1, arg2); } @@ -795,7 +805,7 @@ public IRubyObject gets(ThreadContext context, IRubyObject[] args) { self.checkReadable(); if (limit == 0) { - if (self.getPtr().string == null) return context.nil; + if (self.getPtrForWrite().string == null) return context.nil; return RubyString.newEmptyString(context.runtime, self.getEncoding()); } @@ -811,7 +821,7 @@ public IRubyObject gets(ThreadContext context, IRubyObject[] args) { private static final Getline.Callback GETLINE_YIELD = (context, self, rs, limit, chomp, block) -> { IRubyObject line; - StringIOData ptr = self.getPtr(); + StringIOData ptr = self.getPtrForWrite(); if (ptr.string == null || ptr.pos > ptr.string.size()) { return self; } @@ -834,7 +844,7 @@ public IRubyObject gets(ThreadContext context, IRubyObject[] args) { RubyArray ary = (RubyArray) context.runtime.newArray(); IRubyObject line; - StringIOData ptr = self.getPtr(); + StringIOData ptr = self.getPtrForWrite(); if (ptr.string == null || ptr.pos > ptr.string.size()) { return null; } @@ -866,7 +876,7 @@ private IRubyObject getline(ThreadContext context, final IRubyObject rs, int lim return context.nil; } - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); Encoding enc = getEncoding(); boolean locked = lock(context, ptr); @@ -970,19 +980,19 @@ private static int chompNewlineWidth(byte[] bytes, int s, int e) { @JRubyMethod(name = {"length", "size"}) public IRubyObject length(ThreadContext context) { checkInitialized(); - RubyString myString = getPtr().string; + RubyString myString = getPtrForRead().string; if (myString == null) return RubyFixnum.zero(context.runtime); return getRuntime().newFixnum(myString.size()); } @JRubyMethod(name = "lineno") public IRubyObject lineno(ThreadContext context) { - return context.runtime.newFixnum(getPtr().lineno); + return context.runtime.newFixnum(getPtrForRead().lineno); } @JRubyMethod(name = "lineno=", required = 1) public IRubyObject set_lineno(ThreadContext context, IRubyObject arg) { - getPtr().lineno = RubyNumeric.fix2int(arg); + getPtrForWrite().lineno = RubyNumeric.fix2int(arg); return context.nil; } @@ -991,7 +1001,7 @@ public IRubyObject set_lineno(ThreadContext context, IRubyObject arg) { public IRubyObject pos(ThreadContext context) { checkInitialized(); - return context.runtime.newFixnum(getPtr().pos); + return context.runtime.newFixnum(getPtrForRead().pos); } @JRubyMethod(name = "pos=", required = 1) @@ -1004,13 +1014,13 @@ public IRubyObject set_pos(IRubyObject arg) { if (p > Integer.MAX_VALUE) throw getRuntime().newArgumentError("JRuby does not support StringIO larger than " + Integer.MAX_VALUE + " bytes"); - getPtr().pos = (int)p; + getPtrForWrite().pos = (int)p; return arg; } private void strioExtend(ThreadContext context, int pos, int len) { - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); boolean locked = lock(context, ptr); try { @@ -1049,12 +1059,12 @@ public IRubyObject putc(ThreadContext context, IRubyObject ch) { checkModifiable(); if (ch instanceof RubyString) { - if (getPtr().string == null) return context.nil; + if (getPtrForWrite().string == null) return context.nil; str = substrString((RubyString) ch, str, runtime); } else { byte c = RubyNumeric.num2chr(ch); - if (getPtr().string == null) return context.nil; + if (getPtrForWrite().string == null) return context.nil; str = RubyString.newString(runtime, new byte[]{c}); } write(context, str); @@ -1086,7 +1096,7 @@ private IRubyObject readCommon(ThreadContext context, int argc, IRubyObject arg0 IRubyObject str = context.nil; boolean binary = false; - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); int pos = ptr.pos; boolean locked = lock(context, ptr); @@ -1182,7 +1192,7 @@ public IRubyObject pread(ThreadContext context, IRubyObject arg0, IRubyObject ar @SuppressWarnings("fallthrough") private RubyString preadCommon(ThreadContext context, int argc, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) { IRubyObject str = context.nil; - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForRead(); Ruby runtime = context.runtime; int offset; final RubyString string; @@ -1289,6 +1299,8 @@ public IRubyObject readlines(ThreadContext context, IRubyObject[] args) { // MRI: strio_reopen @JRubyMethod(name = "reopen", keywords = true) public IRubyObject reopen(ThreadContext context) { + checkFrozen(); + // reset the state strioInit(context, 0, null, null, null); return this; @@ -1297,7 +1309,7 @@ public IRubyObject reopen(ThreadContext context) { // MRI: strio_reopen @JRubyMethod(name = "reopen", keywords = true) public IRubyObject reopen(ThreadContext context, IRubyObject arg0) { - checkModifiable(); + checkFrozen(); if (!(arg0 instanceof RubyString)) { return initialize_copy(context, arg0); @@ -1311,7 +1323,7 @@ public IRubyObject reopen(ThreadContext context, IRubyObject arg0) { // MRI: strio_reopen @JRubyMethod(name = "reopen", keywords = true) public IRubyObject reopen(ThreadContext context, IRubyObject arg0, IRubyObject arg1) { - checkModifiable(); + checkFrozen(); // reset the state strioInit(context, 2, arg0, arg1, null); @@ -1321,7 +1333,7 @@ public IRubyObject reopen(ThreadContext context, IRubyObject arg0, IRubyObject a // MRI: strio_reopen @JRubyMethod(name = "reopen", keywords = true) public IRubyObject reopen(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) { - checkModifiable(); + checkFrozen(); // reset the state strioInit(context, 3, arg0, arg1, arg2); @@ -1332,7 +1344,7 @@ public IRubyObject reopen(ThreadContext context, IRubyObject arg0, IRubyObject a public IRubyObject rewind(ThreadContext context) { checkInitialized(); - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); boolean locked = lock(context, ptr); try { @@ -1365,7 +1377,7 @@ private RubyFixnum seekCommon(ThreadContext context, int argc, IRubyObject arg0, whence = arg1; } - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); checkOpen(); @@ -1396,8 +1408,7 @@ private RubyFixnum seekCommon(ThreadContext context, int argc, IRubyObject arg0, @JRubyMethod(name = "string=", required = 1) public IRubyObject set_string(ThreadContext context, IRubyObject arg) { - checkFrozen(); - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); boolean locked = lock(context, ptr); try { @@ -1414,7 +1425,7 @@ public IRubyObject set_string(ThreadContext context, IRubyObject arg) { @JRubyMethod(name = "string") public IRubyObject string(ThreadContext context) { - RubyString string = getPtr().string; + RubyString string = getPtrForRead().string; if (string == null) return context.nil; return string; @@ -1431,7 +1442,7 @@ public IRubyObject truncate(ThreadContext context, IRubyObject len) { checkWritable(); int l = RubyFixnum.fix2int(len); - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); RubyString string = ptr.string; boolean locked = lock(context, ptr); @@ -1463,7 +1474,7 @@ public IRubyObject ungetc(ThreadContext context, IRubyObject arg) { checkModifiable(); checkReadable(); - if (getPtr().string == null) return context.nil; + if (getPtrForWrite().string == null) return context.nil; if (arg.isNil()) return arg; if (arg instanceof RubyInteger) { @@ -1490,7 +1501,7 @@ public IRubyObject ungetc(ThreadContext context, IRubyObject arg) { } private void ungetbyteCommon(ThreadContext context, int c) { - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); boolean locked = lock(context, ptr); try { @@ -1521,7 +1532,7 @@ private void ungetbyteCommon(ThreadContext context, RubyString ungetBytes) { private void ungetbyteCommon(ThreadContext context, byte[] ungetBytes, int cp, int cl) { if (cl == 0) return; - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); boolean locked = lock(context, ptr); try { @@ -1576,7 +1587,7 @@ public IRubyObject ungetbyte(ThreadContext context, IRubyObject arg) { if (arg.isNil()) return arg; checkModifiable(); - if (getPtr().string == null) return context.nil; + if (getPtrForWrite().string == null) return context.nil; if (arg instanceof RubyInteger) { ungetbyteCommon(context, ((RubyInteger) ((RubyInteger) arg).op_mod(context, 256)).getIntValue()); @@ -1690,7 +1701,7 @@ private long stringIOWrite(ThreadContext context, Ruby runtime, IRubyObject arg) RubyString str = arg.asString(); int len, olen; - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); boolean locked = lock(context, ptr); try { @@ -1799,7 +1810,7 @@ public IRubyObject set_encoding(ThreadContext context, IRubyObject ext_enc) { } } - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); boolean locked = lock(context, ptr); try { @@ -1835,7 +1846,7 @@ public IRubyObject set_encoding(ThreadContext context, IRubyObject enc, IRubyObj @JRubyMethod public IRubyObject set_encoding_by_bom(ThreadContext context) { - StringIOData ptr = getPtr(); + StringIOData ptr = getPtrForWrite(); if (setEncodingByBOM(context, ptr) == null) return context.nil; @@ -1918,7 +1929,7 @@ public IRubyObject each_codepoint(ThreadContext context, Block block) { checkReadable(); - StringIOData ptr = this.getPtr(); + StringIOData ptr = this.getPtrForWrite(); boolean locked = lock(context, ptr); try { @@ -2168,17 +2179,17 @@ public IRubyObject puts(ThreadContext context, IRubyObject[] args) { private boolean readable() { return (flags & STRIO_READABLE) != 0 - && (getPtr().flags & OpenFile.READABLE) != 0; + && (ptr.flags & OpenFile.READABLE) != 0; } private boolean writable() { return (flags & STRIO_WRITABLE) != 0 - && (getPtr().flags & OpenFile.WRITABLE) != 0; + && (ptr.flags & OpenFile.WRITABLE) != 0; } private boolean closed() { return !((flags & STRIO_READWRITE) != 0 - && (getPtr().flags & OpenFile.READWRITE) != 0); + && (ptr.flags & OpenFile.READWRITE) != 0); } /* rb: readable */ @@ -2200,12 +2211,11 @@ private void checkWritable() { } private void checkModifiable() { - checkFrozen(); - if (getPtr().string.isFrozen()) throw getRuntime().newIOError("not modifiable string"); + if (getPtrForWrite().string.isFrozen()) throw getRuntime().newIOError("not modifiable string"); } private void checkInitialized() { - if (getPtr() == null) { + if (ptr == null) { throw getRuntime().newIOError("uninitialized stream"); } }