Skip to content

Remove some remnants of the C++ section discovery code that we no longer need. #905

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 8 additions & 24 deletions Sources/Testing/Discovery+Platform.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
20 changes: 9 additions & 11 deletions Sources/Testing/Test+Discovery+Legacy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Any.Type> {
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) }
}
}
134 changes: 29 additions & 105 deletions Sources/_TestingInternals/Discovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@

#include "Discovery.h"

#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <type_traits>
#include <vector>

#if defined(SWT_NO_DYNAMIC_LINKING)
#pragma mark - Statically-linked section bounds

Expand All @@ -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 <algorithm>
#include <array>
#include <atomic>
#include <cstring>
#include <iterator>
#include <memory>
#include <tuple>
#include <type_traits>
#include <vector>
#include <optional>

/// 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 <typename SectionEnumerator>
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<typename T>
struct SWTHeapAllocator {
using value_type = T;

T *allocate(size_t count) {
return reinterpret_cast<T *>(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 <typename T>
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<const T *>(start);
}

const struct SWTTypeMetadataRecord *end(void) const {
return reinterpret_cast<const T *>(reinterpret_cast<uintptr_t>(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<T>`.
template <typename T>
using SWTSectionBoundsList = std::vector<SWTSectionBounds<T>, SWTHeapAllocator<SWTSectionBounds<T>>>;

#pragma mark - Swift ABI

#if defined(__PTRAUTH_INTRINSICS__)
#include <ptrauth.h>
#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.
///
Expand Down Expand Up @@ -164,10 +89,6 @@ struct SWTRelativePointer {
#endif
return reinterpret_cast<const T *>(result);
}

const T *_Nullable operator ->(void) const& {
return get();
}
};

/// A type representing a 32-bit absolute function pointer, usually used on platforms
Expand All @@ -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
Expand Down Expand Up @@ -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<SWTTypeMetadataRecord> sb = { nullptr, sectionBegin, sectionSize };
std::vector<void *, SWTHeapAllocator<void *>> result;
void **result = nullptr;
size_t resultCount = 0;

for (const auto& record : sb) {
auto contextDescriptor = record.getContextDescriptor();
auto records = reinterpret_cast<const SWTTypeMetadataRecord *>(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.
Expand All @@ -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<void **>(std::calloc(recordCount - i, sizeof(void *)));
}
result[resultCount] = typeMetadata;
resultCount += 1;
}
}

auto resultCopy = reinterpret_cast<void **>(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;
}
6 changes: 0 additions & 6 deletions Sources/_TestingInternals/Versions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 4 additions & 3 deletions Sources/_TestingInternals/include/Discovery.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down