Skip to content
Open
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
48 changes: 31 additions & 17 deletions Sources/Testing/SourceAttribution/Backtrace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,11 @@ extension Backtrace {
#endif
}

#if !hasFeature(Embedded) && SWT_TARGET_OS_APPLE && !SWT_NO_DYNAMIC_LINKING
private static var _markerAddressBetweenSwiftWillThrowAndFoundation: Address {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I ended up not needing it.

0x6C7B8BEB4B611F10 // randomly generated
}
#endif
/// Initialize an instance of this type with the previously-cached backtrace
/// for a given error.
///
Expand All @@ -460,30 +465,39 @@ extension Backtrace {
#if !hasFeature(Embedded)
@inline(never)
init?(forFirstThrowOf error: any Error, checkFoundation: Bool = true) {
if checkFoundation && Self.isFoundationCaptureEnabled {
#if !hasFeature(Embedded) && SWT_TARGET_OS_APPLE && !SWT_NO_DYNAMIC_LINKING
if let addresses = Self._CFErrorCopyCallStackReturnAddresses?(error)?.takeRetainedValue() as? [Address] {
self.init(addresses: addresses)
return
}
#endif

if let userInfo = error._userInfo as? [String: Any],
let addresses = userInfo["NSCallStackReturnAddresses"] as? [Address], !addresses.isEmpty {
self.init(addresses: addresses)
return
}
}

var addresses = [Address]()
let entry = Self._errorMappingCache.withLock { cache in
cache[.init(error)]
}
if let entry, entry.errorObject != nil {
// There was an entry and its weak reference is still valid.
self = entry.backtrace
} else {
addresses = entry.backtrace.addresses
}

#if !hasFeature(Embedded) && SWT_TARGET_OS_APPLE && !SWT_NO_DYNAMIC_LINKING
var foundationAddresses = [Address]()
if checkFoundation && Self.isFoundationCaptureEnabled {
foundationAddresses = Self._CFErrorCopyCallStackReturnAddresses?(error)?.takeRetainedValue() as? [Address] ?? []
}
if !foundationAddresses.isEmpty {
// Find any common suffix between the two sequences and insert the
// Foundation backtrace before it. That ought to produce a combined
// backtrace that looks correct, at least temporally speaking.
let indices = zip(addresses.indices.reversed(), foundationAddresses.indices.reversed())
.first { addresses[$0] != foundationAddresses[$1] }
switch indices {
case let .some((insertionIndex, truncationIndex)):
addresses.insert(contentsOf: foundationAddresses[...truncationIndex], at: insertionIndex)
default:
addresses += foundationAddresses
}
}
#endif

if addresses.isEmpty {
return nil
}
self.init(addresses: addresses)
}
#else
init?(forFirstThrowOf error: some Error, checkFoundation: Bool = true) {
Expand Down
13 changes: 11 additions & 2 deletions Tests/TestingTests/BacktraceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,19 @@ struct BacktraceTests {
}
}

@inline(never)
func throwNSErrorObjCStyle(_ outError: NSErrorPointer) -> Bool {
outError?.pointee = NSError(domain: "Oh no!", code: 123, userInfo: [:])
return false
}

@inline(never)
func throwNSError() throws {
let error = NSError(domain: "Oh no!", code: 123, userInfo: [:])
throw error
var error: NSError?
if !throwNSErrorObjCStyle(&error) {
let error = try #require(error)
throw error
}
}

@inline(never)
Expand Down
Loading