blob: 4fe7346231dafa040da4ee21d84b5cbf1fb10729 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
/// 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 {
@_versioned internal 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 : (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 : (ReferenceType) -> ReturnType) -> ReturnType
}
extension _MutableBoxing {
@inline(__always)
mutating func _applyMutation<ReturnType>(_ whatToDo : (ReferenceType) -> ReturnType) -> ReturnType {
// Only create a new box if we are not uniquely referenced
if !isKnownUniquelyReferenced(&_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) -> Any
var hashValue: Int { get }
var description: String { get }
var debugDescription: String { get }
func releaseWrappedObject()
}
extension _SwiftNativeFoundationType {
@inline(__always)
func _mapUnmanaged<ReturnType>(_ whatToDo : (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()
case .Mutable(let m):
m.release()
}
}
func mutableCopy(with zone : NSZone) -> Any {
return _mapUnmanaged { ($0 as NSObject).mutableCopy() }
}
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 : (WrappedSwiftNSType.ImmutableType) throws -> ReturnType) rethrows -> ReturnType {
// We are using Unmananged. 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 : (WrappedSwiftNSType.MutableType) throws -> ReturnType) rethrows -> ReturnType {
// We are using Unmananged. 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 becaue: <rdar://problem/24939065> Value kept live for too long causing uniqueness check to fail
switch (wrapper) {
case .Immutable(_):
break
case .Mutable(_):
unique = isKnownUniquelyReferenced(&_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)
}
}
}
}
}