Skip to content

Commit 87944ed

Browse files
author
Dave Abrahams
committed
WIP: attempt to provide cleaner bridging interface
1 parent af7f149 commit 87944ed

File tree

5 files changed

+55
-121
lines changed

5 files changed

+55
-121
lines changed

apinotes/Foundation.apinotes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ Classes:
9191
- Selector: 'setUserInfoValueProviderForDomain:provider:'
9292
SwiftName: setUserInfoValueProvider(forDomain:provider:)
9393
MethodKind: Class
94+
Properties:
95+
- Name: userInfo
96+
SwiftName: _objc_userInfo
9497
- Name: NSDictionary
9598
SwiftBridge: Swift.Dictionary
9699
- Name: NSSet

stdlib/public/SDK/Foundation/Foundation.swift

Lines changed: 11 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -560,114 +560,26 @@ extension NSDictionary : ExpressibleByDictionaryLiteral {
560560
}
561561
}
562562

563-
extension Dictionary {
564-
/// Private initializer used for bridging.
565-
///
566-
/// The provided `NSDictionary` will be copied to ensure that the copy can
567-
/// not be mutated by other code.
568-
public init(_cocoaDictionary: _NSDictionary) {
569-
_sanityCheck(
570-
_isBridgedVerbatimToObjectiveC(Key.self) &&
571-
_isBridgedVerbatimToObjectiveC(Value.self),
572-
"Dictionary can be backed by NSDictionary storage only when both key and value are bridged verbatim to Objective-C")
573-
// FIXME: We would like to call CFDictionaryCreateCopy() to avoid doing an
574-
// objc_msgSend() for instances of CoreFoundation types. We can't do that
575-
// today because CFDictionaryCreateCopy() copies dictionary contents
576-
// unconditionally, resulting in O(n) copies even for immutable dictionaries.
577-
//
578-
// <rdar://problem/20690755> CFDictionaryCreateCopy() does not call copyWithZone:
579-
//
580-
// The bug is fixed in: OS X 10.11.0, iOS 9.0, all versions of tvOS
581-
// and watchOS.
582-
self = Dictionary(
583-
_unsafeReferenceCast(_cocoaDictionary, to: _NSDictionary.self))
584-
}
585-
}
586-
587563
// Dictionary<Key, Value> is conditionally bridged to NSDictionary
588-
extension Dictionary : _ObjectiveCBridgeable {
564+
extension Dictionary : _CustomObjectiveCBridgeable {
589565
@_semantics("convertToObjectiveC")
590566
public func _bridgeToObjectiveC() -> NSDictionary {
591567
return unsafeBitCast(_bridgeToObjectiveCImpl() as AnyObject,
592568
to: NSDictionary.self)
593569
}
594570

595-
public static func _forceBridgeFromObjectiveC(
596-
_ d: NSDictionary,
597-
result: inout Dictionary?
598-
) {
599-
if let native = [Key : Value]._bridgeFromObjectiveCAdoptingNativeStorageOf(
600-
d as AnyObject) {
601-
result = native
602-
return
603-
}
604-
605-
if _isBridgedVerbatimToObjectiveC(Key.self) &&
606-
_isBridgedVerbatimToObjectiveC(Value.self) {
607-
result = [Key : Value](
608-
_cocoaDictionary: unsafeBitCast(d as AnyObject, to: _NSDictionary.self))
609-
return
610-
}
611-
612-
// `Dictionary<Key, Value>` where either `Key` or `Value` is a value type
613-
// may not be backed by an NSDictionary.
614-
var builder = _DictionaryBuilder<Key, Value>(count: d.count)
615-
d.enumerateKeysAndObjects({
616-
(anyKey: Any, anyValue: Any,
617-
stop: UnsafeMutablePointer<ObjCBool>) in
618-
let anyObjectKey = anyKey as AnyObject
619-
let anyObjectValue = anyValue as AnyObject
620-
builder.add(
621-
key: Swift._forceBridgeFromObjectiveC(anyObjectKey, Key.self),
622-
value: Swift._forceBridgeFromObjectiveC(anyObjectValue, Value.self))
623-
})
624-
result = builder.take()
625-
}
626-
627-
public static func _conditionallyBridgeFromObjectiveC(
628-
_ x: NSDictionary,
629-
result: inout Dictionary?
630-
) -> Bool {
631-
let anyDict = x as [NSObject : AnyObject]
632-
if _isBridgedVerbatimToObjectiveC(Key.self) &&
633-
_isBridgedVerbatimToObjectiveC(Value.self) {
634-
result = Swift._dictionaryDownCastConditional(anyDict)
635-
return result != nil
636-
}
637-
638-
result = Swift._dictionaryBridgeFromObjectiveCConditional(anyDict)
639-
return result != nil
571+
public static func _bridgedFromNil(_: NSDictionary?) -> Dictionary {
572+
return [:]
640573
}
641-
642-
public static func _unconditionallyBridgeFromObjectiveC(
643-
_ d: NSDictionary?
644-
) -> Dictionary {
645-
// `nil` has historically been used as a stand-in for an empty
646-
// dictionary; map it to an empty dictionary.
647-
if _slowPath(d == nil) { return Dictionary() }
648-
649-
if let native = [Key : Value]._bridgeFromObjectiveCAdoptingNativeStorageOf(
650-
d! as AnyObject) {
651-
return native
574+
575+
public static func _bridged<Policy: _BridgePolicy>(
576+
from objc: NSDictionary, by policy: Policy.Type
577+
) -> Dictionary? {
578+
if let n = Dictionary._bridgeFromObjectiveCAdoptingNativeStorageOf(objc) {
579+
return n
652580
}
653-
654-
if _isBridgedVerbatimToObjectiveC(Key.self) &&
655-
_isBridgedVerbatimToObjectiveC(Value.self) {
656-
return [Key : Value](
657-
_cocoaDictionary: unsafeBitCast(d! as AnyObject, to: _NSDictionary.self))
658-
}
659-
660-
// `Dictionary<Key, Value>` where either `Key` or `Value` is a value type
661-
// may not be backed by an NSDictionary.
662-
var builder = _DictionaryBuilder<Key, Value>(count: d!.count)
663-
d!.enumerateKeysAndObjects({
664-
(anyKey: Any, anyValue: Any,
665-
stop: UnsafeMutablePointer<ObjCBool>) in
666-
builder.add(
667-
key: Swift._forceBridgeFromObjectiveC(anyKey as AnyObject, Key.self),
668-
value: Swift._forceBridgeFromObjectiveC(anyValue as AnyObject, Value.self))
669-
})
670-
return builder.take()
581+
return Dictionary(
582+
_unsafeReferenceCast(objc, to: _NSDictionary.self), by: policy)
671583
}
672584
}
673585

stdlib/public/SDK/Foundation/NSError.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,11 +241,21 @@ public func _swift_Foundation_getErrorDefaultUserInfo(_ error: Error)
241241
// magic allows this to be done as a "toll-free" conversion when an NSError
242242
// or CFError is used as an Error existential.
243243

244+
@objc protocol _NSError : class {
245+
@objc var userInfo: NSDictionary { get }
246+
}
247+
244248
extension NSError : Error {
245249
public var _domain: String { return domain }
246250
public var _code: Int { return code }
247251
public var _userInfo: Any? { return userInfo }
248252

253+
@nonobjc
254+
public var userInfo: [AnyHashable : Any] {
255+
let proxy = _unsafeReferenceCast(self, to: _NSError.self)
256+
return proxy.userInfo as! [AnyHashable : Any]
257+
}
258+
249259
/// The "embedded" NSError is itself.
250260
public func _getEmbeddedNSError() -> AnyObject? {
251261
return self

stdlib/public/core/BridgeObjectiveC.swift

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -98,22 +98,20 @@ struct _ConditionalCasting : _BridgePolicy {
9898
}
9999

100100
public protocol _CustomObjectiveCBridgeable : _ObjectiveCBridgeable {
101+
/// An Objective-C type to which `Self` can be losslessly round-trip
102+
/// converted.
103+
associatedtype _ObjectiveCType : AnyObject
104+
105+
/// Convert `self` to Objective-C.
106+
func _bridgeToObjectiveC() -> _ObjectiveCType
107+
101108
static func _bridged<Policy: _BridgePolicy>(
102109
from objc: _ObjectiveCType, by policy: Policy.Type) -> Self?
103110

104111
static func _bridgedFromNil(_: _ObjectiveCType?) -> Self
105112
}
106113

107-
extension _ObjectiveCBridgeable {
108-
public static func _bridgedFromNil(_: _ObjectiveCType?) -> Self {
109-
fatalError(
110-
"Objective-C nil of type \(String(reflecting: _ObjectiveCType.self)) "
111-
+ "does not bridge to \(String(reflecting: Self.self))")
112-
}
113-
}
114-
115-
116-
extension _ObjectiveCBridgeable where Self : _CustomObjectiveCBridgeable {
114+
extension _CustomObjectiveCBridgeable {
117115
/// Bridge from an Objective-C object of the bridged class type to a
118116
/// value of the Self type.
119117
///
@@ -173,7 +171,7 @@ extension _ObjectiveCBridgeable where Self : _CustomObjectiveCBridgeable {
173171
/// measure against mis-annotation, this method accepts `source` as an
174172
/// `Optional`, so implementations can return an appropriate default value
175173
/// (e.g. an empty `Array`) when `source` is `nil`.
176-
static func _unconditionallyBridgeFromObjectiveC(
174+
public static func _unconditionallyBridgeFromObjectiveC(
177175
_ source: _ObjectiveCType?
178176
) -> Self {
179177
if _slowPath(source == nil) {
@@ -188,6 +186,12 @@ extension _ObjectiveCBridgeable where Self : _CustomObjectiveCBridgeable {
188186
+ "\(String(reflecting: Self.self)) failed with actual value "
189187
+ "\(String(reflecting: source))")
190188
}
189+
190+
public static func _bridgedFromNil(_: _ObjectiveCType?) -> Self {
191+
fatalError(
192+
"Objective-C nil of type \(String(reflecting: _ObjectiveCType.self)) "
193+
+ "does not bridge to \(String(reflecting: Self.self))")
194+
}
191195
}
192196

193197
//===--- Bridging for metatypes -------------------------------------------===//

stdlib/public/core/HashedCollections.swift.gyb

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1629,22 +1629,25 @@ public struct Dictionary<Key : Hashable, Value> :
16291629

16301630
#if _runtime(_ObjC)
16311631
/// Private initializer used for bridging.
1632-
///
1633-
/// - Precondition: * `Key` and `Value` are bridged verbatim to Objective-C
1634-
/// (i.e., are reference types).
1635-
public init(_ source: _NSDictionary) {
1636-
_sanityCheck(
1637-
_isBridgedVerbatimToObjectiveC(Key.self) &&
1638-
_isBridgedVerbatimToObjectiveC(Value.self),
1639-
"Expecting both key and value to be bridged verbatim to Objective-C")
1632+
public init?<Policy: _BridgePolicy>(
1633+
_ source: _NSDictionary, by policy: Policy.Type) {
16401634
self.init(minimumCapacity: source.count)
1641-
source._forEachBatch { b in
1635+
let success: Void? = try? source._forEachBatch { b in
16421636
for k in b {
16431637
// FIXME: Use objectsForKeys as an optimization
16441638
// FIXME: Use unsafeDowncasts as an optimization?
1645-
self[k as! Key] = (source.objectFor(k)! as! Value)
1639+
1640+
guard
1641+
let key = policy.bridge(k, to: Key.self),
1642+
let value = policy.bridge(
1643+
source.objectFor(k)!, to: Value.self
1644+
)
1645+
else { throw _StopIteration.stop }
1646+
1647+
self[key] = value
16461648
}
16471649
}
1650+
if success == nil { return nil }
16481651
}
16491652
#endif
16501653

@@ -2229,7 +2232,9 @@ public func _dictionaryDownCast<BaseKey, BaseValue, DerivedKey, DerivedValue>(
22292232
// Note: it is safe to treat the storage as immutable here because
22302233
// Dictionary will not mutate storage with reference count greater than 1.
22312234
return Dictionary(
2232-
_unsafeReferenceCast(nativeOwner, to: _NSDictionary.self))
2235+
_unsafeReferenceCast(nativeOwner, to: _NSDictionary.self),
2236+
by: _ForceCasting.self
2237+
).unsafelyUnwrapped
22332238
}
22342239
}
22352240
#endif

0 commit comments

Comments
 (0)