Skip to content

Commit 5bb619b

Browse files
committed
Ensure fetching works with non-SwiftFoundation values returned on Darwin by as AnyObject.
1 parent 1b6b69d commit 5bb619b

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

Foundation/Bridging.swift

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212

1313
import CoreFoundation
1414

15+
#if canImport(ObjectiveC)
16+
import ObjectiveC
17+
#endif
18+
1519
public protocol _StructBridgeable {
1620
func _bridgeToAny() -> Any
1721
}
@@ -85,7 +89,71 @@ internal final class _SwiftValue : NSObject, NSCopying {
8589
return nil
8690
}
8791

92+
#if canImport(ObjectiveC)
93+
private static var _objCNSNullClassStorage: Any.Type?
94+
private static var objCNSNullClass: Any.Type? {
95+
if let type = _objCNSNullClassStorage {
96+
return type
97+
}
98+
99+
let name = "NSNull"
100+
let maybeType = name.withCString { cString in
101+
return objc_getClass(cString)
102+
}
103+
104+
if let type = maybeType as? Any.Type {
105+
_objCNSNullClassStorage = type
106+
return type
107+
} else {
108+
return nil
109+
}
110+
}
111+
112+
private static var _swiftStdlibSwiftValueClassStorage: Any.Type?
113+
private static var swiftStdlibSwiftValueClass: Any.Type? {
114+
if let type = _swiftStdlibSwiftValueClassStorage {
115+
return type
116+
}
117+
118+
let name = "_SwiftValue"
119+
let maybeType = name.withCString { cString in
120+
return objc_getClass(cString)
121+
}
122+
123+
if let type = maybeType as? Any.Type {
124+
_swiftStdlibSwiftValueClassStorage = type
125+
return type
126+
} else {
127+
return nil
128+
}
129+
}
130+
131+
#endif
132+
88133
static func fetch(nonOptional object: AnyObject) -> Any {
134+
#if canImport(ObjectiveC)
135+
// You can pass the result of a `as AnyObject` expression to this method. This can have one of three results on Darwin:
136+
// - It's a SwiftFoundation type. Bridging will take care of it below.
137+
// - It's nil. The compiler is hardcoded to return [NSNull null] for nils.
138+
// - It's some other Swift type. The compiler will box it in a native _SwiftValue.
139+
// Case 1 is handled below.
140+
// Case 2 is handled here:
141+
if type(of: object as Any) == objCNSNullClass {
142+
return Optional<Any>.none as Any
143+
}
144+
// Case 3 is handled here:
145+
if type(of: object as Any) == swiftStdlibSwiftValueClass {
146+
return object
147+
// Since this returns Any, the object is casted almost immediately — e.g.:
148+
// _SwiftValue.fetch(x) as SomeStruct
149+
// which will immediately unbox the native box. For callers, it will be exactly
150+
// as if we returned the unboxed value directly.
151+
}
152+
153+
// On Linux, case 2 is handled below, and case 3 can't happen —
154+
// the compiler will produce SwiftFoundation._SwiftValue boxes rather than ObjC ones.
155+
#endif
156+
89157
if object === kCFBooleanTrue {
90158
return true
91159
} else if object === kCFBooleanFalse {

0 commit comments

Comments
 (0)