//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 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 // //===----------------------------------------------------------------------===// #if _runtime(_ObjC) import SwiftShims /// Equivalent to `NSDictionary.allKeys`, but does not leave objects on the /// autorelease pool. internal func _stdlib_NSDictionary_allKeys( _ object: AnyObject ) -> _BridgingBuffer { let nsd = unsafe unsafeBitCast(object, to: _NSDictionary.self) let count = nsd.count let storage = _BridgingBuffer(count) unsafe nsd.getObjects(nil, andKeys: storage.baseAddress, count: count) return storage } extension _NativeDictionary { // Bridging @usableFromInline __consuming internal func bridged() -> AnyObject { _connectOrphanedFoundationSubclassesIfNeeded() // We can zero-cost bridge if our keys are verbatim // or if we're the empty singleton. // Temporary var for SOME type safety. let nsDictionary: _NSDictionaryCore if unsafe _storage === __RawDictionaryStorage.empty || count == 0 { unsafe nsDictionary = __RawDictionaryStorage.empty } else if _isBridgedVerbatimToObjectiveC(Key.self), _isBridgedVerbatimToObjectiveC(Value.self) { unsafe nsDictionary = unsafeDowncast( _storage, to: _DictionaryStorage.self) } else { nsDictionary = _SwiftDeferredNSDictionary(self) } return nsDictionary } } /// An NSEnumerator that works with any _NativeDictionary of /// verbatim bridgeable elements. Used by the various NSDictionary impls. @safe final internal class _SwiftDictionaryNSEnumerator : __SwiftNativeNSEnumerator, _NSEnumerator { @nonobjc internal var base: _NativeDictionary @nonobjc internal var bridgedKeys: __BridgingHashBuffer? @nonobjc internal var nextBucket: _NativeDictionary.Bucket @nonobjc internal var endBucket: _NativeDictionary.Bucket @objc internal override required init() { _internalInvariantFailure("don't call this designated initializer") } internal init(_ base: __owned _NativeDictionary) { _internalInvariant(_isBridgedVerbatimToObjectiveC(Key.self)) _internalInvariant(_orphanedFoundationSubclassesReparented) self.base = base unsafe self.bridgedKeys = nil unsafe self.nextBucket = base.hashTable.startBucket unsafe self.endBucket = base.hashTable.endBucket super.init() } @nonobjc internal init(_ deferred: __owned _SwiftDeferredNSDictionary) { _internalInvariant(!_isBridgedVerbatimToObjectiveC(Key.self)) _internalInvariant(_orphanedFoundationSubclassesReparented) self.base = deferred.native unsafe self.bridgedKeys = deferred.bridgeKeys() unsafe self.nextBucket = base.hashTable.startBucket unsafe self.endBucket = base.hashTable.endBucket super.init() } private func bridgedKey(at bucket: _HashTable.Bucket) -> AnyObject { unsafe _internalInvariant(base.hashTable.isOccupied(bucket)) if let bridgedKeys = unsafe self.bridgedKeys { return unsafe bridgedKeys[bucket] } return unsafe _bridgeAnythingToObjectiveC(base.uncheckedKey(at: bucket)) } @objc internal func nextObject() -> AnyObject? { if nextBucket == endBucket { return nil } let bucket = nextBucket unsafe nextBucket = base.hashTable.occupiedBucket(after: nextBucket) return self.bridgedKey(at: bucket) } @objc(countByEnumeratingWithState:objects:count:) internal func countByEnumerating( with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>, objects: UnsafeMutablePointer, count: Int ) -> Int { var theState = unsafe state.pointee if unsafe theState.state == 0 { unsafe theState.state = 1 // Arbitrary non-zero value. unsafe theState.itemsPtr = AutoreleasingUnsafeMutablePointer(objects) unsafe theState.mutationsPtr = _fastEnumerationStorageMutationsPtr } if nextBucket == endBucket { unsafe state.pointee = theState return 0 } // Return only a single element so that code can start iterating via fast // enumeration, terminate it, and continue via NSEnumerator. let unmanagedObjects = unsafe _UnmanagedAnyObjectArray(objects) unsafe unmanagedObjects[0] = self.bridgedKey(at: nextBucket) unsafe nextBucket = base.hashTable.occupiedBucket(after: nextBucket) unsafe state.pointee = theState return 1 } } /// This class exists for Objective-C bridging. It holds a reference to a /// _NativeDictionary, and can be upcast to NSSelf when bridging is /// necessary. This is the fallback implementation for situations where /// toll-free bridging isn't possible. On first access, a _NativeDictionary /// of AnyObject will be constructed containing all the bridged elements. final internal class _SwiftDeferredNSDictionary : __SwiftNativeNSDictionary, _NSDictionaryCore { @usableFromInline internal typealias Bucket = _HashTable.Bucket // This stored property must be stored at offset zero. We perform atomic // operations on it. // // Do not access this property directly. @nonobjc private var _bridgedKeys_DoNotUse: AnyObject? // This stored property must be stored at offset one. We perform atomic // operations on it. // // Do not access this property directly. @nonobjc private var _bridgedValues_DoNotUse: AnyObject? /// The unbridged elements. internal var native: _NativeDictionary internal init(_ native: __owned _NativeDictionary) { _internalInvariant(native.count > 0) _internalInvariant(!_isBridgedVerbatimToObjectiveC(Key.self) || !_isBridgedVerbatimToObjectiveC(Value.self)) self.native = native super.init() } @objc internal required init( objects: UnsafePointer, forKeys: UnsafeRawPointer, count: Int ) { _internalInvariantFailure("don't call this designated initializer") } @nonobjc private var _bridgedKeysPtr: UnsafeMutablePointer { return unsafe _getUnsafePointerToStoredProperties(self) .assumingMemoryBound(to: Optional.self) } @nonobjc private var _bridgedValuesPtr: UnsafeMutablePointer { return unsafe _bridgedKeysPtr + 1 } /// The buffer for bridged keys, if present. @nonobjc private var _bridgedKeys: __BridgingHashBuffer? { guard let ref = unsafe _stdlib_atomicLoadARCRef(object: _bridgedKeysPtr) else { return nil } return unsafe unsafeDowncast(ref, to: __BridgingHashBuffer.self) } /// The buffer for bridged values, if present. @nonobjc private var _bridgedValues: __BridgingHashBuffer? { guard let ref = unsafe _stdlib_atomicLoadARCRef(object: _bridgedValuesPtr) else { return nil } return unsafe unsafeDowncast(ref, to: __BridgingHashBuffer.self) } /// Attach a buffer for bridged Dictionary keys. @nonobjc private func _initializeBridgedKeys(_ storage: __BridgingHashBuffer) { unsafe _stdlib_atomicInitializeARCRef(object: _bridgedKeysPtr, desired: storage) } /// Attach a buffer for bridged Dictionary values. @nonobjc private func _initializeBridgedValues(_ storage: __BridgingHashBuffer) { unsafe _stdlib_atomicInitializeARCRef(object: _bridgedValuesPtr, desired: storage) } @nonobjc internal func bridgeKeys() -> __BridgingHashBuffer? { if _isBridgedVerbatimToObjectiveC(Key.self) { return nil } if let bridgedKeys = unsafe _bridgedKeys { return unsafe bridgedKeys } // Allocate and initialize heap storage for bridged keys. let bridged = unsafe __BridgingHashBuffer.allocate( owner: native._storage, hashTable: native.hashTable) for unsafe bucket in unsafe native.hashTable { let object = unsafe _bridgeAnythingToObjectiveC( native.uncheckedKey(at: bucket)) unsafe bridged.initialize(at: bucket, to: object) } // Atomically put the bridged keys in place. unsafe _initializeBridgedKeys(bridged) return unsafe _bridgedKeys! } @nonobjc internal func bridgeValues() -> __BridgingHashBuffer? { if _isBridgedVerbatimToObjectiveC(Value.self) { return nil } if let bridgedValues = unsafe _bridgedValues { return unsafe bridgedValues } // Allocate and initialize heap storage for bridged values. let bridged = unsafe __BridgingHashBuffer.allocate( owner: native._storage, hashTable: native.hashTable) for unsafe bucket in unsafe native.hashTable { let value = unsafe native.uncheckedValue(at: bucket) let cocoaValue = _bridgeAnythingToObjectiveC(value) unsafe bridged.initialize(at: bucket, to: cocoaValue) } // Atomically put the bridged values in place. unsafe _initializeBridgedValues(bridged) return unsafe _bridgedValues! } @objc(copyWithZone:) internal func copy(with zone: _SwiftNSZone?) -> AnyObject { // Instances of this class should be visible outside of standard library as // having `NSDictionary` type, which is immutable. return self } @inline(__always) private func _key( at bucket: Bucket, bridgedKeys: __BridgingHashBuffer? ) -> AnyObject { if let bridgedKeys = unsafe bridgedKeys { return unsafe bridgedKeys[bucket] } return unsafe _bridgeAnythingToObjectiveC(native.uncheckedKey(at: bucket)) } @inline(__always) private func _value( at bucket: Bucket, bridgedValues: __BridgingHashBuffer? ) -> AnyObject { if let bridgedValues = unsafe bridgedValues { return unsafe bridgedValues[bucket] } return unsafe _bridgeAnythingToObjectiveC(native.uncheckedValue(at: bucket)) } @objc(objectForKey:) internal func object(forKey aKey: AnyObject) -> AnyObject? { guard let nativeKey = _conditionallyBridgeFromObjectiveC(aKey, Key.self) else { return nil } let (bucket, found) = native.find(nativeKey) guard found else { return nil } return unsafe _value(at: bucket, bridgedValues: bridgeValues()) } @objc internal func keyEnumerator() -> _NSEnumerator { if _isBridgedVerbatimToObjectiveC(Key.self) { return _SwiftDictionaryNSEnumerator(native) } return _SwiftDictionaryNSEnumerator(self) } @objc(getObjects:andKeys:count:) internal func getObjects( _ objects: UnsafeMutablePointer?, andKeys keys: UnsafeMutablePointer?, count: Int ) { _precondition(count >= 0, "Invalid count") guard count > 0 else { return } let bridgedKeys = unsafe bridgeKeys() let bridgedValues = unsafe bridgeValues() var i = 0 // Current position in the output buffers defer { _fixLifetime(self) } switch unsafe (_UnmanagedAnyObjectArray(keys), _UnmanagedAnyObjectArray(objects)) { case (let unmanagedKeys?, let unmanagedObjects?): for unsafe bucket in unsafe native.hashTable { unsafe unmanagedKeys[i] = unsafe _key(at: bucket, bridgedKeys: bridgedKeys) unsafe unmanagedObjects[i] = unsafe _value(at: bucket, bridgedValues: bridgedValues) i += 1 guard i < count else { break } } case (let unmanagedKeys?, nil): for unsafe bucket in unsafe native.hashTable { unsafe unmanagedKeys[i] = unsafe _key(at: bucket, bridgedKeys: bridgedKeys) i += 1 guard i < count else { break } } case (nil, let unmanagedObjects?): for unsafe bucket in unsafe native.hashTable { unsafe unmanagedObjects[i] = unsafe _value(at: bucket, bridgedValues: bridgedValues) i += 1 guard i < count else { break } } case (nil, nil): // Do nothing break } } @objc(enumerateKeysAndObjectsWithOptions:usingBlock:) internal func enumerateKeysAndObjects( options: Int, using block: @convention(block) ( Unmanaged, Unmanaged, UnsafeMutablePointer ) -> Void) { let bridgedKeys = unsafe bridgeKeys() let bridgedValues = unsafe bridgeValues() defer { _fixLifetime(self) } var stop: UInt8 = 0 for unsafe bucket in unsafe native.hashTable { let key = unsafe _key(at: bucket, bridgedKeys: bridgedKeys) let value = unsafe _value(at: bucket, bridgedValues: bridgedValues) unsafe block( Unmanaged.passUnretained(key), Unmanaged.passUnretained(value), &stop) if stop != 0 { return } } } @objc internal var count: Int { return native.count } @objc(countByEnumeratingWithState:objects:count:) internal func countByEnumerating( with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>, objects: UnsafeMutablePointer?, count: Int ) -> Int { defer { _fixLifetime(self) } let hashTable = unsafe native.hashTable var theState = unsafe state.pointee if unsafe theState.state == 0 { unsafe theState.state = 1 // Arbitrary non-zero value. unsafe theState.itemsPtr = AutoreleasingUnsafeMutablePointer(objects) unsafe theState.mutationsPtr = _fastEnumerationStorageMutationsPtr unsafe theState.extra.0 = CUnsignedLong(hashTable.startBucket.offset) } // Test 'objects' rather than 'count' because (a) this is very rare anyway, // and (b) the optimizer should then be able to optimize away the // unwrapping check below. if unsafe _slowPath(objects == nil) { return 0 } let unmanagedObjects = unsafe _UnmanagedAnyObjectArray(objects!) var bucket = unsafe _HashTable.Bucket(offset: Int(theState.extra.0)) let endBucket = unsafe hashTable.endBucket unsafe _precondition(bucket == endBucket || hashTable.isOccupied(bucket), "Invalid fast enumeration state") var stored = 0 // Only need to bridge once, so we can hoist it out of the loop. let bridgedKeys = unsafe bridgeKeys() for i in 0.. Bool { return _stdlib_NSObject_isEqual(self.object, other.object) } } extension __CocoaDictionary: _DictionaryBuffer { @usableFromInline internal typealias Key = AnyObject @usableFromInline internal typealias Value = AnyObject @usableFromInline // FIXME(cocoa-index): Should be inlinable internal var startIndex: Index { @_effects(releasenone) get { let allKeys = _stdlib_NSDictionary_allKeys(self.object) return Index(Index.Storage(self, allKeys), offset: 0) } } @usableFromInline // FIXME(cocoa-index): Should be inlinable internal var endIndex: Index { @_effects(releasenone) get { let allKeys = _stdlib_NSDictionary_allKeys(self.object) return Index(Index.Storage(self, allKeys), offset: allKeys.count) } } @usableFromInline // FIXME(cocoa-index): Should be inlinable @_effects(releasenone) internal func index(after index: Index) -> Index { validate(index) var result = index result._offset += 1 return result } internal func validate(_ index: Index) { _precondition(index.storage.base.object === self.object, "Invalid index") _precondition(index._offset < index.storage.allKeys.count, "Attempt to access endIndex") } @usableFromInline // FIXME(cocoa-index): Should be inlinable internal func formIndex(after index: inout Index, isUnique: Bool) { validate(index) index._offset += 1 } @usableFromInline // FIXME(cocoa-index): Should be inlinable @_effects(releasenone) internal func index(forKey key: Key) -> Index? { // Fast path that does not involve creating an array of all keys. In case // the key is present, this lookup is a penalty for the slow path, but the // potential savings are significant: we could skip a memory allocation and // a linear search. if lookup(key) == nil { return nil } let allKeys = _stdlib_NSDictionary_allKeys(object) for i in 0.. Bool { let nsd = unsafe unsafeBitCast(object, to: _NSDictionary.self) return nsd.object(forKey: key) != nil } @usableFromInline internal func lookup(_ key: Key) -> Value? { let nsd = unsafe unsafeBitCast(object, to: _NSDictionary.self) return nsd.object(forKey: key) } @usableFromInline // FIXME(cocoa-index): Should be inlinable @_effects(releasenone) internal func lookup(_ index: Index) -> (key: Key, value: Value) { _precondition(index.storage.base.object === self.object, "Invalid index") let key: Key = index.storage.allKeys[index._offset] let value: Value = unsafe index.storage.base.object.object(forKey: key)! return (key, value) } @usableFromInline // FIXME(cocoa-index): Make inlinable @_effects(releasenone) func key(at index: Index) -> Key { _precondition(index.storage.base.object === self.object, "Invalid index") return index.key } @usableFromInline // FIXME(cocoa-index): Make inlinable @_effects(releasenone) func value(at index: Index) -> Value { _precondition(index.storage.base.object === self.object, "Invalid index") let key = index.storage.allKeys[index._offset] return unsafe index.storage.base.object.object(forKey: key)! } } extension __CocoaDictionary { @inlinable internal func mapValues( _ transform: (Value) throws -> T ) rethrows -> _NativeDictionary { var result = _NativeDictionary(capacity: self.count) for (cocoaKey, cocoaValue) in self { let key = _forceBridgeFromObjectiveC(cocoaKey, Key.self) let value = _forceBridgeFromObjectiveC(cocoaValue, Value.self) try result.insertNew(key: key, value: transform(value)) } return result } } extension __CocoaDictionary { @frozen @usableFromInline internal struct Index { internal var _storage: Builtin.BridgeObject internal var _offset: Int internal var storage: Storage { @inline(__always) get { let storage = _bridgeObject(toNative: _storage) return unsafe unsafeDowncast(storage, to: Storage.self) } } internal init(_ storage: Storage, offset: Int) { self._storage = _bridgeObject(fromNative: storage) self._offset = offset } } } extension __CocoaDictionary.Index { // FIXME(cocoa-index): Try using an NSEnumerator to speed this up. internal class Storage { // Assumption: we rely on NSDictionary.getObjects when being // repeatedly called on the same NSDictionary, returning items in the same // order every time. // Similarly, the same assumption holds for NSSet.allObjects. /// A reference to the NSDictionary, which owns members in `allObjects`, /// or `allKeys`, for NSSet and NSDictionary respectively. internal let base: __CocoaDictionary // FIXME: swift-3-indexing-model: try to remove the cocoa reference, but // make sure that we have a safety check for accessing `allKeys`. Maybe // move both into the dictionary/set itself. /// An unowned array of keys. internal var allKeys: _BridgingBuffer internal init( _ base: __owned __CocoaDictionary, _ allKeys: __owned _BridgingBuffer ) { self.base = base self.allKeys = allKeys } } } extension __CocoaDictionary.Index { @usableFromInline internal var handleBitPattern: UInt { @_effects(readonly) get { return unsafe unsafeBitCast(storage, to: UInt.self) } } @usableFromInline internal var dictionary: __CocoaDictionary { @_effects(releasenone) get { return storage.base } } } extension __CocoaDictionary.Index { @usableFromInline // FIXME(cocoa-index): Make inlinable @nonobjc internal var key: AnyObject { @_effects(readonly) get { _precondition(_offset < storage.allKeys.count, "Attempting to access Dictionary elements using an invalid index") return storage.allKeys[_offset] } } @usableFromInline // FIXME(cocoa-index): Make inlinable @nonobjc internal var age: Int32 { @_effects(readonly) get { return unsafe _HashTable.age(for: storage.base.object) } } } extension __CocoaDictionary.Index: Equatable { @usableFromInline // FIXME(cocoa-index): Make inlinable @_effects(readonly) internal static func == ( lhs: __CocoaDictionary.Index, rhs: __CocoaDictionary.Index ) -> Bool { _precondition(lhs.storage.base.object === rhs.storage.base.object, "Comparing indexes from different dictionaries") return lhs._offset == rhs._offset } } extension __CocoaDictionary.Index: Comparable { @usableFromInline // FIXME(cocoa-index): Make inlinable @_effects(readonly) internal static func < ( lhs: __CocoaDictionary.Index, rhs: __CocoaDictionary.Index ) -> Bool { _precondition(lhs.storage.base.object === rhs.storage.base.object, "Comparing indexes from different dictionaries") return lhs._offset < rhs._offset } } extension __CocoaDictionary: Sequence { @safe @usableFromInline final internal class Iterator { // Cocoa Dictionary iterator has to be a class, otherwise we cannot // guarantee that the fast enumeration struct is pinned to a certain memory // location. // This stored property should be stored at offset zero. There's code below // relying on this. internal var _fastEnumerationState: _SwiftNSFastEnumerationState = unsafe _makeSwiftNSFastEnumerationState() // This stored property should be stored right after // `_fastEnumerationState`. There's code below relying on this. internal var _fastEnumerationStackBuf = unsafe _CocoaFastEnumerationStackBuf() internal let base: __CocoaDictionary internal var _fastEnumerationStatePtr: UnsafeMutablePointer<_SwiftNSFastEnumerationState> { return unsafe _getUnsafePointerToStoredProperties(self).assumingMemoryBound( to: _SwiftNSFastEnumerationState.self) } internal var _fastEnumerationStackBufPtr: UnsafeMutablePointer<_CocoaFastEnumerationStackBuf> { return unsafe UnsafeMutableRawPointer(_fastEnumerationStatePtr + 1) .assumingMemoryBound(to: _CocoaFastEnumerationStackBuf.self) } // These members have to be word-sized integers, they cannot be limited to // Int8 just because our storage holds 16 elements: fast enumeration is // allowed to return inner pointers to the container, which can be much // larger. internal var itemIndex: Int = 0 internal var itemCount: Int = 0 internal init(_ base: __owned __CocoaDictionary) { self.base = base } } @usableFromInline @_effects(releasenone) internal __consuming func makeIterator() -> Iterator { return Iterator(self) } } @available(*, unavailable) extension __CocoaDictionary.Iterator: Sendable {} extension __CocoaDictionary.Iterator: IteratorProtocol { @usableFromInline internal typealias Element = (key: AnyObject, value: AnyObject) @usableFromInline internal func nextKey() -> AnyObject? { if itemIndex < 0 { return nil } let base = self.base if itemIndex == itemCount { let stackBufCount = unsafe _fastEnumerationStackBuf.count // We can't use `withUnsafeMutablePointer` here to get pointers to // properties, because doing so might introduce a writeback storage, but // fast enumeration relies on the pointer identity of the enumeration // state struct. itemCount = unsafe base.object.countByEnumerating( with: _fastEnumerationStatePtr, objects: UnsafeMutableRawPointer(_fastEnumerationStackBufPtr) .assumingMemoryBound(to: AnyObject.self), count: stackBufCount) if itemCount == 0 { itemIndex = -1 return nil } itemIndex = 0 } let itemsPtrUP = unsafe UnsafeMutableRawPointer(_fastEnumerationState.itemsPtr!) .assumingMemoryBound(to: AnyObject.self) let itemsPtr = unsafe _UnmanagedAnyObjectArray(itemsPtrUP) let key: AnyObject = unsafe itemsPtr[itemIndex] itemIndex += 1 return key } @usableFromInline internal func next() -> Element? { guard let key = nextKey() else { return nil } let value: AnyObject = unsafe base.object.object(forKey: key)! return (key, value) } } //===--- Bridging ---------------------------------------------------------===// extension Dictionary { @inlinable public __consuming func _bridgeToObjectiveCImpl() -> AnyObject { guard _variant.isNative else { return _variant.asCocoa.object } return _variant.asNative.bridged() } /// Returns the native Dictionary hidden inside this NSDictionary; /// returns nil otherwise. public static func _bridgeFromObjectiveCAdoptingNativeStorageOf( _ s: __owned AnyObject ) -> Dictionary? { // Try all three NSDictionary impls that we currently provide. if let deferred = s as? _SwiftDeferredNSDictionary { return Dictionary(_native: deferred.native) } if let nativeStorage = unsafe s as? _DictionaryStorage { return Dictionary(_native: unsafe _NativeDictionary(nativeStorage)) } if unsafe s === __RawDictionaryStorage.empty { return Dictionary() } // FIXME: what if `s` is native storage, but for different key/value type? return nil } } #endif // _runtime(_ObjC)