|
| 1 | +// This source file is part of the Swift.org open source project |
| 2 | +// |
| 3 | +// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors |
| 4 | +// Licensed under Apache License v2.0 with Runtime Library Exception |
| 5 | +// |
| 6 | +// See http://swift.org/LICENSE.txt for license information |
| 7 | +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 8 | +// |
| 9 | + |
| 10 | +/// 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). |
| 11 | +/// |
| 12 | +/// 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. |
| 13 | +internal final class _MutableHandle<MutableType : NSObject where MutableType : NSCopying> { |
| 14 | + private var _pointer : MutableType |
| 15 | + |
| 16 | + init(reference : MutableType) { |
| 17 | + _pointer = reference.copy() as! MutableType |
| 18 | + } |
| 19 | + |
| 20 | + init(adoptingReference reference: MutableType) { |
| 21 | + _pointer = reference |
| 22 | + } |
| 23 | + |
| 24 | + /// Apply a closure to the reference type. |
| 25 | + func map<ReturnType>(_ whatToDo : @noescape (MutableType) throws -> ReturnType) rethrows -> ReturnType { |
| 26 | + return try whatToDo(_pointer) |
| 27 | + } |
| 28 | + |
| 29 | + func _copiedReference() -> MutableType { |
| 30 | + return _pointer.copy() as! MutableType |
| 31 | + } |
| 32 | + |
| 33 | + func _uncopiedReference() -> MutableType { |
| 34 | + return _pointer |
| 35 | + } |
| 36 | +} |
| 37 | + |
| 38 | +/// Describes common operations for Foundation struct types that are bridged to a mutable object (e.g. NSURLComponents). |
| 39 | +internal protocol _MutableBoxing : ReferenceConvertible { |
| 40 | + var _handle : _MutableHandle<ReferenceType> { get set } |
| 41 | + |
| 42 | + /// Apply a mutating closure to the reference type, regardless if it is mutable or immutable. |
| 43 | + /// |
| 44 | + /// This function performs the correct copy-on-write check for efficient mutation. |
| 45 | + mutating func _applyMutation<ReturnType>(_ whatToDo : @noescape (ReferenceType) -> ReturnType) -> ReturnType |
| 46 | +} |
| 47 | + |
| 48 | +extension _MutableBoxing { |
| 49 | + @inline(__always) |
| 50 | + mutating func _applyMutation<ReturnType>(_ whatToDo : @noescape(ReferenceType) -> ReturnType) -> ReturnType { |
| 51 | + // Only create a new box if we are not uniquely referenced |
| 52 | + if !isUniquelyReferencedNonObjC(&_handle) { |
| 53 | + let ref = _handle._pointer |
| 54 | + _handle = _MutableHandle(reference: ref) |
| 55 | + } |
| 56 | + return whatToDo(_handle._pointer) |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +internal enum _MutableUnmanagedWrapper<ImmutableType : NSObject, MutableType : NSObject where MutableType : NSMutableCopying> { |
| 61 | + case Immutable(Unmanaged<ImmutableType>) |
| 62 | + case Mutable(Unmanaged<MutableType>) |
| 63 | +} |
| 64 | + |
| 65 | +internal protocol _SwiftNativeFoundationType : class { |
| 66 | + associatedtype ImmutableType : NSObject |
| 67 | + associatedtype MutableType : NSObject, NSMutableCopying |
| 68 | + typealias WrapperType = _MutableUnmanagedWrapper<ImmutableType, MutableType> |
| 69 | + var __wrapped : WrapperType { get } |
| 70 | + |
| 71 | + init(unmanagedImmutableObject: Unmanaged<ImmutableType>) |
| 72 | + init(unmanagedMutableObject: Unmanaged<MutableType>) |
| 73 | + |
| 74 | + func mutableCopy(with zone : NSZone) -> AnyObject |
| 75 | + |
| 76 | + var hashValue: Int { get } |
| 77 | + var description: String { get } |
| 78 | + var debugDescription: String { get } |
| 79 | + |
| 80 | + func releaseWrappedObject() |
| 81 | +} |
| 82 | + |
| 83 | + |
| 84 | +extension _SwiftNativeFoundationType { |
| 85 | + |
| 86 | + @inline(__always) |
| 87 | + func _mapUnmanaged<ReturnType>(_ whatToDo : @noescape (ImmutableType) throws -> ReturnType) rethrows -> ReturnType { |
| 88 | + defer { _fixLifetime(self) } |
| 89 | + |
| 90 | + switch __wrapped { |
| 91 | + case .Immutable(let i): |
| 92 | + return try i._withUnsafeGuaranteedRef { |
| 93 | + _onFastPath() |
| 94 | + return try whatToDo($0) |
| 95 | + } |
| 96 | + case .Mutable(let m): |
| 97 | + return try m._withUnsafeGuaranteedRef { |
| 98 | + _onFastPath() |
| 99 | + return try whatToDo(_unsafeReferenceCast($0, to: ImmutableType.self)) |
| 100 | + } |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + func releaseWrappedObject() { |
| 105 | + switch __wrapped { |
| 106 | + case .Immutable(let i): |
| 107 | + i.release() |
| 108 | + break |
| 109 | + case .Mutable(let m): |
| 110 | + m.release() |
| 111 | + break |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + func mutableCopy(with zone : NSZone) -> AnyObject { |
| 116 | + return _mapUnmanaged { $0.mutableCopy() } |
| 117 | + } |
| 118 | + |
| 119 | + var hashValue: Int { |
| 120 | + return _mapUnmanaged { return $0.hashValue } |
| 121 | + } |
| 122 | + |
| 123 | + var description: String { |
| 124 | + return _mapUnmanaged { return $0.description } |
| 125 | + } |
| 126 | + |
| 127 | + var debugDescription: String { |
| 128 | + return _mapUnmanaged { return $0.debugDescription } |
| 129 | + } |
| 130 | + |
| 131 | + func isEqual(_ other: AnyObject) -> Bool { |
| 132 | + return _mapUnmanaged { return $0.isEqual(other) } |
| 133 | + } |
| 134 | +} |
| 135 | + |
| 136 | +internal protocol _MutablePairBoxing { |
| 137 | + associatedtype WrappedSwiftNSType : _SwiftNativeFoundationType |
| 138 | + typealias ImmutableType = WrappedSwiftNSType.ImmutableType |
| 139 | + typealias MutableType = WrappedSwiftNSType.MutableType |
| 140 | + var _wrapped : WrappedSwiftNSType { get set } |
| 141 | +} |
| 142 | + |
| 143 | +extension _MutablePairBoxing { |
| 144 | + @inline(__always) |
| 145 | + func _mapUnmanaged<ReturnType>(_ whatToDo : @noescape (ImmutableType) throws -> ReturnType) rethrows -> ReturnType { |
| 146 | + // We are using Unmananged. Make sure that the owning container class |
| 147 | + // 'self' is guaranteed to be alive by extending the lifetime of 'self' |
| 148 | + // to the end of the scope of this function. |
| 149 | + // Note: At the time of this writing using withExtendedLifetime here |
| 150 | + // instead of _fixLifetime causes different ARC pair matching behavior |
| 151 | + // foiling optimization. This is why we explicitly use _fixLifetime here |
| 152 | + // instead. |
| 153 | + defer { _fixLifetime(self) } |
| 154 | + |
| 155 | + let unmanagedHandle = Unmanaged.passUnretained(_wrapped) |
| 156 | + let wrapper = unmanagedHandle._withUnsafeGuaranteedRef { $0.__wrapped } |
| 157 | + switch (wrapper) { |
| 158 | + case .Immutable(let i): |
| 159 | + return try i._withUnsafeGuaranteedRef { |
| 160 | + return try whatToDo($0) |
| 161 | + } |
| 162 | + case .Mutable(let m): |
| 163 | + return try m._withUnsafeGuaranteedRef { |
| 164 | + return try whatToDo(_unsafeReferenceCast($0, to: ImmutableType.self)) |
| 165 | + } |
| 166 | + } |
| 167 | + } |
| 168 | + |
| 169 | + @inline(__always) |
| 170 | + mutating func _applyUnmanagedMutation<ReturnType>(_ whatToDo : @noescape (MutableType) throws -> ReturnType) rethrows -> ReturnType { |
| 171 | + // We are using Unmananged. Make sure that the owning container class |
| 172 | + // 'self' is guaranteed to be alive by extending the lifetime of 'self' |
| 173 | + // to the end of the scope of this function. |
| 174 | + // Note: At the time of this writing using withExtendedLifetime here |
| 175 | + // instead of _fixLifetime causes different ARC pair matching behavior |
| 176 | + // foiling optimization. This is why we explicitly use _fixLifetime here |
| 177 | + // instead. |
| 178 | + defer { _fixLifetime(self) } |
| 179 | + |
| 180 | + var unique = true |
| 181 | + let _unmanagedHandle = Unmanaged.passUnretained(_wrapped) |
| 182 | + let wrapper = _unmanagedHandle._withUnsafeGuaranteedRef { $0.__wrapped } |
| 183 | + |
| 184 | + // This check is done twice becaue: <rdar://problem/24939065> Value kept live for too long causing uniqueness check to fail |
| 185 | + switch (wrapper) { |
| 186 | + case .Immutable(_): |
| 187 | + break |
| 188 | + case .Mutable(_): |
| 189 | + unique = isUniquelyReferencedNonObjC(&_wrapped) |
| 190 | + } |
| 191 | + |
| 192 | + switch (wrapper) { |
| 193 | + case .Immutable(let i): |
| 194 | + // We need to become mutable; by creating a new instance we also become unique |
| 195 | + let copy = Unmanaged.passRetained(i._withUnsafeGuaranteedRef { |
| 196 | + return _unsafeReferenceCast($0.mutableCopy(), to: MutableType.self) } |
| 197 | + ) |
| 198 | + |
| 199 | + // Be sure to set the var before calling out; otherwise references to the struct in the closure may be looking at the old value |
| 200 | + _wrapped = WrappedSwiftNSType(unmanagedMutableObject: copy) |
| 201 | + return try copy._withUnsafeGuaranteedRef { |
| 202 | + _onFastPath() |
| 203 | + return try whatToDo($0) |
| 204 | + } |
| 205 | + case .Mutable(let m): |
| 206 | + // Only create a new box if we are not uniquely referenced |
| 207 | + if !unique { |
| 208 | + let copy = Unmanaged.passRetained(m._withUnsafeGuaranteedRef { |
| 209 | + return _unsafeReferenceCast($0.mutableCopy(), to: MutableType.self) |
| 210 | + }) |
| 211 | + _wrapped = WrappedSwiftNSType(unmanagedMutableObject: copy) |
| 212 | + return try copy._withUnsafeGuaranteedRef { |
| 213 | + _onFastPath() |
| 214 | + return try whatToDo($0) |
| 215 | + } |
| 216 | + } else { |
| 217 | + return try m._withUnsafeGuaranteedRef { |
| 218 | + _onFastPath() |
| 219 | + return try whatToDo($0) |
| 220 | + } |
| 221 | + } |
| 222 | + } |
| 223 | + } |
| 224 | +} |
0 commit comments