mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Cache the last ObjC bridging conformance we looked up (#81545)
Fixes rdar://151475392
This commit is contained in:
@@ -97,6 +97,9 @@ public let benchmarks = [
|
|||||||
BenchmarkInfo(name: "NSArray.bridged.repeatedBufferAccess",
|
BenchmarkInfo(name: "NSArray.bridged.repeatedBufferAccess",
|
||||||
runFunction: run_BridgedNSArrayRepeatedBufferAccess, tags: t,
|
runFunction: run_BridgedNSArrayRepeatedBufferAccess, tags: t,
|
||||||
setUpFunction: setup_bridgedArrays),
|
setUpFunction: setup_bridgedArrays),
|
||||||
|
BenchmarkInfo(name: "NSDictionary.bridged.enumerate",
|
||||||
|
runFunction: run_BridgedNSDictionaryEnumerate, tags: t,
|
||||||
|
setUpFunction: setup_bridgedDictionaries),
|
||||||
]
|
]
|
||||||
|
|
||||||
#if _runtime(_ObjC)
|
#if _runtime(_ObjC)
|
||||||
@@ -794,6 +797,7 @@ public func run_UnicodeStringFromCodable(_ n: Int) {
|
|||||||
|
|
||||||
#if _runtime(_ObjC)
|
#if _runtime(_ObjC)
|
||||||
var bridgedArray:NSArray! = nil
|
var bridgedArray:NSArray! = nil
|
||||||
|
var bridgedDictionaryOfNumbersToNumbers:NSDictionary! = nil
|
||||||
var bridgedArrayMutableCopy:NSMutableArray! = nil
|
var bridgedArrayMutableCopy:NSMutableArray! = nil
|
||||||
var nsArray:NSArray! = nil
|
var nsArray:NSArray! = nil
|
||||||
var nsArrayMutableCopy:NSMutableArray! = nil
|
var nsArrayMutableCopy:NSMutableArray! = nil
|
||||||
@@ -804,11 +808,21 @@ public func setup_bridgedArrays() {
|
|||||||
var arr = Array(repeating: NSObject(), count: 100) as [AnyObject]
|
var arr = Array(repeating: NSObject(), count: 100) as [AnyObject]
|
||||||
bridgedArray = arr as NSArray
|
bridgedArray = arr as NSArray
|
||||||
bridgedArrayMutableCopy = (bridgedArray.mutableCopy() as! NSMutableArray)
|
bridgedArrayMutableCopy = (bridgedArray.mutableCopy() as! NSMutableArray)
|
||||||
|
|
||||||
nsArray = NSArray(objects: &arr, count: 100)
|
nsArray = NSArray(objects: &arr, count: 100)
|
||||||
nsArrayMutableCopy = (nsArray.mutableCopy() as! NSMutableArray)
|
nsArrayMutableCopy = (nsArray.mutableCopy() as! NSMutableArray)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func setup_bridgedDictionaries() {
|
||||||
|
var numDict = Dictionary<Int, Int>()
|
||||||
|
for i in 0 ..< 100 {
|
||||||
|
numDict[i] = i
|
||||||
|
}
|
||||||
|
bridgedDictionaryOfNumbersToNumbers = numDict as NSDictionary
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@inline(never)
|
@inline(never)
|
||||||
public func run_BridgedNSArrayObjectAtIndex(_ n: Int) {
|
public func run_BridgedNSArrayObjectAtIndex(_ n: Int) {
|
||||||
#if _runtime(_ObjC)
|
#if _runtime(_ObjC)
|
||||||
@@ -820,6 +834,23 @@ public func run_BridgedNSArrayObjectAtIndex(_ n: Int) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func dictionaryApplier(
|
||||||
|
_ keyPtr: UnsafeRawPointer?,
|
||||||
|
_ valuePtr :UnsafeRawPointer?,
|
||||||
|
_ contextPtr: UnsafeMutableRawPointer?
|
||||||
|
) -> Void {}
|
||||||
|
|
||||||
|
@inline(never)
|
||||||
|
public func run_BridgedNSDictionaryEnumerate(_ n: Int) {
|
||||||
|
#if _runtime(_ObjC)
|
||||||
|
let cf = bridgedDictionaryOfNumbersToNumbers as CFDictionary
|
||||||
|
for _ in 0 ..< n * 50 {
|
||||||
|
// Use CF to prevent Swift from providing an override, forcing going through ObjC bridging
|
||||||
|
CFDictionaryApplyFunction(cf, dictionaryApplier, nil)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
@inline(never)
|
@inline(never)
|
||||||
public func run_BridgedNSArrayBufferAccess(_ n: Int) {
|
public func run_BridgedNSArrayBufferAccess(_ n: Int) {
|
||||||
#if _runtime(_ObjC)
|
#if _runtime(_ObjC)
|
||||||
|
|||||||
@@ -1436,10 +1436,31 @@ extern "C" const _ObjectiveCBridgeableWitnessTable BRIDGING_CONFORMANCE_SYM;
|
|||||||
/// Nominal type descriptor for Swift.String.
|
/// Nominal type descriptor for Swift.String.
|
||||||
extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(SS);
|
extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(SS);
|
||||||
|
|
||||||
|
struct ObjCBridgeWitnessCacheEntry {
|
||||||
|
const Metadata *metadata;
|
||||||
|
const _ObjectiveCBridgeableWitnessTable *witness;
|
||||||
|
};
|
||||||
|
|
||||||
|
// String is so important that we cache it permanently, so we don't want to
|
||||||
|
// pollute this temporary cache with the String entry
|
||||||
|
static const _ObjectiveCBridgeableWitnessTable *
|
||||||
|
swift_conformsToObjectiveCBridgeableNoCache(const Metadata *T) {
|
||||||
|
auto w = swift_conformsToProtocolCommon(
|
||||||
|
T, &PROTOCOL_DESCR_SYM(s21_ObjectiveCBridgeable));
|
||||||
|
return reinterpret_cast<const _ObjectiveCBridgeableWitnessTable *>(w);
|
||||||
|
}
|
||||||
|
|
||||||
static const _ObjectiveCBridgeableWitnessTable *
|
static const _ObjectiveCBridgeableWitnessTable *
|
||||||
swift_conformsToObjectiveCBridgeable(const Metadata *T) {
|
swift_conformsToObjectiveCBridgeable(const Metadata *T) {
|
||||||
return reinterpret_cast<const _ObjectiveCBridgeableWitnessTable *>
|
static std::atomic<ObjCBridgeWitnessCacheEntry> _objcBridgeWitnessCache = {};
|
||||||
(swift_conformsToProtocolCommon(T, &PROTOCOL_DESCR_SYM(s21_ObjectiveCBridgeable)));
|
auto cached = _objcBridgeWitnessCache.load(SWIFT_MEMORY_ORDER_CONSUME);
|
||||||
|
if (cached.metadata == T) {
|
||||||
|
return cached.witness;
|
||||||
|
}
|
||||||
|
cached.witness = swift_conformsToObjectiveCBridgeableNoCache(T);
|
||||||
|
cached.metadata = T;
|
||||||
|
_objcBridgeWitnessCache.store(cached, std::memory_order_release);
|
||||||
|
return cached.witness;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const _ObjectiveCBridgeableWitnessTable *
|
static const _ObjectiveCBridgeableWitnessTable *
|
||||||
@@ -1451,7 +1472,7 @@ findBridgeWitness(const Metadata *T) {
|
|||||||
if (T->getKind() == MetadataKind::Struct) {
|
if (T->getKind() == MetadataKind::Struct) {
|
||||||
auto structDescription = cast<StructMetadata>(T)->Description;
|
auto structDescription = cast<StructMetadata>(T)->Description;
|
||||||
if (structDescription == &NOMINAL_TYPE_DESCR_SYM(SS)) {
|
if (structDescription == &NOMINAL_TYPE_DESCR_SYM(SS)) {
|
||||||
static auto *Swift_String_ObjectiveCBridgeable = swift_conformsToObjectiveCBridgeable(T);
|
static auto *Swift_String_ObjectiveCBridgeable = swift_conformsToObjectiveCBridgeableNoCache(T);
|
||||||
return Swift_String_ObjectiveCBridgeable;
|
return Swift_String_ObjectiveCBridgeable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,11 +206,84 @@ struct _ObjectiveCBridgeableWitnessTable : WitnessTable {
|
|||||||
extern "C" const ProtocolDescriptor
|
extern "C" const ProtocolDescriptor
|
||||||
PROTOCOL_DESCR_SYM(s21_ObjectiveCBridgeable);
|
PROTOCOL_DESCR_SYM(s21_ObjectiveCBridgeable);
|
||||||
|
|
||||||
|
#if SWIFT_OBJC_INTEROP
|
||||||
|
#define BRIDGING_CONFORMANCE_SYM \
|
||||||
|
MANGLE_SYM(s19_BridgeableMetatypeVs21_ObjectiveCBridgeablesWP)
|
||||||
|
|
||||||
|
extern "C" const _ObjectiveCBridgeableWitnessTable BRIDGING_CONFORMANCE_SYM;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Nominal type descriptor for Swift.String.
|
||||||
|
extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(SS);
|
||||||
|
|
||||||
|
struct ObjCBridgeWitnessCacheEntry {
|
||||||
|
const Metadata *metadata;
|
||||||
|
const _ObjectiveCBridgeableWitnessTable *witness;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const _ObjectiveCBridgeableWitnessTable *
|
||||||
|
swift_conformsToObjectiveCBridgeableNoCache(const Metadata *T) {
|
||||||
|
auto w = swift_conformsToProtocolCommon(
|
||||||
|
T, &PROTOCOL_DESCR_SYM(s21_ObjectiveCBridgeable));
|
||||||
|
return reinterpret_cast<const _ObjectiveCBridgeableWitnessTable *>(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const _ObjectiveCBridgeableWitnessTable *
|
||||||
|
swift_conformsToObjectiveCBridgeable(const Metadata *T) {
|
||||||
|
static std::atomic<ObjCBridgeWitnessCacheEntry> _objcBridgeWitnessCache = {};
|
||||||
|
auto cached = _objcBridgeWitnessCache.load(SWIFT_MEMORY_ORDER_CONSUME);
|
||||||
|
if (cached.metadata == T) {
|
||||||
|
return cached.witness;
|
||||||
|
}
|
||||||
|
cached.witness = swift_conformsToObjectiveCBridgeableNoCache(T);
|
||||||
|
cached.metadata = T;
|
||||||
|
_objcBridgeWitnessCache.store(cached, std::memory_order_release);
|
||||||
|
return cached.witness;
|
||||||
|
}
|
||||||
|
|
||||||
static const _ObjectiveCBridgeableWitnessTable *
|
static const _ObjectiveCBridgeableWitnessTable *
|
||||||
findBridgeWitness(const Metadata *T) {
|
findBridgeWitness(const Metadata *T) {
|
||||||
auto w = swift_conformsToProtocolCommon(
|
// Special case: Memoize the bridge witness for Swift.String.
|
||||||
T, &PROTOCOL_DESCR_SYM(s21_ObjectiveCBridgeable));
|
// Swift.String is the most heavily used bridge because of the prevalence of
|
||||||
return reinterpret_cast<const _ObjectiveCBridgeableWitnessTable *>(w);
|
// string-keyed dictionaries in Obj-C. It's worth burning a few words of static
|
||||||
|
// storage to avoid repeatedly looking up this conformance.
|
||||||
|
if (T->getKind() == MetadataKind::Struct) {
|
||||||
|
auto structDescription = cast<StructMetadata>(T)->Description;
|
||||||
|
if (structDescription == &NOMINAL_TYPE_DESCR_SYM(SS)) {
|
||||||
|
static auto *Swift_String_ObjectiveCBridgeable = swift_conformsToObjectiveCBridgeableNoCache(T);
|
||||||
|
return Swift_String_ObjectiveCBridgeable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto w = swift_conformsToObjectiveCBridgeable(T);
|
||||||
|
if (SWIFT_LIKELY(w))
|
||||||
|
return reinterpret_cast<const _ObjectiveCBridgeableWitnessTable *>(w);
|
||||||
|
// Class and ObjC existential metatypes can be bridged, but metatypes can't
|
||||||
|
// directly conform to protocols yet. Use a stand-in conformance for a type
|
||||||
|
// that looks like a metatype value if the metatype can be bridged.
|
||||||
|
switch (T->getKind()) {
|
||||||
|
case MetadataKind::Metatype: {
|
||||||
|
#if SWIFT_OBJC_INTEROP
|
||||||
|
auto metaTy = static_cast<const MetatypeMetadata *>(T);
|
||||||
|
if (metaTy->InstanceType->isAnyClass())
|
||||||
|
return &BRIDGING_CONFORMANCE_SYM;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MetadataKind::ExistentialMetatype: {
|
||||||
|
#if SWIFT_OBJC_INTEROP
|
||||||
|
auto existentialMetaTy =
|
||||||
|
static_cast<const ExistentialMetatypeMetadata *>(T);
|
||||||
|
if (existentialMetaTy->isObjC())
|
||||||
|
return &BRIDGING_CONFORMANCE_SYM;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the bridged Objective-C type for the given type that
|
/// Retrieve the bridged Objective-C type for the given type that
|
||||||
@@ -734,7 +807,7 @@ struct ObjCBridgeMemo {
|
|||||||
#if !NDEBUG
|
#if !NDEBUG
|
||||||
memo->destType = setupData->destType;
|
memo->destType = setupData->destType;
|
||||||
#endif
|
#endif
|
||||||
memo->destBridgeWitness = findBridgeWitness(setupData->destType);
|
memo->destBridgeWitness = swift_conformsToObjectiveCBridgeableNoCache(setupData->destType);
|
||||||
if (memo->destBridgeWitness == nullptr) {
|
if (memo->destBridgeWitness == nullptr) {
|
||||||
memo->targetBridgedType = nullptr;
|
memo->targetBridgedType = nullptr;
|
||||||
memo->targetBridgedObjCClass = nullptr;
|
memo->targetBridgedObjCClass = nullptr;
|
||||||
|
|||||||
Reference in New Issue
Block a user