mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This is the only dependency it has on libswiftCore. Looking this up at runtime allows its use in programs that don't link libswiftCore but might eventually load and run Swift code, such as xctest. While we're in there, enable tests in files ending with `.c`. rdar://problem/55274114
145 lines
5.2 KiB
C++
145 lines
5.2 KiB
C++
//===--- 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;
|
|
}
|