| //===--- ProtocolConformance.cpp - Swift protocol conformance checking ----===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Checking and caching of Swift protocol conformances. |
| // |
| // This implementation is intended to be backward-deployed into Swift 5.0 |
| // runtimes. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Overrides.h" |
| #include "../../public/runtime/Private.h" |
| #include "swift/Basic/Lazy.h" |
| #include <dlfcn.h> |
| #include <mach-o/dyld.h> |
| #include <mach-o/getsect.h> |
| #include <objc/runtime.h> |
| |
| using namespace swift; |
| |
| #if __POINTER_WIDTH__ == 64 |
| using mach_header_platform = mach_header_64; |
| #else |
| using mach_header_platform = mach_header; |
| #endif |
| |
| /// The Mach-O section name for the section containing protocol conformances. |
| /// This lives within SEG_TEXT. |
| constexpr const char ProtocolConformancesSection[] = "__swift5_proto"; |
| |
| // Clone of private function getRootSuperclass. This returns the SwiftObject |
| // class in the ABI-stable dylib, regardless of what the local runtime build |
| // does, since we're always patching an ABI-stable dylib. |
| __attribute__((visibility("hidden"), weak)) |
| const ClassMetadata *swift::getRootSuperclass() { |
| auto theClass = SWIFT_LAZY_CONSTANT(objc_getClass("_TtCs12_SwiftObject")); |
| return (const ClassMetadata *)theClass; |
| } |
| |
| // A dummy target context descriptor to use in conformance records which point |
| // to a NULL descriptor. It doesn't have to be completely valid, just something |
| // that code reading conformance descriptors will ignore. |
| struct { |
| ContextDescriptorFlags flags; |
| int32_t offset; |
| } DummyTargetContextDescriptor = { |
| ContextDescriptorFlags().withKind(ContextDescriptorKind::Extension), |
| 0 |
| }; |
| |
| // Search for any protocol conformance descriptors with a NULL type descriptor |
| // and rewrite those to point to the dummy descriptor. This occurs when an |
| // extension is used to declare a conformance on a weakly linked type and that |
| // type is not present at runtime. |
| static void addImageCallback(const mach_header *mh, intptr_t vmaddr_slide) { |
| unsigned long size; |
| const uint8_t *section = |
| getsectiondata(reinterpret_cast<const mach_header_platform *>(mh), |
| SEG_TEXT, ProtocolConformancesSection, |
| &size); |
| if (!section) |
| return; |
| |
| auto recordsBegin |
| = reinterpret_cast<const ProtocolConformanceRecord*>(section); |
| auto recordsEnd |
| = reinterpret_cast<const ProtocolConformanceRecord*> |
| (section + size); |
| for (auto record = recordsBegin; record != recordsEnd; record++) { |
| auto descriptor = record->get(); |
| if (auto typePtr = descriptor->_getTypeDescriptorLocation()) { |
| if (*typePtr == nullptr) |
| *typePtr = reinterpret_cast<TargetContextDescriptor<InProcess> *>( |
| &DummyTargetContextDescriptor); |
| } |
| } |
| } |
| |
| // Register the add image callback with dyld. |
| static void registerAddImageCallback(void *) { |
| _dyld_register_func_for_add_image(addImageCallback); |
| } |
| |
| static const Metadata *getObjCClassMetadata(const ClassMetadata *c) { |
| // Look up swift_getObjCClassMetadata dynamically. This handles the case |
| // where the main executable can't link against libswiftCore.dylib because |
| // it will be loaded dynamically from a location that isn't known at build |
| // time. |
| using FPtr = const Metadata *(*)(const ClassMetadata *); |
| FPtr func = SWIFT_LAZY_CONSTANT( |
| reinterpret_cast<FPtr>(dlsym(RTLD_DEFAULT, "swift_getObjCClassMetadata"))); |
| |
| return func(c); |
| } |
| |
| // Clone of private helper swift::_swiftoverride_class_getSuperclass |
| // for use in the override implementation. |
| static const Metadata *_swift50override_class_getSuperclass( |
| const Metadata *theClass) { |
| if (const ClassMetadata *classType = theClass->getClassObject()) { |
| if (classHasSuperclass(classType)) |
| return getObjCClassMetadata(classType->Superclass); |
| } |
| |
| if (const ForeignClassMetadata *foreignClassType |
| = dyn_cast<ForeignClassMetadata>(theClass)) { |
| if (const Metadata *superclass = foreignClassType->Superclass) |
| return superclass; |
| } |
| |
| return nullptr; |
| } |
| |
| const WitnessTable * |
| swift::swift50override_conformsToProtocol(const Metadata *type, |
| const ProtocolDescriptor *protocol, |
| ConformsToProtocol_t *original_conformsToProtocol) |
| { |
| // Register our add image callback if necessary. |
| static OnceToken_t token; |
| SWIFT_ONCE_F(token, registerAddImageCallback, nullptr); |
| |
| // The implementation of swift_conformsToProtocol in Swift 5.0 would return |
| // a false negative answer when asking whether a subclass conforms using |
| // a conformance from a superclass. Work around this by walking up the |
| // superclass chain in cases where the original implementation returns |
| // null. |
| do { |
| auto result = original_conformsToProtocol(type, protocol); |
| if (result) |
| return result; |
| } while ((type = _swift50override_class_getSuperclass(type))); |
| |
| return nullptr; |
| } |