@@ -29,21 +29,6 @@ public typealias __TestContentRecord = (
29
29
reserved2: UInt
30
30
)
31
31
32
- /// Resign any pointers in a test content record.
33
- ///
34
- /// - Parameters:
35
- /// - record: The test content record to resign.
36
- ///
37
- /// - Returns: A copy of `record` with its pointers resigned.
38
- ///
39
- /// On platforms/architectures without pointer authentication, this function has
40
- /// no effect.
41
- private func _resign( _ record: __TestContentRecord ) -> __TestContentRecord {
42
- var record = record
43
- record. accessor = record. accessor. map ( swt_resign)
44
- return record
45
- }
46
-
47
32
// MARK: -
48
33
49
34
/// A protocol describing a type that can be stored as test content at compile
@@ -79,42 +64,68 @@ protocol TestContent: ~Copyable {
79
64
associatedtype TestContentAccessorHint : Sendable = Never
80
65
}
81
66
82
- extension TestContent where Self: ~ Copyable {
83
- /// Enumerate all test content records found in the given test content section
84
- /// in the current process that match this ``TestContent`` type.
67
+ // MARK: - Individual test content records
68
+
69
+ /// A type describing a test content record of a particular (known) type.
70
+ ///
71
+ /// Instances of this type can be created by calling
72
+ /// ``TestContent/allTestContentRecords()`` on a type that conforms to
73
+ /// ``TestContent``.
74
+ ///
75
+ /// This type is not part of the public interface of the testing library. In the
76
+ /// future, we could make it public if we want to support runtime discovery of
77
+ /// test content by second- or third-party code.
78
+ struct TestContentRecord < T> : Sendable where T: ~ Copyable {
79
+ /// The base address of the image containing this instance, if known.
85
80
///
86
- /// - Parameters:
87
- /// - sectionBounds: The bounds of the section to inspect .
81
+ /// This property is not available on platforms such as WASI that statically
82
+ /// link to the testing library .
88
83
///
89
- /// - Returns: A sequence of tuples. Each tuple contains an instance of
90
- /// `__TestContentRecord` and the base address of the image containing that
91
- /// test content record. Only test content records matching this
92
- /// ``TestContent`` type's requirements are included in the sequence.
93
- private static func _testContentRecords( in sectionBounds: SectionBounds ) -> some Sequence < ( imageAddress: UnsafeRawPointer ? , record: __TestContentRecord ) > {
94
- sectionBounds. buffer. withMemoryRebound ( to: __TestContentRecord. self) { records in
95
- records. lazy
96
- . filter { $0. kind == testContentKind }
97
- . map ( _resign)
98
- . map { ( sectionBounds. imageAddress, $0) }
99
- }
84
+ /// - Note: The value of this property is distinct from the pointer returned
85
+ /// by `dlopen()` (on platforms that have that function) and cannot be used
86
+ /// with interfaces such as `dlsym()` that expect such a pointer.
87
+ #if SWT_NO_DYNAMIC_LINKING
88
+ @available ( * , unavailable, message: " Image addresses are not available on this platform. " )
89
+ #endif
90
+ nonisolated ( unsafe) var imageAddress: UnsafeRawPointer ?
91
+
92
+ /// The underlying test content record loaded from a metadata section.
93
+ private var _record : __TestContentRecord
94
+
95
+ fileprivate init ( imageAddress: UnsafeRawPointer ? , record: __TestContentRecord ) {
96
+ #if !SWT_NO_DYNAMIC_LINKING
97
+ self . imageAddress = imageAddress
98
+ #endif
99
+ self . _record = record
100
+ }
101
+ }
102
+
103
+ // This `T: TestContent` constraint is in an extension in order to work around a
104
+ // compiler crash. SEE: rdar://143049814
105
+ extension TestContentRecord where T: TestContent & ~ Copyable {
106
+ /// The context value for this test content record.
107
+ var context : UInt {
108
+ _record. context
100
109
}
101
110
102
- /// Call the given accessor function .
111
+ /// Load the value represented by this record .
103
112
///
104
113
/// - Parameters:
105
- /// - accessor: The C accessor function of a test content record matching
106
- /// this type.
107
- /// - hint: A pointer to a kind-specific hint value. If not `nil`, this
108
- /// value is passed to `accessor`, allowing that function to determine if
109
- /// its record matches before initializing its out-result.
114
+ /// - hint: An optional hint value. If not `nil`, this value is passed to
115
+ /// the accessor function of the underlying test content record.
110
116
///
111
- /// - Returns: An instance of this type's accessor result or `nil` if an
112
- /// instance could not be created (or if `hint` did not match.)
117
+ /// - Returns: An instance of the associated ``TestContentAccessorResult``
118
+ /// type, or `nil` if the underlying test content record did not match
119
+ /// `hint` or otherwise did not produce a value.
113
120
///
114
- /// The caller is responsible for ensuring that `accessor` corresponds to a
115
- /// test content record of this type.
116
- private static func _callAccessor( _ accessor: SWTTestContentAccessor , withHint hint: TestContentAccessorHint ? ) -> TestContentAccessorResult ? {
117
- withUnsafeTemporaryAllocation ( of: TestContentAccessorResult . self, capacity: 1 ) { buffer in
121
+ /// If this function is called more than once on the same instance, a new
122
+ /// value is created on each call.
123
+ func load( withHint hint: T . TestContentAccessorHint ? = nil ) -> T . TestContentAccessorResult ? {
124
+ guard let accessor = _record. accessor. map ( swt_resign) else {
125
+ return nil
126
+ }
127
+
128
+ return withUnsafeTemporaryAllocation ( of: T . TestContentAccessorResult. self, capacity: 1 ) { buffer in
118
129
let initialized = if let hint {
119
130
withUnsafePointer ( to: hint) { hint in
120
131
accessor ( buffer. baseAddress!, hint)
@@ -128,46 +139,28 @@ extension TestContent where Self: ~Copyable {
128
139
return buffer. baseAddress!. move ( )
129
140
}
130
141
}
142
+ }
131
143
132
- /// The type of callback called by ``enumerateTestContent(withHint:_:)``.
133
- ///
134
- /// - Parameters:
135
- /// - imageAddress: A pointer to the start of the image. This value is _not_
136
- /// equal to the value returned from `dlopen()`. On platforms that do not
137
- /// support dynamic loading (and so do not have loadable images), the
138
- /// value of this argument is unspecified.
139
- /// - content: The value produced by the test content record's accessor.
140
- /// - context: Context associated with `content`. The value of this argument
141
- /// is dependent on the type of test content being enumerated.
142
- /// - stop: An `inout` boolean variable indicating whether test content
143
- /// enumeration should stop after the function returns. Set `stop` to
144
- /// `true` to stop test content enumeration.
145
- typealias TestContentEnumerator = ( _ imageAddress: UnsafeRawPointer ? , _ content: borrowing TestContentAccessorResult , _ context: UInt , _ stop: inout Bool ) -> Void
144
+ // MARK: - Enumeration of test content records
146
145
147
- /// Enumerate all test content of this type known to Swift and found in the
148
- /// current process.
146
+ extension TestContent where Self: ~ Copyable {
147
+ /// Get all test content of this type known to Swift and found in the current
148
+ /// process.
149
149
///
150
- /// - Parameters:
151
- /// - hint: An optional hint value. If not `nil`, this value is passed to
152
- /// the accessor function of each test content record whose `kind` field
153
- /// matches this type's ``testContentKind`` property.
154
- /// - body: A function to invoke, once per matching test content record.
150
+ /// - Returns: A sequence of instances of ``TestContentRecord``. Only test
151
+ /// content records matching this ``TestContent`` type's requirements are
152
+ /// included in the sequence.
155
153
///
156
- /// This function uses a callback instead of producing a sequence because it
157
- /// is used with move-only types (specifically ``ExitTest``) and
158
- /// `Sequence.Element` must be copyable.
159
- static func enumerateTestContent( withHint hint: TestContentAccessorHint ? = nil , _ body: TestContentEnumerator ) {
160
- let testContentRecords = SectionBounds . all ( . testContent) . lazy. flatMap ( _testContentRecords ( in: ) )
161
-
162
- var stop = false
163
- for (imageAddress, record) in testContentRecords {
164
- if let accessor = record. accessor, let result = _callAccessor ( accessor, withHint: hint) {
165
- // Call the callback.
166
- body ( imageAddress, result, record. context, & stop)
167
- if stop {
168
- break
169
- }
154
+ /// - Bug: This function returns an instance of `AnySequence` instead of an
155
+ /// opaque type due to a compiler crash. ([143080508](rdar://143080508))
156
+ static func allTestContentRecords( ) -> AnySequence < TestContentRecord < Self > > {
157
+ let result = SectionBounds . all ( . testContent) . lazy. flatMap { sb in
158
+ sb. buffer. withMemoryRebound ( to: __TestContentRecord. self) { records in
159
+ records. lazy
160
+ . filter { $0. kind == testContentKind }
161
+ . map { TestContentRecord < Self > ( imageAddress: sb. imageAddress, record: $0) }
170
162
}
171
163
}
164
+ return AnySequence ( result)
172
165
}
173
166
}
0 commit comments