Skip to content

Commit 101216e

Browse files
authored
Merge pull request #1803 from broadwaylamb/master
2 parents a9c0598 + 9eca3c0 commit 101216e

File tree

2 files changed

+53
-8
lines changed

2 files changed

+53
-8
lines changed

Foundation/JSONSerialization.swift

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -738,19 +738,40 @@ private struct JSONReader {
738738
return nil
739739
}
740740

741-
if !UTF16.isLeadSurrogate(codeUnit) {
741+
let isLeadSurrogate = UTF16.isLeadSurrogate(codeUnit)
742+
let isTrailSurrogate = UTF16.isTrailSurrogate(codeUnit)
743+
744+
guard isLeadSurrogate || isTrailSurrogate else {
745+
// The code units that are neither lead surrogates nor trail surrogates
746+
// form valid unicode scalars.
742747
return (String(UnicodeScalar(codeUnit)!), index)
743748
}
744749

745-
guard let (trailCodeUnit, finalIndex) = try consumeASCIISequence("\\u", input: index).flatMap(parseCodeUnit) , UTF16.isTrailSurrogate(trailCodeUnit) else {
746-
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.propertyListReadCorrupt.rawValue, userInfo: [
747-
"NSDebugDescription" : "Unable to convert unicode escape sequence (no low-surrogate code point) to UTF8-encoded character at position \(source.distanceFromStart(input))"
748-
])
750+
// Surrogates must always come in pairs.
751+
752+
guard isLeadSurrogate else {
753+
// Trail surrogate must come after lead surrogate
754+
throw CocoaError.error(.propertyListReadCorrupt,
755+
userInfo: [
756+
"NSDebugDescription" : """
757+
Unable to convert unicode escape sequence (no high-surrogate code point) \
758+
to UTF8-encoded character at position \(source.distanceFromStart(input))
759+
"""
760+
])
761+
}
762+
763+
guard let (trailCodeUnit, finalIndex) = try consumeASCIISequence("\\u", input: index).flatMap(parseCodeUnit),
764+
UTF16.isTrailSurrogate(trailCodeUnit) else {
765+
throw CocoaError.error(.propertyListReadCorrupt,
766+
userInfo: [
767+
"NSDebugDescription" : """
768+
Unable to convert unicode escape sequence (no low-surrogate code point) \
769+
to UTF8-encoded character at position \(source.distanceFromStart(input))
770+
"""
771+
])
749772
}
750773

751-
let highValue = (UInt32(codeUnit - 0xD800) << 10)
752-
let lowValue = UInt32(trailCodeUnit - 0xDC00)
753-
return (String(UnicodeScalar(highValue + lowValue + 0x10000)!), finalIndex)
774+
return (String(UTF16.decode(UTF16.EncodedScalar([codeUnit, trailCodeUnit]))), finalIndex)
754775
}
755776

756777
func isHexChr(_ byte: UInt8) -> Bool {

TestFoundation/TestJSONSerialization.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ extension TestJSONSerialization {
115115
("test_deserialize_invalidValueInArray_withData", test_deserialize_invalidValueInArray_withData),
116116
("test_deserialize_badlyFormedArray_withData", test_deserialize_badlyFormedArray_withData),
117117
("test_deserialize_invalidEscapeSequence_withData", test_deserialize_invalidEscapeSequence_withData),
118+
("test_deserialize_unicodeMissingLeadingSurrogate_withData", test_deserialize_unicodeMissingLeadingSurrogate_withData),
118119
("test_deserialize_unicodeMissingTrailingSurrogate_withData", test_deserialize_unicodeMissingTrailingSurrogate_withData),
119120

120121
//Deserialization with Stream
@@ -146,6 +147,7 @@ extension TestJSONSerialization {
146147
("test_deserialize_invalidValueInArray_withStream", test_deserialize_invalidValueInArray_withStream),
147148
("test_deserialize_badlyFormedArray_withStream", test_deserialize_badlyFormedArray_withStream),
148149
("test_deserialize_invalidEscapeSequence_withStream", test_deserialize_invalidEscapeSequence_withStream),
150+
("test_deserialize_unicodeMissingLeadingSurrogate_withStream", test_deserialize_unicodeMissingLeadingSurrogate_withStream),
149151
("test_deserialize_unicodeMissingTrailingSurrogate_withStream", test_deserialize_unicodeMissingTrailingSurrogate_withStream),
150152
("test_JSONObjectWithStream_withFile", test_JSONObjectWithStream_withFile),
151153
("test_JSONObjectWithStream_withURL", test_JSONObjectWithStream_withURL),
@@ -242,6 +244,10 @@ extension TestJSONSerialization {
242244
deserialize_invalidEscapeSequence(objectType: .data)
243245
}
244246

247+
func test_deserialize_unicodeMissingLeadingSurrogate_withData() {
248+
deserialize_unicodeMissingLeadingSurrogate(objectType: .data)
249+
}
250+
245251
func test_deserialize_unicodeMissingTrailingSurrogate_withData() {
246252
deserialize_unicodeMissingTrailingSurrogate(objectType: .data)
247253
}
@@ -336,6 +342,10 @@ extension TestJSONSerialization {
336342
deserialize_invalidEscapeSequence(objectType: .stream)
337343
}
338344

345+
func test_deserialize_unicodeMissingLeadingSurrogate_withStream() {
346+
deserialize_unicodeMissingLeadingSurrogate(objectType: .stream)
347+
}
348+
339349
func test_deserialize_unicodeMissingTrailingSurrogate_withStream() {
340350
deserialize_unicodeMissingTrailingSurrogate(objectType: .stream)
341351
}
@@ -738,6 +748,20 @@ extension TestJSONSerialization {
738748
}
739749
}
740750

751+
func deserialize_unicodeMissingLeadingSurrogate(objectType: ObjectType) {
752+
let subject = "[\"\\uDFF3\"]"
753+
do {
754+
guard let data = subject.data(using: .utf8) else {
755+
XCTFail("Unable to convert string to data")
756+
return
757+
}
758+
let _ = try getjsonObjectResult(data, objectType) as? [String]
759+
XCTFail("Expected error: Missing Leading Surrogate")
760+
} catch {
761+
// Passing case; the unicode character is malformed
762+
}
763+
}
764+
741765
func deserialize_unicodeMissingTrailingSurrogate(objectType: ObjectType) {
742766
let subject = "[\"\\uD834\"]"
743767
do {

0 commit comments

Comments
 (0)