diff --git a/Sources/Testing/Discovery+Platform.swift b/Sources/Testing/Discovery+Platform.swift index 7fcb94fe5..1615c2384 100644 --- a/Sources/Testing/Discovery+Platform.swift +++ b/Sources/Testing/Discovery+Platform.swift @@ -72,36 +72,20 @@ private let _startCollectingSectionBounds: Void = { return } - // If this image contains the Swift section we need, acquire the lock and + // If this image contains the Swift section(s) we need, acquire the lock and // store the section's bounds. - let testContentSectionBounds: SectionBounds? = { + func findSectionBounds(forSectionNamed segmentName: String, _ sectionName: String, ofKind kind: SectionBounds.Kind) { var size = CUnsignedLong(0) - if let start = getsectiondata(mh, "__DATA_CONST", "__swift5_tests", &size), size > 0 { + if let start = getsectiondata(mh, segmentName, sectionName, &size), size > 0 { let buffer = UnsafeRawBufferPointer(start: start, count: Int(clamping: size)) - return SectionBounds(imageAddress: mh, buffer: buffer) - } - return nil - }() - - let typeMetadataSectionBounds: SectionBounds? = { - var size = CUnsignedLong(0) - if let start = getsectiondata(mh, "__TEXT", "__swift5_types", &size), size > 0 { - let buffer = UnsafeRawBufferPointer(start: start, count: Int(clamping: size)) - return SectionBounds(imageAddress: mh, buffer: buffer) - } - return nil - }() - - if testContentSectionBounds != nil || typeMetadataSectionBounds != nil { - _sectionBounds.withLock { sectionBounds in - if let testContentSectionBounds { - sectionBounds[.testContent]!.append(testContentSectionBounds) - } - if let typeMetadataSectionBounds { - sectionBounds[.typeMetadata]!.append(typeMetadataSectionBounds) + let sb = SectionBounds(imageAddress: mh, buffer: buffer) + _sectionBounds.withLock { sectionBounds in + sectionBounds[kind]!.append(sb) } } } + findSectionBounds(forSectionNamed: "__DATA_CONST", "__swift5_tests", ofKind: .testContent) + findSectionBounds(forSectionNamed: "__TEXT", "__swift5_types", ofKind: .typeMetadata) } #if _runtime(_ObjC) diff --git a/Sources/Testing/Test+Discovery+Legacy.swift b/Sources/Testing/Test+Discovery+Legacy.swift index d330104ef..e24fca55d 100644 --- a/Sources/Testing/Test+Discovery+Legacy.swift +++ b/Sources/Testing/Test+Discovery+Legacy.swift @@ -57,15 +57,13 @@ let exitTestContainerTypeNameMagic = "__🟠$exit_test_body__" /// /// - Returns: A sequence of Swift types whose names contain `nameSubstring`. func types(withNamesContaining nameSubstring: String) -> some Sequence { - SectionBounds.all(.typeMetadata).lazy - .map { sb in - var count = 0 - let start = swt_copyTypes(in: sb.buffer.baseAddress!, sb.buffer.count, withNamesContaining: nameSubstring, count: &count) - defer { - free(start) - } - return start.withMemoryRebound(to: Any.Type.self, capacity: count) { start in - Array(UnsafeBufferPointer(start: start, count: count)) - } - }.joined() + SectionBounds.all(.typeMetadata).lazy.flatMap { sb in + var count = 0 + let start = swt_copyTypes(in: sb.buffer.baseAddress!, sb.buffer.count, withNamesContaining: nameSubstring, count: &count) + defer { + free(start) + } + return UnsafeBufferPointer(start: start, count: count) + .withMemoryRebound(to: Any.Type.self) { Array($0) } + } } diff --git a/Sources/_TestingInternals/Discovery.cpp b/Sources/_TestingInternals/Discovery.cpp index 08ffc5f47..f66eaf874 100644 --- a/Sources/_TestingInternals/Discovery.cpp +++ b/Sources/_TestingInternals/Discovery.cpp @@ -10,6 +10,12 @@ #include "Discovery.h" +#include +#include +#include +#include +#include + #if defined(SWT_NO_DYNAMIC_LINKING) #pragma mark - Statically-linked section bounds @@ -31,103 +37,22 @@ static const char typeMetadataSectionBegin = 0; static const char& typeMetadataSectionEnd = typeMetadataSectionBegin; #endif -/// The bounds of the test content section statically linked into the image -/// containing Swift Testing. const void *_Nonnull const SWTTestContentSectionBounds[2] = { - &testContentSectionBegin, - &testContentSectionEnd + &testContentSectionBegin, &testContentSectionEnd }; -/// The bounds of the type metadata section statically linked into the image -/// containing Swift Testing. const void *_Nonnull const SWTTypeMetadataSectionBounds[2] = { - &typeMetadataSectionBegin, - &typeMetadataSectionEnd + &typeMetadataSectionBegin, &typeMetadataSectionEnd }; #endif -#pragma mark - Legacy test discovery - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/// Enumerate over all Swift type metadata sections in the current process. -/// -/// - Parameters: -/// - body: A function to call once for every section in the current process. -/// A pointer to the first type metadata record and the number of records -/// are passed to this function. -template -static void enumerateTypeMetadataSections(const SectionEnumerator& body); - -/// A type that acts as a C++ [Allocator](https://en.cppreference.com/w/cpp/named_req/Allocator) -/// without using global `operator new` or `operator delete`. -/// -/// This type is necessary because global `operator new` and `operator delete` -/// can be overridden in developer-supplied code and cause deadlocks or crashes -/// when subsequently used while holding a dyld- or libobjc-owned lock. Using -/// `std::malloc()` and `std::free()` allows the use of C++ container types -/// without this risk. -template -struct SWTHeapAllocator { - using value_type = T; - - T *allocate(size_t count) { - return reinterpret_cast(std::calloc(count, sizeof(T))); - } - - void deallocate(T *ptr, size_t count) { - std::free(ptr); - } -}; - -/// A structure describing the bounds of a Swift metadata section. -/// -/// The template argument `T` is the element type of the metadata section. -/// Instances of this type can be used with a range-based `for`-loop to iterate -/// the contents of the section. -template -struct SWTSectionBounds { - /// The base address of the image containing the section, if known. - const void *imageAddress; - - /// The base address of the section. - const void *start; - - /// The size of the section in bytes. - size_t size; - - const struct SWTTypeMetadataRecord *begin(void) const { - return reinterpret_cast(start); - } - - const struct SWTTypeMetadataRecord *end(void) const { - return reinterpret_cast(reinterpret_cast(start) + size); - } -}; - -/// A type that acts as a C++ [Container](https://en.cppreference.com/w/cpp/named_req/Container) -/// and which contains a sequence of instances of `SWTSectionBounds`. -template -using SWTSectionBoundsList = std::vector, SWTHeapAllocator>>; - #pragma mark - Swift ABI #if defined(__PTRAUTH_INTRINSICS__) -#include -#define SWT_PTRAUTH __ptrauth +#define SWT_PTRAUTH_SWIFT_TYPE_DESCRIPTOR __ptrauth(ptrauth_key_process_independent_data, 1, 0xae86) #else -#define SWT_PTRAUTH(...) +#define SWT_PTRAUTH_SWIFT_TYPE_DESCRIPTOR #endif -#define SWT_PTRAUTH_SWIFT_TYPE_DESCRIPTOR SWT_PTRAUTH(ptrauth_key_process_independent_data, 1, 0xae86) /// A type representing a pointer relative to itself. /// @@ -164,10 +89,6 @@ struct SWTRelativePointer { #endif return reinterpret_cast(result); } - - const T *_Nullable operator ->(void) const& { - return get(); - } }; /// A type representing a 32-bit absolute function pointer, usually used on platforms @@ -184,10 +105,6 @@ struct SWTAbsoluteFunctionPointer { const T *_Nullable get(void) const & { return _pointer; } - - const T *_Nullable operator ->(void) const & { - return get(); - } }; /// A type representing a pointer relative to itself with low bits reserved for @@ -270,14 +187,16 @@ struct SWTTypeMetadataRecord { } }; -#pragma mark - +#pragma mark - Legacy test discovery void **swt_copyTypesWithNamesContaining(const void *sectionBegin, size_t sectionSize, const char *nameSubstring, size_t *outCount) { - SWTSectionBounds sb = { nullptr, sectionBegin, sectionSize }; - std::vector> result; + void **result = nullptr; + size_t resultCount = 0; - for (const auto& record : sb) { - auto contextDescriptor = record.getContextDescriptor(); + auto records = reinterpret_cast(sectionBegin); + size_t recordCount = sectionSize / sizeof(SWTTypeMetadataRecord); + for (size_t i = 0; i < recordCount; i++) { + auto contextDescriptor = records[i].getContextDescriptor(); if (!contextDescriptor) { // This type metadata record is invalid (or we don't understand how to // get its context descriptor), so skip it. @@ -297,14 +216,19 @@ void **swt_copyTypesWithNamesContaining(const void *sectionBegin, size_t section } if (void *typeMetadata = contextDescriptor->getMetadata()) { - result.push_back(typeMetadata); + if (!result) { + // This is the first matching type we've found. That presumably means + // we'll find more, so allocate enough space for all remaining types in + // the section. Is this necessarily space-efficient? No, but this + // allocation is short-lived and is immediately copied and freed in the + // Swift caller. + result = reinterpret_cast(std::calloc(recordCount - i, sizeof(void *))); + } + result[resultCount] = typeMetadata; + resultCount += 1; } } - auto resultCopy = reinterpret_cast(std::calloc(sizeof(void *), result.size())); - if (resultCopy) { - std::uninitialized_move(result.begin(), result.end(), resultCopy); - *outCount = result.size(); - } - return resultCopy; + *outCount = resultCount; + return result; } diff --git a/Sources/_TestingInternals/Versions.cpp b/Sources/_TestingInternals/Versions.cpp index 9af19c50c..97eace99e 100644 --- a/Sources/_TestingInternals/Versions.cpp +++ b/Sources/_TestingInternals/Versions.cpp @@ -10,12 +10,6 @@ #include "Versions.h" -#if defined(_SWT_TESTING_LIBRARY_VERSION) && !defined(SWT_TESTING_LIBRARY_VERSION) -#warning _SWT_TESTING_LIBRARY_VERSION is deprecated -#warning Define SWT_TESTING_LIBRARY_VERSION and optionally SWT_TARGET_TRIPLE instead -#define SWT_TESTING_LIBRARY_VERSION _SWT_TESTING_LIBRARY_VERSION -#endif - const char *swt_getTestingLibraryVersion(void) { #if defined(SWT_TESTING_LIBRARY_VERSION) return SWT_TESTING_LIBRARY_VERSION; diff --git a/Sources/_TestingInternals/include/Discovery.h b/Sources/_TestingInternals/include/Discovery.h index 2cfd63339..cbcfc2d79 100644 --- a/Sources/_TestingInternals/include/Discovery.h +++ b/Sources/_TestingInternals/include/Discovery.h @@ -94,9 +94,10 @@ SWT_EXTERN const void *_Nonnull const SWTTypeMetadataSectionBounds[2]; /// - nameSubstring: A string which the names of matching classes all contain. /// - outCount: On return, the number of type metadata pointers returned. /// -/// - Returns: A pointer to an array of type metadata pointers. The caller is -/// responsible for freeing this memory with `free()` when done. -SWT_EXTERN void *_Nonnull *_Nonnull swt_copyTypesWithNamesContaining( +/// - Returns: A pointer to an array of type metadata pointers, or `nil` if no +/// matching types were found. The caller is responsible for freeing this +/// memory with `free()` when done. +SWT_EXTERN void *_Nonnull *_Nullable swt_copyTypesWithNamesContaining( const void *sectionBegin, size_t sectionSize, const char *nameSubstring,