mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Now that Array and Dictionary conform to Hashable, we need to make sure that their bridged counterparts provide the same hash values when converted to AnyHashable.
258 lines
9.1 KiB
Swift
258 lines
9.1 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
@_exported import Foundation // Clang module
|
|
import _SwiftFoundationOverlayShims
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Dictionaries
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
extension NSDictionary : ExpressibleByDictionaryLiteral {
|
|
public required convenience init(
|
|
dictionaryLiteral elements: (Any, Any)...
|
|
) {
|
|
// FIXME: Unfortunate that the `NSCopying` check has to be done at runtime.
|
|
self.init(
|
|
objects: elements.map { $0.1 as AnyObject },
|
|
forKeys: elements.map { $0.0 as AnyObject as! NSCopying },
|
|
count: elements.count)
|
|
}
|
|
}
|
|
|
|
extension Dictionary {
|
|
/// Private initializer used for bridging.
|
|
///
|
|
/// The provided `NSDictionary` will be copied to ensure that the copy can
|
|
/// not be mutated by other code.
|
|
public init(_cocoaDictionary: _NSDictionary) {
|
|
_sanityCheck(
|
|
_isBridgedVerbatimToObjectiveC(Key.self) &&
|
|
_isBridgedVerbatimToObjectiveC(Value.self),
|
|
"Dictionary can be backed by NSDictionary storage only when both key and value are bridged verbatim to Objective-C")
|
|
// FIXME: We would like to call CFDictionaryCreateCopy() to avoid doing an
|
|
// objc_msgSend() for instances of CoreFoundation types. We can't do that
|
|
// today because CFDictionaryCreateCopy() copies dictionary contents
|
|
// unconditionally, resulting in O(n) copies even for immutable dictionaries.
|
|
//
|
|
// <rdar://problem/20690755> CFDictionaryCreateCopy() does not call copyWithZone:
|
|
//
|
|
// The bug is fixed in: OS X 10.11.0, iOS 9.0, all versions of tvOS
|
|
// and watchOS.
|
|
self = Dictionary(
|
|
_immutableCocoaDictionary:
|
|
unsafeBitCast(_cocoaDictionary.copy(with: nil) as AnyObject,
|
|
to: _NSDictionary.self))
|
|
}
|
|
}
|
|
|
|
// Dictionary<Key, Value> is conditionally bridged to NSDictionary
|
|
extension Dictionary : _ObjectiveCBridgeable {
|
|
@_semantics("convertToObjectiveC")
|
|
public func _bridgeToObjectiveC() -> NSDictionary {
|
|
return unsafeBitCast(_bridgeToObjectiveCImpl() as AnyObject,
|
|
to: NSDictionary.self)
|
|
}
|
|
|
|
public static func _forceBridgeFromObjectiveC(
|
|
_ d: NSDictionary,
|
|
result: inout Dictionary?
|
|
) {
|
|
if let native = [Key : Value]._bridgeFromObjectiveCAdoptingNativeStorageOf(
|
|
d as AnyObject) {
|
|
result = native
|
|
return
|
|
}
|
|
|
|
if _isBridgedVerbatimToObjectiveC(Key.self) &&
|
|
_isBridgedVerbatimToObjectiveC(Value.self) {
|
|
result = [Key : Value](
|
|
_cocoaDictionary: unsafeBitCast(d as AnyObject, to: _NSDictionary.self))
|
|
return
|
|
}
|
|
|
|
if Key.self == String.self {
|
|
// String and NSString have different concepts of equality, so
|
|
// string-keyed NSDictionaries may generate key collisions when bridged
|
|
// over to Swift. See rdar://problem/35995647
|
|
var dict = Dictionary(minimumCapacity: d.count)
|
|
d.enumerateKeysAndObjects({ (anyKey: Any, anyValue: Any, _) in
|
|
let key = Swift._forceBridgeFromObjectiveC(
|
|
anyKey as AnyObject, Key.self)
|
|
let value = Swift._forceBridgeFromObjectiveC(
|
|
anyValue as AnyObject, Value.self)
|
|
// FIXME: Log a warning if `dict` already had a value for `key`
|
|
dict[key] = value
|
|
})
|
|
result = dict
|
|
return
|
|
}
|
|
|
|
// `Dictionary<Key, Value>` where either `Key` or `Value` is a value type
|
|
// may not be backed by an NSDictionary.
|
|
var builder = _DictionaryBuilder<Key, Value>(count: d.count)
|
|
d.enumerateKeysAndObjects({ (anyKey: Any, anyValue: Any, _) in
|
|
let anyObjectKey = anyKey as AnyObject
|
|
let anyObjectValue = anyValue as AnyObject
|
|
builder.add(
|
|
key: Swift._forceBridgeFromObjectiveC(anyObjectKey, Key.self),
|
|
value: Swift._forceBridgeFromObjectiveC(anyObjectValue, Value.self))
|
|
})
|
|
result = builder.take()
|
|
}
|
|
|
|
public static func _conditionallyBridgeFromObjectiveC(
|
|
_ x: NSDictionary,
|
|
result: inout Dictionary?
|
|
) -> Bool {
|
|
let anyDict = x as [NSObject : AnyObject]
|
|
if _isBridgedVerbatimToObjectiveC(Key.self) &&
|
|
_isBridgedVerbatimToObjectiveC(Value.self) {
|
|
result = Swift._dictionaryDownCastConditional(anyDict)
|
|
return result != nil
|
|
}
|
|
|
|
result = Swift._dictionaryBridgeFromObjectiveCConditional(anyDict)
|
|
return result != nil
|
|
}
|
|
|
|
public static func _unconditionallyBridgeFromObjectiveC(
|
|
_ d: NSDictionary?
|
|
) -> Dictionary {
|
|
// `nil` has historically been used as a stand-in for an empty
|
|
// dictionary; map it to an empty dictionary.
|
|
if _slowPath(d == nil) { return Dictionary() }
|
|
|
|
var result: Dictionary? = nil
|
|
_forceBridgeFromObjectiveC(d!, result: &result)
|
|
return result!
|
|
}
|
|
}
|
|
|
|
extension NSDictionary : _HasCustomAnyHashableRepresentation {
|
|
// Must be @nonobjc to avoid infinite recursion during bridging
|
|
@nonobjc
|
|
public func _toCustomAnyHashable() -> AnyHashable? {
|
|
return AnyHashable(self as! Dictionary<AnyHashable, AnyHashable>)
|
|
}
|
|
}
|
|
|
|
extension NSDictionary : Sequence {
|
|
// FIXME: A class because we can't pass a struct with class fields through an
|
|
// [objc] interface without prematurely destroying the references.
|
|
final public class Iterator : IteratorProtocol {
|
|
var _fastIterator: NSFastEnumerationIterator
|
|
var _dictionary: NSDictionary {
|
|
return _fastIterator.enumerable as! NSDictionary
|
|
}
|
|
|
|
public func next() -> (key: Any, value: Any)? {
|
|
if let key = _fastIterator.next() {
|
|
// Deliberately avoid the subscript operator in case the dictionary
|
|
// contains non-copyable keys. This is rare since NSMutableDictionary
|
|
// requires them, but we don't want to paint ourselves into a corner.
|
|
return (key: key, value: _dictionary.object(forKey: key)!)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
internal init(_ _dict: NSDictionary) {
|
|
_fastIterator = NSFastEnumerationIterator(_dict)
|
|
}
|
|
}
|
|
|
|
// Bridging subscript.
|
|
@objc
|
|
public subscript(key: Any) -> Any? {
|
|
@objc(_swift_objectForKeyedSubscript:)
|
|
get {
|
|
// Deliberately avoid the subscript operator in case the dictionary
|
|
// contains non-copyable keys. This is rare since NSMutableDictionary
|
|
// requires them, but we don't want to paint ourselves into a corner.
|
|
return self.object(forKey: key)
|
|
}
|
|
}
|
|
|
|
/// Return an *iterator* over the elements of this *sequence*.
|
|
///
|
|
/// - Complexity: O(1).
|
|
public func makeIterator() -> Iterator {
|
|
return Iterator(self)
|
|
}
|
|
}
|
|
|
|
extension NSMutableDictionary {
|
|
// Bridging subscript.
|
|
override public subscript(key: Any) -> Any? {
|
|
get {
|
|
return self.object(forKey: key)
|
|
}
|
|
@objc(_swift_setObject:forKeyedSubscript:)
|
|
set {
|
|
// FIXME: Unfortunate that the `NSCopying` check has to be done at
|
|
// runtime.
|
|
let copyingKey = key as AnyObject as! NSCopying
|
|
if let newValue = newValue {
|
|
self.setObject(newValue, forKey: copyingKey)
|
|
} else {
|
|
self.removeObject(forKey: copyingKey)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension NSDictionary {
|
|
/// Initializes a newly allocated dictionary and adds to it objects from
|
|
/// another given dictionary.
|
|
///
|
|
/// - Returns: An initialized dictionary--which might be different
|
|
/// than the original receiver--containing the keys and values
|
|
/// found in `otherDictionary`.
|
|
@objc(_swiftInitWithDictionary_NSDictionary:)
|
|
public convenience init(dictionary otherDictionary: NSDictionary) {
|
|
// FIXME(performance)(compiler limitation): we actually want to do just
|
|
// `self = otherDictionary.copy()`, but Swift does not have factory
|
|
// initializers right now.
|
|
let numElems = otherDictionary.count
|
|
let stride = MemoryLayout<AnyObject>.stride
|
|
let alignment = MemoryLayout<AnyObject>.alignment
|
|
let singleSize = stride * numElems
|
|
let totalSize = singleSize * 2
|
|
_sanityCheck(stride == MemoryLayout<NSCopying>.stride)
|
|
_sanityCheck(alignment == MemoryLayout<NSCopying>.alignment)
|
|
|
|
// Allocate a buffer containing both the keys and values.
|
|
let buffer = UnsafeMutableRawPointer.allocate(
|
|
byteCount: totalSize, alignment: alignment)
|
|
defer {
|
|
buffer.deallocate()
|
|
_fixLifetime(otherDictionary)
|
|
}
|
|
|
|
let valueBuffer = buffer.bindMemory(to: AnyObject.self, capacity: numElems)
|
|
let buffer2 = buffer + singleSize
|
|
|
|
__NSDictionaryGetObjects(otherDictionary, buffer, buffer2, numElems)
|
|
|
|
let keyBufferCopying = buffer2.assumingMemoryBound(to: NSCopying.self)
|
|
self.init(objects: valueBuffer, forKeys: keyBufferCopying, count: numElems)
|
|
}
|
|
}
|
|
|
|
extension NSDictionary : CustomReflectable {
|
|
public var customMirror: Mirror {
|
|
return Mirror(reflecting: self as [NSObject : AnyObject])
|
|
}
|
|
}
|
|
|
|
extension Dictionary: CVarArg {}
|