@@ -141,7 +141,7 @@ public Encoding getEncoding() {
141141 }
142142
143143 RubyString string = ptr .string ;
144- if (!string .isNil ()) {
144+ if (string != null && !string .isNil ()) {
145145 return string .getEncoding ();
146146 }
147147
@@ -334,8 +334,7 @@ private void strioInit(ThreadContext context, int argc, IRubyObject arg0, IRubyO
334334 EncodingUtils .extractModeEncoding (context , ioEncodable , vmodeAndVpermP , maybeOptions , OFLAGS_UNUSED , FMODE_TL .get ());
335335
336336 // clear shared vmodeVperm
337- EncodingUtils .vmode (vmodeAndVpermP , null );
338- EncodingUtils .vperm (vmodeAndVpermP , null );
337+ clearVmodeVperm (vmodeAndVpermP );
339338
340339 ptr .flags = FMODE_TL .get ()[0 ];
341340
@@ -357,7 +356,9 @@ private void strioInit(ThreadContext context, int argc, IRubyObject arg0, IRubyO
357356 if (!string .isNil () && (ptr .flags & OpenFile .TRUNC ) != 0 ) {
358357 ((RubyString ) string ).clear ();
359358 }
360- ptr .string = (RubyString ) string ;
359+ if (string instanceof RubyString ) {
360+ ptr .string = (RubyString ) string ;
361+ }
361362 if (argc == 1 && !string .isNil ()) {
362363 ptr .enc = ((RubyString ) string ).getEncoding ();
363364 } else {
@@ -624,7 +625,7 @@ public IRubyObject eof(ThreadContext context) {
624625 }
625626
626627 private boolean isEndOfString () {
627- return ptr .pos >= ptr .string .size ();
628+ return ptr .string == null || ptr . pos >= ptr .string .size ();
628629 }
629630
630631 @ JRubyMethod (name = "getc" )
@@ -721,21 +722,25 @@ private static int bm_search(byte[] little, int lstart, int llen, byte[] big, in
721722
722723 @ JRubyMethod (name = "gets" , writes = FrameField .LASTLINE )
723724 public IRubyObject gets (ThreadContext context ) {
725+ if (ptr .string == null ) return context .nil ;
724726 return Getline .getlineCall (context , GETLINE , this , getEncoding ());
725727 }
726728
727729 @ JRubyMethod (name = "gets" , writes = FrameField .LASTLINE )
728730 public IRubyObject gets (ThreadContext context , IRubyObject arg0 ) {
731+ if (ptr .string == null ) return context .nil ;
729732 return Getline .getlineCall (context , GETLINE , this , getEncoding (), arg0 );
730733 }
731734
732735 @ JRubyMethod (name = "gets" , writes = FrameField .LASTLINE )
733736 public IRubyObject gets (ThreadContext context , IRubyObject arg0 , IRubyObject arg1 ) {
737+ if (ptr .string == null ) return context .nil ;
734738 return Getline .getlineCall (context , GETLINE , this , getEncoding (), arg0 , arg1 );
735739 }
736740
737741 @ JRubyMethod (name = "gets" , writes = FrameField .LASTLINE )
738742 public IRubyObject gets (ThreadContext context , IRubyObject arg0 , IRubyObject arg1 , IRubyObject arg2 ) {
743+ if (ptr .string == null ) return context .nil ;
739744 return Getline .getlineCall (context , GETLINE , this , getEncoding (), arg0 , arg1 , arg2 );
740745 }
741746
@@ -756,6 +761,8 @@ public IRubyObject gets(ThreadContext context, IRubyObject[] args) {
756761 }
757762
758763 private static final Getline .Callback <StringIO , IRubyObject > GETLINE = (context , self , rs , limit , chomp , block ) -> {
764+ if (self .isEndOfString ()) return context .nil ;
765+
759766 if (limit == 0 ) {
760767 return RubyString .newEmptyString (context .runtime , self .getEncoding ());
761768 }
@@ -772,6 +779,11 @@ public IRubyObject gets(ThreadContext context, IRubyObject[] args) {
772779 private static final Getline .Callback <StringIO , StringIO > GETLINE_YIELD = (context , self , rs , limit , chomp , block ) -> {
773780 IRubyObject line ;
774781
782+ StringIOData ptr = self .ptr ;
783+ if (ptr .string == null || ptr .pos > ptr .string .size ()) {
784+ return self ;
785+ }
786+
775787 if (limit == 0 ) {
776788 throw context .runtime .newArgumentError ("invalid limit: 0 for each_line" );
777789 }
@@ -790,6 +802,11 @@ public IRubyObject gets(ThreadContext context, IRubyObject[] args) {
790802 RubyArray <IRubyObject > ary = (RubyArray <IRubyObject >) context .runtime .newArray ();
791803 IRubyObject line ;
792804
805+ StringIOData ptr = self .ptr ;
806+ if (ptr .string == null || ptr .pos > ptr .string .size ()) {
807+ return null ;
808+ }
809+
793810 if (limit == 0 ) {
794811 throw context .runtime .newArgumentError ("invalid limit: 0 for readlines" );
795812 }
@@ -919,10 +936,11 @@ private static int chompNewlineWidth(byte[] bytes, int s, int e) {
919936 }
920937
921938 @ JRubyMethod (name = {"length" , "size" })
922- public IRubyObject length () {
939+ public IRubyObject length (ThreadContext context ) {
923940 checkInitialized ();
924- checkFinalized ();
925- return getRuntime ().newFixnum (ptr .string .size ());
941+ RubyString myString = ptr .string ;
942+ if (myString == null ) return RubyFixnum .zero (context .runtime );
943+ return getRuntime ().newFixnum (myString .size ());
926944 }
927945
928946 @ JRubyMethod (name = "lineno" )
@@ -995,10 +1013,12 @@ public IRubyObject putc(ThreadContext context, IRubyObject ch) {
9951013
9961014 checkModifiable ();
9971015 if (ch instanceof RubyString ) {
1016+ if (ptr .string == null ) return context .nil ;
9981017 str = substrString ((RubyString ) ch , str , runtime );
9991018 }
10001019 else {
10011020 byte c = RubyNumeric .num2chr (ch );
1021+ if (ptr .string == null ) return context .nil ;
10021022 str = RubyString .newString (runtime , new byte []{c });
10031023 }
10041024 write (context , str );
@@ -1059,6 +1079,10 @@ private IRubyObject readCommon(ThreadContext context, int argc, IRubyObject arg0
10591079 break ;
10601080 }
10611081 case 0 :
1082+ RubyString myString = ptr .string ;
1083+ if (myString == null ) {
1084+ return context .nil ;
1085+ }
10621086 len = ptr .string .size ();
10631087 if (len <= pos ) {
10641088 Encoding enc = binary ? ASCIIEncoding .INSTANCE : getEncoding ();
@@ -1287,7 +1311,6 @@ public IRubyObject seek(ThreadContext context, IRubyObject arg0, IRubyObject arg
12871311
12881312 private RubyFixnum seekCommon (ThreadContext context , int argc , IRubyObject arg0 , IRubyObject arg1 ) {
12891313 checkFrozen ();
1290- checkFinalized ();
12911314
12921315 Ruby runtime = context .runtime ;
12931316
@@ -1369,10 +1392,13 @@ public IRubyObject truncate(ThreadContext context, IRubyObject len) {
13691392
13701393 boolean locked = lock (context , ptr );
13711394 try {
1372- int plen = string .size ();
13731395 if (l < 0 ) {
13741396 throw context .runtime .newErrnoEINVALError ("negative legnth" );
13751397 }
1398+ if (string == null ) {
1399+ return RubyFixnum .zero (context .runtime );
1400+ }
1401+ int plen = string .size ();
13761402 string .resize (l );
13771403 ByteList buf = string .getByteList ();
13781404 if (plen < l ) {
@@ -1393,6 +1419,8 @@ public IRubyObject ungetc(ThreadContext context, IRubyObject arg) {
13931419 checkModifiable ();
13941420 checkReadable ();
13951421
1422+ if (ptr .string == null ) return context .nil ;
1423+
13961424 if (arg .isNil ()) return arg ;
13971425 if (arg instanceof RubyInteger ) {
13981426 int len , cc = RubyNumeric .num2int (arg );
@@ -1486,6 +1514,7 @@ public IRubyObject ungetbyte(ThreadContext context, IRubyObject arg) {
14861514 if (arg .isNil ()) return arg ;
14871515
14881516 checkModifiable ();
1517+ if (ptr .string == null ) return context .nil ;
14891518
14901519 if (arg instanceof RubyInteger ) {
14911520 ungetbyteCommon (context , ((RubyInteger ) ((RubyInteger ) arg ).op_mod (context , 256 )).getIntValue ());
@@ -1597,6 +1626,7 @@ private long stringIOWrite(ThreadContext context, Ruby runtime, IRubyObject arg)
15971626 boolean locked = lock (context , ptr );
15981627 try {
15991628 final Encoding enc = getEncoding ();
1629+ if (enc == null ) return 0 ;
16001630 final Encoding encStr = str .getEncoding ();
16011631 if (enc != encStr && enc != EncodingUtils .ascii8bitEncoding (runtime )
16021632 // this is a hack because we don't seem to handle incoming ASCII-8BIT properly in transcoder
@@ -1639,11 +1669,19 @@ private long stringIOWrite(ThreadContext context, Ruby runtime, IRubyObject arg)
16391669
16401670 @ JRubyMethod
16411671 public IRubyObject set_encoding (ThreadContext context , IRubyObject ext_enc ) {
1642- final Encoding enc ;
1672+ Encoding enc ;
16431673 if ( ext_enc .isNil () ) {
16441674 enc = EncodingUtils .defaultExternalEncoding (context .runtime );
16451675 } else {
1646- enc = EncodingUtils .rbToEncoding (context , ext_enc );
1676+ enc = context .runtime .getEncodingService ().getEncodingFromObjectNoError (ext_enc );
1677+ if (enc == null ) {
1678+ IOEncodable convconfig = new IOEncodable .ConvConfig ();
1679+ Object vmodeAndVpermP = VMODE_VPERM_TL .get ();
1680+ EncodingUtils .vmode (vmodeAndVpermP , ext_enc .convertToString ().prepend (context , context .runtime .newString ("r:" )));
1681+ EncodingUtils .extractModeEncoding (context , convconfig , vmodeAndVpermP , context .nil , OFLAGS_UNUSED , FMODE_TL .get ());
1682+ clearVmodeVperm (vmodeAndVpermP );
1683+ enc = convconfig .getEnc2 ();
1684+ }
16471685 }
16481686
16491687 StringIOData ptr = this .ptr ;
@@ -1653,8 +1691,8 @@ public IRubyObject set_encoding(ThreadContext context, IRubyObject ext_enc) {
16531691 ptr .enc = enc ;
16541692
16551693 // in read-only mode, StringIO#set_encoding no longer sets the encoding
1656- RubyString string ;
1657- if (writable () && ( string = ptr . string ) .getEncoding () != enc ) {
1694+ RubyString string = ptr . string ;
1695+ if (string != null && writable () && string .getEncoding () != enc ) {
16581696 string .modify ();
16591697 string .setEncoding (enc );
16601698 }
@@ -1665,6 +1703,11 @@ public IRubyObject set_encoding(ThreadContext context, IRubyObject ext_enc) {
16651703 return this ;
16661704 }
16671705
1706+ private static void clearVmodeVperm (Object vmodeAndVpermP ) {
1707+ EncodingUtils .vmode (vmodeAndVpermP , null );
1708+ EncodingUtils .vperm (vmodeAndVpermP , null );
1709+ }
1710+
16681711 @ JRubyMethod
16691712 public IRubyObject set_encoding (ThreadContext context , IRubyObject enc , IRubyObject ignored ) {
16701713 return set_encoding (context , enc );
@@ -2056,12 +2099,6 @@ private void checkInitialized() {
20562099 }
20572100 }
20582101
2059- private void checkFinalized () {
2060- if (ptr .string == null ) {
2061- throw getRuntime ().newIOError ("not opened" );
2062- }
2063- }
2064-
20652102 private void checkOpen () {
20662103 if (closed ()) {
20672104 throw getRuntime ().newIOError (RubyIO .CLOSED_STREAM_MSG );
0 commit comments