Skip to content

Commit 19e5152

Browse files
authored
Merge pull request #26172 from Catfish-Man/serrated-surrogates-5.1
Restore more-correct behavior of getting the full contents of bridged NSStrings containing invalid UTF-8
2 parents c2188ff + 46ed286 commit 19e5152

File tree

3 files changed

+34
-1
lines changed

3 files changed

+34
-1
lines changed

stdlib/public/core/StringGuts.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,23 @@ extension _StringGuts {
248248
) -> Int? {
249249
#if _runtime(_ObjC)
250250
// Currently, foreign means NSString
251-
return _cocoaStringCopyUTF8(_object.cocoaObject, into: mbp)
251+
if let res = _cocoaStringCopyUTF8(_object.cocoaObject, into: mbp) {
252+
return res
253+
}
254+
255+
// If the NSString contains invalid UTF8 (e.g. unpaired surrogates), we
256+
// can get nil from cocoaStringCopyUTF8 in situations where a character by
257+
// character loop would get something more useful like repaired contents
258+
var ptr = mbp.baseAddress._unsafelyUnwrappedUnchecked
259+
var numWritten = 0
260+
for cu in String(self).utf8 {
261+
guard numWritten < mbp.count else { return nil }
262+
ptr.initialize(to: cu)
263+
ptr += 1
264+
numWritten += 1
265+
}
266+
267+
return numWritten
252268
#else
253269
fatalError("No foreign strings on Linux in this version of Swift")
254270
#endif

test/stdlib/Inputs/FoundationBridge/FoundationBridge.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,13 @@ static inline BOOL NSStringBridgeTestEqual(NSString * _Nonnull a, NSString * _No
8585
return [a isEqual:b];
8686
}
8787

88+
static inline NSString *getNSStringWithUnpairedSurrogate() {
89+
unichar chars[16] = {
90+
0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
91+
0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
92+
0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
93+
0xD800 };
94+
return [NSString stringWithCharacters:chars length:1];
95+
}
96+
8897
NS_ASSUME_NONNULL_END

test/stdlib/TestNSString.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ class TestNSString : TestNSStringSuper {
5757
string.enumerateSubstrings(in: middleIndex..<string.endIndex, options: .byLines) { (_, _, _, _) in } //shouldn't crash
5858
}
5959

60+
func test_unpairedSurrogates() {
61+
let evil = getNSStringWithUnpairedSurrogate();
62+
print("\(evil)")
63+
}
64+
6065
}
6166

6267
#if !FOUNDATION_XCTEST
@@ -65,5 +70,8 @@ NSStringTests.test("test_equalOverflow") { TestNSString().test_equalOverflow() }
6570
NSStringTests.test("test_smallString_BOM") {
6671
TestNSString().test_smallString_BOM()
6772
}
73+
NSStringTests.test("test_unpairedSurrogates") {
74+
TestNSString().test_unpairedSurrogates()
75+
}
6876
runAllTests()
6977
#endif

0 commit comments

Comments
 (0)