Skip to content

[Reflection] Implement an SPI to iterate types who conform to some protocol #63955

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

Closed
wants to merge 2 commits into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#define SWIFT_RUNTIME_IMAGEINSPECTION_H

#include "swift/Runtime/Config.h"
#include "swift/Runtime/SymbolInfo.h"

#include <cstdint>
#include <cstddef>
Expand All @@ -35,8 +36,6 @@
#include <Windows.h>
#endif

#include "SymbolInfo.h"

namespace swift {

/// Load the metadata from the image necessary to find protocols by name.
Expand Down
142 changes: 142 additions & 0 deletions include/swift/Runtime/ImageInspectionMachO.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//===--- ImageInspectionMachO.cpp - Mach-O image inspection ---------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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 the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// This file includes routines that interact with dyld on Mach-O-based
/// platforms to extract runtime metadata embedded in images generated by the
/// Swift compiler.
///
//===----------------------------------------------------------------------===//

#ifndef SWIFT_RUNTIME_IMAGEINSPECTIONMACHO_H
#define SWIFT_RUNTIME_IMAGEINSPECTIONMACHO_H

#if defined(__APPLE__) && defined(__MACH__) && \
!defined(SWIFT_RUNTIME_STATIC_IMAGE_INSPECTION)

#include "swift/Runtime/Config.h"
#include "swift/Runtime/ImageInspection.h"
#include "swift/Runtime/ImageInspectionCommon.h"
#include <mach-o/dyld.h>
#include <mach-o/getsect.h>
#include <objc/runtime.h>
#include <assert.h>
#include <dlfcn.h>

namespace {

constexpr const char ProtocolsSection[] = MachOProtocolsSection;
constexpr const char ProtocolConformancesSection[] =
MachOProtocolConformancesSection;
constexpr const char TypeMetadataRecordSection[] =
MachOTypeMetadataRecordSection;
constexpr const char DynamicReplacementSection[] =
MachODynamicReplacementSection;
constexpr const char DynamicReplacementSomeSection[] =
MachODynamicReplacementSomeSection;
constexpr const char AccessibleFunctionsSection[] =
MachOAccessibleFunctionsSection;
constexpr const char TextSegment[] = MachOTextSegment;

#if __POINTER_WIDTH__ == 64
using mach_header_platform = mach_header_64;
#else
using mach_header_platform = mach_header;
#endif

template <const char *SEGMENT_NAME, const char *SECTION_NAME,
void CONSUME_BLOCK(const void *baseAddress,
const void *start, uintptr_t size)>
void addImageCallback(const mach_header *mh) {
#if __POINTER_WIDTH__ == 64
assert(mh->magic == MH_MAGIC_64 && "loaded non-64-bit image?!");
#endif

// Look for a __swift5_proto section.
unsigned long size;
const uint8_t *section =
getsectiondata(reinterpret_cast<const mach_header_platform *>(mh),
SEGMENT_NAME, SECTION_NAME,
&size);

if (!section)
return;

CONSUME_BLOCK(mh, section, size);
}
template <const char *SEGMENT_NAME, const char *SECTION_NAME,
void CONSUME_BLOCK(const void *baseAddress,
const void *start, uintptr_t size)>
void addImageCallback(const mach_header *mh, intptr_t vmaddr_slide) {
addImageCallback<SEGMENT_NAME, SECTION_NAME, CONSUME_BLOCK>(mh);
}

template <const char *SEGMENT_NAME, const char *SECTION_NAME,
const char *SEGMENT_NAME2, const char *SECTION_NAME2,
void CONSUME_BLOCK(const void *baseAddress,
const void *start, uintptr_t size,
const void *start2, uintptr_t size2)>
void addImageCallback2Sections(const mach_header *mh) {
#if __POINTER_WIDTH__ == 64
assert(mh->magic == MH_MAGIC_64 && "loaded non-64-bit image?!");
#endif

// Look for a section.
unsigned long size;
const uint8_t *section =
getsectiondata(reinterpret_cast<const mach_header_platform *>(mh),
SEGMENT_NAME, SECTION_NAME,
&size);

if (!section)
return;

// Look for another section.
unsigned long size2;
const uint8_t *section2 =
getsectiondata(reinterpret_cast<const mach_header_platform *>(mh),
SEGMENT_NAME2, SECTION_NAME2,
&size2);
if (!section2)
size2 = 0;

CONSUME_BLOCK(mh, section, size, section2, size2);
}
template <const char *SEGMENT_NAME, const char *SECTION_NAME,
const char *SEGMENT_NAME2, const char *SECTION_NAME2,
void CONSUME_BLOCK(const void *baseAddress,
const void *start, uintptr_t size,
const void *start2, uintptr_t size2)>
void addImageCallback2Sections(const mach_header *mh, intptr_t vmaddr_slide) {
addImageCallback2Sections<SEGMENT_NAME, SECTION_NAME,
SEGMENT_NAME2, SECTION_NAME2,
CONSUME_BLOCK>(mh);
}

} // end anonymous namespace

#if OBJC_ADDLOADIMAGEFUNC_DEFINED && SWIFT_OBJC_INTEROP
#define REGISTER_FUNC(...) \
if (__builtin_available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)) { \
objc_addLoadImageFunc(__VA_ARGS__); \
} else { \
_dyld_register_func_for_add_image(__VA_ARGS__); \
}
#else
#define REGISTER_FUNC(...) _dyld_register_func_for_add_image(__VA_ARGS__)
#endif

#endif // defined(__APPLE__) && defined(__MACH__) &&
// !defined(SWIFT_RUNTIME_STATIC_IMAGE_INSPECTION)

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

#ifdef SWIFT_STDLIB_SUPPORT_BACK_DEPLOYMENT

#include "../runtime/ImageInspection.h"
#include "swift/Runtime/ImageInspection.h"
#include "swift/Runtime/Once.h"
#include <assert.h>
#include <atomic>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ list(APPEND SWIFT_REFLECTION_SWIFT_FLAGS

add_swift_target_library(swiftReflection ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB
Case.swift
Conformances.swift
Field.swift
GenericArguments.swift
KeyPath.swift
Expand Down
66 changes: 66 additions & 0 deletions stdlib/public/Reflection/Sources/Reflection/Conformances.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2023 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
//
//===----------------------------------------------------------------------===//

#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)

import Swift
import _Runtime

@available(SwiftStdlib 5.9, *)
@_silgen_name("_swift_reflection_withConformanceCache")
func _withConformanceCache(
_ proto: ProtocolDescriptor,
_ context: UnsafeMutableRawPointer,
_ callback: @convention(c) (
/* Array of types */ UnsafePointer<UnsafeRawPointer>,
/* Number of types */ Int,
/* Context we just passed */ UnsafeMutableRawPointer
) -> ()
)

@available(SwiftStdlib 5.9, *)
@_spi(Reflection)
public func _typesThatConform(to type: Any.Type) -> [Any.Type]? {
let meta = Metadata(type)

guard meta.kind == .existential else {
return nil
}

let existential = meta.existential

let protos = existential.protocols

guard protos.count == 1 else {
return nil
}

let proto = protos[0]

var result: [Any.Type] = []

withUnsafeMutablePointer(to: &result) {
_withConformanceCache(proto, UnsafeMutableRawPointer($0)) {
let buffer = UnsafeBufferPointer<Any.Type>(
start: UnsafePointer<Any.Type>($0._rawValue),
count: $1
)

let arrayPtr = $2.assumingMemoryBound(to: [Any.Type].self)

arrayPtr.pointee = Array(buffer)
}
}

return result
}

#endif
1 change: 1 addition & 0 deletions stdlib/public/Reflection/Sources/_Runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ add_swift_target_library(swift_Runtime ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_ST
Utils/RelativePointer.swift
Utils/TypeCache.swift

Caches.cpp
ConformanceDescriptor.swift
ExistentialContainer.swift
Functions.swift
Expand Down
Loading