Skip to content

Commit 7eb8283

Browse files
authored
[Runtime] Let CF provide us with state, avoiding runtime lookup. rdar://111104451 (#66606)
Provide a hook for CF to tell us about the state we need from it, rather than us having to look it up at runtime
1 parent 58d41a1 commit 7eb8283

File tree

7 files changed

+142
-62
lines changed

7 files changed

+142
-62
lines changed

stdlib/public/SwiftShims/swift/shims/CoreFoundationShims.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ _swift_stdlib_NSStringGetCStringTrampoline(id _Nonnull obj,
7474
SWIFT_RUNTIME_STDLIB_API
7575
__swift_uint8_t
7676
_swift_stdlib_dyld_is_objc_constant_string(const void * _Nonnull addr);
77-
77+
78+
/// Get the NSError Objective-C class.
79+
_Nullable Class getNSErrorClass();
80+
7881
#endif // __OBJC2__
7982

8083
#ifdef __cplusplus

stdlib/public/runtime/ErrorObject.mm

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,6 @@ - (BOOL)isEqual:(id)other {
185185

186186
@end
187187

188-
Class swift::getNSErrorClass() {
189-
return SWIFT_LAZY_CONSTANT(objc_lookUpClass("NSError"));
190-
}
191-
192188
const Metadata *swift::getNSErrorMetadata() {
193189
return SWIFT_LAZY_CONSTANT(
194190
swift_getObjCClassMetadata((const ClassMetadata *)getNSErrorClass()));
@@ -220,16 +216,8 @@ - (BOOL)isEqual:(id)other {
220216
}
221217

222218
static Class getAndBridgeSwiftNativeNSErrorClass() {
223-
Class nsErrorClass = swift::getNSErrorClass();
224-
Class ourClass = [__SwiftNativeNSError class];
225-
// We want "err as AnyObject" to do *something* even without Foundation
226-
if (nsErrorClass) {
227-
#pragma clang diagnostic push
228-
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
229-
class_setSuperclass(ourClass, nsErrorClass);
230-
#pragma clang diagnostic pop
231-
}
232-
return ourClass;
219+
(void)getNSErrorClass(); //make sure the bridge is set up
220+
return [__SwiftNativeNSError class];
233221
}
234222

235223
static Class getSwiftNativeNSErrorClass() {

stdlib/public/runtime/SwiftObject.mm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,7 @@ static bool isObjCForUnownedReference(void *value) {
10861086
/*****************************************************************************/
10871087

10881088
#if SWIFT_OBJC_INTEROP
1089+
10891090
static const void *
10901091
swift_dynamicCastObjCClassImpl(const void *object,
10911092
const ClassMetadata *targetType) {

stdlib/public/stubs/FoundationHelpers.mm

Lines changed: 112 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,53 +31,140 @@
3131

3232
using namespace swift;
3333

34-
static CFHashCode(*_CFStringHashCString)(const uint8_t *bytes, CFIndex len);
35-
static CFHashCode(*_CFStringHashNSString)(id str);
36-
static CFTypeID(*_CFGetTypeID)(CFTypeRef obj);
37-
static CFTypeID _CFStringTypeID = 0;
38-
static swift_once_t initializeBridgingFuncsOnce;
34+
typedef struct _CFBridgingState {
35+
long version;
36+
CFHashCode(*_CFStringHashCString)(const uint8_t *bytes, CFIndex len);
37+
CFHashCode(*_CFStringHashNSString)(id str);
38+
CFTypeID(*_CFGetTypeID)(CFTypeRef obj);
39+
CFTypeID _CFStringTypeID = 0;
40+
Class NSErrorClass;
41+
Class NSStringClass;
42+
Class NSArrayClass;
43+
Class NSMutableArrayClass;
44+
Class NSSetClass;
45+
Class NSDictionaryClass;
46+
Class NSEnumeratorClass;
47+
//version 0 ends here
48+
} CFBridgingState;
49+
50+
static CFBridgingState *bridgingState;
51+
static swift_once_t initializeBridgingStateOnce;
3952

4053
extern "C" bool _dyld_is_objc_constant(DyldObjCConstantKind kind,
4154
const void *addr) SWIFT_RUNTIME_WEAK_IMPORT;
4255

56+
static void _initializeBridgingFunctionsFromCFImpl(void *ctxt) {
57+
bridgingState = (CFBridgingState *)ctxt;
58+
}
59+
60+
@class __SwiftNativeNSStringBase, __SwiftNativeNSError, __SwiftNativeNSArrayBase, __SwiftNativeNSMutableArrayBase, __SwiftNativeNSDictionaryBase, __SwiftNativeNSSetBase, __SwiftNativeNSEnumeratorBase;
61+
62+
static void _reparentClasses() {
63+
#pragma clang diagnostic push
64+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
65+
if (bridgingState->NSStringClass) {
66+
[bridgingState->NSStringClass class]; //make sure the class is realized
67+
class_setSuperclass([__SwiftNativeNSStringBase class], bridgingState->NSStringClass);
68+
}
69+
if (bridgingState->NSErrorClass) {
70+
[bridgingState->NSErrorClass class]; //make sure the class is realized
71+
class_setSuperclass([__SwiftNativeNSError class], bridgingState->NSErrorClass);
72+
}
73+
if (bridgingState->NSArrayClass) {
74+
[bridgingState->NSArrayClass class]; //make sure the class is realized
75+
class_setSuperclass([__SwiftNativeNSArrayBase class], bridgingState->NSArrayClass);
76+
}
77+
if (bridgingState->NSMutableArrayClass) {
78+
[bridgingState->NSMutableArrayClass class]; //make sure the class is realized
79+
class_setSuperclass([__SwiftNativeNSMutableArrayBase class], bridgingState->NSMutableArrayClass);
80+
}
81+
if (bridgingState->NSDictionaryClass) {
82+
[bridgingState->NSDictionaryClass class]; //make sure the class is realized
83+
class_setSuperclass([__SwiftNativeNSDictionaryBase class], bridgingState->NSDictionaryClass);
84+
}
85+
if (bridgingState->NSSetClass) {
86+
[bridgingState->NSSetClass class]; //make sure the class is realized
87+
class_setSuperclass([__SwiftNativeNSSetBase class], bridgingState->NSSetClass);
88+
}
89+
if (bridgingState->NSEnumeratorClass) {
90+
[bridgingState->NSEnumeratorClass class]; //make sure the class is realized
91+
class_setSuperclass([__SwiftNativeNSEnumeratorBase class], bridgingState->NSEnumeratorClass);
92+
}
93+
#pragma clang diagnostic pop
94+
}
95+
4396
static void _initializeBridgingFunctionsImpl(void *ctxt) {
44-
auto getStringTypeID =
45-
(CFTypeID(*)(void))
46-
dlsym(RTLD_DEFAULT, "CFStringGetTypeID");
47-
assert(getStringTypeID);
48-
_CFStringTypeID = getStringTypeID();
49-
50-
_CFGetTypeID = (CFTypeID(*)(CFTypeRef obj))dlsym(RTLD_DEFAULT, "CFGetTypeID");
51-
_CFStringHashNSString = (CFHashCode(*)(id))dlsym(RTLD_DEFAULT,
52-
"CFStringHashNSString");
53-
_CFStringHashCString = (CFHashCode(*)(const uint8_t *, CFIndex))dlsym(
54-
RTLD_DEFAULT,
55-
"CFStringHashCString");
97+
assert(!bridgingState);
98+
auto getStringTypeID = (CFTypeID(*)(void))dlsym(RTLD_DEFAULT, "CFStringGetTypeID");
99+
if (!getStringTypeID) {
100+
return; //CF not loaded
101+
}
102+
bridgingState = (CFBridgingState *)calloc(1, sizeof(CFBridgingState));
103+
bridgingState->version = 0;
104+
bridgingState->_CFStringTypeID = getStringTypeID();
105+
bridgingState->_CFGetTypeID = (CFTypeID(*)(CFTypeRef obj))dlsym(RTLD_DEFAULT, "CFGetTypeID");
106+
bridgingState->_CFStringHashNSString = (CFHashCode(*)(id))dlsym(RTLD_DEFAULT, "CFStringHashNSString");
107+
bridgingState->_CFStringHashCString = (CFHashCode(*)(const uint8_t *, CFIndex))dlsym(RTLD_DEFAULT, "CFStringHashCString");
108+
bridgingState->NSErrorClass = objc_lookUpClass("NSError");
109+
bridgingState->NSStringClass = objc_lookUpClass("NSString");
110+
bridgingState->NSArrayClass = objc_lookUpClass("NSArray");
111+
bridgingState->NSMutableArrayClass = objc_lookUpClass("NSMutableArray");
112+
bridgingState->NSSetClass = objc_lookUpClass("NSSet");
113+
bridgingState->NSDictionaryClass = objc_lookUpClass("NSDictionary");
114+
bridgingState->NSEnumeratorClass = objc_lookUpClass("NSEnumerator");
115+
_reparentClasses();
56116
}
57117

58-
static inline void initializeBridgingFunctions() {
59-
swift_once(&initializeBridgingFuncsOnce,
118+
static inline bool initializeBridgingFunctions() {
119+
swift_once(&initializeBridgingStateOnce,
60120
_initializeBridgingFunctionsImpl,
61121
nullptr);
122+
return bridgingState && bridgingState->NSStringClass != nullptr;
123+
}
124+
125+
SWIFT_RUNTIME_EXPORT void swift_initializeCoreFoundationState(CFBridgingState const * const state) {
126+
swift_once(&initializeBridgingStateOnce,
127+
_initializeBridgingFunctionsFromCFImpl,
128+
(void *)state);
129+
//It's fine if this runs more than once, it's a noop if it's been done before
130+
//and we want to make sure it happens if CF loads late after it failed initially
131+
_reparentClasses();
132+
}
133+
134+
namespace swift {
135+
Class getNSErrorClass();
136+
}
137+
138+
Class swift::getNSErrorClass() {
139+
if (initializeBridgingFunctions()) {
140+
return bridgingState->NSErrorClass;
141+
}
142+
return nullptr;
143+
}
144+
145+
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_SPI
146+
bool
147+
swift_stdlib_connectNSBaseClasses() {
148+
return initializeBridgingFunctions();
62149
}
63150

64151
__swift_uint8_t
65152
_swift_stdlib_isNSString(id obj) {
66-
initializeBridgingFunctions();
67-
return _CFGetTypeID((CFTypeRef)obj) == _CFStringTypeID ? 1 : 0;
153+
assert(initializeBridgingFunctions());
154+
return bridgingState->_CFGetTypeID((CFTypeRef)obj) == bridgingState->_CFStringTypeID ? 1 : 0;
68155
}
69156

70157
_swift_shims_CFHashCode
71158
_swift_stdlib_CFStringHashNSString(id _Nonnull obj) {
72-
initializeBridgingFunctions();
73-
return _CFStringHashNSString(obj);
159+
assert(initializeBridgingFunctions());
160+
return bridgingState->_CFStringHashNSString(obj);
74161
}
75162

76163
_swift_shims_CFHashCode
77164
_swift_stdlib_CFStringHashCString(const _swift_shims_UInt8 * _Nonnull bytes,
78165
_swift_shims_CFIndex length) {
79-
initializeBridgingFunctions();
80-
return _CFStringHashCString(bytes, length);
166+
assert(initializeBridgingFunctions());
167+
return bridgingState->_CFStringHashCString(bytes, length);
81168
}
82169

83170
const __swift_uint8_t *

stdlib/public/stubs/SwiftNativeNSXXXBase.mm.gyb

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -87,22 +87,6 @@ swift_stdlib_NSObject_isEqual(id lhs,
8787
return (lhs == rhs) || [lhs isEqual:rhs];
8888
}
8989

90-
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_SPI
91-
bool
92-
swift_stdlib_connectNSBaseClasses() {
93-
% for Class in ('Array', 'MutableArray', 'Dictionary', 'Set', 'String', 'Enumerator'):
94-
Class NS${Class}Super = objc_lookUpClass("NS${Class}");
95-
if (!NS${Class}Super) return false;
96-
Class NS${Class}OurClass = objc_lookUpClass("__SwiftNativeNS${Class}Base");
97-
if (!NS${Class}OurClass) return false;
98-
#pragma clang diagnostic push
99-
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
100-
class_setSuperclass(NS${Class}OurClass, NS${Class}Super);
101-
#pragma clang diagnostic pop
102-
% end
103-
return true;
104-
}
105-
10690
#endif
10791

10892
// ${'Local Variables'}:

unittests/runtime/CMakeLists.txt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND
4040

4141
set(PLATFORM_SOURCES)
4242
set(PLATFORM_TARGET_LINK_LIBRARIES)
43+
set(PLATFORM_TARGET_RUNTIME_ARCHIVES)
44+
45+
list(APPEND PLATFORM_TARGET_RUNTIME_ARCHIVES
46+
$<TARGET_OBJECTS:swiftRuntime${SWIFT_PRIMARY_VARIANT_SUFFIX}>
47+
$<TARGET_OBJECTS:swiftLLVMSupport${SWIFT_PRIMARY_VARIANT_SUFFIX}>
48+
$<TARGET_OBJECTS:swiftDemangling${SWIFT_PRIMARY_VARIANT_SUFFIX}>
49+
)
50+
4351
if(SWIFT_HOST_VARIANT MATCHES "${SWIFT_DARWIN_VARIANTS}")
4452
find_library(FOUNDATION_LIBRARY Foundation)
4553
list(APPEND PLATFORM_SOURCES
@@ -53,6 +61,9 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND
5361
swift_Concurrency${SWIFT_PRIMARY_VARIANT_SUFFIX}
5462
swiftStdlibUnittest${SWIFT_PRIMARY_VARIANT_SUFFIX}
5563
)
64+
list(APPEND PLATFORM_TARGET_RUNTIME_ARCHIVES
65+
$<TARGET_OBJECTS:swiftStdlibStubs${SWIFT_PRIMARY_VARIANT_SUFFIX}>
66+
)
5667
elseif(SWIFT_HOST_VARIANT STREQUAL "Linux")
5768
if(SWIFT_HOST_VARIANT_ARCH MATCHES "armv6|armv7|i686")
5869
list(APPEND PLATFORM_TARGET_LINK_LIBRARIES
@@ -116,9 +127,7 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND
116127
# The runtime tests link to internal runtime symbols, which aren't exported
117128
# from the swiftCore dylib, so we need to link to both the runtime archive
118129
# and the stdlib.
119-
$<TARGET_OBJECTS:swiftRuntime${SWIFT_PRIMARY_VARIANT_SUFFIX}>
120-
$<TARGET_OBJECTS:swiftLLVMSupport${SWIFT_PRIMARY_VARIANT_SUFFIX}>
121-
$<TARGET_OBJECTS:swiftDemangling${SWIFT_PRIMARY_VARIANT_SUFFIX}>
130+
${PLATFORM_TARGET_RUNTIME_ARCHIVES}
122131
)
123132

124133
# The local stdlib implementation provides definitions of the swiftCore

unittests/runtime/LongTests/CMakeLists.txt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,18 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND
33

44
set(PLATFORM_SOURCES)
55
set(PLATFORM_TARGET_LINK_LIBRARIES)
6+
set(PLATFORM_TARGET_RUNTIME_ARCHIVES)
7+
8+
list(APPEND PLATFORM_TARGET_RUNTIME_ARCHIVES
9+
$<TARGET_OBJECTS:swiftRuntime${SWIFT_PRIMARY_VARIANT_SUFFIX}>
10+
$<TARGET_OBJECTS:swiftLLVMSupport${SWIFT_PRIMARY_VARIANT_SUFFIX}>
11+
$<TARGET_OBJECTS:swiftDemangling${SWIFT_PRIMARY_VARIANT_SUFFIX}>
12+
)
613

714
if(SWIFT_HOST_VARIANT MATCHES "${SWIFT_DARWIN_VARIANTS}")
15+
list(APPEND PLATFORM_TARGET_RUNTIME_ARCHIVES
16+
$<TARGET_OBJECTS:swiftStdlibStubs${SWIFT_PRIMARY_VARIANT_SUFFIX}>
17+
)
818
# Placeholder for Darwin Foundation tests (currently none)
919
# find_library(FOUNDATION_LIBRARY Foundation)
1020
# list(APPEND PLATFORM_SOURCES
@@ -42,9 +52,7 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND
4252
# The runtime tests link to internal runtime symbols, which aren't exported
4353
# from the swiftCore dylib, so we need to link to both the runtime archive
4454
# and the stdlib.
45-
$<TARGET_OBJECTS:swiftRuntime${SWIFT_PRIMARY_VARIANT_SUFFIX}>
46-
$<TARGET_OBJECTS:swiftLLVMSupport${SWIFT_PRIMARY_VARIANT_SUFFIX}>
47-
$<TARGET_OBJECTS:swiftDemangling${SWIFT_PRIMARY_VARIANT_SUFFIX}>
55+
${PLATFORM_TARGET_RUNTIME_ARCHIVES}
4856
)
4957

5058
# The local stdlib implementation provides definitions of the swiftCore

0 commit comments

Comments
 (0)