Skip to content

Commit 1ea9dcc

Browse files
committed
Add internal boxing protocols for reference conversion to structural types
1 parent 0cc6f02 commit 1ea9dcc

File tree

3 files changed

+253
-0
lines changed

3 files changed

+253
-0
lines changed

Diff for: Foundation.xcodeproj/project.pbxproj

+12
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
5B13B3501C582D4C00651CE2 /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6F17961C48631C00935030 /* TestUtils.swift */; };
5959
5B13B3511C582D4C00651CE2 /* TestNSByteCountFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A34B551C18C85D00FD972B /* TestNSByteCountFormatter.swift */; };
6060
5B13B3521C582D4C00651CE2 /* TestNSValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3047AEB1C38BC3300295652 /* TestNSValue.swift */; };
61+
5B23AB871CE62D17000DB898 /* Boxing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B23AB861CE62D17000DB898 /* Boxing.swift */; };
62+
5B23AB891CE62D4D000DB898 /* ReferenceConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B23AB881CE62D4D000DB898 /* ReferenceConvertible.swift */; };
6163
5B2B59821C24D00500271109 /* CFStream.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B5D89031BBC9BC300234F36 /* CFStream.c */; };
6264
5B2B59831C24D00C00271109 /* CFSocketStream.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B5D89871BBDB1B400234F36 /* CFSocketStream.c */; };
6365
5B2B59841C24D01100271109 /* CFConcreteStreams.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B5D89831BBDB13800234F36 /* CFConcreteStreams.c */; };
@@ -402,6 +404,8 @@
402404
528776181BF27D9500CB0090 /* Test.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Test.plist; sourceTree = "<group>"; };
403405
555683BC1C1250E70041D4C6 /* TestNSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSUserDefaults.swift; sourceTree = "<group>"; usesTabs = 1; };
404406
5B0C6C211C1E07E600705A0E /* TestNSRegularExpression.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSRegularExpression.swift; sourceTree = "<group>"; };
407+
5B23AB861CE62D17000DB898 /* Boxing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Boxing.swift; sourceTree = "<group>"; };
408+
5B23AB881CE62D4D000DB898 /* ReferenceConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReferenceConvertible.swift; sourceTree = "<group>"; };
405409
5B40F9EB1C124F45000E72E3 /* CFXMLInterface.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CFXMLInterface.c; sourceTree = "<group>"; };
406410
5B40F9EC1C124F45000E72E3 /* CFXMLInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFXMLInterface.h; sourceTree = "<group>"; };
407411
5B40F9F11C125187000E72E3 /* TestNSXMLParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSXMLParser.swift; sourceTree = "<group>"; };
@@ -1378,6 +1382,8 @@
13781382
5BDC3F3E1BCC5DCB00ED97BB /* NSObjCRuntime.swift */,
13791383
5BDC3F3F1BCC5DCB00ED97BB /* NSObject.swift */,
13801384
5BDC3F461BCC5DCB00ED97BB /* NSSwiftRuntime.swift */,
1385+
5B23AB861CE62D17000DB898 /* Boxing.swift */,
1386+
5B23AB881CE62D4D000DB898 /* ReferenceConvertible.swift */,
13811387
);
13821388
name = Runtime;
13831389
sourceTree = "<group>";
@@ -1694,15 +1700,19 @@
16941700
TargetAttributes = {
16951701
5B5D885C1BBC938800234F36 = {
16961702
CreatedOnToolsVersion = 7.1;
1703+
ProvisioningStyle = Manual;
16971704
};
16981705
5B7C8A6D1BEA7F8F00C5B690 = {
16991706
CreatedOnToolsVersion = 7.2;
1707+
ProvisioningStyle = Manual;
17001708
};
17011709
5BDC405B1BD6D83B00ED97BB = {
17021710
CreatedOnToolsVersion = 7.1;
1711+
ProvisioningStyle = Manual;
17031712
};
17041713
EA66F66E1BF56CCB00136161 = {
17051714
CreatedOnToolsVersion = 7.1;
1715+
ProvisioningStyle = Manual;
17061716
};
17071717
};
17081718
};
@@ -1802,7 +1812,9 @@
18021812
EADE0BA31BD15E0000C49C64 /* NSKeyedArchiver.swift in Sources */,
18031813
5BF7AEAD1BCD51F9008F214A /* NSError.swift in Sources */,
18041814
EADE0BB61BD15E0000C49C64 /* NSSortDescriptor.swift in Sources */,
1815+
5B23AB871CE62D17000DB898 /* Boxing.swift in Sources */,
18051816
5BF7AEA41BCD51F9008F214A /* NSBundle.swift in Sources */,
1817+
5B23AB891CE62D4D000DB898 /* ReferenceConvertible.swift in Sources */,
18061818
D3E8D6D11C367AB600295652 /* NSSpecialValue.swift in Sources */,
18071819
EAB57B721BD1C7A5004AC5C5 /* NSPortMessage.swift in Sources */,
18081820
EADE0BBB1BD15E0000C49C64 /* NSURLAuthenticationChallenge.swift in Sources */,

Diff for: Foundation/Boxing.swift

+224
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
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+
}

Diff for: Foundation/ReferenceConvertible.swift

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
11+
/// Decorates types which are backed by a Foundation reference type.
12+
///
13+
/// All `ReferenceConvertible` types are hashable, equatable, and provide description functions.
14+
public protocol ReferenceConvertible : _ObjectiveCBridgeable, CustomStringConvertible, CustomDebugStringConvertible, Hashable, Equatable {
15+
associatedtype ReferenceType : NSObject, NSCopying
16+
}
17+

0 commit comments

Comments
 (0)