//===----------------------------------------------------------------------===// // // 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. // // 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 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` where either `Key` or `Value` is a value type // may not be backed by an NSDictionary. var builder = _DictionaryBuilder(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) } } 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.stride let alignment = MemoryLayout.alignment let singleSize = stride * numElems let totalSize = singleSize * 2 _sanityCheck(stride == MemoryLayout.stride) _sanityCheck(alignment == MemoryLayout.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 {}