Skip to content

Remove stdlib and runtime dependencies on Foundation and CF #27155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/swift/Runtime/ObjCBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ namespace swift {
SWIFT_RUNTIME_EXPORT
void swift_rootObjCDealloc(HeapObject *self);

// Uses Swift bridging to box a C string into an NSString without introducing
// a link-time dependency on NSString.
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
id swift_stdlib_NSStringFromUTF8(const char *cstr, int len);

}

#endif // SWIFT_OBJC_INTEROP
Expand Down
16 changes: 16 additions & 0 deletions stdlib/public/core/BridgeObjectiveC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,22 @@ public protocol _ObjectiveCBridgeable {

#if _runtime(_ObjC)

@_silgen_name("swift_stdlib_connectNSBaseClasses")
internal func _connectNSBaseClasses() -> Bool


private let _bridgeInitializedSuccessfully = _connectNSBaseClasses()
internal var _orphanedFoundationSubclassesReparented: Bool = false

/// Reparents the SwiftNativeNS*Base classes to be subclasses of their respective
/// Foundation types, or is false if they couldn't be reparented. Must be run
/// in order to bridge Swift Strings, Arrays, Dictionarys, Sets, or Enumerators to ObjC.
internal func _connectOrphanedFoundationSubclassesIfNeeded() -> Void {
let bridgeWorks = _bridgeInitializedSuccessfully
_debugPrecondition(bridgeWorks)
_orphanedFoundationSubclassesReparented = true
}

//===--- Bridging for metatypes -------------------------------------------===//

/// A stand-in for a value of metatype type.
Expand Down
2 changes: 0 additions & 2 deletions stdlib/public/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,6 @@ set(swift_core_private_link_libraries)
set(swift_stdlib_compile_flags "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}")
if(SWIFT_PRIMARY_VARIANT_SDK IN_LIST SWIFT_APPLE_PLATFORMS)
list(APPEND swift_core_link_flags "-all_load")
list(APPEND swift_core_framework_depends Foundation)
list(APPEND swift_core_framework_depends CoreFoundation)
list(APPEND swift_core_private_link_libraries icucore)
else()
# With the GNU linker the equivalent of -all_load is to tell the linker
Expand Down
16 changes: 15 additions & 1 deletion stdlib/public/core/ContiguousArrayBuffer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -417,13 +417,27 @@ internal struct _ContiguousArrayBuffer<Element>: _ArrayBufferProtocol {
}

#if _runtime(_ObjC)

/// Convert to an NSArray.
///
/// - Precondition: `Element` is bridged to Objective-C.
///
/// - Complexity: O(1).
@inlinable
@usableFromInline
Copy link
Member

@milseman milseman Sep 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this seems pedantic, but could you add a comment after the @usableFromInline? Something to the effect of:

@usableFromInline // was @inlinable through Swift 5.1 (see comment below)

I want some indication right on the decl of the binary compatibility story, without reading comments inside the body (which may be about the implementation).

Copy link
Contributor

@stephentyrone stephentyrone Sep 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also mark these deprecated at this point, with a message that explains why they're there and not to use them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we mark things as deprecated without breaking our own/Foundation's callsites?

internal __consuming func _asCocoaArray() -> AnyObject {
// _asCocoaArray was @inlinable in Swift 5.0 and 5.1, which means that there
// are existing apps out there that effectively have the old implementation
// Be careful with future changes to this function. Here be dragons!
// The old implementation was
// if count == 0 {
// return _emptyArrayStorage
// }
// if _isBridgedVerbatimToObjectiveC(Element.self) {
// return _storage
// }
// return __SwiftDeferredNSArray(_nativeStorage: _storage)

_connectOrphanedFoundationSubclassesIfNeeded()
if count == 0 {
return _emptyArrayStorage
}
Expand Down
3 changes: 3 additions & 0 deletions stdlib/public/core/DictionaryBridging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ internal func _stdlib_NSDictionary_allKeys(
extension _NativeDictionary { // Bridging
@usableFromInline
__consuming internal func bridged() -> AnyObject {
_connectOrphanedFoundationSubclassesIfNeeded()
// We can zero-cost bridge if our keys are verbatim
// or if we're the empty singleton.

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

internal init(_ base: __owned _NativeDictionary<Key, Value>) {
_internalInvariant(_isBridgedVerbatimToObjectiveC(Key.self))
_internalInvariant(_orphanedFoundationSubclassesReparented)
self.base = base
self.bridgedKeys = nil
self.nextBucket = base.hashTable.startBucket
Expand All @@ -77,6 +79,7 @@ final internal class _SwiftDictionaryNSEnumerator<Key: Hashable, Value>
@nonobjc
internal init(_ deferred: __owned _SwiftDeferredNSDictionary<Key, Value>) {
_internalInvariant(!_isBridgedVerbatimToObjectiveC(Key.self))
_internalInvariant(_orphanedFoundationSubclassesReparented)
self.base = deferred.native
self.bridgedKeys = deferred.bridgeKeys()
self.nextBucket = base.hashTable.startBucket
Expand Down
5 changes: 4 additions & 1 deletion stdlib/public/core/Hashing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ internal struct _UnmanagedAnyObjectArray {
// renamed. The old name must not be used in the new runtime.
final internal class __SwiftEmptyNSEnumerator
: __SwiftNativeNSEnumerator, _NSEnumerator {
internal override required init() { super.init() }
internal override required init() {
super.init()
_internalInvariant(_orphanedFoundationSubclassesReparented)
}

@objc
internal func nextObject() -> AnyObject? {
Expand Down
6 changes: 4 additions & 2 deletions stdlib/public/core/ReflectionMirror.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@ internal func getChild<T>(of value: T, type: Any.Type, index: Int) -> (label: St
internal func _getQuickLookObject<T>(_: T) -> AnyObject?

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

internal func _is(_ object: AnyObject, kindOf `class`: String) -> Bool {
return _isImpl(object, kindOf: `class` as AnyObject)
return `class`.withCString {
return _isImpl(object, kindOf: $0)
}
}

internal func _getClassPlaygroundQuickLook(
Expand Down
15 changes: 7 additions & 8 deletions stdlib/public/core/SetBridging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,21 @@

import SwiftShims

@_silgen_name("swift_stdlib_CFSetGetValues")
internal
func _stdlib_CFSetGetValues(
_ nss: AnyObject,
_: UnsafeMutablePointer<AnyObject>)

/// Equivalent to `NSSet.allObjects`, but does not leave objects on the
/// autorelease pool.
internal func _stdlib_NSSet_allObjects(_ object: AnyObject) -> _BridgingBuffer {
let nss = unsafeBitCast(object, to: _NSSet.self)
let storage = _BridgingBuffer(nss.count)
_stdlib_CFSetGetValues(nss, storage.baseAddress)
let count = nss.count
let storage = _BridgingBuffer(count)
nss.getObjects(storage.baseAddress, count: count)
return storage
}

extension _NativeSet { // Bridging
@usableFromInline
internal __consuming func bridged() -> AnyObject {
_connectOrphanedFoundationSubclassesIfNeeded()

// We can zero-cost bridge if our keys are verbatim
// or if we're the empty singleton.

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

internal init(_ base: __owned _NativeSet<Element>) {
_internalInvariant(_isBridgedVerbatimToObjectiveC(Element.self))
_internalInvariant(_orphanedFoundationSubclassesReparented)
self.base = base
self.bridgedElements = nil
self.nextBucket = base.hashTable.startBucket
Expand All @@ -80,6 +78,7 @@ final internal class _SwiftSetNSEnumerator<Element: Hashable>
@nonobjc
internal init(_ deferred: __owned _SwiftDeferredNSSet<Element>) {
_internalInvariant(!_isBridgedVerbatimToObjectiveC(Element.self))
_internalInvariant(_orphanedFoundationSubclassesReparented)
self.base = deferred.native
self.bridgedElements = deferred.bridgeElements()
self.nextBucket = base.hashTable.startBucket
Expand Down
4 changes: 4 additions & 0 deletions stdlib/public/core/ShadowProtocols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ internal protocol _NSSetCore: _NSCopying, _NSFastEnumeration {
/// supplies.
@unsafe_no_objc_tagged_pointer @objc
internal protocol _NSSet: _NSSetCore {
@objc(getObjects:count:) func getObjects(
_ buffer: UnsafeMutablePointer<AnyObject>,
count: Int
)
}

/// A shadow for the API of NSNumber we will use in the core
Expand Down
15 changes: 15 additions & 0 deletions stdlib/public/core/StringBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,9 @@ extension String {
@_effects(releasenone)
public // SPI(Foundation)
func _bridgeToObjectiveCImpl() -> AnyObject {

_connectOrphanedFoundationSubclassesIfNeeded()

// Smol ASCII a) may bridge to tagged pointers, b) can't contain a BOM
if _guts.isSmallASCII {
let maybeTagged = _guts.asSmall.withUTF8 { bufPtr in
Expand All @@ -453,6 +456,7 @@ extension String {
}
if let tagged = maybeTagged { return tagged }
}

if _guts.isSmall {
// We can't form a tagged pointer String, so grow to a non-small String,
// and bridge that instead. Also avoids CF deleting any BOM that may be
Expand Down Expand Up @@ -497,6 +501,17 @@ public func _getDescription<T>(_ x: T) -> AnyObject {
return String(reflecting: x)._bridgeToObjectiveCImpl()
}

@_silgen_name("swift_stdlib_NSStringFromUTF8")
@usableFromInline //this makes the symbol available to the runtime :(
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
internal func _NSStringFromUTF8(_ s: UnsafePointer<UInt8>, _ len: Int)
-> AnyObject {
return String(
decoding: UnsafeBufferPointer(start: s, count: len),
as: UTF8.self
)._bridgeToObjectiveCImpl()
}

#else // !_runtime(_ObjC)

internal class __SwiftNativeNSString {
Expand Down
Loading