13
13
import java .util .Arrays ;
14
14
import java .util .concurrent .locks .ReentrantLock ;
15
15
16
+ import com .oracle .truffle .api .CompilerAsserts ;
16
17
import com .oracle .truffle .api .TruffleSafepoint ;
17
18
import com .oracle .truffle .api .dsl .Bind ;
18
19
import com .oracle .truffle .api .dsl .GenerateCached ;
@@ -157,23 +158,25 @@ public abstract class CExtNodes {
157
158
public static final int RUBY_TAG_THROW = 0x7 ;
158
159
public static final int RUBY_TAG_FATAL = 0x8 ;
159
160
161
+ /** We need up to 4 \0 bytes for UTF-32. Always use 4 for speed rather than checking the encoding min length.
162
+ * Corresponds to TERM_LEN() in MRI. */
163
+ public static final int NATIVE_STRING_TERMINATOR_LENGTH = 4 ;
164
+
160
165
public static Pointer newNativeStringPointer (RubyLanguage language , RubyContext context , int capacity ) {
161
- // We need up to 4 \0 bytes for UTF-32. Always use 4 for speed rather than checking the encoding min length.
162
- Pointer pointer = Pointer .mallocAutoRelease (language , context , capacity + 4 );
166
+ Pointer pointer = Pointer .mallocAutoRelease (language , context , capacity + NATIVE_STRING_TERMINATOR_LENGTH );
163
167
pointer .writeInt (capacity , 0 );
164
168
return pointer ;
165
169
}
166
170
167
171
public static Pointer newZeroedNativeStringPointer (RubyLanguage language , RubyContext context , int capacity ) {
168
- // We need up to 4 \0 bytes for UTF-32. Always use 4 for speed rather than checking the encoding min length.
169
- return Pointer .callocAutoRelease (language , context , capacity + 4 );
172
+ return Pointer .callocAutoRelease (language , context , capacity + NATIVE_STRING_TERMINATOR_LENGTH );
170
173
}
171
174
172
175
private static long getNativeStringCapacity (Pointer pointer ) {
173
176
final long nativeBufferSize = pointer .getSize ();
174
177
assert nativeBufferSize > 0 ;
175
- // Do not count the extra byte for \0 , like MRI.
176
- return nativeBufferSize - 1 ;
178
+ // Do not count the extra terminator bytes , like MRI.
179
+ return nativeBufferSize - NATIVE_STRING_TERMINATOR_LENGTH ;
177
180
}
178
181
179
182
@ Primitive (name = "call_with_c_mutex_and_frame" )
@@ -712,7 +715,7 @@ public abstract static class RbEncCodeRangeClear extends CoreMethodArrayArgument
712
715
@ Specialization
713
716
RubyString clearCodeRange (RubyString string ,
714
717
@ Cached StringToNativeNode stringToNativeNode ) {
715
- stringToNativeNode .executeToNative (this , string );
718
+ stringToNativeNode .executeToNative (this , string , true );
716
719
string .clearCodeRange ();
717
720
718
721
return string ;
@@ -815,7 +818,7 @@ public abstract static class RbStrCapacityNode extends CoreMethodArrayArgumentsN
815
818
@ Specialization
816
819
long capacity (Object string ,
817
820
@ Cached StringToNativeNode stringToNativeNode ) {
818
- return getNativeStringCapacity (stringToNativeNode .executeToNative (this , string ));
821
+ return getNativeStringCapacity (stringToNativeNode .executeToNative (this , string , true ));
819
822
}
820
823
}
821
824
@@ -828,7 +831,7 @@ RubyString strSetLen(RubyString string, int newByteLength,
828
831
@ Cached StringToNativeNode stringToNativeNode ,
829
832
@ Cached MutableTruffleString .FromNativePointerNode fromNativePointerNode ,
830
833
@ Cached InlinedConditionProfile minLengthOneProfile ) {
831
- var pointer = stringToNativeNode .executeToNative (this , string );
834
+ var pointer = stringToNativeNode .executeToNative (this , string , true );
832
835
833
836
var encoding = libString .getEncoding (string );
834
837
int minLength = encoding .jcoding .minLength ();
@@ -858,7 +861,7 @@ RubyString rbStrResize(RubyString string, int newByteLength,
858
861
@ Cached RubyStringLibrary libString ,
859
862
@ Cached StringToNativeNode stringToNativeNode ,
860
863
@ Cached MutableTruffleString .FromNativePointerNode fromNativePointerNode ) {
861
- var pointer = stringToNativeNode .executeToNative (this , string );
864
+ var pointer = stringToNativeNode .executeToNative (this , string , true );
862
865
var tencoding = libString .getTEncoding (string );
863
866
int byteLength = string .tstring .byteLength (tencoding );
864
867
@@ -887,7 +890,7 @@ RubyString trStrCapaResize(RubyString string, int newCapacity,
887
890
@ Cached RubyStringLibrary libString ,
888
891
@ Cached StringToNativeNode stringToNativeNode ,
889
892
@ Cached MutableTruffleString .FromNativePointerNode fromNativePointerNode ) {
890
- var pointer = stringToNativeNode .executeToNative (this , string );
893
+ var pointer = stringToNativeNode .executeToNative (this , string , true );
891
894
var tencoding = libString .getTEncoding (string );
892
895
893
896
if (getNativeStringCapacity (pointer ) == newCapacity ) {
@@ -1325,24 +1328,29 @@ int rbHash(Object object,
1325
1328
}
1326
1329
}
1327
1330
1331
+ /** If inplace is true, this node mutates the RubyString to use native memory. It should be avoided unless there is
1332
+ * no other way because e.g. Regexp matching later on that String would then copy to managed byte[] back, and
1333
+ * copying back-and-forth is quite expensive. OTOH if the String will need to be used as native memory again soon
1334
+ * after and without needing to go to managed in between then it is valuable to avoid extra copies. */
1328
1335
@ GenerateInline
1329
1336
@ GenerateCached (false )
1330
1337
public abstract static class StringToNativeNode extends RubyBaseNode {
1331
1338
1332
- public abstract Pointer executeToNative (Node node , Object string );
1339
+ public abstract Pointer executeToNative (Node node , Object string , boolean inplace );
1333
1340
1334
1341
@ Specialization
1335
- static Pointer toNative (Node node , RubyString string ,
1342
+ static Pointer toNative (Node node , RubyString string , boolean inplace ,
1336
1343
@ Cached RubyStringLibrary libString ,
1337
1344
@ Cached InlinedConditionProfile convertProfile ,
1338
1345
@ Cached (inline = false ) TruffleString .CopyToNativeMemoryNode copyToNativeMemoryNode ,
1339
1346
@ Cached (inline = false ) MutableTruffleString .FromNativePointerNode fromNativePointerNode ,
1340
1347
@ Cached (inline = false ) TruffleString .GetInternalNativePointerNode getInternalNativePointerNode ) {
1348
+ CompilerAsserts .partialEvaluationConstant (inplace );
1349
+
1341
1350
var tstring = string .tstring ;
1342
1351
var tencoding = libString .getTEncoding (string );
1343
1352
1344
1353
final Pointer pointer ;
1345
-
1346
1354
if (convertProfile .profile (node , tstring .isNative ())) {
1347
1355
assert tstring .isMutable ();
1348
1356
pointer = (Pointer ) getInternalNativePointerNode .execute (tstring , tencoding );
@@ -1351,16 +1359,18 @@ static Pointer toNative(Node node, RubyString string,
1351
1359
pointer = allocateAndCopyToNative (getLanguage (node ), getContext (node ), tstring , tencoding , byteLength ,
1352
1360
copyToNativeMemoryNode );
1353
1361
1354
- var nativeTString = fromNativePointerNode .execute (pointer , 0 , byteLength , tencoding , false );
1355
- string .setTString (nativeTString );
1362
+ if (inplace ) {
1363
+ var nativeTString = fromNativePointerNode .execute (pointer , 0 , byteLength , tencoding , false );
1364
+ string .setTString (nativeTString );
1365
+ }
1356
1366
}
1357
1367
1358
1368
return pointer ;
1359
1369
}
1360
1370
1361
1371
@ Specialization
1362
- static Pointer toNativeImmutable (Node node , ImmutableRubyString string ) {
1363
- return string .getNativeString (getLanguage (node ));
1372
+ static Pointer toNativeImmutable (Node node , ImmutableRubyString string , boolean inplace ) {
1373
+ return string .getNativeString (getLanguage (node ), getContext ( node ) );
1364
1374
}
1365
1375
1366
1376
public static Pointer allocateAndCopyToNative (RubyLanguage language , RubyContext context ,
@@ -1379,17 +1389,34 @@ public abstract static class StringPointerToNativeNode extends PrimitiveArrayArg
1379
1389
@ Specialization
1380
1390
long toNative (Object string ,
1381
1391
@ Cached StringToNativeNode stringToNativeNode ) {
1382
- return stringToNativeNode .executeToNative (this , string ).getAddress ();
1392
+ return stringToNativeNode .executeToNative (this , string , true ).getAddress ();
1393
+ }
1394
+ }
1395
+
1396
+ @ CoreMethod (names = "string_to_ffi_pointer_inplace" , onSingleton = true , required = 1 )
1397
+ public abstract static class StringToFFIPointerInplaceNode extends CoreMethodArrayArgumentsNode {
1398
+
1399
+ @ Specialization
1400
+ RubyPointer toFFIPointerInplace (Object string ,
1401
+ @ Cached StringToNativeNode stringToNativeNode ) {
1402
+ var pointer = stringToNativeNode .executeToNative (this , string , true );
1403
+
1404
+ final RubyPointer instance = new RubyPointer (
1405
+ coreLibrary ().truffleFFIPointerClass ,
1406
+ getLanguage ().truffleFFIPointerShape ,
1407
+ pointer );
1408
+ AllocationTracing .trace (instance , this );
1409
+ return instance ;
1383
1410
}
1384
1411
}
1385
1412
1386
- @ CoreMethod (names = "string_to_ffi_pointer " , onSingleton = true , required = 1 )
1387
- public abstract static class StringToFFIPointerNode extends CoreMethodArrayArgumentsNode {
1413
+ @ CoreMethod (names = "string_to_ffi_pointer_copy " , onSingleton = true , required = 1 )
1414
+ public abstract static class StringToFFIPointerCopyNode extends CoreMethodArrayArgumentsNode {
1388
1415
1389
1416
@ Specialization
1390
- RubyPointer toNative (Object string ,
1417
+ RubyPointer toFFIPointerCopy (Object string ,
1391
1418
@ Cached StringToNativeNode stringToNativeNode ) {
1392
- var pointer = stringToNativeNode .executeToNative (this , string );
1419
+ var pointer = stringToNativeNode .executeToNative (this , string , false );
1393
1420
1394
1421
final RubyPointer instance = new RubyPointer (
1395
1422
coreLibrary ().truffleFFIPointerClass ,
0 commit comments