mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
250 lines
8.8 KiB
Swift
250 lines
8.8 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
|
|
}
|
|
|
|
// `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() }
|
|
|
|
if let native = [Key : Value]._bridgeFromObjectiveCAdoptingNativeStorageOf(
|
|
d! as AnyObject) {
|
|
return native
|
|
}
|
|
|
|
if _isBridgedVerbatimToObjectiveC(Key.self) &&
|
|
_isBridgedVerbatimToObjectiveC(Value.self) {
|
|
return [Key : Value](
|
|
_cocoaDictionary: unsafeBitCast(d! as AnyObject, to: _NSDictionary.self))
|
|
}
|
|
|
|
// `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
|
|
builder.add(
|
|
key: Swift._forceBridgeFromObjectiveC(anyKey as AnyObject, Key.self),
|
|
value: Swift._forceBridgeFromObjectiveC(anyValue as AnyObject, Value.self))
|
|
})
|
|
return builder.take()
|
|
}
|
|
}
|
|
|
|
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 {}
|