Skip to content

Commit c3da189

Browse files
authored
Simplify NSImageRep-checking logic to use Bundle. (#1217)
This PR simplifies the logic we use to check if subclasses of `NSImageRep` are likely to be thread-safe by using Foundation's `Bundle` instead of querying dyld. AppKit always links Foundation, so there's no need for us to roll our own logic for this check. ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated.
1 parent 43fb3fb commit c3da189

File tree

1 file changed

+22
-35
lines changed

1 file changed

+22
-35
lines changed

Sources/Overlays/_Testing_AppKit/Attachments/NSImage+AttachableAsCGImage.swift

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,27 @@
1212
public import AppKit
1313
@_spi(Experimental) public import _Testing_CoreGraphics
1414

15+
extension NSImageRep {
16+
/// AppKit's bundle.
17+
private static let _appKitBundle: Bundle = Bundle(for: NSImageRep.self)
18+
19+
/// Whether or not this image rep's class is effectively thread-safe and can
20+
/// be treated as if it conforms to `Sendable`.
21+
fileprivate var isEffectivelySendable: Bool {
22+
if isMember(of: NSImageRep.self) || isKind(of: NSCustomImageRep.self) {
23+
// NSImageRep itself is an abstract class. NSCustomImageRep includes an
24+
// arbitrary rendering block that may not be concurrency-safe in Swift.
25+
return false
26+
}
27+
28+
// Treat all other classes declared in AppKit as safe. We can't reason about
29+
// classes declared in other bundles, so treat them all as if they're unsafe.
30+
return Bundle(for: Self.self) == Self._appKitBundle
31+
}
32+
}
33+
34+
// MARK: -
35+
1536
@_spi(Experimental)
1637
extension NSImage: AttachableAsCGImage {
1738
public var attachableCGImage: CGImage {
@@ -32,29 +53,6 @@ extension NSImage: AttachableAsCGImage {
3253
return maxRepWidth ?? 1.0
3354
}
3455

35-
/// Get the base address of the loaded image containing `class`.
36-
///
37-
/// - Parameters:
38-
/// - class: The class to look for.
39-
///
40-
/// - Returns: The base address of the image containing `class`, or `nil` if
41-
/// no image was found (for instance, if the class is generic or dynamically
42-
/// generated.)
43-
///
44-
/// "Image" in this context refers to a binary/executable image.
45-
private static func _baseAddressOfImage(containing `class`: AnyClass) -> UnsafeRawPointer? {
46-
let classAsAddress = Unmanaged.passUnretained(`class` as AnyObject).toOpaque()
47-
48-
var info = Dl_info()
49-
guard 0 != dladdr(classAsAddress, &info) else {
50-
return nil
51-
}
52-
return .init(info.dli_fbase)
53-
}
54-
55-
/// The base address of the image containing AppKit's symbols, if known.
56-
private static nonisolated(unsafe) let _appKitBaseAddress = _baseAddressOfImage(containing: NSImageRep.self)
57-
5856
public func _makeCopyForAttachment() -> Self {
5957
// If this image is of an NSImage subclass, we cannot reliably make a deep
6058
// copy of it because we don't know what its `init(data:)` implementation
@@ -69,18 +67,7 @@ extension NSImage: AttachableAsCGImage {
6967

7068
// Check whether the image contains any representations that we don't think
7169
// are safe. If it does, then make a "safe" copy.
72-
let allImageRepsAreSafe = representations.allSatisfy { imageRep in
73-
// NSCustomImageRep includes an arbitrary rendering block that may not be
74-
// concurrency-safe in Swift.
75-
if imageRep is NSCustomImageRep {
76-
return false
77-
}
78-
79-
// Treat all other classes declared in AppKit as safe. We can't reason
80-
// about classes declared in other modules, so treat them all as if they
81-
// are unsafe.
82-
return Self._baseAddressOfImage(containing: type(of: imageRep)) == Self._appKitBaseAddress
83-
}
70+
let allImageRepsAreSafe = representations.allSatisfy(\.isEffectivelySendable)
8471
if !allImageRepsAreSafe, let safeCopy = tiffRepresentation.flatMap(Self.init(data:)) {
8572
// Create a "safe" copy of this image by flattening it to TIFF and then
8673
// creating a new NSImage instance from it.

0 commit comments

Comments
 (0)