@@ -940,7 +940,7 @@ private void strioExtend(ThreadContext context, int pos, int len) {
940940 if (pos + len > olen ) {
941941 string .resize (pos + len );
942942 if (pos > olen ) {
943- string . modify19 ( );
943+ modifyString ( string );
944944 ByteList ptrByteList = string .getByteList ();
945945 // zero the gap
946946 int begin = ptrByteList .getBegin ();
@@ -950,7 +950,7 @@ private void strioExtend(ThreadContext context, int pos, int len) {
950950 (byte ) 0 );
951951 }
952952 } else {
953- string . modify19 ( );
953+ modifyString ( string );
954954 }
955955 } finally {
956956 if (locked ) unlock (ptr );
@@ -962,11 +962,11 @@ private void strioExtend(ThreadContext context, int pos, int len) {
962962 public IRubyObject putc (ThreadContext context , IRubyObject ch ) {
963963 Ruby runtime = context .runtime ;
964964 checkWritable ();
965- IRubyObject str ;
965+ IRubyObject str = null ;
966966
967967 checkModifiable ();
968968 if (ch instanceof RubyString ) {
969- str = ((RubyString )ch ). substr19 ( runtime , 0 , 1 );
969+ str = substrString ((RubyString ) ch , str , runtime );
970970 }
971971 else {
972972 byte c = RubyNumeric .num2chr (ch );
@@ -1501,20 +1501,54 @@ public IRubyObject write(ThreadContext context, IRubyObject[] args) {
15011501 }
15021502
15031503 private static final MethodHandle CAT_WITH_CODE_RANGE ;
1504+ private static final MethodHandle MODIFY_AND_CLEAR_CODE_RANGE ;
1505+ private static final MethodHandle SUBSTR_ENC ;
15041506
15051507 static {
1506- MethodHandle cat ;
1508+ MethodHandle cat , modify , substr ;
1509+ MethodHandles .Lookup lookup = MethodHandles .publicLookup ();
15071510 try {
1508- cat = MethodHandles .publicLookup ().findVirtual (RubyString .class , "catWithCodeRange" , MethodType .methodType (RubyString .class , RubyString .class ));
1511+ cat = lookup .findVirtual (RubyString .class , "catWithCodeRange" , MethodType .methodType (RubyString .class , RubyString .class ));
1512+ modify = lookup .findVirtual (RubyString .class , "modifyAndClearCodeRange" , MethodType .methodType (void .class ));
1513+ substr = lookup .findVirtual (RubyString .class , "substrEnc" , MethodType .methodType (IRubyObject .class , Ruby .class , int .class , int .class ));
15091514 } catch (NoSuchMethodException | IllegalAccessException ex ) {
15101515 try {
1511- cat = MethodHandles .publicLookup ().findVirtual (RubyString .class , "cat19" , MethodType .methodType (RubyString .class , RubyString .class ));
1516+ cat = lookup .findVirtual (RubyString .class , "cat19" , MethodType .methodType (RubyString .class , RubyString .class ));
1517+ modify = lookup .findVirtual (RubyString .class , "modify19" , MethodType .methodType (void .class ));
1518+ substr = lookup .findVirtual (RubyString .class , "substr19" , MethodType .methodType (IRubyObject .class , Ruby .class , int .class , int .class ));
15121519 } catch (NoSuchMethodException | IllegalAccessException ex2 ) {
15131520 throw new ExceptionInInitializerError (ex2 );
15141521 }
15151522 }
15161523
15171524 CAT_WITH_CODE_RANGE = cat ;
1525+ MODIFY_AND_CLEAR_CODE_RANGE = modify ;
1526+ SUBSTR_ENC = substr ;
1527+ }
1528+
1529+ private static void catString (RubyString myString , RubyString str ) {
1530+ try {
1531+ RubyString unused = (RubyString ) CAT_WITH_CODE_RANGE .invokeExact (myString , str );
1532+ } catch (Throwable t ) {
1533+ throw new RuntimeException (t );
1534+ }
1535+ }
1536+
1537+ private static void modifyString (RubyString string ) {
1538+ try {
1539+ MODIFY_AND_CLEAR_CODE_RANGE .invokeExact (string );
1540+ } catch (Throwable t ) {
1541+ Helpers .throwException (t );
1542+ }
1543+ }
1544+
1545+ private static IRubyObject substrString (RubyString ch , IRubyObject str , Ruby runtime ) {
1546+ try {
1547+ str = (IRubyObject ) SUBSTR_ENC .invokeExact (ch , runtime , 0 , 1 );
1548+ } catch (Throwable t ) {
1549+ Helpers .throwException (t );
1550+ }
1551+ return str ;
15181552 }
15191553
15201554 // MRI: strio_write
@@ -1549,11 +1583,7 @@ private long stringIOWrite(ThreadContext context, Ruby runtime, IRubyObject arg)
15491583 if (enc == EncodingUtils .ascii8bitEncoding (runtime ) || encStr == EncodingUtils .ascii8bitEncoding (runtime )) {
15501584 EncodingUtils .encStrBufCat (runtime , myString , strByteList , enc );
15511585 } else {
1552- try {
1553- RubyString unused = (RubyString ) CAT_WITH_CODE_RANGE .invokeExact (myString , str );
1554- } catch (Throwable t ) {
1555- throw new RuntimeException (t );
1556- }
1586+ catString (myString , str );
15571587 }
15581588 } else {
15591589 strioExtend (context , pos , len );
0 commit comments