blob: 824bc6169161932d1103373b36eebb525cd9f1c9 [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
@_implementationOnly import _SwiftFoundationOverlayShims
// We don't check for NSCopying here for performance reasons. We would
// just crash anyway, and NSMutableDictionary will still do that when
// it tries to call -copyWithZone: and it's not there
private func duckCastToNSCopying(_ x: Any) -> NSCopying {
return _unsafeReferenceCast(x as AnyObject, to: NSCopying.self)
}
//===----------------------------------------------------------------------===//
// Dictionaries
//===----------------------------------------------------------------------===//
extension NSDictionary : ExpressibleByDictionaryLiteral {
public required convenience init(
dictionaryLiteral elements: (Any, Any)...
) {
self.init(
objects: elements.map { $0.1 as AnyObject },
forKeys: elements.map { duckCastToNSCopying($0.0) },
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.
private init(_cocoaDictionary: __shared NSDictionary) {
assert(
_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: _cocoaDictionary.copy(with: nil) as AnyObject)
}
}
// Dictionary<Key, Value> is conditionally bridged to NSDictionary
extension Dictionary : _ObjectiveCBridgeable {
@_semantics("convertToObjectiveC")
public func _bridgeToObjectiveC() -> NSDictionary {
return unsafeBitCast(_bridgeToObjectiveCImpl(),
to: NSDictionary.self)
}
/***
Precondition: `buffer` points to a region of memory bound to `AnyObject`,
with a capacity large enough to fit at least `index`+1 elements of type `T`
_bridgeInitialize rebinds the `index`th `T` of `buffer` to `T`,
and initializes it to `value`
Note: *not* the `index`th element of `buffer`, since T and AnyObject may be
different sizes. e.g. if T is String (2 words) then given a buffer like so:
[object:AnyObject, object:AnyObject, uninitialized, uninitialized]
`_bridgeInitialize(1, of: buffer, to: buffer[1] as! T)` will leave it as:
[object:AnyObject, object:AnyObject, string:String]
and `_bridgeInitialize(0, of: buffer, to: buffer[0] as! T)` will then leave:
[string:String, string:String]
Doing this in reverse order as shown above is required if T and AnyObject are
different sizes. Here's what we get if instead of 1, 0 we did 0, 1:
[object:AnyObject, object:AnyObject, uninitialized, uninitialized]
[string:String, uninitialized, uninitialized]
<segfault trying to treat the second word of 'string' as an AnyObject>
Note: if you have retained any of the objects in `buffer`, you must release
them separately, _bridgeInitialize will overwrite them without releasing them
*/
@inline(__always)
private static func _bridgeInitialize<T>(index:Int,
of buffer: UnsafePointer<AnyObject>, to value: T) {
let typedBase = UnsafeMutableRawPointer(mutating:
buffer).assumingMemoryBound(to: T.self)
let rawTarget = UnsafeMutableRawPointer(mutating: typedBase + index)
rawTarget.initializeMemory(as: T.self, repeating: value, count: 1)
}
@inline(__always)
private static func _verbatimForceBridge<T>(
_ buffer: UnsafeMutablePointer<AnyObject>,
count: Int,
to: T.Type
) {
//doesn't have to iterate in reverse because sizeof(T) == sizeof(AnyObject)
for i in 0..<count {
_bridgeInitialize(index: i, of: buffer, to: buffer[i] as! T)
}
}
@inline(__always)
private static func _verbatimBridge<T>(
_ buffer: UnsafeMutablePointer<AnyObject>,
count: Int,
to type: T.Type
) -> Int {
var numUninitialized = count
while numUninitialized > 0 {
guard let bridged = buffer[numUninitialized - 1] as? T else {
return numUninitialized
}
numUninitialized -= 1
_bridgeInitialize(index: numUninitialized, of: buffer, to: bridged)
}
return numUninitialized
}
@inline(__always)
private static func _nonVerbatimForceBridge<T>(
_ buffer: UnsafeMutablePointer<AnyObject>,
count: Int,
to: T.Type
) {
for i in (0..<count).reversed() {
let bridged = buffer[i] as! T
_bridgeInitialize(index: i, of: buffer, to: bridged)
}
}
@inline(__always)
private static func _nonVerbatimBridge<T>(
_ buffer: UnsafeMutablePointer<AnyObject>,
count: Int,
to: T.Type
) -> Int {
var numUninitialized = count
while numUninitialized > 0 {
guard let bridged = Swift._conditionallyBridgeFromObjectiveC(
buffer[numUninitialized - 1], T.self)
else {
return numUninitialized
}
numUninitialized -= 1
_bridgeInitialize(index: numUninitialized, of: buffer, to: bridged)
}
return numUninitialized
}
@inline(__always)
private static func _forceBridge<T>(
_ buffer: UnsafeMutablePointer<AnyObject>,
count: Int,
to: T.Type
) {
if _isBridgedVerbatimToObjectiveC(T.self) {
_verbatimForceBridge(buffer, count: count, to: T.self)
} else {
_nonVerbatimForceBridge(buffer, count: count, to: T.self)
}
}
@inline(__always)
private static func _conditionallyBridge<T>(
_ buffer: UnsafeMutablePointer<AnyObject>,
count: Int,
to: T.Type
) -> Bool {
let numUninitialized:Int
if _isBridgedVerbatimToObjectiveC(T.self) {
numUninitialized = _verbatimBridge(buffer, count: count, to: T.self)
} else {
numUninitialized = _nonVerbatimBridge(buffer, count: count, to: T.self)
}
if numUninitialized == 0 {
return true
}
let numInitialized = count - numUninitialized
(UnsafeMutableRawPointer(mutating: buffer).assumingMemoryBound(to:
T.self) + numUninitialized).deinitialize(count: numInitialized)
return false
}
@_specialize(where Key == String, Value == Any)
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) {
//Lazily type-checked on access
result = [Key : Value](_cocoaDictionary: d)
return
}
let keyStride = MemoryLayout<Key>.stride
let valueStride = MemoryLayout<Value>.stride
let objectStride = MemoryLayout<AnyObject>.stride
//If Key or Value are smaller than AnyObject, a Dictionary with N elements
//doesn't have large enough backing stores to hold the objects to be bridged
//For now we just handle that case the slow way.
if keyStride < objectStride || valueStride < objectStride {
var builder = _DictionaryBuilder<Key, Value>(count: d.count)
d.enumerateKeysAndObjects({ (anyKey: Any, anyValue: Any, _) in
builder.add(
key: anyKey as! Key,
value: anyValue as! Value)
})
result = builder.take()
} else {
defer { _fixLifetime(d) }
let numElems = d.count
// 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
let handleDuplicates = (Key.self == String.self)
result = Dictionary(_unsafeUninitializedCapacity: numElems,
allowingDuplicates: handleDuplicates) { keys, vals in
let objectKeys = UnsafeMutableRawPointer(mutating:
keys.baseAddress!).assumingMemoryBound(to: AnyObject.self)
let objectVals = UnsafeMutableRawPointer(mutating:
vals.baseAddress!).assumingMemoryBound(to: AnyObject.self)
//This initializes the first N AnyObjects of the Dictionary buffers.
//Any unused buffer space is left uninitialized
//This is fixed up in-place as we bridge elements, by _bridgeInitialize
__NSDictionaryGetObjects(d, objectVals, objectKeys, numElems)
_forceBridge(objectKeys, count: numElems, to: Key.self)
_forceBridge(objectVals, count: numElems, to: Value.self)
return numElems
}
}
}
@_specialize(where Key == String, Value == Any)
public static func _conditionallyBridgeFromObjectiveC(
_ x: NSDictionary,
result: inout Dictionary?
) -> Bool {
if let native = [Key : Value]._bridgeFromObjectiveCAdoptingNativeStorageOf(
x as AnyObject) {
result = native
return true
}
let keyStride = MemoryLayout<Key>.stride
let valueStride = MemoryLayout<Value>.stride
let objectStride = MemoryLayout<AnyObject>.stride
//If Key or Value are smaller than AnyObject, a Dictionary with N elements
//doesn't have large enough backing stores to hold the objects to be bridged
//For now we just handle that case the slow way.
if keyStride < objectStride || valueStride < objectStride {
result = x as [NSObject : AnyObject] as? Dictionary
return result != nil
}
defer { _fixLifetime(x) }
let numElems = x.count
var success = true
// 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
let handleDuplicates = (Key.self == String.self)
let tmpResult = Dictionary(_unsafeUninitializedCapacity: numElems,
allowingDuplicates: handleDuplicates) { keys, vals in
let objectKeys = UnsafeMutableRawPointer(mutating:
keys.baseAddress!).assumingMemoryBound(to: AnyObject.self)
let objectVals = UnsafeMutableRawPointer(mutating:
vals.baseAddress!).assumingMemoryBound(to: AnyObject.self)
//This initializes the first N AnyObjects of the Dictionary buffers.
//Any unused buffer space is left uninitialized
//This is fixed up in-place as we bridge elements, by _bridgeInitialize
__NSDictionaryGetObjects(x, objectVals, objectKeys, numElems)
success = _conditionallyBridge(objectKeys, count: numElems, to: Key.self)
if success {
success = _conditionallyBridge(objectVals,
count: numElems, to: Value.self)
if !success {
(UnsafeMutableRawPointer(mutating: objectKeys).assumingMemoryBound(to:
Key.self)).deinitialize(count: numElems)
}
}
return success ? numElems : 0
}
result = success ? tmpResult : nil
return success
}
@_effects(readonly)
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.
// NOTE: older runtimes had
// _TtCE10FoundationCSo12NSDictionary8Iterator as the ObjC name. The
// two must coexist, so it was renamed. The old name must not be used
// in the new runtime.
@_objcRuntimeName(_TtCE10FoundationCSo12NSDictionary9_Iterator)
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: __shared 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.
@objc override public subscript(key: Any) -> Any? {
@objc(__swift_objectForKeyedSubscript:)
get {
return self.object(forKey: key)
}
@objc(__swift_setObject:forKeyedSubscript:)
set {
let copyingKey = duckCastToNSCopying(key)
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: __shared 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
assert(stride == MemoryLayout<NSCopying>.stride)
assert(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 {}