Skip to content

Avoid protocol conformance lookups in more NSString bridging scenarios #39378

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
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
6 changes: 6 additions & 0 deletions stdlib/public/core/AnyHashable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,13 @@ public struct AnyHashable {
/// Creates a type-erased hashable value that wraps the given instance.
///
/// - Parameter base: A hashable value to wrap.
@_specialize(where H == String)
public init<H: Hashable>(_ base: H) {
if H.self == String.self {
self.init(_box: _ConcreteHashableBox(base))
return
}

if let custom =
(base as? _HasCustomAnyHashableRepresentation)?._toCustomAnyHashable() {
self = custom
Expand Down
22 changes: 16 additions & 6 deletions stdlib/public/runtime/DynamicCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -764,14 +764,21 @@ tryCastToAnyHashable(
assert(cast<StructMetadata>(destType)->Description
== &STRUCT_TYPE_DESCR_SYM(s11AnyHashable));

const HashableWitnessTable *hashableConformance = nullptr;

switch (srcType->getKind()) {
case MetadataKind::ForeignClass: // CF -> String
case MetadataKind::ObjCClassWrapper: { // Obj-C -> String
#if SWIFT_OBJC_INTEROP
// TODO: Implement a fast path for NSString->AnyHashable casts.
// These are incredibly common because an NSDictionary with
// NSString keys is bridged by default to [AnyHashable:Any].
// Until this is implemented, fall through to the general case
auto cls = srcType;
auto nsString = getNSStringMetadata();
do {
if (cls == nsString) {
hashableConformance = getNSStringHashableConformance();
break;
}
cls = _swift_class_getSuperclass(cls);
} while (cls != nullptr);
break;
#else
// If no Obj-C interop, just fall through to the general case.
Expand Down Expand Up @@ -805,8 +812,11 @@ tryCastToAnyHashable(


// General case: If it conforms to Hashable, we cast it
auto hashableConformance = reinterpret_cast<const HashableWitnessTable *>(
swift_conformsToProtocol(srcType, &HashableProtocolDescriptor));
if (hashableConformance == nullptr) {
hashableConformance = reinterpret_cast<const HashableWitnessTable *>(
swift_conformsToProtocol(srcType, &HashableProtocolDescriptor)
);
}
if (hashableConformance) {
_swift_convertToAnyHashableIndirect(srcValue, destLocation,
srcType, hashableConformance);
Expand Down
5 changes: 5 additions & 0 deletions stdlib/public/runtime/SwiftObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <cstdint>
#include <utility>
#include "swift/Runtime/HeapObject.h"
#include "../runtime/SwiftHashableSupport.h"
#if SWIFT_OBJC_INTEROP
#include <objc/NSObject.h>
#endif
Expand Down Expand Up @@ -89,7 +90,11 @@ namespace swift {

/// Get the NSObject metadata.
const Metadata *getNSObjectMetadata();
const Metadata *getNSStringMetadata();

namespace hashable_support {
const HashableWitnessTable *getNSStringHashableConformance();
}
}

#endif
19 changes: 19 additions & 0 deletions stdlib/public/runtime/SwiftObject.mm
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#endif

using namespace swift;
using namespace hashable_support;

#if SWIFT_HAS_ISA_MASKING
OBJC_EXPORT __attribute__((__weak_import__))
Expand Down Expand Up @@ -1600,6 +1601,24 @@ void swift_objc_swift3ImplicitObjCEntrypoint(id self, SEL selector,
swift_getObjCClassMetadata((const ClassMetadata *)[NSObject class]));
}

const Metadata *swift::getNSStringMetadata() {
return SWIFT_LAZY_CONSTANT(swift_getObjCClassMetadata(
(const ClassMetadata *)objc_lookUpClass("NSString")
));
}

const HashableWitnessTable *
swift::hashable_support::getNSStringHashableConformance() {
return SWIFT_LAZY_CONSTANT(
reinterpret_cast<const HashableWitnessTable *>(
swift_conformsToProtocol(
getNSStringMetadata(),
&HashableProtocolDescriptor
)
)
);
}

#endif

const ClassMetadata *swift::getRootSuperclass() {
Expand Down