Skip to content

Commit ba6158d

Browse files
committed
[test] Internalize _StringGuts; Add shared testing struct; NFC
Create a _StringRepresentation struct to standardize internal testing on. Internalize much of _StringGuts, except for some SPI hacks, and update tests to use _StringRepresentation.
1 parent 1859ae4 commit ba6158d

14 files changed

+187
-115
lines changed

β€Žstdlib/public/core/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,14 @@ set(SWIFTLIB_ESSENTIAL
134134
StringObject.swift
135135
StringProtocol.swift
136136
StringIndex.swift
137+
StringIndexConversions.swift
137138
StringInterpolation.swift
138139
StringLegacy.swift
140+
StringNormalization.swift
139141
StringRangeReplaceableCollection.swift
140142
StringStorage.swift
141143
StringSwitch.swift
142-
StringIndexConversions.swift
143-
StringNormalization.swift
144+
StringTesting.swift
144145
StringUnicodeScalarView.swift
145146
StringUTF16View.swift
146147
StringUTF8View.swift

β€Žstdlib/public/core/GroupInfo.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"StringRangeReplaceableCollection.swift",
3333
"StringStorage.swift",
3434
"StringSwitch.swift",
35+
"StringTesting.swift",
3536
"StringUTF16View.swift",
3637
"StringUTF8View.swift",
3738
"StringUnicodeScalarView.swift",

β€Žstdlib/public/core/String.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,8 @@ extension String {
540540
/// [equivalence]: http://www.unicode.org/glossary/#canonical_equivalent
541541
@_fixed_layout
542542
public struct String {
543-
public var _guts: _StringGuts
543+
@usableFromInline
544+
internal var _guts: _StringGuts
544545

545546
/// Creates an empty string.
546547
///
@@ -555,8 +556,7 @@ public struct String {
555556
}
556557

557558
@inlinable // FIXME(sil-serialize-all)
558-
public // @testable
559-
init(_ _guts: _StringGuts) {
559+
internal init(_ _guts: _StringGuts) {
560560
self._guts = _guts
561561
}
562562
}

β€Žstdlib/public/core/StringBridge.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ internal func _bridgeASCIICocoaString(
136136
return length == numCharWritten ? count : nil
137137
}
138138

139-
public // @testable
140-
func _bridgeToCocoa(_ small: _SmallUTF8String) -> _CocoaString {
139+
@usableFromInline
140+
internal func _bridgeToCocoa(_ small: _SmallUTF8String) -> _CocoaString {
141141
return small.withUTF8CodeUnits { bufPtr in
142142
return _swift_stdlib_CFStringCreateWithBytes(
143143
nil, bufPtr.baseAddress._unsafelyUnwrappedUnchecked,

β€Žstdlib/public/core/StringGuts.swift

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,7 @@ extension _StringGuts {
100100

101101
@inlinable
102102
@inline(__always)
103-
public // @testable
104-
mutating func _isUniqueNative() -> Bool {
103+
internal mutating func _isUniqueNative() -> Bool {
105104
guard _isNative else { return false }
106105
// Note that the isUnique test must be in a separate statement;
107106
// `isNative && _isUnique` always evaluates to false in debug builds,
@@ -121,8 +120,7 @@ extension _StringGuts {
121120

122121
extension _StringGuts {
123122
@inlinable
124-
public // @testable
125-
var isASCII: Bool {
123+
internal var isASCII: Bool {
126124
@inline(__always) get { return _object.isContiguousASCII }
127125
}
128126

@@ -135,40 +133,34 @@ extension _StringGuts {
135133
}
136134

137135
@inlinable
138-
public // @testable
139-
var _isNative: Bool {
136+
internal var _isNative: Bool {
140137
return _object.isNative
141138
}
142139

143140
#if _runtime(_ObjC)
144141
@inlinable
145-
public // @testable
146-
var _isCocoa: Bool {
142+
internal var _isCocoa: Bool {
147143
return _object.isCocoa
148144
}
149145
#endif
150146

151147
@inlinable
152-
public // @testable
153-
var _isUnmanaged: Bool {
148+
internal var _isUnmanaged: Bool {
154149
return _object.isUnmanaged
155150
}
156151

157152
@inlinable
158-
public // @testable
159-
var _isSmall: Bool {
153+
internal var _isSmall: Bool {
160154
return _object.isSmall
161155
}
162156

163157
@inlinable
164-
public // @testable
165-
var _owner: AnyObject? {
158+
internal var _owner: AnyObject? {
166159
return _object.owner
167160
}
168161

169162
@inlinable
170-
public // @testable
171-
var isSingleByte: Bool {
163+
internal var isSingleByte: Bool {
172164
// FIXME: Currently used to sometimes mean contiguous ASCII
173165
return _object.isSingleByte
174166
}
@@ -180,8 +172,7 @@ extension _StringGuts {
180172
}
181173

182174
@inlinable
183-
public // @testable
184-
var byteWidth: Int {
175+
internal var byteWidth: Int {
185176
return _object.byteWidth
186177
}
187178

@@ -215,8 +206,7 @@ extension _StringGuts {
215206
extension _StringGuts {
216207
@inlinable
217208
@inline(__always)
218-
public // @testable
219-
init() {
209+
internal init() {
220210
self.init(object: _StringObject(), otherBits: 0)
221211
_invariantCheck()
222212
}
@@ -474,8 +464,8 @@ extension _StringGuts {
474464
/// Return the object identifier for the reference counted heap object
475465
/// referred to by this string (if any). This is useful for testing allocation
476466
/// behavior.
477-
public // @testable
478-
var _objectIdentifier: ObjectIdentifier? {
467+
@usableFromInline
468+
internal var _objectIdentifier: ObjectIdentifier? {
479469
if _object.isNative {
480470
return ObjectIdentifier(_object.nativeRawStorage)
481471
}
@@ -793,8 +783,7 @@ extension _StringGuts {
793783
}
794784

795785
@inlinable
796-
public // @testable
797-
var count: Int {
786+
internal var count: Int {
798787
if _slowPath(!_hasStoredCount) {
799788
return _nonStoredCount
800789
}
@@ -823,8 +812,7 @@ extension _StringGuts {
823812
}
824813

825814
@inlinable
826-
public // @testable
827-
var capacity: Int {
815+
internal var capacity: Int {
828816
if _fastPath(_object.isNative) {
829817
return _object.nativeRawStorage.capacity
830818
}
@@ -839,8 +827,7 @@ extension _StringGuts {
839827

840828
/// Get the UTF-16 code unit stored at the specified position in this string.
841829
@inlinable // FIXME(sil-serialize-all)
842-
public // @testable
843-
subscript(position: Int) -> UTF16.CodeUnit {
830+
internal subscript(position: Int) -> UTF16.CodeUnit {
844831
if _slowPath(_isOpaque) {
845832
return _opaquePosition(position)
846833
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
// Declarations to enable ease-of-testing
14+
15+
public // @testable
16+
struct _StringRepresentation {
17+
public var _isASCII: Bool
18+
public var _count: Int
19+
public var _capacity: Int
20+
21+
public enum _Form {
22+
case _small
23+
case _cocoa(object: AnyObject)
24+
case _native(object: AnyObject)
25+
case _immortal(address: UInt)
26+
}
27+
public var _form: _Form
28+
29+
public var _objectIdentifier: ObjectIdentifier? {
30+
switch _form {
31+
case ._cocoa(let object): return ObjectIdentifier(object)
32+
case ._native(let object): return ObjectIdentifier(object)
33+
default: return nil
34+
}
35+
}
36+
}
37+
38+
extension String {
39+
public // @testable
40+
func _classify() -> _StringRepresentation {
41+
var result = _StringRepresentation(
42+
_isASCII: _guts._isASCIIOrSmallASCII,
43+
_count: _guts.count,
44+
_capacity: _guts.capacity,
45+
_form: ._small
46+
)
47+
if _guts._isSmall {
48+
return result
49+
}
50+
if _guts._isNative {
51+
result._form = ._native(object: _guts._owner!)
52+
return result
53+
}
54+
if _guts._isCocoa {
55+
result._form = ._cocoa(object: _guts._owner!)
56+
return result
57+
}
58+
if _guts._isUnmanaged {
59+
result._form = ._immortal(
60+
address: UInt(bitPattern: _guts._unmanagedRawStart))
61+
return result
62+
}
63+
fatalError()
64+
}
65+
}
66+

β€Žtest/stdlib/BridgedObjectDebuggerSupport.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ func debugVal<T>(_ x: inout T) -> String {
3030

3131
// Check if @x uses the small-string or Cocoa representations.
3232
func hasSmallStringOrCocoaVariant(_ x: String) -> Bool {
33-
return x._guts._isCocoa || x._guts._isSmall
33+
switch x._classify()._form {
34+
case ._small: return true
35+
case ._cocoa: return true
36+
default: return false
37+
}
3438
}
3539

3640
StringForPrintObjectTests.test("Basic") {

β€Žtest/stdlib/Character.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,9 @@ func checkUnicodeScalars(_ s: String) {
256256
}
257257

258258
func checkRepresentation(_ s: String) {
259+
let utf16 = Array(s.utf16)
259260
let expectSmall
260-
= s.utf16.count < 4 || s.utf16.count == 4 && s._guts[3] < 0x8000
261+
= utf16.count < 4 || utf16.count == 4 && utf16[3] < 0x8000
261262
let isSmall = isSmallRepresentation(s)
262263

263264
let expectedSize = expectSmall ? "small" : "large"

β€Žtest/stdlib/NewString.swift

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,37 +18,37 @@ func repr(_ x: NSString) -> String {
1818
return "\(NSStringFromClass(object_getClass(x)!))\(hexAddrVal(x)) = \"\(x)\""
1919
}
2020

21-
func repr(_ x: _StringGuts) -> String {
22-
if x._isNative {
21+
func repr(_ x: _StringRepresentation) -> String {
22+
switch x._form {
23+
case ._small:
2324
return """
24-
Native(\
25-
owner: \(hexAddrVal(x._owner)), \
26-
count: \(x.count), \
27-
capacity: \(x.capacity))
25+
Small(count: \(x._count))
2826
"""
29-
} else if x._isCocoa {
27+
case ._cocoa(let object):
3028
return """
3129
Cocoa(\
32-
owner: \(hexAddrVal(x._owner)), \
33-
count: \(x.count))
30+
owner: \(hexAddrVal(object)), \
31+
count: \(x._count))
3432
"""
35-
} else if x._isSmall {
33+
case ._native(let object):
3634
return """
37-
Small(count: \(x.count))
35+
Native(\
36+
owner: \(hexAddrVal(object)), \
37+
count: \(x._count), \
38+
capacity: \(x._capacity))
3839
"""
39-
} else if x._isUnmanaged {
40+
case ._immortal(_):
4041
return """
41-
Unmanaged(count: \(x.count))
42+
Unmanaged(count: \(x._count))
4243
"""
4344
}
44-
return "?????"
4545
}
4646

4747
func repr(_ x: String) -> String {
48-
return "String(\(repr(x._guts))) = \"\(x)\""
48+
return "String(\(repr(x._classify()))) = \"\(x)\""
4949
}
5050

51-
// CHECK: Testing
51+
// CHECK-LABEL: Testing...
5252
print("Testing...")
5353

5454
//===--------- Native Strings ---------===
@@ -77,7 +77,7 @@ func nonASCII() {
7777
// CHECK-NEXT: has UTF-16: true
7878
print("has UTF-16: \(CFStringGetCharactersPtr(unsafeBitCast(nsUTF16, to: CFString.self)) != nil)")
7979

80-
// CHECK: --- UTF-16 basic round-tripping ---
80+
// CHECK-LABEL: --- UTF-16 basic round-tripping ---
8181
print("--- UTF-16 basic round-tripping ---")
8282

8383
// check that no extraneous objects are created
@@ -127,7 +127,7 @@ func ascii() {
127127
print("has ASCII pointer: \(CFStringGetCStringPtr(unsafeBitCast(nsASCII, to: CFString.self), 0x0600) != nil)")
128128
print("has ASCII pointer: \(CFStringGetCStringPtr(unsafeBitCast(nsASCII, to: CFString.self), 0x08000100) != nil)")
129129

130-
// CHECK: --- ASCII basic round-tripping ---
130+
// CHECK-LABEL: --- ASCII basic round-tripping ---
131131
print("--- ASCII basic round-tripping ---")
132132

133133
// CHECK-NEXT: [[nsstringclass:(__NSCFString|NSTaggedPointerString)]]@[[asciiaddress:[x0-9a-f]+]] = "foobar"
@@ -141,12 +141,12 @@ func ascii() {
141141
let nsRoundTripASCII = newNSASCII as NSString
142142
print(" \(repr(nsRoundTripASCII))")
143143

144-
// CHECK: --- ASCII slicing ---
144+
// CHECK-LABEL: --- ASCII slicing ---
145145
print("--- ASCII slicing ---")
146146

147147
let i3 = newNSASCII.index(newNSASCII.startIndex, offsetBy: 3)
148148
let i6 = newNSASCII.index(newNSASCII.startIndex, offsetBy: 6)
149-
149+
150150
// Slicing the String
151151
print(" \(repr(String(newNSASCII[i3..<i6])))")
152152

@@ -163,27 +163,30 @@ ascii()
163163

164164
// String literals default to UTF-16.
165165

166-
// CHECK: --- Literals ---
166+
// CHECK-LABEL: --- Literals ---
167167
print("--- Literals ---")
168168

169169
// CHECK-NEXT: String({{Unmanaged|Small}}(count: 6)) = "foobar"
170-
// X_CHECK-NEXT: true // FIXME: _StringGuts's isASCII should be renamed
170+
// CHECK-NEXT: true
171171
let asciiLiteral: String = "foobar"
172172
print(" \(repr(asciiLiteral))")
173-
//print(" \(asciiLiteral._guts.isASCII)")
173+
print(" \(asciiLiteral._classify()._isASCII)")
174174

175175
// CHECK-NEXT: String(Unmanaged(count: 11)) = "πŸ‚β˜ƒβ…β†β„οΈŽβ›„οΈβ„οΈ"
176176
// CHECK-NEXT: false
177177
let nonASCIILiteral: String = "πŸ‚β˜ƒβ…β†β„οΈŽβ›„οΈβ„οΈ"
178178
print(" \(repr(nonASCIILiteral))")
179-
print(" \(nonASCIILiteral._guts.isASCII)")
179+
print(" \(nonASCIILiteral._classify()._isASCII)")
180180

181181
// ===------- Appending -------===
182182

183183
// These tests are in NewStringAppending.swift.
184184

185185
// ===---------- Comparison --------===
186186

187+
// CHECK-LABEL: --- Comparison ---
188+
print("--- Comparison ---")
189+
187190
var s = "ABCDEF"
188191
let s1 = s + "G"
189192

0 commit comments

Comments
Β (0)