//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/Runtime/Config.h" #include "swift/Basic/Lazy.h" #include "swift/Runtime/Concurrent.h" #include "swift/Runtime/Debug.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Casting.h" #include "Private.h" #include "SwiftValue.h" #include "SwiftHashableSupport.h" using namespace swift; using namespace swift::hashable_support; namespace { struct HashableConformanceKey { /// The lookup key, the metadata of a type that is possibly derived /// from a type that conforms to `Hashable`. const Metadata *derivedType; }; struct HashableConformanceEntry { /// The lookup key, the metadata of a type that is possibly derived /// from a type that conforms to `Hashable`. const Metadata *derivedType; /// The highest (closest to the root) type in the superclass chain /// that conforms to `Hashable`. /// /// Always non-NULL. We don't cache negative responses so that we /// don't have to deal with cache invalidation. const Metadata *baseTypeThatConformsToHashable; HashableConformanceEntry(HashableConformanceKey key, const Metadata *baseTypeThatConformsToHashable) : derivedType(key.derivedType), baseTypeThatConformsToHashable(baseTypeThatConformsToHashable) {} int compareWithKey(const HashableConformanceKey &key) const { if (key.derivedType != derivedType) { return (uintptr_t(key.derivedType) < uintptr_t(derivedType) ? -1 : 1); } else { return 0; } } static size_t getExtraAllocationSize(HashableConformanceKey key, const Metadata *baseTypeThatConformsToHashable) { return 0; } size_t getExtraAllocationSize() const { return 0; } }; } // end unnamed namespace // FIXME(performance): consider merging this cache into the regular // protocol conformance cache. static ConcurrentMap HashableConformances; template LLVM_ATTRIBUTE_ALWAYS_INLINE static const Metadata *findHashableBaseTypeImpl(const Metadata *type) { // Check the cache first. if (HashableConformanceEntry *entry = HashableConformances.find(HashableConformanceKey{type})) { return entry->baseTypeThatConformsToHashable; } if (!KnownToConformToHashable && !swift_conformsToProtocol(type, &HashableProtocolDescriptor)) { // Don't cache the negative response because we don't invalidate // this cache when a new conformance is loaded dynamically. return nullptr; } // By this point, `type` is known to conform to `Hashable`. const Metadata *baseTypeThatConformsToHashable = type; while (true) { const Metadata *superclass = _swift_class_getSuperclass(baseTypeThatConformsToHashable); if (!superclass) break; if (!swift_conformsToProtocol(superclass, &HashableProtocolDescriptor)) break; baseTypeThatConformsToHashable = superclass; } HashableConformances.getOrInsert(HashableConformanceKey{type}, baseTypeThatConformsToHashable); return baseTypeThatConformsToHashable; } /// Find the base type that introduces the `Hashable` conformance. /// Because the provided type is known to conform to `Hashable`, this /// function always returns non-null. /// /// - Precondition: `type` conforms to `Hashable` (not checked). const Metadata *swift::hashable_support::findHashableBaseTypeOfHashableType( const Metadata *type) { auto result = findHashableBaseTypeImpl(type); assert(result && "Known-hashable types should have a `Hashable` conformance."); return result; } /// Find the base type that introduces the `Hashable` conformance. /// If `type` does not conform to `Hashable`, `nullptr` is returned. const Metadata *swift::hashable_support::findHashableBaseType( const Metadata *type) { return findHashableBaseTypeImpl(type); } SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE void _swift_stdlib_makeAnyHashableUsingDefaultRepresentation( const OpaqueValue *value, const void *anyHashableResultPointer, const Metadata *T, const WitnessTable *hashableWT ); SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE void _swift_stdlib_makeAnyHashableUpcastingToHashableBaseType( OpaqueValue *value, const void *anyHashableResultPointer, const Metadata *type, const WitnessTable *hashableWT ) { switch (type->getKind()) { case MetadataKind::Class: case MetadataKind::ObjCClassWrapper: case MetadataKind::ForeignClass: { #if SWIFT_OBJC_INTEROP id srcObject; memcpy(&srcObject, value, sizeof(id)); // Do we have a _SwiftValue? if (_SwiftValue *srcSwiftValue = getAsSwiftValue(srcObject)) { // If so, extract the boxed value and try to cast it. const Metadata *unboxedType; const OpaqueValue *unboxedValue; std::tie(unboxedType, unboxedValue) = getValueFromSwiftValue(srcSwiftValue); if (auto unboxedHashableWT = swift_conformsToProtocol(type, &HashableProtocolDescriptor)) { ValueBuffer unboxedCopyBuf; // Allocate buffer. OpaqueValue *unboxedValueCopy = unboxedType->allocateBufferIn(&unboxedCopyBuf); // initWithCopy. unboxedType->vw_initializeWithCopy( unboxedValueCopy, const_cast(unboxedValue)); _swift_stdlib_makeAnyHashableUpcastingToHashableBaseType( unboxedValueCopy, anyHashableResultPointer, unboxedType, unboxedHashableWT); // Deallocate buffer. unboxedType->deallocateBufferIn(&unboxedCopyBuf); type->vw_destroy(value); return; } } #endif _swift_stdlib_makeAnyHashableUsingDefaultRepresentation( value, anyHashableResultPointer, findHashableBaseTypeOfHashableType(type), hashableWT); return; } case MetadataKind::Struct: case MetadataKind::Enum: case MetadataKind::Optional: _swift_stdlib_makeAnyHashableUsingDefaultRepresentation( value, anyHashableResultPointer, type, hashableWT); return; case MetadataKind::ErrorObject: // ErrorObject metadata is not used for any Swift-level values, so // this case is unreachable. _failCorruptType(type); case MetadataKind::Opaque: case MetadataKind::Tuple: case MetadataKind::Function: case MetadataKind::Existential: case MetadataKind::Metatype: case MetadataKind::ExistentialMetatype: case MetadataKind::HeapLocalVariable: case MetadataKind::HeapGenericLocalVariable: // We assume that the value can not be an existential, // because existentials can't conform to Hashable today. // // FIXME: handle generalized existentials when Swift has them. _failCorruptType(type); } _failCorruptType(type); }