Skip to content

Commit b8fb2e5

Browse files
authored
Merge pull request #27155 from Catfish-Man/shim-shim-sher-ee-2
Remove stdlib and runtime dependencies on Foundation and CF
2 parents c5ae17d + 1efa946 commit b8fb2e5

24 files changed

+424
-334
lines changed

include/swift/Runtime/ObjCBridge.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ namespace swift {
7979
SWIFT_RUNTIME_EXPORT
8080
void swift_rootObjCDealloc(HeapObject *self);
8181

82+
// Uses Swift bridging to box a C string into an NSString without introducing
83+
// a link-time dependency on NSString.
84+
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
85+
id swift_stdlib_NSStringFromUTF8(const char *cstr, int len);
86+
8287
}
8388

8489
#endif // SWIFT_OBJC_INTEROP

stdlib/public/core/BridgeObjectiveC.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,22 @@ public protocol _ObjectiveCBridgeable {
8585

8686
#if _runtime(_ObjC)
8787

88+
@_silgen_name("swift_stdlib_connectNSBaseClasses")
89+
internal func _connectNSBaseClasses() -> Bool
90+
91+
92+
private let _bridgeInitializedSuccessfully = _connectNSBaseClasses()
93+
internal var _orphanedFoundationSubclassesReparented: Bool = false
94+
95+
/// Reparents the SwiftNativeNS*Base classes to be subclasses of their respective
96+
/// Foundation types, or is false if they couldn't be reparented. Must be run
97+
/// in order to bridge Swift Strings, Arrays, Dictionarys, Sets, or Enumerators to ObjC.
98+
internal func _connectOrphanedFoundationSubclassesIfNeeded() -> Void {
99+
let bridgeWorks = _bridgeInitializedSuccessfully
100+
_debugPrecondition(bridgeWorks)
101+
_orphanedFoundationSubclassesReparented = true
102+
}
103+
88104
//===--- Bridging for metatypes -------------------------------------------===//
89105

90106
/// A stand-in for a value of metatype type.

stdlib/public/core/CMakeLists.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,6 @@ set(swift_core_private_link_libraries)
229229
set(swift_stdlib_compile_flags "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}")
230230
if(SWIFT_PRIMARY_VARIANT_SDK IN_LIST SWIFT_APPLE_PLATFORMS)
231231
list(APPEND swift_core_link_flags "-all_load")
232-
list(APPEND swift_core_framework_depends Foundation)
233-
list(APPEND swift_core_framework_depends CoreFoundation)
234232
list(APPEND swift_core_private_link_libraries icucore)
235233
else()
236234
# With the GNU linker the equivalent of -all_load is to tell the linker

stdlib/public/core/ContiguousArrayBuffer.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,13 +417,27 @@ internal struct _ContiguousArrayBuffer<Element>: _ArrayBufferProtocol {
417417
}
418418

419419
#if _runtime(_ObjC)
420+
420421
/// Convert to an NSArray.
421422
///
422423
/// - Precondition: `Element` is bridged to Objective-C.
423424
///
424425
/// - Complexity: O(1).
425-
@inlinable
426+
@usableFromInline
426427
internal __consuming func _asCocoaArray() -> AnyObject {
428+
// _asCocoaArray was @inlinable in Swift 5.0 and 5.1, which means that there
429+
// are existing apps out there that effectively have the old implementation
430+
// Be careful with future changes to this function. Here be dragons!
431+
// The old implementation was
432+
// if count == 0 {
433+
// return _emptyArrayStorage
434+
// }
435+
// if _isBridgedVerbatimToObjectiveC(Element.self) {
436+
// return _storage
437+
// }
438+
// return __SwiftDeferredNSArray(_nativeStorage: _storage)
439+
440+
_connectOrphanedFoundationSubclassesIfNeeded()
427441
if count == 0 {
428442
return _emptyArrayStorage
429443
}

stdlib/public/core/DictionaryBridging.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ internal func _stdlib_NSDictionary_allKeys(
2929
extension _NativeDictionary { // Bridging
3030
@usableFromInline
3131
__consuming internal func bridged() -> AnyObject {
32+
_connectOrphanedFoundationSubclassesIfNeeded()
3233
// We can zero-cost bridge if our keys are verbatim
3334
// or if we're the empty singleton.
3435

@@ -67,6 +68,7 @@ final internal class _SwiftDictionaryNSEnumerator<Key: Hashable, Value>
6768

6869
internal init(_ base: __owned _NativeDictionary<Key, Value>) {
6970
_internalInvariant(_isBridgedVerbatimToObjectiveC(Key.self))
71+
_internalInvariant(_orphanedFoundationSubclassesReparented)
7072
self.base = base
7173
self.bridgedKeys = nil
7274
self.nextBucket = base.hashTable.startBucket
@@ -77,6 +79,7 @@ final internal class _SwiftDictionaryNSEnumerator<Key: Hashable, Value>
7779
@nonobjc
7880
internal init(_ deferred: __owned _SwiftDeferredNSDictionary<Key, Value>) {
7981
_internalInvariant(!_isBridgedVerbatimToObjectiveC(Key.self))
82+
_internalInvariant(_orphanedFoundationSubclassesReparented)
8083
self.base = deferred.native
8184
self.bridgedKeys = deferred.bridgeKeys()
8285
self.nextBucket = base.hashTable.startBucket

stdlib/public/core/Hashing.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ internal struct _UnmanagedAnyObjectArray {
7676
// renamed. The old name must not be used in the new runtime.
7777
final internal class __SwiftEmptyNSEnumerator
7878
: __SwiftNativeNSEnumerator, _NSEnumerator {
79-
internal override required init() { super.init() }
79+
internal override required init() {
80+
super.init()
81+
_internalInvariant(_orphanedFoundationSubclassesReparented)
82+
}
8083

8184
@objc
8285
internal func nextObject() -> AnyObject? {

stdlib/public/core/ReflectionMirror.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@ internal func getChild<T>(of value: T, type: Any.Type, index: Int) -> (label: St
4747
internal func _getQuickLookObject<T>(_: T) -> AnyObject?
4848

4949
@_silgen_name("_swift_stdlib_NSObject_isKindOfClass")
50-
internal func _isImpl(_ object: AnyObject, kindOf: AnyObject) -> Bool
50+
internal func _isImpl(_ object: AnyObject, kindOf: UnsafePointer<CChar>) -> Bool
5151

5252
internal func _is(_ object: AnyObject, kindOf `class`: String) -> Bool {
53-
return _isImpl(object, kindOf: `class` as AnyObject)
53+
return `class`.withCString {
54+
return _isImpl(object, kindOf: $0)
55+
}
5456
}
5557

5658
internal func _getClassPlaygroundQuickLook(

stdlib/public/core/SetBridging.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,21 @@
1414

1515
import SwiftShims
1616

17-
@_silgen_name("swift_stdlib_CFSetGetValues")
18-
internal
19-
func _stdlib_CFSetGetValues(
20-
_ nss: AnyObject,
21-
_: UnsafeMutablePointer<AnyObject>)
22-
2317
/// Equivalent to `NSSet.allObjects`, but does not leave objects on the
2418
/// autorelease pool.
2519
internal func _stdlib_NSSet_allObjects(_ object: AnyObject) -> _BridgingBuffer {
2620
let nss = unsafeBitCast(object, to: _NSSet.self)
27-
let storage = _BridgingBuffer(nss.count)
28-
_stdlib_CFSetGetValues(nss, storage.baseAddress)
21+
let count = nss.count
22+
let storage = _BridgingBuffer(count)
23+
nss.getObjects(storage.baseAddress, count: count)
2924
return storage
3025
}
3126

3227
extension _NativeSet { // Bridging
3328
@usableFromInline
3429
internal __consuming func bridged() -> AnyObject {
30+
_connectOrphanedFoundationSubclassesIfNeeded()
31+
3532
// We can zero-cost bridge if our keys are verbatim
3633
// or if we're the empty singleton.
3734

@@ -70,6 +67,7 @@ final internal class _SwiftSetNSEnumerator<Element: Hashable>
7067

7168
internal init(_ base: __owned _NativeSet<Element>) {
7269
_internalInvariant(_isBridgedVerbatimToObjectiveC(Element.self))
70+
_internalInvariant(_orphanedFoundationSubclassesReparented)
7371
self.base = base
7472
self.bridgedElements = nil
7573
self.nextBucket = base.hashTable.startBucket
@@ -80,6 +78,7 @@ final internal class _SwiftSetNSEnumerator<Element: Hashable>
8078
@nonobjc
8179
internal init(_ deferred: __owned _SwiftDeferredNSSet<Element>) {
8280
_internalInvariant(!_isBridgedVerbatimToObjectiveC(Element.self))
81+
_internalInvariant(_orphanedFoundationSubclassesReparented)
8382
self.base = deferred.native
8483
self.bridgedElements = deferred.bridgeElements()
8584
self.nextBucket = base.hashTable.startBucket

stdlib/public/core/ShadowProtocols.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ internal protocol _NSSetCore: _NSCopying, _NSFastEnumeration {
171171
/// supplies.
172172
@unsafe_no_objc_tagged_pointer @objc
173173
internal protocol _NSSet: _NSSetCore {
174+
@objc(getObjects:count:) func getObjects(
175+
_ buffer: UnsafeMutablePointer<AnyObject>,
176+
count: Int
177+
)
174178
}
175179

176180
/// A shadow for the API of NSNumber we will use in the core

stdlib/public/core/StringBridge.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,9 @@ extension String {
442442
@_effects(releasenone)
443443
public // SPI(Foundation)
444444
func _bridgeToObjectiveCImpl() -> AnyObject {
445+
446+
_connectOrphanedFoundationSubclassesIfNeeded()
447+
445448
// Smol ASCII a) may bridge to tagged pointers, b) can't contain a BOM
446449
if _guts.isSmallASCII {
447450
let maybeTagged = _guts.asSmall.withUTF8 { bufPtr in
@@ -453,6 +456,7 @@ extension String {
453456
}
454457
if let tagged = maybeTagged { return tagged }
455458
}
459+
456460
if _guts.isSmall {
457461
// We can't form a tagged pointer String, so grow to a non-small String,
458462
// and bridge that instead. Also avoids CF deleting any BOM that may be
@@ -497,6 +501,17 @@ public func _getDescription<T>(_ x: T) -> AnyObject {
497501
return String(reflecting: x)._bridgeToObjectiveCImpl()
498502
}
499503

504+
@_silgen_name("swift_stdlib_NSStringFromUTF8")
505+
@usableFromInline //this makes the symbol available to the runtime :(
506+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
507+
internal func _NSStringFromUTF8(_ s: UnsafePointer<UInt8>, _ len: Int)
508+
-> AnyObject {
509+
return String(
510+
decoding: UnsafeBufferPointer(start: s, count: len),
511+
as: UTF8.self
512+
)._bridgeToObjectiveCImpl()
513+
}
514+
500515
#else // !_runtime(_ObjC)
501516

502517
internal class __SwiftNativeNSString {

0 commit comments

Comments
 (0)