blob: 57eb4cdf4092600a5462d3b33532d246fda1dd31 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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 : 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 {}