@@ -29,21 +29,6 @@ public typealias __TestContentRecord = (
2929 reserved2: UInt
3030)
3131
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-
4732// MARK: -
4833
4934/// A protocol describing a type that can be stored as test content at compile
@@ -79,42 +64,68 @@ protocol TestContent: ~Copyable {
7964 associatedtype TestContentAccessorHint : Sendable = Never
8065}
8166
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.
8580 ///
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 .
8883 ///
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
100109 }
101110
102- /// Call the given accessor function .
111+ /// Load the value represented by this record .
103112 ///
104113 /// - 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.
110116 ///
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.
113120 ///
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
118129 let initialized = if let hint {
119130 withUnsafePointer ( to: hint) { hint in
120131 accessor ( buffer. baseAddress!, hint)
@@ -128,46 +139,28 @@ extension TestContent where Self: ~Copyable {
128139 return buffer. baseAddress!. move ( )
129140 }
130141 }
142+ }
131143
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
146145
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.
149149 ///
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.
155153 ///
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) }
170162 }
171163 }
164+ return AnySequence ( result)
172165 }
173166}
0 commit comments