mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
226 lines
8.7 KiB
Swift
226 lines
8.7 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
@_exported import Foundation // Clang module
|
|
|
|
/// A class type which acts as a handle (pointer-to-pointer) to a Foundation reference type which has only a mutable class (e.g., NSURLComponents).
|
|
///
|
|
/// Note: This assumes that the result of calling copy() is mutable. The documentation says that classes which do not have a mutable/immutable distinction should just adopt NSCopying instead of NSMutableCopying.
|
|
internal final class _MutableHandle<MutableType : NSObject where MutableType : NSCopying> {
|
|
private var _pointer : MutableType
|
|
|
|
init(reference : MutableType) {
|
|
_pointer = reference.copy() as! MutableType
|
|
}
|
|
|
|
init(adoptingReference reference: MutableType) {
|
|
_pointer = reference
|
|
}
|
|
|
|
/// Apply a closure to the reference type.
|
|
func map<ReturnType>(_ whatToDo : @noescape (MutableType) throws -> ReturnType) rethrows -> ReturnType {
|
|
return try whatToDo(_pointer)
|
|
}
|
|
|
|
func _copiedReference() -> MutableType {
|
|
return _pointer.copy() as! MutableType
|
|
}
|
|
|
|
func _uncopiedReference() -> MutableType {
|
|
return _pointer
|
|
}
|
|
}
|
|
|
|
/// Describes common operations for Foundation struct types that are bridged to a mutable object (e.g. NSURLComponents).
|
|
internal protocol _MutableBoxing : ReferenceConvertible {
|
|
var _handle : _MutableHandle<ReferenceType> { get set }
|
|
|
|
/// Apply a mutating closure to the reference type, regardless if it is mutable or immutable.
|
|
///
|
|
/// This function performs the correct copy-on-write check for efficient mutation.
|
|
mutating func _applyMutation<ReturnType>(_ whatToDo : @noescape (ReferenceType) -> ReturnType) -> ReturnType
|
|
}
|
|
|
|
extension _MutableBoxing {
|
|
@inline(__always)
|
|
mutating func _applyMutation<ReturnType>(_ whatToDo : @noescape(ReferenceType) -> ReturnType) -> ReturnType {
|
|
// Only create a new box if we are not uniquely referenced
|
|
if !isUniquelyReferencedNonObjC(&_handle) {
|
|
let ref = _handle._pointer
|
|
_handle = _MutableHandle(reference: ref)
|
|
}
|
|
return whatToDo(_handle._pointer)
|
|
}
|
|
}
|
|
|
|
internal enum _MutableUnmanagedWrapper<ImmutableType : NSObject, MutableType : NSObject where MutableType : NSMutableCopying> {
|
|
case Immutable(Unmanaged<ImmutableType>)
|
|
case Mutable(Unmanaged<MutableType>)
|
|
}
|
|
|
|
internal protocol _SwiftNativeFoundationType : class {
|
|
associatedtype ImmutableType : NSObject
|
|
associatedtype MutableType : NSObject, NSMutableCopying
|
|
var __wrapped : _MutableUnmanagedWrapper<ImmutableType, MutableType> { get }
|
|
|
|
init(unmanagedImmutableObject: Unmanaged<ImmutableType>)
|
|
init(unmanagedMutableObject: Unmanaged<MutableType>)
|
|
|
|
func mutableCopy(with zone : NSZone) -> AnyObject
|
|
|
|
var hashValue: Int { get }
|
|
var description: String { get }
|
|
var debugDescription: String { get }
|
|
|
|
func releaseWrappedObject()
|
|
}
|
|
|
|
extension _SwiftNativeFoundationType {
|
|
|
|
@inline(__always)
|
|
func _mapUnmanaged<ReturnType>(_ whatToDo : @noescape (ImmutableType) throws -> ReturnType) rethrows -> ReturnType {
|
|
defer { _fixLifetime(self) }
|
|
|
|
switch __wrapped {
|
|
case .Immutable(let i):
|
|
return try i._withUnsafeGuaranteedRef {
|
|
_onFastPath()
|
|
return try whatToDo($0)
|
|
}
|
|
case .Mutable(let m):
|
|
return try m._withUnsafeGuaranteedRef {
|
|
_onFastPath()
|
|
return try whatToDo(_unsafeReferenceCast($0, to: ImmutableType.self))
|
|
}
|
|
}
|
|
}
|
|
|
|
func releaseWrappedObject() {
|
|
switch __wrapped {
|
|
case .Immutable(let i):
|
|
i.release()
|
|
break
|
|
case .Mutable(let m):
|
|
m.release()
|
|
break
|
|
}
|
|
}
|
|
|
|
func mutableCopy(with zone : NSZone) -> AnyObject {
|
|
return _mapUnmanaged { ($0 as AnyObject).mutableCopy(with: zone) }
|
|
}
|
|
|
|
var hashValue: Int {
|
|
return _mapUnmanaged { return $0.hashValue }
|
|
}
|
|
|
|
var description: String {
|
|
return _mapUnmanaged { return $0.description }
|
|
}
|
|
|
|
var debugDescription: String {
|
|
return _mapUnmanaged { return $0.debugDescription }
|
|
}
|
|
|
|
func isEqual(_ other: AnyObject) -> Bool {
|
|
return _mapUnmanaged { return $0.isEqual(other) }
|
|
}
|
|
}
|
|
|
|
internal protocol _MutablePairBoxing {
|
|
associatedtype WrappedSwiftNSType : _SwiftNativeFoundationType
|
|
var _wrapped : WrappedSwiftNSType { get set }
|
|
}
|
|
|
|
extension _MutablePairBoxing {
|
|
@inline(__always)
|
|
func _mapUnmanaged<ReturnType>(_ whatToDo : @noescape (WrappedSwiftNSType.ImmutableType) throws -> ReturnType) rethrows -> ReturnType {
|
|
// We are using Unmanaged. Make sure that the owning container class
|
|
// 'self' is guaranteed to be alive by extending the lifetime of 'self'
|
|
// to the end of the scope of this function.
|
|
// Note: At the time of this writing using withExtendedLifetime here
|
|
// instead of _fixLifetime causes different ARC pair matching behavior
|
|
// foiling optimization. This is why we explicitly use _fixLifetime here
|
|
// instead.
|
|
defer { _fixLifetime(self) }
|
|
|
|
let unmanagedHandle = Unmanaged.passUnretained(_wrapped)
|
|
let wrapper = unmanagedHandle._withUnsafeGuaranteedRef { $0.__wrapped }
|
|
switch (wrapper) {
|
|
case .Immutable(let i):
|
|
return try i._withUnsafeGuaranteedRef {
|
|
return try whatToDo($0)
|
|
}
|
|
case .Mutable(let m):
|
|
return try m._withUnsafeGuaranteedRef {
|
|
return try whatToDo(_unsafeReferenceCast($0, to: WrappedSwiftNSType.ImmutableType.self))
|
|
}
|
|
}
|
|
}
|
|
|
|
@inline(__always)
|
|
mutating func _applyUnmanagedMutation<ReturnType>(_ whatToDo : @noescape (WrappedSwiftNSType.MutableType) throws -> ReturnType) rethrows -> ReturnType {
|
|
// We are using Unmanaged. Make sure that the owning container class
|
|
// 'self' is guaranteed to be alive by extending the lifetime of 'self'
|
|
// to the end of the scope of this function.
|
|
// Note: At the time of this writing using withExtendedLifetime here
|
|
// instead of _fixLifetime causes different ARC pair matching behavior
|
|
// foiling optimization. This is why we explicitly use _fixLifetime here
|
|
// instead.
|
|
defer { _fixLifetime(self) }
|
|
|
|
var unique = true
|
|
let _unmanagedHandle = Unmanaged.passUnretained(_wrapped)
|
|
let wrapper = _unmanagedHandle._withUnsafeGuaranteedRef { $0.__wrapped }
|
|
|
|
// This check is done twice because: <rdar://problem/24939065> Value kept live for too long causing uniqueness check to fail
|
|
switch (wrapper) {
|
|
case .Immutable(_):
|
|
break
|
|
case .Mutable(_):
|
|
unique = isUniquelyReferencedNonObjC(&_wrapped)
|
|
}
|
|
|
|
switch (wrapper) {
|
|
case .Immutable(let i):
|
|
// We need to become mutable; by creating a new instance we also become unique
|
|
let copy = Unmanaged.passRetained(i._withUnsafeGuaranteedRef {
|
|
return _unsafeReferenceCast($0.mutableCopy(), to: WrappedSwiftNSType.MutableType.self) }
|
|
)
|
|
|
|
// Be sure to set the var before calling out; otherwise references to the struct in the closure may be looking at the old value
|
|
_wrapped = WrappedSwiftNSType(unmanagedMutableObject: copy)
|
|
return try copy._withUnsafeGuaranteedRef {
|
|
_onFastPath()
|
|
return try whatToDo($0)
|
|
}
|
|
case .Mutable(let m):
|
|
// Only create a new box if we are not uniquely referenced
|
|
if !unique {
|
|
let copy = Unmanaged.passRetained(m._withUnsafeGuaranteedRef {
|
|
return _unsafeReferenceCast($0.mutableCopy(), to: WrappedSwiftNSType.MutableType.self)
|
|
})
|
|
_wrapped = WrappedSwiftNSType(unmanagedMutableObject: copy)
|
|
return try copy._withUnsafeGuaranteedRef {
|
|
_onFastPath()
|
|
return try whatToDo($0)
|
|
}
|
|
} else {
|
|
return try m._withUnsafeGuaranteedRef {
|
|
_onFastPath()
|
|
return try whatToDo($0)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|