Skip to content

Commit 3adf6d6

Browse files
authored
Cache fully-qualified type name components derived from strings. (#1624)
This PR augments the cache used in `TypeInfo` to also cache split fully-qualified name strings. This improves overall performance as Xcode spends a fair amount of time looking up instances of `TypeInfo` this way. ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated.
1 parent 6ab7fbf commit 3adf6d6

File tree

1 file changed

+30
-5
lines changed

1 file changed

+30
-5
lines changed

Sources/Testing/Parameterization/TypeInfo.swift

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,17 @@ extension TypeInfo {
193193
return String(String.UnicodeScalarView(result))
194194
}
195195

196+
/// Keys into the fully-qualified name cache.
197+
private enum _CacheKey: Sendable, Equatable, Hashable {
198+
/// The key is a type (cast to `ObjectIdentifier`).
199+
case type(ObjectIdentifier)
200+
201+
/// The key is an unsplit fully-qualified name string.
202+
case string(String)
203+
}
204+
196205
/// An in-memory cache of fully-qualified type name components.
197-
private static let _fullyQualifiedNameComponentsCache = Mutex<[ObjectIdentifier: [String]]>()
206+
private static let _fullyQualifiedNameComponentsCache = Mutex<[_CacheKey: [String]]>()
198207

199208
/// Split the given fully-qualified type name into its components.
200209
///
@@ -203,6 +212,13 @@ extension TypeInfo {
203212
///
204213
/// - Returns: The components of `fullyQualifiedName` as substrings thereof.
205214
static func fullyQualifiedNameComponents(ofTypeWithName fullyQualifiedName: String) -> [String] {
215+
let cachedResult = _fullyQualifiedNameComponentsCache.withLock { cache in
216+
return cache[.string(fullyQualifiedName)]
217+
}
218+
if let cachedResult {
219+
return cachedResult
220+
}
221+
206222
var components = rawIdentifierAwareSplit(fullyQualifiedName, separator: ".")
207223

208224
// If a type is extended in another module and then referenced by name,
@@ -217,7 +233,7 @@ extension TypeInfo {
217233
components[0] = moduleName
218234
}
219235

220-
return components.lazy
236+
let result: [String] = components.lazy
221237
.filter { component in
222238
// If a type is private or embedded in a function, its fully qualified
223239
// name may include "(unknown context at $xxxxxxxx)" as a component.
@@ -232,6 +248,12 @@ extension TypeInfo {
232248
component
233249
}
234250
}.map(String.init)
251+
252+
_fullyQualifiedNameComponentsCache.withLock { cache in
253+
cache[.string(fullyQualifiedName)] = result
254+
}
255+
256+
return result
235257
}
236258

237259
/// The complete name of this type, with the names of all referenced types
@@ -252,14 +274,17 @@ extension TypeInfo {
252274
public var fullyQualifiedNameComponents: [String] {
253275
switch _kind {
254276
case let .type(type):
255-
if let cachedResult = Self._fullyQualifiedNameComponentsCache.rawValue[ObjectIdentifier(type)] {
277+
let cachedResult = Self._fullyQualifiedNameComponentsCache.withLock { cache in
278+
return cache[.type(ObjectIdentifier(type))]
279+
}
280+
if let cachedResult {
256281
return cachedResult
257282
}
258283

259284
let result = Self.fullyQualifiedNameComponents(ofTypeWithName: String(reflecting: type))
260285

261-
Self._fullyQualifiedNameComponentsCache.withLock { fullyQualifiedNameComponentsCache in
262-
fullyQualifiedNameComponentsCache[ObjectIdentifier(type)] = result
286+
Self._fullyQualifiedNameComponentsCache.withLock { cache in
287+
cache[.type(ObjectIdentifier(type))] = result
263288
}
264289

265290
return result

0 commit comments

Comments
 (0)