File: TypeInfo.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (501 lines) | stat: -rw-r--r-- 18,581 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

/// A description of the type of a value encountered during testing or a
/// parameter of a test function.
@_spi(ForToolsIntegrationOnly)
public struct TypeInfo: Sendable {
  /// An enumeration defining backing storage for an instance of ``TypeInfo``.
  private enum _Kind: Sendable {
    /// The type info represents a concrete metatype.
    ///
    /// - Parameters:
    ///   - type: The concrete metatype.
    case type(_ type: any ~Copyable.Type)

    /// The type info represents a metatype, but a reference to that metatype is
    /// not available at runtime.
    ///
    /// - Parameters:
    ///   - fullyQualifiedComponents: The fully-qualified name components of the
    ///     type.
    ///   - unqualified: The unqualified name of the type.
    ///   - mangled: The mangled name of the type, if available.
    case nameOnly(fullyQualifiedComponents: [String], unqualified: String, mangled: String?)
  }

  /// The kind of type info.
  private var _kind: _Kind

  /// The described type, if available.
  ///
  /// If this instance was created from a type name, or if it was previously
  /// encoded and decoded, the value of this property is `nil`.
  public var type: (any ~Copyable.Type)? {
    if case let .type(type) = _kind {
      return type
    }
    return nil
  }

  /// Initialize an instance of this type with the specified names.
  ///
  /// - Parameters:
  ///   - fullyQualifiedComponents: The fully-qualified name components of the
  ///     type.
  ///   - unqualified: The unqualified name of the type.
  ///   - mangled: The mangled name of the type, if available.
  init(fullyQualifiedNameComponents: [String], unqualifiedName: String, mangledName: String? = nil) {
    _kind = .nameOnly(
      fullyQualifiedComponents: fullyQualifiedNameComponents,
      unqualified: unqualifiedName,
      mangled: mangledName
    )
  }

  /// Initialize an instance of this type with the specified names.
  ///
  /// - Parameters:
  ///   - fullyQualifiedName: The fully-qualified name of the type, with its
  ///     components separated by a period character (`"."`).
  ///   - unqualified: The unqualified name of the type.
  ///   - mangled: The mangled name of the type, if available.
  init(fullyQualifiedName: String, unqualifiedName: String, mangledName: String?) {
    self.init(
      fullyQualifiedNameComponents: Self.fullyQualifiedNameComponents(ofTypeWithName: fullyQualifiedName),
      unqualifiedName: unqualifiedName,
      mangledName: mangledName
    )
  }

  /// Initialize an instance of this type describing the specified type.
  ///
  /// - Parameters:
  ///   - type: The type which this instance should describe.
  init(describing type: any ~Copyable.Type) {
    _kind = .type(type)
  }

  /// Initialize an instance of this type describing the type of the specified
  /// value.
  ///
  /// - Parameters:
  ///   - value: The value whose type this instance should describe.
  init(describingTypeOf value: Any) {
    self.init(describing: Swift.type(of: value))
  }
}

// MARK: - Name

/// Split a string with a separator while respecting raw identifiers and their
/// enclosing backtick characters.
///
/// - Parameters:
///   - string: The string to split.
///   - separator: The character that separates components of `string`.
///   - maxSplits: The maximum number of splits to perform on `string`. The
///     resulting array contains up to `maxSplits + 1` elements.
///
/// - Returns: An array of substrings of `string`.
///
/// Unlike `String.split(separator:maxSplits:omittingEmptySubsequences:)`, this
/// function does not split the string on separator characters that occur
/// between pairs of backtick characters. This is useful when splitting strings
/// containing raw identifiers.
///
/// - Complexity: O(_n_), where _n_ is the length of `string`.
func rawIdentifierAwareSplit<S>(_ string: S, separator: Character, maxSplits: Int = .max) -> [S.SubSequence] where S: StringProtocol {
  var result = [S.SubSequence]()

  var inRawIdentifier = false
  var componentStartIndex = string.startIndex
  for i in string.indices {
    let c = string[i]
    if c == "`" {
      // We are either entering or exiting a raw identifier. While inside a raw
      // identifier, separator characters are ignored.
      inRawIdentifier.toggle()
    } else if c == separator && !inRawIdentifier {
      // Add everything up to this separator as the next component, then start
      // a new component after the separator.
      result.append(string[componentStartIndex ..< i])
      componentStartIndex = string.index(after: i)

      if result.count == maxSplits {
        // We don't need to find more separators. We'll add the remainder of the
        // string outside the loop as the last component, then return.
        break
      }
    }
  }
  result.append(string[componentStartIndex...])

  return result
}

extension TypeInfo {
  /// Replace any non-breaking spaces in the given string with normal spaces.
  ///
  /// - Parameters:
  ///   - rawIdentifier: The string to rewrite.
  ///
  /// - Returns: A copy of `rawIdentifier` with non-breaking spaces (`U+00A0`)
  ///   replaced with normal spaces (`U+0020`).
  ///
  /// When the Swift runtime demangles a raw identifier, it [replaces](https://github.com/swiftlang/swift/blob/d033eec1aa427f40dcc38679d43b83d9dbc06ae7/lib/Basic/Mangler.cpp#L250)
  /// normal ASCII spaces with non-breaking spaces to maintain compatibility
  /// with historical usages of spaces in mangled name forms. Non-breaking
  /// spaces are not otherwise valid in raw identifiers, so this transformation
  /// is reversible.
  private static func _rewriteNonBreakingSpacesAsASCIISpaces(in rawIdentifier: some StringProtocol) -> String? {
    let nbsp = "\u{00A0}" as UnicodeScalar

    // If there are no non-breaking spaces in the string, exit early to avoid
    // any further allocations.
    let unicodeScalars = rawIdentifier.unicodeScalars
    guard unicodeScalars.contains(nbsp) else {
      return nil
    }

    // Replace non-breaking spaces, then construct a new string from the
    // resulting sequence.
    let result = unicodeScalars.lazy.map { $0 == nbsp ? " " : $0 }
    return String(String.UnicodeScalarView(result))
  }

  /// An in-memory cache of fully-qualified type name components.
  private static let _fullyQualifiedNameComponentsCache = Locked<[ObjectIdentifier: [String]]>()

  /// Split the given fully-qualified type name into its components.
  ///
  /// - Parameters:
  ///   - fullyQualifiedName: The string to split.
  ///
  /// - Returns: The components of `fullyQualifiedName` as substrings thereof.
  static func fullyQualifiedNameComponents(ofTypeWithName fullyQualifiedName: String) -> [String] {
    var components = rawIdentifierAwareSplit(fullyQualifiedName, separator: ".")

    // If a type is extended in another module and then referenced by name,
    // its name according to the String(reflecting:) API will be prefixed with
    // "(extension in MODULE_NAME):". For our purposes, we never want to
    // preserve that prefix.
    if let firstComponent = components.first, firstComponent.starts(with: "(extension in "),
       let moduleName = rawIdentifierAwareSplit(firstComponent, separator: ":", maxSplits: 1).last {
      // NOTE: even if the module name is a raw identifier, it comprises a
      // single identifier (no splitting required) so we don't need to process
      // it any further.
      components[0] = moduleName
    }

    return components.lazy
      .filter { component in
        // If a type is private or embedded in a function, its fully qualified
        // name may include "(unknown context at $xxxxxxxx)" as a component.
        // Strip those out as they're uninteresting to us.
        !component.starts(with: "(unknown context at")
      }.map { component in
        // Replace non-breaking spaces with spaces. See the helper function's
        // documentation for more information.
        if let component = _rewriteNonBreakingSpacesAsASCIISpaces(in: component) {
          component[...]
        } else {
          component
        }
      }.map(String.init)
  }

  /// The complete name of this type, with the names of all referenced types
  /// fully-qualified by their module names when possible.
  ///
  /// The value of this property is equal to ``fullyQualifiedName``, but is
  /// split into components. For instance, given the following declaration in
  /// the `Example` module:
  ///
  /// ```swift
  /// struct A {
  ///   struct B {}
  /// }
  /// ```
  ///
  /// The value of this property for the type `A.B` would be
  /// `["Example", "A", "B"]`.
  public var fullyQualifiedNameComponents: [String] {
    switch _kind {
    case let .type(type):
      if let cachedResult = Self._fullyQualifiedNameComponentsCache.rawValue[ObjectIdentifier(type)] {
        return cachedResult
      }

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

      Self._fullyQualifiedNameComponentsCache.withLock { fullyQualifiedNameComponentsCache in
        fullyQualifiedNameComponentsCache[ObjectIdentifier(type)] = result
      }

      return result
    case let .nameOnly(fullyQualifiedNameComponents, _, _):
      return fullyQualifiedNameComponents
    }
  }

  /// The complete name of this type, with the names of all referenced types
  /// fully-qualified by their module names when possible.
  ///
  /// The value of this property is equal to ``fullyQualifiedNameComponents``,
  /// but is represented as a single string. For instance, given the following
  /// declaration in the `Example` module:
  ///
  /// ```swift
  /// struct A {
  ///   struct B {}
  /// }
  /// ```
  ///
  /// The value of this property for the type `A.B` would be `"Example.A.B"`.
  public var fullyQualifiedName: String {
    fullyQualifiedNameComponents.joined(separator: ".")
  }

  /// A simplified name of this type, by leaving the names of all referenced
  /// types unqualified, i.e. without module name prefixes.
  ///
  /// The value of this property is equal to the name of the type in isolation.
  /// For instance, given the following declaration in the `Example` module:
  ///
  /// ```swift
  /// struct A {
  ///   struct B {}
  /// }
  /// ```
  ///
  /// The value of this property for the type `A.B` would simply be `"B"`.
  public var unqualifiedName: String {
    switch _kind {
    case let .type(type):
      // Replace non-breaking spaces with spaces. See the helper function's
      // documentation for more information.
      var result = String(describing: type)
      result = Self._rewriteNonBreakingSpacesAsASCIISpaces(in: result) ?? result

      return result
    case let .nameOnly(_, unqualifiedName, _):
      return unqualifiedName
    }
  }

  /// The mangled name of this type as determined by the Swift compiler, if
  /// available.
  ///
  /// This property is used by other members of ``TypeInfo``. It should not be
  /// exposed as API or SPI because the mangled name of a type may include
  /// components derived at runtime that vary between processes. A type's
  /// mangled name should not be used if its unmangled name is sufficient.
  ///
  /// If the underlying Swift interface is unavailable or if the Swift runtime
  /// could not determine the mangled name of the represented type, the value of
  /// this property is `nil`.
  var mangledName: String? {
    guard #available(_mangledTypeNameAPI, *) else {
      return nil
    }
    switch _kind {
    case let .type(type):
#if compiler(>=6.1)
      return _mangledTypeName(type)
#else
      return _mangledTypeName(unsafeBitCast(type, to: Any.Type.self))
#endif
    case let .nameOnly(_, _, mangledName):
      return mangledName
    }
  }
}

// MARK: - Properties

extension TypeInfo {
  /// Whether or not the described type is a Swift `enum` type.
  ///
  /// Per the [Swift mangling ABI](https://github.com/swiftlang/swift/blob/main/docs/ABI/Mangling.rst),
  /// enumeration types are mangled as `"O"`.
  ///
  /// - Bug: We use the internal Swift standard library function
  ///   `_mangledTypeName()` to derive this information. We should use supported
  ///   API instead. ([swift-#69147](https://github.com/swiftlang/swift/issues/69147))
  var isSwiftEnumeration: Bool {
    mangledName?.last == "O"
  }

  /// Whether or not the described type is imported from C, C++, or Objective-C.
  ///
  /// Per the [Swift mangling ABI](https://github.com/swiftlang/swift/blob/main/docs/ABI/Mangling.rst),
  /// types imported from C-family languages are placed in a single flat `__C`
  /// module. That module has a standardized mangling of `"So"`. The presence of
  /// those characters at the start of a type's mangled name indicates that it
  /// is an imported type.
  ///
  /// - Bug: We use the internal Swift standard library function
  ///   `_mangledTypeName()` to derive this information. We should use supported
  ///   API instead. ([swift-#69146](https://github.com/swiftlang/swift/issues/69146))
  var isImportedFromC: Bool {
    guard let mangledName, mangledName.count > 2 else {
      return false
    }

    let prefixEndIndex = mangledName.index(mangledName.startIndex, offsetBy: 2)
    return mangledName[..<prefixEndIndex] == "So"
  }
}

/// Check if a class is a subclass (or equal to) another class.
///
/// - Parameters:
///   - subclass: The (possible) subclass to check.
///   - superclass The (possible) superclass to check.
///
/// - Returns: Whether `subclass` is a subclass of, or is equal to,
///   `superclass`.
func isClass(_ subclass: AnyClass, subclassOf superclass: AnyClass) -> Bool {
  if subclass == superclass {
    true
  } else if let subclassImmediateSuperclass = _getSuperclass(subclass) {
    isClass(subclassImmediateSuperclass, subclassOf: superclass)
  } else {
    false
  }
}

// MARK: - CustomStringConvertible, CustomDebugStringConvertible, CustomTestStringConvertible

extension TypeInfo: CustomStringConvertible, CustomDebugStringConvertible {
  public var description: String {
    unqualifiedName
  }

  public var debugDescription: String {
    fullyQualifiedName
  }
}

// MARK: - Equatable, Hashable

extension TypeInfo: Hashable {
  /// Check if this instance describes a given type.
  ///
  /// - Parameters:
  ///   - type: The type to compare against.
  ///
  /// - Returns: Whether or not this instance represents `type`.
  public func describes(_ type: Any.Type) -> Bool {
    self == TypeInfo(describing: type)
  }

  public static func ==(lhs: Self, rhs: Self) -> Bool {
    switch (lhs._kind, rhs._kind) {
    case let (.type(lhs), .type(rhs)):
      return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
    default:
      return lhs.fullyQualifiedNameComponents == rhs.fullyQualifiedNameComponents
    }
  }

  public func hash(into hasher: inout Hasher) {
    hasher.combine(fullyQualifiedName)
  }
}

// MARK: - ObjectIdentifier support

extension ObjectIdentifier {
  /// Initialize an instance of this type from a type reference.
  ///
  /// - Parameters:
  ///   - type: The type to initialize this instance from.
  ///
  /// - Bug: The standard library should support this conversion.
  ///   ([134276458](rdar://134276458), [134415960](rdar://134415960))
  fileprivate init(_ type: any ~Copyable.Type) {
    self.init(unsafeBitCast(type, to: Any.Type.self))
  }
}

// MARK: - Codable

extension TypeInfo: Codable {
  /// A simplified version of ``TypeInfo`` suitable for encoding and decoding.
  fileprivate struct EncodedForm {
    /// The complete name of this type, with the names of all referenced types
    /// fully-qualified by their module names when possible.
    public var fullyQualifiedName: String

    /// A simplified name of this type, by leaving the names of all referenced
    /// types unqualified, i.e. without module name prefixes.
    public var unqualifiedName: String

    /// The mangled name of this type as determined by the Swift compiler, if
    /// available.
    public var mangledName: String?
  }

  public func encode(to encoder: any Encoder) throws {
    let encodedForm = EncodedForm(fullyQualifiedName: fullyQualifiedName, unqualifiedName: unqualifiedName, mangledName: mangledName)
    try encodedForm.encode(to: encoder)
  }

  public init(from decoder: any Decoder) throws {
    let encodedForm = try EncodedForm(from: decoder)
    self.init(fullyQualifiedName: encodedForm.fullyQualifiedName, unqualifiedName: encodedForm.unqualifiedName, mangledName: encodedForm.mangledName)
  }
}

extension TypeInfo.EncodedForm: Codable {}

// MARK: - Custom casts

/// Cast the given data pointer to a C function pointer.
///
/// - Parameters:
///   - address: The C function pointer as an untyped data pointer.
///   - type: The type of the C function. This type must be a function type with
///     the "C" convention (i.e. `@convention (...) -> ...`).
///
/// - Returns: `address` cast to the given C function type.
///
/// This function serves to make code that casts C function pointers more
/// self-documenting. In debug builds, it checks that `type` is a C function
/// type. In release builds, it behaves the same as `unsafeBitCast(_:to:)`.
func castCFunction<T>(at address: UnsafeRawPointer, to type: T.Type) -> T {
#if DEBUG
  if let mangledName = TypeInfo(describing: T.self).mangledName {
    precondition(mangledName.last == "C", "\(#function) should only be used to cast a pointer to a C function type.")
  }
#endif
  return unsafeBitCast(address, to: type)
}

/// Cast the given C function pointer to a data pointer.
///
/// - Parameters:
///   - function: The C function pointer.
///
/// - Returns: `function` cast to an untyped data pointer.
///
/// This function serves to make code that casts C function pointers more
/// self-documenting. In debug builds, it checks that `function` is a C function
/// pointer. In release builds, it behaves the same as `unsafeBitCast(_:to:)`.
func castCFunction<T>(_ function: T, to _: UnsafeRawPointer.Type) -> UnsafeRawPointer {
#if DEBUG
  if let mangledName = TypeInfo(describing: T.self).mangledName {
    precondition(mangledName.last == "C", "\(#function) should only be used to cast a C function.")
  }
#endif
  return unsafeBitCast(function, to: UnsafeRawPointer.self)
}