Skip to content

Commit 3fe46e3

Browse files
committed
Remove stdlib and runtime dependencies on Foundation and CF
1 parent 01cb1ed commit 3fe46e3

22 files changed

+389
-312
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 {

stdlib/public/runtime/ErrorObject.mm

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,28 @@
4242
using namespace swift;
4343
using namespace swift::hashable_support;
4444

45+
// Mimic the memory layout of NSError so things don't go haywire when we
46+
// switch superclasses to the real thing.
47+
@interface __SwiftNSErrorLayoutStandin : NSObject {
48+
@private
49+
void *_reserved;
50+
NSInteger _code;
51+
id _domain;
52+
id _userInfo;
53+
}
54+
@end
55+
56+
@implementation __SwiftNSErrorLayoutStandin
57+
@end
58+
4559
/// A subclass of NSError used to represent bridged native Swift errors.
4660
/// This type cannot be subclassed, and should not ever be instantiated
4761
/// except by the Swift runtime.
4862
///
4963
/// NOTE: older runtimes called this _SwiftNativeNSError. The two must
5064
/// coexist, so it was renamed. The old name must not be used in the new
5165
/// runtime.
52-
@interface __SwiftNativeNSError : NSError
66+
@interface __SwiftNativeNSError : __SwiftNSErrorLayoutStandin
5367
@end
5468

5569
@implementation __SwiftNativeNSError
@@ -71,29 +85,29 @@ - (void)dealloc {
7185
// layout. This gives us a buffer in case NSError decides to change its stored
7286
// property order.
7387

74-
- (NSString*)domain {
88+
- (id /* NSString */)domain {
7589
auto error = (const SwiftError*)self;
7690
// The domain string should not be nil; if it is, then this error box hasn't
7791
// been initialized yet as an NSError.
7892
auto domain = error->domain.load(SWIFT_MEMORY_ORDER_CONSUME);
7993
assert(domain
8094
&& "Error box used as NSError before initialization");
8195
// Don't need to .retain.autorelease since it's immutable.
82-
return cf_const_cast<NSString*>(domain);
96+
return cf_const_cast<id>(domain);
8397
}
8498

8599
- (NSInteger)code {
86100
auto error = (const SwiftError*)self;
87101
return error->code.load(SWIFT_MEMORY_ORDER_CONSUME);
88102
}
89103

90-
- (NSDictionary*)userInfo {
104+
- (id /* NSDictionary */)userInfo {
91105
auto error = (const SwiftError*)self;
92106
auto userInfo = error->userInfo.load(SWIFT_MEMORY_ORDER_CONSUME);
93107
assert(userInfo
94108
&& "Error box used as NSError before initialization");
95109
// Don't need to .retain.autorelease since it's immutable.
96-
return cf_const_cast<NSDictionary*>(userInfo);
110+
return cf_const_cast<id>(userInfo);
97111
}
98112

99113
- (id)copyWithZone:(NSZone *)zone {
@@ -153,16 +167,29 @@ - (BOOL)isEqual:(id)other {
153167
@end
154168

155169
Class swift::getNSErrorClass() {
156-
return SWIFT_LAZY_CONSTANT([NSError class]);
170+
return SWIFT_LAZY_CONSTANT(objc_lookUpClass("NSError"));
157171
}
158172

159173
const Metadata *swift::getNSErrorMetadata() {
160174
return SWIFT_LAZY_CONSTANT(
161175
swift_getObjCClassMetadata((const ClassMetadata *)getNSErrorClass()));
162176
}
163177

178+
static Class getAndBridgeSwiftNativeNSErrorClass() {
179+
Class nsErrorClass = swift::getNSErrorClass();
180+
Class ourClass = [__SwiftNativeNSError class];
181+
// We want "err as AnyObject" to do *something* even without Foundation
182+
if (nsErrorClass) {
183+
#pragma clang diagnostic push
184+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
185+
class_setSuperclass(ourClass, nsErrorClass);
186+
#pragma clang diagnostic pop
187+
}
188+
return ourClass;
189+
}
190+
164191
static Class getSwiftNativeNSErrorClass() {
165-
return SWIFT_LAZY_CONSTANT([__SwiftNativeNSError class]);
192+
return SWIFT_LAZY_CONSTANT(getAndBridgeSwiftNativeNSErrorClass());
166193
}
167194

168195
/// Allocate a catchable error object.
@@ -274,7 +301,7 @@ static ErrorBridgingInfo getErrorBridgingInfo() {
274301

275302
const Metadata *SwiftError::getType() const {
276303
if (isPureNSError()) {
277-
auto asError = reinterpret_cast<NSError *>(const_cast<SwiftError *>(this));
304+
id asError = reinterpret_cast<id>(const_cast<SwiftError *>(this));
278305
return swift_getObjCClassMetadata((ClassMetadata*)[asError class]);
279306
}
280307
return type;
@@ -363,9 +390,9 @@ static ErrorBridgingInfo getErrorBridgingInfo() {
363390
#define getErrorDomainNSString \
364391
MANGLE_SYM(s23_getErrorDomainNSStringyyXlSPyxGs0B0RzlF)
365392
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
366-
NSString *getErrorDomainNSString(const OpaqueValue *error,
367-
const Metadata *T,
368-
const WitnessTable *Error);
393+
id getErrorDomainNSString(const OpaqueValue *error,
394+
const Metadata *T,
395+
const WitnessTable *Error);
369396

370397
// internal func _getErrorCode<T : Error>(_ x: UnsafePointer<T>) -> Int
371398
#define getErrorCode \
@@ -379,17 +406,17 @@ NSInteger getErrorCode(const OpaqueValue *error,
379406
#define getErrorUserInfoNSDictionary \
380407
MANGLE_SYM(s29_getErrorUserInfoNSDictionaryyyXlSgSPyxGs0B0RzlF)
381408
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
382-
NSDictionary *getErrorUserInfoNSDictionary(
409+
id getErrorUserInfoNSDictionary(
383410
const OpaqueValue *error,
384411
const Metadata *T,
385412
const WitnessTable *Error);
386413

387414
// @_silgen_name("_swift_stdlib_getErrorDefaultUserInfo")
388415
// internal func _getErrorDefaultUserInfo<T : Error>(_ x: T) -> AnyObject
389416
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
390-
NSDictionary *_swift_stdlib_getErrorDefaultUserInfo(OpaqueValue *error,
391-
const Metadata *T,
392-
const WitnessTable *Error) {
417+
id _swift_stdlib_getErrorDefaultUserInfo(OpaqueValue *error,
418+
const Metadata *T,
419+
const WitnessTable *Error) {
393420
// public func Foundation._getErrorDefaultUserInfo<T: Error>(_ error: T)
394421
// -> AnyObject?
395422
auto foundationGetDefaultUserInfo = getErrorBridgingInfo().GetErrorDefaultUserInfo;
@@ -407,7 +434,7 @@ NSInteger getErrorCode(const OpaqueValue *error,
407434
/// at +1.
408435
id
409436
swift::_swift_stdlib_bridgeErrorToNSError(SwiftError *errorObject) {
410-
auto ns = reinterpret_cast<NSError *>(errorObject);
437+
id ns = reinterpret_cast<id>(errorObject);
411438

412439
// If we already have a domain set, then we've already initialized.
413440
// If this is a real NSError, then Cocoa and Core Foundation's initializers
@@ -431,9 +458,9 @@ NSInteger getErrorCode(const OpaqueValue *error,
431458
auto type = errorObject->getType();
432459
auto witness = errorObject->getErrorConformance();
433460

434-
NSString *domain = getErrorDomainNSString(value, type, witness);
461+
id domain = getErrorDomainNSString(value, type, witness);
435462
NSInteger code = getErrorCode(value, type, witness);
436-
NSDictionary *userInfo = getErrorUserInfoNSDictionary(value, type, witness);
463+
id userInfo = getErrorUserInfoNSDictionary(value, type, witness);
437464

438465
// Never produce an empty userInfo dictionary.
439466
if (!userInfo)
@@ -480,7 +507,7 @@ NSInteger getErrorCode(const OpaqueValue *error,
480507
if (![reinterpret_cast<id>(object) isKindOfClass: NSErrorClass])
481508
return false;
482509

483-
NSError *srcInstance = reinterpret_cast<NSError *>(object);
510+
id srcInstance = reinterpret_cast<id>(object);
484511

485512
// A __SwiftNativeNSError box can always be unwrapped to cast the value back
486513
// out as an Error existential.
@@ -523,7 +550,7 @@ ProtocolDescriptorRef theErrorProtocol(&PROTOCOL_DESCR_SYM(s5Error),
523550
auto *destTypeExistential = dyn_cast<ExistentialTypeMetadata>(destType);
524551
if (destTypeExistential &&
525552
destTypeExistential->getRepresentation() == ExistentialTypeRepresentation::Error) {
526-
auto destBoxAddr = reinterpret_cast<NSError**>(dest);
553+
auto destBoxAddr = reinterpret_cast<id*>(dest);
527554
*destBoxAddr = objc_retain(srcInstance);
528555
return true;
529556
}

0 commit comments

Comments
 (0)