12
12
13
13
import CoreFoundation
14
14
15
+ #if canImport(ObjectiveC)
16
+ import ObjectiveC
17
+ #endif
18
+
15
19
public protocol _StructBridgeable {
16
20
func _bridgeToAny( ) -> Any
17
21
}
18
22
23
+ fileprivate protocol Unwrappable {
24
+ func unwrap( ) -> Any ?
25
+ }
26
+
27
+ extension Optional : Unwrappable {
28
+ func unwrap( ) -> Any ? {
29
+ return self
30
+ }
31
+ }
32
+
19
33
/// - Note: This does not exist currently on Darwin but it is the inverse correlation to the bridge types such that a
20
34
/// reference type can be converted via a callout to a conversion method.
21
35
public protocol _StructTypeBridgeable : _StructBridgeable {
@@ -54,18 +68,92 @@ internal protocol _NSBridgeable {
54
68
}
55
69
56
70
71
+ #if !canImport(ObjectiveC)
72
+ // The _NSSwiftValue protocol is in the stdlib, and only available on platforms without ObjC.
73
+ extension _SwiftValue : _NSSwiftValue { }
74
+ #endif
75
+
57
76
/// - Note: This is an internal boxing value for containing abstract structures
58
- internal final class _SwiftValue : NSObject , NSCopying , _NSSwiftValue {
77
+ internal final class _SwiftValue : NSObject , NSCopying {
59
78
public private( set) var value : Any
60
79
61
80
static func fetch( _ object: AnyObject ? ) -> Any ? {
62
81
if let obj = object {
63
- return fetch ( nonOptional: obj)
82
+ let value = fetch ( nonOptional: obj)
83
+ if let wrapper = value as? Unwrappable , wrapper. unwrap ( ) == nil {
84
+ return nil
85
+ } else {
86
+ return value
87
+ }
64
88
}
65
89
return nil
66
90
}
67
91
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
+
68
133
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 by the stdlib bridging machinery, and case 3 can't happen —
154
+ // the compiler will produce SwiftFoundation._SwiftValue boxes rather than ObjC ones.
155
+ #endif
156
+
69
157
if object === kCFBooleanTrue {
70
158
return true
71
159
} else if object === kCFBooleanFalse {
@@ -79,6 +167,13 @@ internal final class _SwiftValue : NSObject, NSCopying, _NSSwiftValue {
79
167
}
80
168
}
81
169
170
+ static func store( optional value: Any ? ) -> NSObject ? {
171
+ if let val = value {
172
+ return store ( val)
173
+ }
174
+ return nil
175
+ }
176
+
82
177
static func store( _ value: Any ? ) -> NSObject ? {
83
178
if let val = value {
84
179
return store ( val)
@@ -89,8 +184,20 @@ internal final class _SwiftValue : NSObject, NSCopying, _NSSwiftValue {
89
184
static func store( _ value: Any ) -> NSObject {
90
185
if let val = value as? NSObject {
91
186
return val
187
+ } else if let opt = value as? Unwrappable , opt. unwrap ( ) == nil {
188
+ return NSNull ( )
92
189
} else {
93
- return ( value as AnyObject ) as! NSObject
190
+ #if canImport(ObjectiveC)
191
+ // On Darwin, this can be a native (ObjC) _SwiftValue.
192
+ let boxed = ( value as AnyObject )
193
+ if !( boxed is NSObject ) {
194
+ return _SwiftValue ( value) // Do not emit native boxes — wrap them in Swift Foundation boxes instead.
195
+ } else {
196
+ return boxed as! NSObject
197
+ }
198
+ #else
199
+ return ( value as AnyObject ) as! NSObject
200
+ #endif
94
201
}
95
202
}
96
203
0 commit comments