Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement Runtime Attributes in Reflection
add docs fix error Fix linux and windows build initialize the lock value
- Loading branch information
Showing
9 changed files
with
384 additions
and
59 deletions.
There are no files selected for viewing
113 changes: 113 additions & 0 deletions
113
stdlib/public/Reflection/Sources/Reflection/Attribute.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import Swift | ||
import _Runtime | ||
|
||
/// A namespace used for working with runtime attributes. | ||
@available(SwiftStdlib 5.9, *) | ||
@frozen | ||
public enum Attribute { | ||
/// Get all the instances of a runtime attribute wherever it's attached to. | ||
/// | ||
/// Example: | ||
/// | ||
/// @runtimeMetadata | ||
/// struct Field { | ||
/// let name: String | ||
/// | ||
/// init<T, U>(attachedTo: KeyPath<T, U>, _ name: String) { | ||
/// self.name = name | ||
/// } | ||
/// } | ||
/// | ||
/// struct Dog { | ||
/// @Field("dog_breed") | ||
/// let breed: String | ||
/// } | ||
/// | ||
/// let fields = Attribute.allInstances(of: Field.self) | ||
/// | ||
/// for field in fields { | ||
/// print(field.name) // "dog_breed" | ||
/// } | ||
/// | ||
/// - Parameters: | ||
/// - type: The type of the attribute that is attached to various sources. | ||
/// - Returns: A sequence of attribute instances of `type` in no particular | ||
/// order. | ||
@available(SwiftStdlib 5.9, *) | ||
public static func allInstances<T>(of type: T.Type) -> AttributeInstances<T> { | ||
let meta = Metadata(T.self) | ||
|
||
guard meta.kind == .struct || | ||
meta.kind == .enum || | ||
meta.kind == .class else { | ||
return AttributeInstances([]) | ||
} | ||
|
||
return ImageInspection.withAttributeCache { | ||
let attrDescriptor = meta.type.descriptor.base | ||
|
||
guard let fnPtrs = $0[attrDescriptor] else { | ||
return AttributeInstances([]) | ||
} | ||
|
||
return AttributeInstances(fnPtrs) | ||
} | ||
} | ||
} | ||
|
||
/// A sequence wrapper over some runtime attribute instances. | ||
/// | ||
/// Instances of `AttributeInstances` are created with the | ||
/// `Attribute.allInstances(of:)` function. | ||
@available(SwiftStdlib 5.9, *) | ||
@frozen | ||
public struct AttributeInstances<T> { | ||
@usableFromInline | ||
let fnPtrs: [UnsafeRawPointer] | ||
|
||
@usableFromInline | ||
var index = 0 | ||
|
||
@available(SwiftStdlib 5.9, *) | ||
init(_ fnPtrs: [UnsafeRawPointer]) { | ||
self.fnPtrs = fnPtrs | ||
} | ||
} | ||
|
||
@available(SwiftStdlib 5.9, *) | ||
extension AttributeInstances: IteratorProtocol { | ||
@available(SwiftStdlib 5.9, *) | ||
@inlinable | ||
public mutating func next() -> T? { | ||
while index < fnPtrs.endIndex { | ||
let fnPtr = fnPtrs[index] | ||
index += 1 | ||
|
||
typealias AttributeFn = @convention(thin) () -> T? | ||
|
||
let fn = unsafeBitCast(fnPtr, to: AttributeFn.self) | ||
|
||
guard let attribute = fn() else { | ||
continue | ||
} | ||
|
||
return attribute | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
@available(SwiftStdlib 5.9, *) | ||
extension AttributeInstances: Sequence {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
86 changes: 86 additions & 0 deletions
86
stdlib/public/Reflection/Sources/_Runtime/ImageInspection.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include <stdint.h> | ||
|
||
#include "swift/Runtime/Config.h" | ||
#include "swift/Threading/Once.h" | ||
|
||
extern "C" | ||
void registerAttributes(const void *section, intptr_t size); | ||
|
||
//===----------------------------------------------------------------------===// | ||
// Mach-O Image Inspection | ||
//===----------------------------------------------------------------------===// | ||
|
||
#if defined(__MACH__) | ||
|
||
#include <mach-o/dyld.h> | ||
#include <mach-o/getsect.h> | ||
|
||
#if __POINTER_WIDTH__ == 64 | ||
typedef mach_header_64 mach_header_platform; | ||
#else | ||
typedef mach_header mach_header_platform; | ||
#endif | ||
|
||
void lookupSection(const struct mach_header *header, const char *segment, | ||
const char *section, | ||
void (*registerFunc)(const void *, intptr_t)) { | ||
unsigned long size = 0; | ||
|
||
auto sectionData = getsectiondata( | ||
reinterpret_cast<const mach_header_platform *>(header), segment, section, | ||
&size); | ||
|
||
registerFunc(sectionData, size); | ||
} | ||
|
||
void imageFunc(const struct mach_header *header, intptr_t size) { | ||
lookupSection(header, "__TEXT", "__swift5_rattrs", registerAttributes); | ||
} | ||
|
||
extern "C" SWIFT_CC(swift) | ||
void snapshot() { | ||
static swift::once_t token; | ||
swift::once(token, []{ | ||
_dyld_register_func_for_add_image(imageFunc); | ||
}); | ||
} | ||
|
||
#else | ||
|
||
#include "swift/shims/MetadataSections.h" | ||
|
||
extern "C" | ||
void swift_enumerateAllMetadataSections( | ||
bool (* body)(const swift::MetadataSections *sections, void *context), | ||
void *context | ||
); | ||
|
||
bool imageFunc(const swift::MetadataSections *sections, void *context) { | ||
if (sections->version >= 3) { | ||
return false; | ||
} | ||
|
||
auto runtimeAttrs = sections->swift5_runtime_attributes; | ||
registerAttributes(reinterpret_cast<const void *>(runtimeAttrs.start), | ||
runtimeAttrs.length); | ||
|
||
return false; | ||
} | ||
|
||
extern "C" SWIFT_CC(swift) | ||
void snapshot() { | ||
swift_enumerateAllMetadataSections(imageFunc, nullptr); | ||
} | ||
|
||
#endif |
97 changes: 97 additions & 0 deletions
97
stdlib/public/Reflection/Sources/_Runtime/ImageInspection.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import Swift | ||
|
||
@available(SwiftStdlib 5.9, *) | ||
@frozen | ||
public enum ImageInspection {} | ||
|
||
@_silgen_name("snapshot") | ||
func snapshot() | ||
|
||
//===----------------------------------------------------------------------===// | ||
// Runtime Attributes | ||
//===----------------------------------------------------------------------===// | ||
|
||
@available(SwiftStdlib 5.9, *) | ||
var attributeCache: Lock<[ContextDescriptor: [UnsafeRawPointer]]> = .create( | ||
with: [:] | ||
) | ||
|
||
@available(SwiftStdlib 5.9, *) | ||
extension ImageInspection { | ||
@available(SwiftStdlib 5.9, *) | ||
public static func withAttributeCache<T>( | ||
_ body: @Sendable ([ContextDescriptor: [UnsafeRawPointer]]) throws -> T | ||
) rethrows -> T { | ||
#if !(os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) | ||
attributeCache.withLock { | ||
$0.removeAll(keepingCapacity: true) | ||
} | ||
#endif | ||
|
||
snapshot() | ||
|
||
return try attributeCache.withLock { | ||
try body($0) | ||
} | ||
} | ||
} | ||
|
||
@available(SwiftStdlib 5.9, *) | ||
@_cdecl("registerAttributes") | ||
func registerAttributes(section: UnsafeRawPointer, size: Int) { | ||
var address = section | ||
let end = address + size | ||
|
||
while address < end { | ||
// Flags (always 0 for right now) | ||
address += MemoryLayout<Int32>.size | ||
|
||
let attributeAddr = address.relativeIndirectableAddress( | ||
as: ContextDescriptor.self | ||
) | ||
|
||
let attributeDescriptor = ContextDescriptor(attributeAddr) | ||
|
||
// Attribute Context Descriptor | ||
address += MemoryLayout<Int32>.size | ||
|
||
let numberOfInstances = address.loadUnaligned(as: UInt32.self) | ||
|
||
// Number of records this attribute has | ||
address += MemoryLayout<Int32>.size | ||
|
||
var fnPtrs: [UnsafeRawPointer] = [] | ||
fnPtrs.reserveCapacity(Int(numberOfInstances)) | ||
|
||
for _ in 0 ..< numberOfInstances { | ||
// The type this attribute was on (not always an actual type) | ||
address += MemoryLayout<Int32>.size | ||
|
||
var fnRecord = address.relativeDirectAddress(as: UnsafeRawPointer.self) | ||
fnRecord += MemoryLayout<RelativeDirectPointer<Void>>.size * 3 | ||
|
||
let fnPtr = fnRecord.relativeDirectAddress(as: UnsafeRawPointer.self) | ||
fnPtrs.append(fnPtr) | ||
|
||
// Function pointer to attribute initializer | ||
address += MemoryLayout<Int32>.size | ||
} | ||
|
||
let copyFnPtrs = fnPtrs | ||
|
||
attributeCache.withLock { | ||
$0[attributeDescriptor, default: []].append(contentsOf: copyFnPtrs) | ||
} | ||
} | ||
} |
Oops, something went wrong.