mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
3209 lines
113 KiB
C++
3209 lines
113 KiB
C++
//===--- Casting.cpp - Swift Language Dynamic Casting Support -------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Implementations of the dynamic cast runtime functions.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/Basic/Demangle.h"
|
|
#include "swift/Basic/Fallthrough.h"
|
|
#include "swift/Basic/Lazy.h"
|
|
#include "swift/Runtime/Concurrent.h"
|
|
#include "swift/Runtime/Config.h"
|
|
#include "swift/Runtime/Enum.h"
|
|
#include "swift/Runtime/HeapObject.h"
|
|
#include "swift/Runtime/Metadata.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/PointerIntPair.h"
|
|
#include "swift/Runtime/Debug.h"
|
|
#include "ErrorObject.h"
|
|
#include "ExistentialMetadataImpl.h"
|
|
#include "Private.h"
|
|
#include "../SwiftShims/RuntimeShims.h"
|
|
#include "stddef.h"
|
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
#include <mach-o/dyld.h>
|
|
#include <mach-o/getsect.h>
|
|
#elif defined(__ELF__)
|
|
#include <elf.h>
|
|
#include <link.h>
|
|
#endif
|
|
|
|
#include <dlfcn.h>
|
|
#include <cstring>
|
|
#include <mutex>
|
|
#include <atomic>
|
|
#include <type_traits>
|
|
|
|
// FIXME: Clang defines max_align_t in stddef.h since 3.6.
|
|
// Remove this hack when we don't care about older Clangs on all platforms.
|
|
#ifdef __APPLE__
|
|
typedef std::max_align_t swift_max_align_t;
|
|
#else
|
|
typedef long double swift_max_align_t;
|
|
#endif
|
|
|
|
using namespace swift;
|
|
using namespace metadataimpl;
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
//#include <objc/objc-runtime.h>
|
|
#include <objc/NSObject.h>
|
|
#include <objc/runtime.h>
|
|
#include <objc/message.h>
|
|
#include <objc/objc.h>
|
|
|
|
// Aliases for Objective-C runtime entry points.
|
|
const char *class_getName(const ClassMetadata* type) {
|
|
return class_getName(
|
|
reinterpret_cast<Class>(const_cast<ClassMetadata*>(type)));
|
|
}
|
|
|
|
// Aliases for Swift runtime entry points for Objective-C types.
|
|
extern "C" const void *swift_dynamicCastObjCProtocolConditional(
|
|
const void *object,
|
|
size_t numProtocols,
|
|
const ProtocolDescriptor * const *protocols);
|
|
#endif
|
|
|
|
namespace {
|
|
enum class TypeSyntaxLevel {
|
|
/// Any type syntax is valid.
|
|
Type,
|
|
/// Function types must be parenthesized.
|
|
TypeSimple,
|
|
};
|
|
}
|
|
|
|
static void _buildNameForMetadata(const Metadata *type,
|
|
TypeSyntaxLevel level,
|
|
bool qualified,
|
|
std::string &result);
|
|
|
|
static void _buildNominalTypeName(const NominalTypeDescriptor *ntd,
|
|
const Metadata *type,
|
|
bool qualified,
|
|
std::string &result) {
|
|
auto options = Demangle::DemangleOptions();
|
|
options.DisplayDebuggerGeneratedModule = false;
|
|
options.QualifyEntities = qualified;
|
|
|
|
// Demangle the basic type name.
|
|
result += Demangle::demangleTypeAsString(ntd->Name,
|
|
strlen(ntd->Name),
|
|
options);
|
|
|
|
// If generic, demangle the type parameters.
|
|
if (ntd->GenericParams.NumPrimaryParams > 0) {
|
|
result += "<";
|
|
|
|
auto typeBytes = reinterpret_cast<const char *>(type);
|
|
auto genericParam = reinterpret_cast<const Metadata * const *>(
|
|
typeBytes + sizeof(void*) * ntd->GenericParams.Offset);
|
|
for (unsigned i = 0, e = ntd->GenericParams.NumPrimaryParams;
|
|
i < e; ++i, ++genericParam) {
|
|
if (i > 0)
|
|
result += ", ";
|
|
_buildNameForMetadata(*genericParam, TypeSyntaxLevel::Type, qualified,
|
|
result);
|
|
}
|
|
|
|
result += ">";
|
|
}
|
|
}
|
|
|
|
static const char *_getProtocolName(const ProtocolDescriptor *protocol) {
|
|
const char *name = protocol->Name;
|
|
|
|
// An Objective-C protocol's name is unmangled.
|
|
#if SWIFT_OBJC_INTEROP
|
|
if (!protocol->Flags.isSwift())
|
|
return name;
|
|
#endif
|
|
|
|
// Protocol names are emitted with the _Tt prefix so that ObjC can
|
|
// recognize them as mangled Swift names.
|
|
assert(name[0] == '_' && name[1] == 'T' && name[2] == 't');
|
|
return name + 3;
|
|
}
|
|
|
|
static void _buildExistentialTypeName(const ProtocolDescriptorList *protocols,
|
|
bool qualified,
|
|
std::string &result) {
|
|
auto options = Demangle::DemangleOptions();
|
|
options.QualifyEntities = qualified;
|
|
options.DisplayDebuggerGeneratedModule = false;
|
|
|
|
// If there's only one protocol, the existential type name is the protocol
|
|
// name.
|
|
auto descriptors = protocols->getProtocols();
|
|
|
|
if (protocols->NumProtocols == 1) {
|
|
auto name = _getProtocolName(descriptors[0]);
|
|
result += Demangle::demangleTypeAsString(name,
|
|
strlen(name),
|
|
options);
|
|
return;
|
|
}
|
|
|
|
result += "protocol<";
|
|
for (unsigned i = 0, e = protocols->NumProtocols; i < e; ++i) {
|
|
if (i > 0)
|
|
result += ", ";
|
|
auto name = _getProtocolName(descriptors[i]);
|
|
result += Demangle::demangleTypeAsString(name,
|
|
strlen(name),
|
|
options);
|
|
}
|
|
result += ">";
|
|
}
|
|
|
|
static void _buildFunctionTypeName(const FunctionTypeMetadata *func,
|
|
bool qualified,
|
|
std::string &result) {
|
|
|
|
if (func->getNumArguments() == 1) {
|
|
auto firstArgument = func->getArguments()[0].getPointer();
|
|
bool isInout = func->getArguments()[0].getFlag();
|
|
|
|
// This could be a single input tuple, with one or more arguments inside,
|
|
// but guaranteed to not have inout types.
|
|
if (auto tupleMetadata = dyn_cast<TupleTypeMetadata>(firstArgument)) {
|
|
_buildNameForMetadata(tupleMetadata,
|
|
TypeSyntaxLevel::TypeSimple,
|
|
qualified,
|
|
result);
|
|
} else {
|
|
if (isInout)
|
|
result += "inout ";
|
|
|
|
_buildNameForMetadata(firstArgument,
|
|
TypeSyntaxLevel::TypeSimple,
|
|
qualified,
|
|
result);
|
|
}
|
|
} else {
|
|
result += "(";
|
|
for (size_t i = 0; i < func->getNumArguments(); ++i) {
|
|
auto arg = func->getArguments()[i].getPointer();
|
|
bool isInout = func->getArguments()[i].getFlag();
|
|
if (isInout)
|
|
result += "inout ";
|
|
_buildNameForMetadata(arg, TypeSyntaxLevel::TypeSimple,
|
|
qualified, result);
|
|
if (i < func->getNumArguments() - 1) {
|
|
result += ", ";
|
|
}
|
|
}
|
|
result += ")";
|
|
}
|
|
|
|
if (func->throws()) {
|
|
result += " throws";
|
|
}
|
|
|
|
result += " -> ";
|
|
_buildNameForMetadata(func->ResultType,
|
|
TypeSyntaxLevel::Type,
|
|
qualified,
|
|
result);
|
|
}
|
|
|
|
// Build a user-comprehensible name for a type.
|
|
static void _buildNameForMetadata(const Metadata *type,
|
|
TypeSyntaxLevel level,
|
|
bool qualified,
|
|
std::string &result) {
|
|
auto options = Demangle::DemangleOptions();
|
|
options.DisplayDebuggerGeneratedModule = false;
|
|
|
|
switch (type->getKind()) {
|
|
case MetadataKind::Class: {
|
|
auto classType = static_cast<const ClassMetadata *>(type);
|
|
#if SWIFT_OBJC_INTEROP
|
|
// Look through artificial subclasses.
|
|
while (classType->isTypeMetadata() && classType->isArtificialSubclass())
|
|
classType = classType->SuperClass;
|
|
|
|
// Ask the Objective-C runtime to name ObjC classes.
|
|
if (!classType->isTypeMetadata()) {
|
|
result += class_getName(classType);
|
|
return;
|
|
}
|
|
#endif
|
|
return _buildNominalTypeName(classType->getDescription(),
|
|
classType, qualified,
|
|
result);
|
|
}
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Struct: {
|
|
auto structType = static_cast<const StructMetadata *>(type);
|
|
return _buildNominalTypeName(structType->Description,
|
|
type, qualified, result);
|
|
}
|
|
case MetadataKind::ObjCClassWrapper: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
auto objcWrapper = static_cast<const ObjCClassWrapperMetadata *>(type);
|
|
result += class_getName(objcWrapper->Class);
|
|
#else
|
|
assert(false && "no ObjC interop");
|
|
#endif
|
|
return;
|
|
}
|
|
case MetadataKind::ForeignClass: {
|
|
auto foreign = static_cast<const ForeignClassMetadata *>(type);
|
|
const char *name = foreign->getName();
|
|
size_t len = strlen(name);
|
|
result += Demangle::demangleTypeAsString(name, len, options);
|
|
return;
|
|
}
|
|
case MetadataKind::Existential: {
|
|
auto exis = static_cast<const ExistentialTypeMetadata *>(type);
|
|
_buildExistentialTypeName(&exis->Protocols, qualified, result);
|
|
return;
|
|
}
|
|
case MetadataKind::ExistentialMetatype: {
|
|
auto metatype = static_cast<const ExistentialMetatypeMetadata *>(type);
|
|
_buildNameForMetadata(metatype->InstanceType, TypeSyntaxLevel::TypeSimple,
|
|
qualified,
|
|
result);
|
|
result += ".Type";
|
|
return;
|
|
}
|
|
case MetadataKind::Function: {
|
|
if (level >= TypeSyntaxLevel::TypeSimple)
|
|
result += "(";
|
|
|
|
auto func = static_cast<const FunctionTypeMetadata *>(type);
|
|
|
|
switch (func->getConvention()) {
|
|
case FunctionMetadataConvention::Swift:
|
|
break;
|
|
case FunctionMetadataConvention::Thin:
|
|
result += "@convention(thin) ";
|
|
break;
|
|
case FunctionMetadataConvention::Block:
|
|
result += "@convention(block) ";
|
|
break;
|
|
case FunctionMetadataConvention::CFunctionPointer:
|
|
result += "@convention(c) ";
|
|
break;
|
|
}
|
|
|
|
_buildFunctionTypeName(func, qualified, result);
|
|
|
|
if (level >= TypeSyntaxLevel::TypeSimple)
|
|
result += ")";
|
|
return;
|
|
}
|
|
case MetadataKind::Metatype: {
|
|
auto metatype = static_cast<const MetatypeMetadata *>(type);
|
|
_buildNameForMetadata(metatype->InstanceType, TypeSyntaxLevel::TypeSimple,
|
|
qualified, result);
|
|
if (metatype->InstanceType->isAnyExistentialType())
|
|
result += ".Protocol";
|
|
else
|
|
result += ".Type";
|
|
return;
|
|
}
|
|
case MetadataKind::Tuple: {
|
|
auto tuple = static_cast<const TupleTypeMetadata *>(type);
|
|
result += "(";
|
|
auto elts = tuple->getElements();
|
|
for (unsigned i = 0, e = tuple->NumElements; i < e; ++i) {
|
|
if (i > 0)
|
|
result += ", ";
|
|
_buildNameForMetadata(elts[i].Type, TypeSyntaxLevel::Type, qualified,
|
|
result);
|
|
}
|
|
result += ")";
|
|
return;
|
|
}
|
|
case MetadataKind::Opaque: {
|
|
// TODO
|
|
result += "<<<opaque type>>>";
|
|
return;
|
|
}
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
break;
|
|
}
|
|
result += "<<<invalid type>>>";
|
|
}
|
|
|
|
/// Return a user-comprehensible name for the given type.
|
|
std::string swift::nameForMetadata(const Metadata *type,
|
|
bool qualified) {
|
|
std::string result;
|
|
_buildNameForMetadata(type, TypeSyntaxLevel::Type, qualified, result);
|
|
return result;
|
|
}
|
|
|
|
extern "C"
|
|
TwoWordPair<const char *, uintptr_t>::Return
|
|
swift_getTypeName(const Metadata *type, bool qualified) {
|
|
using Pair = TwoWordPair<const char *, uintptr_t>;
|
|
using Key = llvm::PointerIntPair<const Metadata *, 1, bool>;
|
|
|
|
static pthread_rwlock_t TypeNameCacheLock = PTHREAD_RWLOCK_INITIALIZER;
|
|
static Lazy<llvm::DenseMap<Key, std::pair<const char *, size_t>>>
|
|
TypeNameCache;
|
|
|
|
Key key(type, qualified);
|
|
auto &cache = TypeNameCache.get();
|
|
|
|
pthread_rwlock_rdlock(&TypeNameCacheLock);
|
|
auto found = cache.find(key);
|
|
if (found != cache.end()) {
|
|
auto result = found->second;
|
|
pthread_rwlock_unlock(&TypeNameCacheLock);
|
|
return Pair{result.first, result.second};
|
|
}
|
|
|
|
pthread_rwlock_unlock(&TypeNameCacheLock);
|
|
pthread_rwlock_wrlock(&TypeNameCacheLock);
|
|
// Someone may have beaten us to the write lock.
|
|
found = cache.find(key);
|
|
if (found != cache.end()) {
|
|
auto result = found->second;
|
|
pthread_rwlock_unlock(&TypeNameCacheLock);
|
|
return Pair{result.first, result.second};
|
|
}
|
|
|
|
// Build the metadata name.
|
|
auto name = nameForMetadata(type, qualified);
|
|
// Copy it to memory we can reference forever.
|
|
auto size = name.size();
|
|
auto result = (char*)malloc(size + 1);
|
|
memcpy(result, name.data(), size);
|
|
result[size] = 0;
|
|
cache.insert({key, {result, size}});
|
|
pthread_rwlock_unlock(&TypeNameCacheLock);
|
|
return Pair{result, size};
|
|
}
|
|
|
|
/// Report a dynamic cast failure.
|
|
// This is noinline with asm("") to preserve this frame in stack traces.
|
|
// We want "dynamicCastFailure" to appear in crash logs even we crash
|
|
// during the diagnostic because some Metadata is invalid.
|
|
LLVM_ATTRIBUTE_NORETURN
|
|
LLVM_ATTRIBUTE_NOINLINE
|
|
void
|
|
swift::swift_dynamicCastFailure(const void *sourceType, const char *sourceName,
|
|
const void *targetType, const char *targetName,
|
|
const char *message) {
|
|
asm("");
|
|
|
|
swift::fatalError("Could not cast value of type '%s' (%p) to '%s' (%p)%s%s\n",
|
|
sourceName, sourceType,
|
|
targetName, targetType,
|
|
message ? ": " : ".",
|
|
message ? message : "");
|
|
}
|
|
|
|
LLVM_ATTRIBUTE_NORETURN
|
|
void
|
|
swift::swift_dynamicCastFailure(const Metadata *sourceType,
|
|
const Metadata *targetType,
|
|
const char *message) {
|
|
std::string sourceName = nameForMetadata(sourceType);
|
|
std::string targetName = nameForMetadata(targetType);
|
|
|
|
swift_dynamicCastFailure(sourceType, sourceName.c_str(),
|
|
targetType, targetName.c_str(), message);
|
|
}
|
|
|
|
|
|
/// Report a corrupted type object.
|
|
LLVM_ATTRIBUTE_NORETURN
|
|
LLVM_ATTRIBUTE_ALWAYS_INLINE // Minimize trashed registers
|
|
static void _failCorruptType(const Metadata *type) {
|
|
swift::crash("Corrupt Swift type object");
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
// Objective-c bridging helpers.
|
|
namespace {
|
|
struct _ObjectiveCBridgeableWitnessTable;
|
|
}
|
|
static const _ObjectiveCBridgeableWitnessTable *
|
|
findBridgeWitness(const Metadata *T);
|
|
|
|
static bool _dynamicCastValueToClassViaObjCBridgeable(
|
|
OpaqueValue *dest,
|
|
OpaqueValue *src,
|
|
const Metadata *srcType,
|
|
const Metadata *targetType,
|
|
const _ObjectiveCBridgeableWitnessTable *srcBridgeWitness,
|
|
DynamicCastFlags flags);
|
|
|
|
static bool _dynamicCastValueToClassExistentialViaObjCBridgeable(
|
|
OpaqueValue *dest,
|
|
OpaqueValue *src,
|
|
const Metadata *srcType,
|
|
const ExistentialTypeMetadata *targetType,
|
|
const _ObjectiveCBridgeableWitnessTable *srcBridgeWitness,
|
|
DynamicCastFlags flags);
|
|
|
|
static bool _dynamicCastClassToValueViaObjCBridgeable(
|
|
OpaqueValue *dest,
|
|
OpaqueValue *src,
|
|
const Metadata *srcType,
|
|
const Metadata *targetType,
|
|
const _ObjectiveCBridgeableWitnessTable *targetBridgeWitness,
|
|
DynamicCastFlags flags);
|
|
#endif
|
|
|
|
/// A convenient method for failing out of a dynamic cast.
|
|
static bool _fail(OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *targetType, DynamicCastFlags flags) {
|
|
if (flags & DynamicCastFlags::Unconditional)
|
|
swift_dynamicCastFailure(srcType, targetType);
|
|
if (flags & DynamicCastFlags::DestroyOnFailure)
|
|
srcType->vw_destroy(srcValue);
|
|
return false;
|
|
}
|
|
|
|
/// A convenient method for succeeding at a dynamic cast.
|
|
static bool _succeed(OpaqueValue *dest, OpaqueValue *src,
|
|
const Metadata *srcType, DynamicCastFlags flags) {
|
|
if (flags & DynamicCastFlags::TakeOnSuccess) {
|
|
srcType->vw_initializeWithTake(dest, src);
|
|
} else {
|
|
srcType->vw_initializeWithCopy(dest, src);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Dynamically cast a class metatype to a Swift class metatype.
|
|
static const ClassMetadata *
|
|
_dynamicCastClassMetatype(const ClassMetadata *sourceType,
|
|
const ClassMetadata *targetType) {
|
|
do {
|
|
if (sourceType == targetType) {
|
|
return sourceType;
|
|
}
|
|
sourceType = _swift_getSuperclass(sourceType);
|
|
} while (sourceType);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// Dynamically cast a class instance to a Swift class type.
|
|
const void *
|
|
swift::swift_dynamicCastClass(const void *object,
|
|
const ClassMetadata *targetType) {
|
|
#if SWIFT_OBJC_INTEROP
|
|
assert(!targetType->isPureObjC());
|
|
|
|
// Swift native classes never have a tagged-pointer representation.
|
|
if (isObjCTaggedPointerOrNull(object)) {
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
|
|
auto isa = _swift_getClassOfAllocated(object);
|
|
|
|
if (_dynamicCastClassMetatype(isa, targetType))
|
|
return object;
|
|
return nullptr;
|
|
}
|
|
|
|
/// Dynamically cast a class object to a Swift class type.
|
|
const void *
|
|
swift::swift_dynamicCastClassUnconditional(const void *object,
|
|
const ClassMetadata *targetType) {
|
|
auto value = swift_dynamicCastClass(object, targetType);
|
|
if (value) return value;
|
|
|
|
swift_dynamicCastFailure(_swift_getClass(object), targetType);
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
static bool _unknownClassConformsToObjCProtocol(const OpaqueValue *value,
|
|
const ProtocolDescriptor *protocol) {
|
|
const void *object
|
|
= *reinterpret_cast<const void * const *>(value);
|
|
return swift_dynamicCastObjCProtocolConditional(object, 1, &protocol);
|
|
}
|
|
#endif
|
|
|
|
/// Check whether a type conforms to a protocol.
|
|
///
|
|
/// \param value - can be null, in which case the question should
|
|
/// be answered abstractly if possible
|
|
/// \param conformance - if non-null, and the protocol requires a
|
|
/// witness table, and the type implements the protocol, the witness
|
|
/// table will be placed here
|
|
static bool _conformsToProtocol(const OpaqueValue *value,
|
|
const Metadata *type,
|
|
const ProtocolDescriptor *protocol,
|
|
const WitnessTable **conformance) {
|
|
// Handle AnyObject directly.
|
|
if (protocol->Flags.getSpecialProtocol() == SpecialProtocol::AnyObject) {
|
|
switch (type->getKind()) {
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
case MetadataKind::ForeignClass:
|
|
// Classes conform to AnyObject.
|
|
return true;
|
|
|
|
case MetadataKind::Existential: {
|
|
auto sourceExistential = cast<ExistentialTypeMetadata>(type);
|
|
// The existential conforms to AnyObject if it's class-constrained.
|
|
// FIXME: It also must not carry witness tables.
|
|
return sourceExistential->isClassBounded();
|
|
}
|
|
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
return false;
|
|
}
|
|
_failCorruptType(type);
|
|
}
|
|
|
|
// Look up the witness table for protocols that need them.
|
|
if (protocol->Flags.needsWitnessTable()) {
|
|
auto witness = swift_conformsToProtocol(type, protocol);
|
|
if (!witness)
|
|
return false;
|
|
if (conformance)
|
|
*conformance = witness;
|
|
return true;
|
|
}
|
|
|
|
// For Objective-C protocols, check whether we have a class that
|
|
// conforms to the given protocol.
|
|
switch (type->getKind()) {
|
|
case MetadataKind::Class:
|
|
#if SWIFT_OBJC_INTEROP
|
|
if (value) {
|
|
return _unknownClassConformsToObjCProtocol(value, protocol);
|
|
} else {
|
|
return _swift_classConformsToObjCProtocol(type, protocol);
|
|
}
|
|
#endif
|
|
return false;
|
|
|
|
case MetadataKind::ObjCClassWrapper: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
if (value) {
|
|
return _unknownClassConformsToObjCProtocol(value, protocol);
|
|
} else {
|
|
auto wrapper = cast<ObjCClassWrapperMetadata>(type);
|
|
return _swift_classConformsToObjCProtocol(wrapper->Class, protocol);
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
case MetadataKind::ForeignClass:
|
|
#if SWIFT_OBJC_INTEROP
|
|
if (value)
|
|
return _unknownClassConformsToObjCProtocol(value, protocol);
|
|
return false;
|
|
#else
|
|
_failCorruptType(type);
|
|
#endif
|
|
|
|
case MetadataKind::Existential: // FIXME
|
|
case MetadataKind::ExistentialMetatype: // FIXME
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Check whether a type conforms to the given protocols, filling in a
|
|
/// list of conformances.
|
|
static bool _conformsToProtocols(const OpaqueValue *value,
|
|
const Metadata *type,
|
|
const ProtocolDescriptorList &protocols,
|
|
const WitnessTable **conformances) {
|
|
for (unsigned i = 0, n = protocols.NumProtocols; i != n; ++i) {
|
|
const ProtocolDescriptor *protocol = protocols[i];
|
|
if (!_conformsToProtocol(value, type, protocol, conformances))
|
|
return false;
|
|
if (protocol->Flags.needsWitnessTable()) {
|
|
assert(*conformances != nullptr);
|
|
++conformances;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool shouldDeallocateSource(bool castSucceeded, DynamicCastFlags flags) {
|
|
return (castSucceeded && (flags & DynamicCastFlags::TakeOnSuccess)) ||
|
|
(!castSucceeded && (flags & DynamicCastFlags::DestroyOnFailure));
|
|
}
|
|
|
|
/// Given that a cast operation is complete, maybe deallocate an
|
|
/// opaque existential value.
|
|
static void _maybeDeallocateOpaqueExistential(OpaqueValue *srcExistential,
|
|
bool castSucceeded,
|
|
DynamicCastFlags flags) {
|
|
if (shouldDeallocateSource(castSucceeded, flags)) {
|
|
auto container =
|
|
reinterpret_cast<OpaqueExistentialContainer *>(srcExistential);
|
|
container->Type->vw_deallocateBuffer(&container->Buffer);
|
|
}
|
|
}
|
|
|
|
/// Given a possibly-existential value, find its dynamic type and the
|
|
/// address of its storage.
|
|
static void findDynamicValueAndType(OpaqueValue *value, const Metadata *type,
|
|
OpaqueValue *&outValue,
|
|
const Metadata *&outType,
|
|
bool &inoutCanTake) {
|
|
switch (type->getKind()) {
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
case MetadataKind::ForeignClass: {
|
|
// TODO: avoid unnecessary repeat lookup of
|
|
// ObjCClassWrapper/ForeignClass when the type matches.
|
|
outValue = value;
|
|
outType = swift_getObjectType(*reinterpret_cast<HeapObject**>(value));
|
|
return;
|
|
}
|
|
|
|
case MetadataKind::Existential: {
|
|
auto existentialType = cast<ExistentialTypeMetadata>(type);
|
|
|
|
switch (existentialType->getRepresentation()) {
|
|
case ExistentialTypeRepresentation::Class: {
|
|
// Class existentials can't recursively contain existential containers,
|
|
// so we can fast-path by not bothering to recur.
|
|
auto existential =
|
|
reinterpret_cast<ClassExistentialContainer*>(value);
|
|
outValue = (OpaqueValue*) &existential->Value;
|
|
outType = swift_getObjectType((HeapObject*) existential->Value);
|
|
return;
|
|
}
|
|
|
|
case ExistentialTypeRepresentation::Opaque:
|
|
case ExistentialTypeRepresentation::ErrorType: {
|
|
OpaqueValue *innerValue
|
|
= existentialType->projectValue(value);
|
|
const Metadata *innerType = existentialType->getDynamicType(value);
|
|
inoutCanTake &= existentialType->mayTakeValue(value);
|
|
return findDynamicValueAndType(innerValue, innerType,
|
|
outValue, outType, inoutCanTake);
|
|
}
|
|
}
|
|
}
|
|
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::ExistentialMetatype: {
|
|
auto storedType = *(const Metadata **) value;
|
|
outValue = value;
|
|
outType = swift_getMetatypeMetadata(storedType);
|
|
return;
|
|
}
|
|
|
|
// Non-polymorphic types.
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
outValue = value;
|
|
outType = type;
|
|
return;
|
|
}
|
|
_failCorruptType(type);
|
|
}
|
|
|
|
extern "C" const Metadata *
|
|
swift::swift_getDynamicType(OpaqueValue *value, const Metadata *self) {
|
|
OpaqueValue *outValue;
|
|
const Metadata *outType;
|
|
bool canTake = false;
|
|
findDynamicValueAndType(value, self, outValue, outType, canTake);
|
|
return outType;
|
|
}
|
|
|
|
/// Given a possibly-existential value, deallocate any buffer in its storage.
|
|
static void deallocateDynamicValue(OpaqueValue *value, const Metadata *type) {
|
|
switch (type->getKind()) {
|
|
case MetadataKind::Existential: {
|
|
auto existentialType = cast<ExistentialTypeMetadata>(type);
|
|
|
|
switch (existentialType->getRepresentation()) {
|
|
case ExistentialTypeRepresentation::Class:
|
|
// Nothing to clean up.
|
|
break;
|
|
|
|
case ExistentialTypeRepresentation::ErrorType:
|
|
// TODO: We could clean up from a reclaimed uniquely-referenced error box.
|
|
break;
|
|
|
|
case ExistentialTypeRepresentation::Opaque:
|
|
auto existential =
|
|
reinterpret_cast<OpaqueExistentialContainer*>(value);
|
|
|
|
// Handle the possibility of nested existentials.
|
|
OpaqueValue *existentialValue =
|
|
existential->Type->vw_projectBuffer(&existential->Buffer);
|
|
deallocateDynamicValue(existentialValue, existential->Type);
|
|
|
|
// Deallocate the buffer.
|
|
existential->Type->vw_deallocateBuffer(&existential->Buffer);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// None of the rest of these require deallocation.
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ForeignClass:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
return;
|
|
}
|
|
_failCorruptType(type);
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
extern "C" id
|
|
swift_dynamicCastMetatypeToObjectConditional(const Metadata *metatype) {
|
|
switch (metatype->getKind()) {
|
|
case MetadataKind::Class:
|
|
// Swift classes are objects in and of themselves.
|
|
return (id)metatype;
|
|
|
|
case MetadataKind::ObjCClassWrapper: {
|
|
// Unwrap ObjC class objects.
|
|
auto wrapper = static_cast<const ObjCClassWrapperMetadata*>(metatype);
|
|
return (id)wrapper->getClassObject();
|
|
}
|
|
|
|
// Other kinds of metadata don't cast to AnyObject.
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Tuple:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::Existential:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::ForeignClass:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
extern "C" id
|
|
swift_dynamicCastMetatypeToObjectUnconditional(const Metadata *metatype) {
|
|
switch (metatype->getKind()) {
|
|
case MetadataKind::Class:
|
|
// Swift classes are objects in and of themselves.
|
|
return (id)metatype;
|
|
|
|
case MetadataKind::ObjCClassWrapper: {
|
|
// Unwrap ObjC class objects.
|
|
auto wrapper = static_cast<const ObjCClassWrapperMetadata*>(metatype);
|
|
return (id)wrapper->getClassObject();
|
|
}
|
|
|
|
// Other kinds of metadata don't cast to AnyObject.
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Tuple:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::Existential:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::ForeignClass:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject: {
|
|
std::string sourceName = nameForMetadata(metatype);
|
|
swift_dynamicCastFailure(metatype, sourceName.c_str(),
|
|
nullptr, "AnyObject",
|
|
"only class metatypes can be converted to AnyObject");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/// Perform a dynamic cast to an existential type.
|
|
static bool _dynamicCastToExistential(OpaqueValue *dest,
|
|
OpaqueValue *src,
|
|
const Metadata *srcType,
|
|
const ExistentialTypeMetadata *targetType,
|
|
DynamicCastFlags flags) {
|
|
#if SWIFT_OBJC_INTEROP
|
|
// This variable's lifetime needs to be for the whole function, but is
|
|
// only valid with Objective-C interop enabled.
|
|
id tmp;
|
|
#endif
|
|
// Find the actual type of the source.
|
|
OpaqueValue *srcDynamicValue;
|
|
const Metadata *srcDynamicType;
|
|
bool canTake = true;
|
|
findDynamicValueAndType(src, srcType, srcDynamicValue, srcDynamicType,
|
|
canTake);
|
|
|
|
auto maybeDeallocateSourceAfterSuccess = [&] {
|
|
if (shouldDeallocateSource(/*succeeded*/ true, flags)) {
|
|
// If we're able to take the dynamic value, then clean up any leftover
|
|
// buffers it may have been contained in.
|
|
if (canTake && src != srcDynamicValue)
|
|
deallocateDynamicValue(src, srcType);
|
|
// Otherwise, deallocate the original value wholesale if we couldn't take
|
|
// it.
|
|
else if (!canTake)
|
|
srcType->vw_destroy(src);
|
|
}
|
|
};
|
|
|
|
// The representation of an existential is different for some protocols.
|
|
switch (targetType->getRepresentation()) {
|
|
case ExistentialTypeRepresentation::Class: {
|
|
auto destExistential =
|
|
reinterpret_cast<ClassExistentialContainer*>(dest);
|
|
|
|
// If the source type is a value type, it cannot possibly conform
|
|
// to a class-bounded protocol.
|
|
switch (srcDynamicType->getKind()) {
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::Metatype: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
// Class metadata can be used as an object when ObjC interop is available.
|
|
auto metatypePtr = reinterpret_cast<const Metadata **>(src);
|
|
auto metatype = *metatypePtr;
|
|
tmp = swift_dynamicCastMetatypeToObjectConditional(metatype);
|
|
// If the cast succeeded, use the result value as the class instance
|
|
// below.
|
|
if (tmp) {
|
|
srcDynamicValue = reinterpret_cast<OpaqueValue*>(&tmp);
|
|
srcDynamicType = reinterpret_cast<const Metadata*>(tmp);
|
|
break;
|
|
}
|
|
#endif
|
|
// Otherwise, metatypes aren't class objects.
|
|
return _fail(src, srcType, targetType, flags);
|
|
}
|
|
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
case MetadataKind::ForeignClass:
|
|
case MetadataKind::Existential:
|
|
// Handle these cases below.
|
|
break;
|
|
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
#if SWIFT_OBJC_INTEROP
|
|
// If the source type is bridged to Objective-C, try to bridge.
|
|
if (auto srcBridgeWitness = findBridgeWitness(srcDynamicType)) {
|
|
DynamicCastFlags subFlags
|
|
= flags - (DynamicCastFlags::TakeOnSuccess |
|
|
DynamicCastFlags::DestroyOnFailure);
|
|
bool success = _dynamicCastValueToClassExistentialViaObjCBridgeable(
|
|
dest,
|
|
srcDynamicValue,
|
|
srcDynamicType,
|
|
targetType,
|
|
srcBridgeWitness,
|
|
subFlags);
|
|
|
|
// Destroy the source value, since we avoided taking or destroying
|
|
// it above.
|
|
if (shouldDeallocateSource(success, flags)) {
|
|
srcType->vw_destroy(src);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Tuple:
|
|
// Will never succeed.
|
|
return _fail(src, srcType, targetType, flags);
|
|
}
|
|
|
|
// Check for protocol conformances and fill in the witness tables.
|
|
if (!_conformsToProtocols(srcDynamicValue, srcDynamicType,
|
|
targetType->Protocols,
|
|
destExistential->getWitnessTables())) {
|
|
return _fail(srcDynamicValue, srcDynamicType, targetType, flags);
|
|
}
|
|
|
|
auto object = *(reinterpret_cast<HeapObject**>(srcDynamicValue));
|
|
destExistential->Value = object;
|
|
if (!canTake || !(flags & DynamicCastFlags::TakeOnSuccess)) {
|
|
swift_retain(object);
|
|
}
|
|
maybeDeallocateSourceAfterSuccess();
|
|
return true;
|
|
}
|
|
case ExistentialTypeRepresentation::Opaque: {
|
|
auto destExistential =
|
|
reinterpret_cast<OpaqueExistentialContainer*>(dest);
|
|
|
|
// Check for protocol conformances and fill in the witness tables.
|
|
if (!_conformsToProtocols(srcDynamicValue, srcDynamicType,
|
|
targetType->Protocols,
|
|
destExistential->getWitnessTables()))
|
|
return _fail(srcDynamicValue, srcDynamicType, targetType, flags);
|
|
|
|
// Fill in the type and value.
|
|
destExistential->Type = srcDynamicType;
|
|
if (canTake && (flags & DynamicCastFlags::TakeOnSuccess)) {
|
|
srcDynamicType->vw_initializeBufferWithTake(&destExistential->Buffer,
|
|
srcDynamicValue);
|
|
} else {
|
|
srcDynamicType->vw_initializeBufferWithCopy(&destExistential->Buffer,
|
|
srcDynamicValue);
|
|
}
|
|
maybeDeallocateSourceAfterSuccess();
|
|
return true;
|
|
}
|
|
case ExistentialTypeRepresentation::ErrorType: {
|
|
auto destBoxAddr =
|
|
reinterpret_cast<SwiftError**>(dest);
|
|
// Check for the ErrorType protocol conformance, which should be the only
|
|
// one we need.
|
|
assert(targetType->Protocols.NumProtocols == 1);
|
|
const WitnessTable *errorWitness;
|
|
if (!_conformsToProtocols(srcDynamicValue, srcDynamicType,
|
|
targetType->Protocols,
|
|
&errorWitness))
|
|
return _fail(srcDynamicValue, srcDynamicType, targetType, flags);
|
|
|
|
BoxPair destBox = swift_allocError(srcDynamicType, errorWitness,
|
|
srcDynamicValue,
|
|
/*isTake*/ canTake && (flags & DynamicCastFlags::TakeOnSuccess));
|
|
*destBoxAddr = reinterpret_cast<SwiftError*>(destBox.first);
|
|
maybeDeallocateSourceAfterSuccess();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
static const void *
|
|
_dynamicCastUnknownClassToExistential(const void *object,
|
|
const ExistentialTypeMetadata *targetType) {
|
|
for (unsigned i = 0, e = targetType->Protocols.NumProtocols; i < e; ++i) {
|
|
const ProtocolDescriptor *protocol = targetType->Protocols[i];
|
|
|
|
switch (protocol->Flags.getDispatchStrategy()) {
|
|
case ProtocolDispatchStrategy::Swift:
|
|
// If the target existential requires witness tables, we can't do this cast.
|
|
// The result type would not have a single-refcounted-pointer rep.
|
|
return nullptr;
|
|
case ProtocolDispatchStrategy::ObjC:
|
|
#if SWIFT_OBJC_INTEROP
|
|
// All classes conform to AnyObject.
|
|
if (protocol->Flags.getSpecialProtocol() == SpecialProtocol::AnyObject)
|
|
break;
|
|
|
|
if (!_swift_objectConformsToObjCProtocol(object, protocol))
|
|
return nullptr;
|
|
break;
|
|
#else
|
|
assert(false && "ObjC interop disabled?!");
|
|
return nullptr;
|
|
#endif
|
|
case ProtocolDispatchStrategy::Empty:
|
|
// The only non-@objc, non-witness-table-requiring protocol should be
|
|
// AnyObject for now.
|
|
assert(protocol->Flags.getSpecialProtocol() == SpecialProtocol::AnyObject
|
|
&& "swift protocols besides AnyObject should always require a "
|
|
"witness table");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return object;
|
|
}
|
|
|
|
/// Perform a dynamic class of some sort of class instance to some
|
|
/// sort of class type.
|
|
const void *
|
|
swift::swift_dynamicCastUnknownClass(const void *object,
|
|
const Metadata *targetType) {
|
|
switch (targetType->getKind()) {
|
|
case MetadataKind::Class: {
|
|
auto targetClassType = static_cast<const ClassMetadata *>(targetType);
|
|
return swift_dynamicCastClass(object, targetClassType);
|
|
}
|
|
|
|
case MetadataKind::ObjCClassWrapper: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
auto targetClassType
|
|
= static_cast<const ObjCClassWrapperMetadata *>(targetType)->Class;
|
|
return swift_dynamicCastObjCClass(object, targetClassType);
|
|
#else
|
|
_failCorruptType(targetType);
|
|
#endif
|
|
}
|
|
|
|
case MetadataKind::ForeignClass: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
auto targetClassType = static_cast<const ForeignClassMetadata*>(targetType);
|
|
return swift_dynamicCastForeignClass(object, targetClassType);
|
|
#else
|
|
_failCorruptType(targetType);
|
|
#endif
|
|
}
|
|
|
|
case MetadataKind::Existential: {
|
|
return _dynamicCastUnknownClassToExistential(object,
|
|
static_cast<const ExistentialTypeMetadata *>(targetType));
|
|
}
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
return nullptr;
|
|
}
|
|
_failCorruptType(targetType);
|
|
}
|
|
|
|
/// Perform a dynamic class of some sort of class instance to some
|
|
/// sort of class type.
|
|
const void *
|
|
swift::swift_dynamicCastUnknownClassUnconditional(const void *object,
|
|
const Metadata *targetType) {
|
|
switch (targetType->getKind()) {
|
|
case MetadataKind::Class: {
|
|
auto targetClassType = static_cast<const ClassMetadata *>(targetType);
|
|
return swift_dynamicCastClassUnconditional(object, targetClassType);
|
|
}
|
|
|
|
case MetadataKind::ObjCClassWrapper: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
auto targetClassType
|
|
= static_cast<const ObjCClassWrapperMetadata *>(targetType)->Class;
|
|
return swift_dynamicCastObjCClassUnconditional(object, targetClassType);
|
|
#else
|
|
_failCorruptType(targetType);
|
|
#endif
|
|
}
|
|
|
|
case MetadataKind::ForeignClass: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
auto targetClassType = static_cast<const ForeignClassMetadata*>(targetType);
|
|
return swift_dynamicCastForeignClassUnconditional(object, targetClassType);
|
|
#else
|
|
_failCorruptType(targetType);
|
|
#endif
|
|
}
|
|
|
|
case MetadataKind::Existential: {
|
|
// We can cast to ObjC existentials. Non-ObjC existentials don't have
|
|
// a single-refcounted-pointer representation.
|
|
if (auto result = _dynamicCastUnknownClassToExistential(object,
|
|
static_cast<const ExistentialTypeMetadata *>(targetType)))
|
|
return result;
|
|
|
|
swift_dynamicCastFailure(_swift_getClass(object), targetType);
|
|
}
|
|
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
swift_dynamicCastFailure(_swift_getClass(object), targetType);
|
|
}
|
|
_failCorruptType(targetType);
|
|
}
|
|
|
|
const Metadata *
|
|
swift::swift_dynamicCastMetatype(const Metadata *sourceType,
|
|
const Metadata *targetType) {
|
|
auto origSourceType = sourceType;
|
|
|
|
switch (targetType->getKind()) {
|
|
case MetadataKind::ObjCClassWrapper:
|
|
// Get the actual class object.
|
|
targetType = static_cast<const ObjCClassWrapperMetadata*>(targetType)
|
|
->Class;
|
|
SWIFT_FALLTHROUGH;
|
|
case MetadataKind::Class:
|
|
// The source value must also be a class; otherwise the cast fails.
|
|
switch (sourceType->getKind()) {
|
|
case MetadataKind::ObjCClassWrapper:
|
|
// Get the actual class object.
|
|
sourceType = static_cast<const ObjCClassWrapperMetadata*>(sourceType)
|
|
->Class;
|
|
SWIFT_FALLTHROUGH;
|
|
case MetadataKind::Class: {
|
|
// Check if the source is a subclass of the target.
|
|
#if SWIFT_OBJC_INTEROP
|
|
// We go through ObjC lookup to deal with potential runtime magic in ObjC
|
|
// land.
|
|
if (swift_dynamicCastObjCClassMetatype((const ClassMetadata*)sourceType,
|
|
(const ClassMetadata*)targetType))
|
|
return origSourceType;
|
|
#else
|
|
if (_dynamicCastClassMetatype((const ClassMetadata*)sourceType,
|
|
(const ClassMetadata*)targetType))
|
|
return origSourceType;
|
|
#endif
|
|
return nullptr;
|
|
}
|
|
case MetadataKind::ForeignClass: {
|
|
// Check if the source is a subclass of the target.
|
|
if (swift_dynamicCastForeignClassMetatype(
|
|
(const ClassMetadata*)sourceType,
|
|
(const ClassMetadata*)targetType))
|
|
return origSourceType;
|
|
return nullptr;
|
|
}
|
|
|
|
case MetadataKind::Existential:
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
return nullptr;
|
|
}
|
|
break;
|
|
|
|
case MetadataKind::ForeignClass:
|
|
switch (sourceType->getKind()) {
|
|
case MetadataKind::ObjCClassWrapper:
|
|
// Get the actual class object.
|
|
sourceType = static_cast<const ObjCClassWrapperMetadata*>(sourceType)
|
|
->Class;
|
|
SWIFT_FALLTHROUGH;
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ForeignClass:
|
|
// Check if the source is a subclass of the target.
|
|
if (swift_dynamicCastForeignClassMetatype(
|
|
(const ClassMetadata*)sourceType,
|
|
(const ClassMetadata*)targetType))
|
|
return origSourceType;
|
|
return nullptr;
|
|
case MetadataKind::Existential:
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
return nullptr;
|
|
}
|
|
break;
|
|
|
|
case MetadataKind::Existential:
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
// The cast succeeds only if the metadata pointers are statically
|
|
// equivalent.
|
|
if (sourceType != targetType)
|
|
return nullptr;
|
|
return origSourceType;
|
|
}
|
|
}
|
|
|
|
const Metadata *
|
|
swift::swift_dynamicCastMetatypeUnconditional(const Metadata *sourceType,
|
|
const Metadata *targetType) {
|
|
auto origSourceType = sourceType;
|
|
|
|
switch (targetType->getKind()) {
|
|
case MetadataKind::ObjCClassWrapper:
|
|
// Get the actual class object.
|
|
targetType = static_cast<const ObjCClassWrapperMetadata*>(targetType)
|
|
->Class;
|
|
SWIFT_FALLTHROUGH;
|
|
case MetadataKind::Class:
|
|
// The source value must also be a class; otherwise the cast fails.
|
|
switch (sourceType->getKind()) {
|
|
case MetadataKind::ObjCClassWrapper:
|
|
// Get the actual class object.
|
|
sourceType = static_cast<const ObjCClassWrapperMetadata*>(sourceType)
|
|
->Class;
|
|
SWIFT_FALLTHROUGH;
|
|
case MetadataKind::Class: {
|
|
// Check if the source is a subclass of the target.
|
|
#if SWIFT_OBJC_INTEROP
|
|
// We go through ObjC lookup to deal with potential runtime magic in ObjC
|
|
// land.
|
|
swift_dynamicCastObjCClassMetatypeUnconditional(
|
|
(const ClassMetadata*)sourceType,
|
|
(const ClassMetadata*)targetType);
|
|
#else
|
|
if (!_dynamicCastClassMetatype((const ClassMetadata*)sourceType,
|
|
(const ClassMetadata*)targetType))
|
|
swift_dynamicCastFailure(sourceType, targetType);
|
|
#endif
|
|
// If we returned, then the cast succeeded.
|
|
return origSourceType;
|
|
}
|
|
case MetadataKind::ForeignClass: {
|
|
// Check if the source is a subclass of the target.
|
|
swift_dynamicCastForeignClassMetatypeUnconditional(
|
|
(const ClassMetadata*)sourceType,
|
|
(const ClassMetadata*)targetType);
|
|
// If we returned, then the cast succeeded.
|
|
return origSourceType;
|
|
}
|
|
case MetadataKind::Existential:
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
swift_dynamicCastFailure(sourceType, targetType);
|
|
}
|
|
break;
|
|
|
|
case MetadataKind::ForeignClass:
|
|
// The source value must also be a class; otherwise the cast fails.
|
|
switch (sourceType->getKind()) {
|
|
case MetadataKind::ObjCClassWrapper:
|
|
// Get the actual class object.
|
|
sourceType = static_cast<const ObjCClassWrapperMetadata*>(sourceType)
|
|
->Class;
|
|
SWIFT_FALLTHROUGH;
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ForeignClass:
|
|
// Check if the source is a subclass of the target.
|
|
swift_dynamicCastForeignClassMetatypeUnconditional(
|
|
(const ClassMetadata*)sourceType,
|
|
(const ClassMetadata*)targetType);
|
|
// If we returned, then the cast succeeded.
|
|
return origSourceType;
|
|
case MetadataKind::Existential:
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
swift_dynamicCastFailure(sourceType, targetType);
|
|
}
|
|
break;
|
|
case MetadataKind::Existential:
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
// The cast succeeds only if the metadata pointers are statically
|
|
// equivalent.
|
|
if (sourceType != targetType)
|
|
swift_dynamicCastFailure(sourceType, targetType);
|
|
return origSourceType;
|
|
}
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
/// Do a dynamic cast to the target class.
|
|
static void *_dynamicCastUnknownClass(void *object,
|
|
const Metadata *targetType,
|
|
bool unconditional) {
|
|
// The unconditional path avoids some failure logic.
|
|
if (unconditional) {
|
|
return const_cast<void*>(
|
|
swift_dynamicCastUnknownClassUnconditional(object, targetType));
|
|
}
|
|
|
|
return const_cast<void*>(swift_dynamicCastUnknownClass(object, targetType));
|
|
}
|
|
#endif
|
|
|
|
static bool _dynamicCastUnknownClassIndirect(OpaqueValue *dest,
|
|
void *object,
|
|
const Metadata *targetType,
|
|
DynamicCastFlags flags) {
|
|
void **destSlot = reinterpret_cast<void **>(dest);
|
|
|
|
// The unconditional path avoids some failure logic.
|
|
if (flags & DynamicCastFlags::Unconditional) {
|
|
void *result = const_cast<void*>(
|
|
swift_dynamicCastUnknownClassUnconditional(object, targetType));
|
|
*destSlot = result;
|
|
|
|
if (!(flags & DynamicCastFlags::TakeOnSuccess)) {
|
|
swift_unknownRetain(result);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Okay, we're doing a conditional cast.
|
|
void *result =
|
|
const_cast<void*>(swift_dynamicCastUnknownClass(object, targetType));
|
|
assert(result == nullptr || object == result);
|
|
|
|
// If the cast failed, destroy the input and return false.
|
|
if (!result) {
|
|
if (flags & DynamicCastFlags::DestroyOnFailure) {
|
|
swift_unknownRelease(object);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Otherwise, store to the destination and return true.
|
|
*destSlot = result;
|
|
if (!(flags & DynamicCastFlags::TakeOnSuccess)) {
|
|
swift_unknownRetain(result);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
extern "C" const ProtocolDescriptor _TMps9ErrorType;
|
|
|
|
static const WitnessTable *findErrorTypeWitness(const Metadata *srcType) {
|
|
return swift_conformsToProtocol(srcType, &_TMps9ErrorType);
|
|
}
|
|
|
|
static const Metadata *getNSErrorTypeMetadata() {
|
|
return SWIFT_LAZY_CONSTANT(
|
|
swift_getObjCClassMetadata((const ClassMetadata *)getNSErrorClass()));
|
|
}
|
|
#endif
|
|
|
|
/// Perform a dynamic cast from an existential type to some kind of
|
|
/// class type.
|
|
static bool _dynamicCastToUnknownClassFromExistential(OpaqueValue *dest,
|
|
OpaqueValue *src,
|
|
const ExistentialTypeMetadata *srcType,
|
|
const Metadata *targetType,
|
|
DynamicCastFlags flags) {
|
|
switch (srcType->getRepresentation()) {
|
|
case ExistentialTypeRepresentation::Class: {
|
|
auto classContainer =
|
|
reinterpret_cast<ClassExistentialContainer*>(src);
|
|
void *obj = classContainer->Value;
|
|
#if SWIFT_OBJC_INTEROP
|
|
// If we're casting to NSError, we may need a representation change,
|
|
// so fall into the general swift_dynamicCast path.
|
|
if (targetType == getNSErrorTypeMetadata()) {
|
|
return swift_dynamicCast(dest, src, swift_getObjectType((HeapObject*)obj),
|
|
targetType, flags);
|
|
}
|
|
#endif
|
|
return _dynamicCastUnknownClassIndirect(dest, obj, targetType, flags);
|
|
}
|
|
case ExistentialTypeRepresentation::Opaque: {
|
|
auto opaqueContainer =
|
|
reinterpret_cast<OpaqueExistentialContainer*>(src);
|
|
auto srcCapturedType = opaqueContainer->Type;
|
|
OpaqueValue *srcValue =
|
|
srcCapturedType->vw_projectBuffer(&opaqueContainer->Buffer);
|
|
bool result = swift_dynamicCast(dest,
|
|
srcValue,
|
|
srcCapturedType,
|
|
targetType,
|
|
flags);
|
|
if (src != srcValue)
|
|
_maybeDeallocateOpaqueExistential(src, result, flags);
|
|
return result;
|
|
}
|
|
case ExistentialTypeRepresentation::ErrorType: {
|
|
const SwiftError *errorBox =
|
|
*reinterpret_cast<const SwiftError * const *>(src);
|
|
auto srcCapturedType = errorBox->getType();
|
|
const OpaqueValue *srcValue;
|
|
// A bridged NSError is itself the value.
|
|
if (errorBox->isPureNSError())
|
|
srcValue = src;
|
|
else
|
|
srcValue = errorBox->getValue();
|
|
|
|
// We can't take or destroy the value out of the box since it might be
|
|
// shared.
|
|
auto subFlags = flags - (DynamicCastFlags::TakeOnSuccess
|
|
| DynamicCastFlags::DestroyOnFailure);
|
|
bool result = swift_dynamicCast(dest,
|
|
const_cast<OpaqueValue*>(srcValue),
|
|
srcCapturedType, targetType,
|
|
subFlags);
|
|
if (shouldDeallocateSource(result, flags))
|
|
srcType->vw_destroy(src);
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Perform a dynamic cast from an existential type to a
|
|
/// non-existential type.
|
|
static bool _dynamicCastFromExistential(OpaqueValue *dest,
|
|
OpaqueValue *src,
|
|
const ExistentialTypeMetadata *srcType,
|
|
const Metadata *targetType,
|
|
DynamicCastFlags flags) {
|
|
OpaqueValue *srcValue;
|
|
const Metadata *srcCapturedType;
|
|
bool isOutOfLine;
|
|
bool canTake;
|
|
|
|
switch (srcType->getRepresentation()) {
|
|
case ExistentialTypeRepresentation::Class: {
|
|
auto classContainer =
|
|
reinterpret_cast<const ClassExistentialContainer*>(src);
|
|
srcValue = (OpaqueValue*) &classContainer->Value;
|
|
void *obj = classContainer->Value;
|
|
srcCapturedType = swift_getObjectType(reinterpret_cast<HeapObject*>(obj));
|
|
isOutOfLine = false;
|
|
canTake = true;
|
|
break;
|
|
}
|
|
case ExistentialTypeRepresentation::Opaque: {
|
|
auto opaqueContainer = reinterpret_cast<OpaqueExistentialContainer*>(src);
|
|
srcCapturedType = opaqueContainer->Type;
|
|
srcValue = srcCapturedType->vw_projectBuffer(&opaqueContainer->Buffer);
|
|
isOutOfLine = (src != srcValue);
|
|
canTake = true;
|
|
break;
|
|
}
|
|
case ExistentialTypeRepresentation::ErrorType: {
|
|
const SwiftError *errorBox
|
|
= *reinterpret_cast<const SwiftError * const *>(src);
|
|
|
|
srcCapturedType = errorBox->getType();
|
|
// A bridged NSError is itself the value.
|
|
if (errorBox->isPureNSError())
|
|
srcValue = src;
|
|
else
|
|
srcValue = const_cast<OpaqueValue*>(errorBox->getValue());
|
|
|
|
// The value is out-of-line, but we can't take it, since it may be shared.
|
|
isOutOfLine = true;
|
|
canTake = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
auto subFlags = flags;
|
|
if (!canTake)
|
|
subFlags = subFlags - (DynamicCastFlags::DestroyOnFailure
|
|
| DynamicCastFlags::TakeOnSuccess);
|
|
|
|
bool result = swift_dynamicCast(dest, srcValue, srcCapturedType,
|
|
targetType, subFlags);
|
|
// Deallocate the existential husk if we took from it.
|
|
if (canTake && result && isOutOfLine)
|
|
_maybeDeallocateOpaqueExistential(src, result, flags);
|
|
// If we couldn't take, we still may need to destroy the whole value.
|
|
else if (!canTake && shouldDeallocateSource(result, flags))
|
|
srcType->vw_destroy(src);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// Perform a dynamic cast of a metatype to a metatype.
|
|
///
|
|
/// Note that the check is whether 'metatype' is an *instance of*
|
|
/// 'targetType', not a *subtype of it*.
|
|
static bool _dynamicCastMetatypeToMetatype(OpaqueValue *dest,
|
|
const Metadata *metatype,
|
|
const MetatypeMetadata *targetType,
|
|
DynamicCastFlags flags) {
|
|
const Metadata *result;
|
|
if (flags & DynamicCastFlags::Unconditional) {
|
|
result = swift_dynamicCastMetatypeUnconditional(metatype,
|
|
targetType->InstanceType);
|
|
} else {
|
|
result = swift_dynamicCastMetatype(metatype, targetType->InstanceType);
|
|
if (!result) return false;
|
|
}
|
|
|
|
*((const Metadata **) dest) = result;
|
|
return true;
|
|
}
|
|
|
|
/// Check whether an unknown class instance is actually a class object.
|
|
static const Metadata *_getUnknownClassAsMetatype(void *object) {
|
|
#if SWIFT_OBJC_INTEROP
|
|
// Objective-C class metadata are objects, so an AnyObject (or NSObject)
|
|
// may refer to a class object.
|
|
|
|
// Test whether the object's isa is a metaclass, which indicates that the
|
|
// object is a class.
|
|
|
|
Class isa = object_getClass((id)object);
|
|
if (class_isMetaClass(isa)) {
|
|
return swift_getObjCClassMetadata((const ClassMetadata *)object);
|
|
}
|
|
#endif
|
|
|
|
// Class values are currently never metatypes in the native runtime.
|
|
return nullptr;
|
|
}
|
|
|
|
/// Perform a dynamic cast of a class value to a metatype type.
|
|
static bool _dynamicCastUnknownClassToMetatype(OpaqueValue *dest,
|
|
void *object,
|
|
const MetatypeMetadata *targetType,
|
|
DynamicCastFlags flags) {
|
|
if (auto metatype = _getUnknownClassAsMetatype(object))
|
|
return _dynamicCastMetatypeToMetatype(dest, metatype, targetType, flags);
|
|
|
|
if (flags & DynamicCastFlags::Unconditional)
|
|
swift_dynamicCastFailure(_swift_getClass(object), targetType);
|
|
if (flags & DynamicCastFlags::DestroyOnFailure)
|
|
swift_unknownRelease((HeapObject*) object);
|
|
return false;
|
|
}
|
|
|
|
/// Perform a dynamic cast to a metatype type.
|
|
static bool _dynamicCastToMetatype(OpaqueValue *dest,
|
|
OpaqueValue *src,
|
|
const Metadata *srcType,
|
|
const MetatypeMetadata *targetType,
|
|
DynamicCastFlags flags) {
|
|
|
|
switch (srcType->getKind()) {
|
|
case MetadataKind::Metatype: {
|
|
const Metadata *srcMetatype = *(const Metadata * const *) src;
|
|
return _dynamicCastMetatypeToMetatype(dest, srcMetatype,
|
|
targetType, flags);
|
|
}
|
|
|
|
case MetadataKind::ExistentialMetatype: {
|
|
const Metadata *srcMetatype = *(const Metadata * const *) src;
|
|
return _dynamicCastMetatypeToMetatype(dest, srcMetatype,
|
|
targetType, flags);
|
|
}
|
|
|
|
case MetadataKind::Existential: {
|
|
auto srcExistentialType = cast<ExistentialTypeMetadata>(srcType);
|
|
switch (srcExistentialType->getRepresentation()) {
|
|
case ExistentialTypeRepresentation::Class: {
|
|
auto srcExistential = (ClassExistentialContainer*) src;
|
|
return _dynamicCastUnknownClassToMetatype(dest,
|
|
srcExistential->Value,
|
|
targetType, flags);
|
|
}
|
|
case ExistentialTypeRepresentation::Opaque: {
|
|
auto srcExistential = (OpaqueExistentialContainer*) src;
|
|
auto srcValueType = srcExistential->Type;
|
|
auto srcValue = srcValueType->vw_projectBuffer(&srcExistential->Buffer);
|
|
bool result = _dynamicCastToMetatype(dest, srcValue, srcValueType,
|
|
targetType, flags);
|
|
if (src != srcValue)
|
|
_maybeDeallocateOpaqueExistential(src, result, flags);
|
|
return result;
|
|
}
|
|
case ExistentialTypeRepresentation::ErrorType: {
|
|
const SwiftError *srcBox
|
|
= *reinterpret_cast<const SwiftError * const *>(src);
|
|
|
|
auto srcValueType = srcBox->getType();
|
|
const OpaqueValue *srcValue;
|
|
if (srcBox->isPureNSError())
|
|
srcValue = src;
|
|
else
|
|
srcValue = srcBox->getValue();
|
|
|
|
// Can't take from a box since the value may be shared.
|
|
auto subFlags = flags - (DynamicCastFlags::TakeOnSuccess
|
|
| DynamicCastFlags::DestroyOnFailure);
|
|
bool result = _dynamicCastToMetatype(dest,
|
|
const_cast<OpaqueValue*>(srcValue),
|
|
srcValueType,
|
|
targetType, subFlags);
|
|
if (shouldDeallocateSource(result, flags))
|
|
srcType->vw_destroy(src);
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
case MetadataKind::ForeignClass: {
|
|
void *object = *reinterpret_cast<void**>(src);
|
|
return _dynamicCastUnknownClassToMetatype(dest, object, targetType, flags);
|
|
}
|
|
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
return _fail(src, srcType, targetType, flags);
|
|
}
|
|
_failCorruptType(srcType);
|
|
}
|
|
|
|
/// Perform a dynamic cast of a metatype to an existential metatype type.
|
|
static bool _dynamicCastMetatypeToExistentialMetatype(OpaqueValue *dest,
|
|
const Metadata *srcMetatype,
|
|
const ExistentialMetatypeMetadata *targetType,
|
|
DynamicCastFlags flags,
|
|
bool writeDestMetatype = true) {
|
|
// The instance type of an existential metatype must be either an
|
|
// existential or an existential metatype.
|
|
auto destMetatype = reinterpret_cast<ExistentialMetatypeContainer*>(dest);
|
|
|
|
// If it's an existential, we need to check for conformances.
|
|
auto targetInstanceType = targetType->InstanceType;
|
|
if (auto targetInstanceTypeAsExistential =
|
|
dyn_cast<ExistentialTypeMetadata>(targetInstanceType)) {
|
|
// Check for conformance to all the protocols.
|
|
// TODO: collect the witness tables.
|
|
auto &protocols = targetInstanceTypeAsExistential->Protocols;
|
|
const WitnessTable **conformance
|
|
= writeDestMetatype ? destMetatype->getWitnessTables() : nullptr;
|
|
for (unsigned i = 0, n = protocols.NumProtocols; i != n; ++i) {
|
|
const ProtocolDescriptor *protocol = protocols[i];
|
|
if (!_conformsToProtocol(nullptr, srcMetatype, protocol, conformance)) {
|
|
if (flags & DynamicCastFlags::Unconditional)
|
|
swift_dynamicCastFailure(srcMetatype, targetType);
|
|
return false;
|
|
}
|
|
if (conformance && protocol->Flags.needsWitnessTable())
|
|
++conformance;
|
|
}
|
|
|
|
if (writeDestMetatype)
|
|
destMetatype->Value = srcMetatype;
|
|
return true;
|
|
}
|
|
|
|
// Otherwise, we're casting to SomeProtocol.Type.Type.
|
|
auto targetInstanceTypeAsMetatype =
|
|
cast<ExistentialMetatypeMetadata>(targetInstanceType);
|
|
|
|
// If the source type isn't a metatype, the cast fails.
|
|
auto srcMetatypeMetatype = dyn_cast<MetatypeMetadata>(srcMetatype);
|
|
if (!srcMetatypeMetatype) {
|
|
if (flags & DynamicCastFlags::Unconditional)
|
|
swift_dynamicCastFailure(srcMetatype, targetType);
|
|
return false;
|
|
}
|
|
|
|
// The representation of an existential metatype remains consistent
|
|
// arbitrarily deep: a metatype, followed by some protocols. The
|
|
// protocols are the same at every level, so we can just set the
|
|
// metatype correctly and then recurse, letting the recursive call
|
|
// fill in the conformance information correctly.
|
|
|
|
// Proactively set the destination metatype so that we can tail-recur,
|
|
// unless we've already done so. There's no harm in doing this even if
|
|
// the cast fails.
|
|
if (writeDestMetatype)
|
|
*((const Metadata **) dest) = srcMetatype;
|
|
|
|
// Recurse.
|
|
auto srcInstanceType = srcMetatypeMetatype->InstanceType;
|
|
return _dynamicCastMetatypeToExistentialMetatype(dest, srcInstanceType,
|
|
targetInstanceTypeAsMetatype,
|
|
flags,
|
|
/*overwrite*/ false);
|
|
}
|
|
|
|
/// Perform a dynamic cast of a class value to an existential metatype type.
|
|
static bool _dynamicCastUnknownClassToExistentialMetatype(OpaqueValue *dest,
|
|
void *object,
|
|
const ExistentialMetatypeMetadata *targetType,
|
|
DynamicCastFlags flags) {
|
|
if (auto metatype = _getUnknownClassAsMetatype(object))
|
|
return _dynamicCastMetatypeToExistentialMetatype(dest, metatype,
|
|
targetType, flags);
|
|
|
|
// Class values are currently never metatypes (?).
|
|
if (flags & DynamicCastFlags::Unconditional)
|
|
swift_dynamicCastFailure(_swift_getClass(object), targetType);
|
|
if (flags & DynamicCastFlags::DestroyOnFailure)
|
|
swift_unknownRelease((HeapObject*) object);
|
|
return false;
|
|
}
|
|
|
|
/// Perform a dynamic cast to an existential metatype type.
|
|
static bool _dynamicCastToExistentialMetatype(OpaqueValue *dest,
|
|
OpaqueValue *src,
|
|
const Metadata *srcType,
|
|
const ExistentialMetatypeMetadata *targetType,
|
|
DynamicCastFlags flags) {
|
|
|
|
switch (srcType->getKind()) {
|
|
case MetadataKind::Metatype: {
|
|
const Metadata *srcMetatype = *(const Metadata * const *) src;
|
|
return _dynamicCastMetatypeToExistentialMetatype(dest, srcMetatype,
|
|
targetType, flags);
|
|
}
|
|
|
|
// TODO: take advantage of protocol conformances already known.
|
|
case MetadataKind::ExistentialMetatype: {
|
|
const Metadata *srcMetatype = *(const Metadata * const *) src;
|
|
return _dynamicCastMetatypeToExistentialMetatype(dest, srcMetatype,
|
|
targetType, flags);
|
|
}
|
|
|
|
case MetadataKind::Existential: {
|
|
auto srcExistentialType = cast<ExistentialTypeMetadata>(srcType);
|
|
switch (srcExistentialType->getRepresentation()) {
|
|
case ExistentialTypeRepresentation::Class: {
|
|
auto srcExistential = (ClassExistentialContainer*) src;
|
|
return _dynamicCastUnknownClassToExistentialMetatype(dest,
|
|
srcExistential->Value,
|
|
targetType, flags);
|
|
}
|
|
case ExistentialTypeRepresentation::Opaque: {
|
|
auto srcExistential = (OpaqueExistentialContainer*) src;
|
|
auto srcValueType = srcExistential->Type;
|
|
auto srcValue = srcValueType->vw_projectBuffer(&srcExistential->Buffer);
|
|
bool result = _dynamicCastToExistentialMetatype(dest, srcValue, srcValueType,
|
|
targetType, flags);
|
|
if (src != srcValue)
|
|
_maybeDeallocateOpaqueExistential(src, result, flags);
|
|
return result;
|
|
}
|
|
case ExistentialTypeRepresentation::ErrorType: {
|
|
const SwiftError *srcBox
|
|
= *reinterpret_cast<const SwiftError * const *>(src);
|
|
|
|
auto srcValueType = srcBox->getType();
|
|
const OpaqueValue *srcValue;
|
|
if (srcBox->isPureNSError())
|
|
srcValue = src;
|
|
else
|
|
srcValue = srcBox->getValue();
|
|
|
|
// Can't take from a box since the value may be shared.
|
|
auto subFlags = flags - (DynamicCastFlags::TakeOnSuccess
|
|
| DynamicCastFlags::DestroyOnFailure);
|
|
bool result = _dynamicCastToExistentialMetatype(dest,
|
|
const_cast<OpaqueValue*>(srcValue),
|
|
srcValueType,
|
|
targetType, subFlags);
|
|
if (shouldDeallocateSource(result, flags))
|
|
srcType->vw_destroy(src);
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
case MetadataKind::ForeignClass: {
|
|
void *object = *reinterpret_cast<void**>(src);
|
|
return _dynamicCastUnknownClassToExistentialMetatype(dest, object,
|
|
targetType, flags);
|
|
}
|
|
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
if (flags & DynamicCastFlags::Unconditional) {
|
|
swift_dynamicCastFailure(srcType, targetType);
|
|
}
|
|
return false;
|
|
}
|
|
_failCorruptType(srcType);
|
|
}
|
|
|
|
static bool _dynamicCastToFunction(OpaqueValue *dest,
|
|
OpaqueValue *src,
|
|
const Metadata *srcType,
|
|
const FunctionTypeMetadata *targetType,
|
|
DynamicCastFlags flags) {
|
|
// Function casts succeed on exact matches, or if the target type is
|
|
// throwier than the source type.
|
|
//
|
|
// TODO: We could also allow ABI-compatible variance, such as casting
|
|
// a dynamic Base -> Derived to Derived -> Base. We wouldn't be able to
|
|
// perform a dynamic cast that required any ABI adjustment without a JIT
|
|
// though.
|
|
|
|
// Check for an exact type match first.
|
|
if (srcType == targetType) {
|
|
return _succeed(dest, src, srcType, flags);
|
|
}
|
|
|
|
switch (srcType->getKind()) {
|
|
case MetadataKind::Function: {
|
|
auto srcFn = static_cast<const FunctionTypeMetadata *>(srcType);
|
|
auto targetFn = static_cast<const FunctionTypeMetadata *>(targetType);
|
|
|
|
// Check that argument counts and convention match. "throws" can vary.
|
|
if (srcFn->Flags.withThrows(false) != targetFn->Flags.withThrows(false))
|
|
return _fail(src, srcType, targetType, flags);
|
|
|
|
// If the target type can't throw, neither can the source.
|
|
if (srcFn->throws() && !targetFn->throws())
|
|
return _fail(src, srcType, targetType, flags);
|
|
|
|
// The result and argument types must match.
|
|
if (srcFn->ResultType != targetFn->ResultType)
|
|
return _fail(src, srcType, targetType, flags);
|
|
if (srcFn->getNumArguments() != targetFn->getNumArguments())
|
|
return _fail(src, srcType, targetType, flags);
|
|
for (unsigned i = 0, e = srcFn->getNumArguments(); i < e; ++i)
|
|
if (srcFn->getArguments()[i] != targetFn->getArguments()[i])
|
|
return _fail(src, srcType, targetType, flags);
|
|
|
|
return _succeed(dest, src, srcType, flags);
|
|
}
|
|
|
|
case MetadataKind::Existential:
|
|
return _dynamicCastFromExistential(dest, src,
|
|
static_cast<const ExistentialTypeMetadata*>(srcType),
|
|
targetType, flags);
|
|
|
|
case MetadataKind::Class:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
case MetadataKind::ForeignClass:
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Tuple:
|
|
return _fail(src, srcType, targetType, flags);
|
|
}
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
static id dynamicCastValueToNSError(OpaqueValue *src,
|
|
const Metadata *srcType,
|
|
const WitnessTable *srcErrorTypeWitness,
|
|
DynamicCastFlags flags) {
|
|
BoxPair errorBox = swift_allocError(srcType, srcErrorTypeWitness, src,
|
|
/*isTake*/ flags & DynamicCastFlags::TakeOnSuccess);
|
|
return swift_bridgeErrorTypeToNSError((SwiftError*)errorBox.first);
|
|
}
|
|
#endif
|
|
|
|
static bool canCastToExistential(OpaqueValue *dest, OpaqueValue *src,
|
|
const Metadata *srcType,
|
|
const Metadata *targetType) {
|
|
if (targetType->getKind() != MetadataKind::Existential)
|
|
return false;
|
|
|
|
return _dynamicCastToExistential(dest, src, srcType,
|
|
cast<ExistentialTypeMetadata>(targetType),
|
|
DynamicCastFlags::Default);
|
|
}
|
|
|
|
/// Perform a dynamic cast to an arbitrary type.
|
|
bool swift::swift_dynamicCast(OpaqueValue *dest,
|
|
OpaqueValue *src,
|
|
const Metadata *srcType,
|
|
const Metadata *targetType,
|
|
DynamicCastFlags flags) {
|
|
// Check if the cast source is Optional and the target is not an existential
|
|
// that Optional conforms to. Unwrap one level of Optional and continue.
|
|
if (srcType->getKind() == MetadataKind::Optional
|
|
&& !canCastToExistential(dest, src, srcType, targetType)) {
|
|
const Metadata *payloadType =
|
|
cast<EnumMetadata>(srcType)->getGenericArgs()[0];
|
|
int enumCase =
|
|
swift_getEnumCaseSinglePayload(src, payloadType, 1 /*emptyCases=*/);
|
|
if (enumCase != -1) {
|
|
// Allow Optional<T>.None -> Optional<U>.None
|
|
if (targetType->getKind() != MetadataKind::Optional)
|
|
return _fail(src, srcType, targetType, flags);
|
|
// Inject the .None tag
|
|
swift_storeEnumTagSinglePayload(dest, payloadType, enumCase,
|
|
1 /*emptyCases=*/);
|
|
return _succeed(dest, src, srcType, flags);
|
|
}
|
|
// .Some
|
|
// Single payload enums are guaranteed layout compatible with their
|
|
// payload. Only the source's payload needs to be taken or destroyed.
|
|
srcType = payloadType;
|
|
}
|
|
|
|
switch (targetType->getKind()) {
|
|
// Handle wrapping an Optional target.
|
|
case MetadataKind::Optional: {
|
|
// Recursively cast into the layout compatible payload area.
|
|
const Metadata *payloadType =
|
|
cast<EnumMetadata>(targetType)->getGenericArgs()[0];
|
|
if (swift_dynamicCast(dest, src, srcType, payloadType, flags)) {
|
|
swift_storeEnumTagSinglePayload(dest, payloadType, -1 /*case*/,
|
|
1 /*emptyCases*/);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Casts to class type.
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
#if SWIFT_OBJC_INTEROP
|
|
// If the destination type is an NSError, and the source type is an
|
|
// ErrorType, then the cast can succeed by NSError bridging.
|
|
if (targetType == getNSErrorTypeMetadata()) {
|
|
// Don't rebridge if the source is already some kind of NSError.
|
|
if (srcType->isAnyClass()
|
|
&& swift_dynamicCastObjCClass(*reinterpret_cast<id*>(src),
|
|
static_cast<const ObjCClassWrapperMetadata*>(targetType)->Class))
|
|
return _succeed(dest, src, srcType, flags);
|
|
if (auto srcErrorTypeWitness = findErrorTypeWitness(srcType)) {
|
|
auto error = dynamicCastValueToNSError(src, srcType,
|
|
srcErrorTypeWitness, flags);
|
|
*reinterpret_cast<id *>(dest) = error;
|
|
return true;
|
|
}
|
|
}
|
|
SWIFT_FALLTHROUGH;
|
|
#endif
|
|
|
|
case MetadataKind::ForeignClass:
|
|
switch (srcType->getKind()) {
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
case MetadataKind::ForeignClass: {
|
|
// Do a dynamic cast on the instance pointer.
|
|
void *object = *reinterpret_cast<void * const *>(src);
|
|
return _dynamicCastUnknownClassIndirect(dest, object,
|
|
targetType, flags);
|
|
}
|
|
|
|
case MetadataKind::Existential: {
|
|
auto srcExistentialType = cast<ExistentialTypeMetadata>(srcType);
|
|
return _dynamicCastToUnknownClassFromExistential(dest, src,
|
|
srcExistentialType,
|
|
targetType, flags);
|
|
}
|
|
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Struct: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
// If the source type is bridged to Objective-C, try to bridge.
|
|
if (auto srcBridgeWitness = findBridgeWitness(srcType)) {
|
|
return _dynamicCastValueToClassViaObjCBridgeable(dest, src, srcType,
|
|
targetType,
|
|
srcBridgeWitness,
|
|
flags);
|
|
}
|
|
#endif
|
|
return _fail(src, srcType, targetType, flags);
|
|
}
|
|
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Tuple:
|
|
return _fail(src, srcType, targetType, flags);
|
|
}
|
|
break;
|
|
|
|
case MetadataKind::Existential:
|
|
return _dynamicCastToExistential(dest, src, srcType,
|
|
cast<ExistentialTypeMetadata>(targetType),
|
|
flags);
|
|
|
|
case MetadataKind::Metatype:
|
|
return _dynamicCastToMetatype(dest, src, srcType,
|
|
cast<MetatypeMetadata>(targetType),
|
|
flags);
|
|
|
|
case MetadataKind::ExistentialMetatype:
|
|
return _dynamicCastToExistentialMetatype(dest, src, srcType,
|
|
cast<ExistentialMetatypeMetadata>(targetType),
|
|
flags);
|
|
|
|
// Function types.
|
|
case MetadataKind::Function: {
|
|
return _dynamicCastToFunction(dest, src, srcType,
|
|
cast<FunctionTypeMetadata>(targetType),
|
|
flags);
|
|
}
|
|
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Enum:
|
|
switch (srcType->getKind()) {
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
case MetadataKind::ForeignClass: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
// If the target type is bridged to Objective-C, try to bridge.
|
|
if (auto targetBridgeWitness = findBridgeWitness(targetType)) {
|
|
return _dynamicCastClassToValueViaObjCBridgeable(dest, src, srcType,
|
|
targetType,
|
|
targetBridgeWitness,
|
|
flags);
|
|
}
|
|
|
|
// If the source is an NSError, and the target is a bridgeable ErrorType,
|
|
// try to bridge.
|
|
if (tryDynamicCastNSErrorToValue(dest, src, srcType, targetType, flags)) {
|
|
return true;
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Existential:
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Tuple:
|
|
break;
|
|
}
|
|
|
|
SWIFT_FALLTHROUGH;
|
|
|
|
// The non-polymorphic types.
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Tuple:
|
|
// If there's an exact type match, we're done.
|
|
if (srcType == targetType)
|
|
return _succeed(dest, src, srcType, flags);
|
|
|
|
// If we have an existential, look at its dynamic type.
|
|
if (auto srcExistentialType = dyn_cast<ExistentialTypeMetadata>(srcType)) {
|
|
return _dynamicCastFromExistential(dest, src, srcExistentialType,
|
|
targetType, flags);
|
|
}
|
|
|
|
// Otherwise, we have a failure.
|
|
return _fail(src, srcType, targetType, flags);
|
|
}
|
|
_failCorruptType(srcType);
|
|
}
|
|
|
|
#if defined(NDEBUG) && SWIFT_OBJC_INTEROP
|
|
void ProtocolConformanceRecord::dump() const {
|
|
auto symbolName = [&](const void *addr) -> const char * {
|
|
Dl_info info;
|
|
int ok = dladdr(addr, &info);
|
|
if (!ok)
|
|
return "<unknown addr>";
|
|
return info.dli_sname;
|
|
};
|
|
|
|
switch (auto kind = getTypeKind()) {
|
|
case ProtocolConformanceTypeKind::Universal:
|
|
printf("universal");
|
|
break;
|
|
case ProtocolConformanceTypeKind::UniqueDirectType:
|
|
case ProtocolConformanceTypeKind::NonuniqueDirectType:
|
|
printf("%s direct type ",
|
|
kind == ProtocolConformanceTypeKind::UniqueDirectType
|
|
? "unique" : "nonunique");
|
|
if (auto ntd = getDirectType()->getNominalTypeDescriptor()) {
|
|
printf("%s", ntd->Name);
|
|
} else {
|
|
printf("<structural type>");
|
|
}
|
|
break;
|
|
case ProtocolConformanceTypeKind::UniqueDirectClass:
|
|
printf("unique direct class %s",
|
|
class_getName(getDirectClass()));
|
|
break;
|
|
case ProtocolConformanceTypeKind::UniqueIndirectClass:
|
|
printf("unique indirect class %s",
|
|
class_getName(*getIndirectClass()));
|
|
break;
|
|
|
|
case ProtocolConformanceTypeKind::UniqueGenericPattern:
|
|
printf("unique generic type %s", symbolName(getGenericPattern()));
|
|
break;
|
|
}
|
|
|
|
printf(" => ");
|
|
|
|
switch (getConformanceKind()) {
|
|
case ProtocolConformanceReferenceKind::WitnessTable:
|
|
printf("witness table %s\n", symbolName(getStaticWitnessTable()));
|
|
break;
|
|
case ProtocolConformanceReferenceKind::WitnessTableAccessor:
|
|
printf("witness table accessor %s\n",
|
|
symbolName((const void *)(uintptr_t)getWitnessTableAccessor()));
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/// Take the type reference inside a protocol conformance record and fetch the
|
|
/// canonical metadata pointer for the type it refers to.
|
|
/// Returns nil for universal or generic type references.
|
|
const Metadata *ProtocolConformanceRecord::getCanonicalTypeMetadata()
|
|
const {
|
|
switch (getTypeKind()) {
|
|
case ProtocolConformanceTypeKind::UniqueDirectType:
|
|
// Already unique.
|
|
return getDirectType();
|
|
case ProtocolConformanceTypeKind::NonuniqueDirectType:
|
|
// Ask the runtime for the unique metadata record we've canonized.
|
|
return swift_getForeignTypeMetadata((ForeignTypeMetadata*)getDirectType());
|
|
case ProtocolConformanceTypeKind::UniqueIndirectClass:
|
|
// The class may be ObjC, in which case we need to instantiate its Swift
|
|
// metadata. The class additionally may be weak-linked, so we have to check
|
|
// for null.
|
|
if (auto *ClassMetadata = *getIndirectClass())
|
|
return swift_getObjCClassMetadata(ClassMetadata);
|
|
return nullptr;
|
|
|
|
case ProtocolConformanceTypeKind::UniqueDirectClass:
|
|
// The class may be ObjC, in which case we need to instantiate its Swift
|
|
// metadata.
|
|
if (auto *ClassMetadata = getDirectClass())
|
|
return swift_getObjCClassMetadata(ClassMetadata);
|
|
return nullptr;
|
|
|
|
case ProtocolConformanceTypeKind::UniqueGenericPattern:
|
|
case ProtocolConformanceTypeKind::Universal:
|
|
// The record does not apply to a single type.
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
const WitnessTable *ProtocolConformanceRecord::getWitnessTable(const Metadata *type)
|
|
const {
|
|
switch (getConformanceKind()) {
|
|
case ProtocolConformanceReferenceKind::WitnessTable:
|
|
return getStaticWitnessTable();
|
|
|
|
case ProtocolConformanceReferenceKind::WitnessTableAccessor:
|
|
return getWitnessTableAccessor()(type);
|
|
}
|
|
}
|
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
#define SWIFT_PROTOCOL_CONFORMANCES_SECTION "__swift2_proto"
|
|
#elif defined(__ELF__)
|
|
#define SWIFT_PROTOCOL_CONFORMANCES_SECTION ".swift2_protocol_conformances_start"
|
|
#endif
|
|
|
|
// std:once_flag token to install the dyld callback to enqueue images for
|
|
// protocol conformance lookup.
|
|
static std::once_flag InstallProtocolConformanceAddImageCallbackOnce;
|
|
|
|
namespace {
|
|
struct ConformanceSection {
|
|
const ProtocolConformanceRecord *Begin, *End;
|
|
const ProtocolConformanceRecord *begin() const {
|
|
return Begin;
|
|
}
|
|
const ProtocolConformanceRecord *end() const {
|
|
return End;
|
|
}
|
|
};
|
|
|
|
struct ConformanceCacheEntry {
|
|
private:
|
|
const void *Type;
|
|
const ProtocolDescriptor *Proto;
|
|
uintptr_t Data;
|
|
// All Darwin 64-bit platforms reserve the low 2^32 of address space, which
|
|
// is more than enough invalid pointer values for any realistic generation
|
|
// number. It's a little easier to overflow on 32-bit, so we need an extra
|
|
// bit there.
|
|
#if !__LP64__
|
|
bool Success;
|
|
#endif
|
|
|
|
ConformanceCacheEntry(const void *type,
|
|
const ProtocolDescriptor *proto,
|
|
uintptr_t Data, bool Success)
|
|
: Type(type), Proto(proto), Data(Data)
|
|
#if !__LP64__
|
|
, Success(Success)
|
|
#endif
|
|
{
|
|
#if __LP64__
|
|
# if __APPLE__
|
|
assert((!Success && Data <= 0xFFFFFFFFU) ||
|
|
(Success && Data > 0xFFFFFFFFU));
|
|
# elif __linux__ || __FreeBSD__
|
|
assert((!Success && Data <= 0x0FFFU) ||
|
|
(Success && Data > 0x0FFFU));
|
|
# else
|
|
# error "port me"
|
|
# endif
|
|
#endif
|
|
}
|
|
|
|
public:
|
|
ConformanceCacheEntry() = default;
|
|
|
|
static ConformanceCacheEntry createSuccess(
|
|
const void *type, const ProtocolDescriptor *proto,
|
|
const swift::WitnessTable *witness) {
|
|
return ConformanceCacheEntry(type, proto, (uintptr_t) witness, true);
|
|
}
|
|
|
|
static ConformanceCacheEntry createFailure(
|
|
const void *type, const ProtocolDescriptor *proto,
|
|
unsigned failureGeneration) {
|
|
return ConformanceCacheEntry(type, proto, (uintptr_t) failureGeneration,
|
|
false);
|
|
}
|
|
|
|
/// \returns true if the entry represents an entry for the pair \p type
|
|
/// and \p proto.
|
|
bool matches(const void *type, const ProtocolDescriptor *proto) {
|
|
return type == Type && Proto == proto;
|
|
}
|
|
|
|
bool isSuccessful() const {
|
|
#if __LP64__
|
|
# if __APPLE__
|
|
return Data > 0xFFFFFFFFU;
|
|
# elif __linux__ || __FreeBSD__
|
|
return Data > 0x0FFFU;
|
|
# else
|
|
# error "port me"
|
|
# endif
|
|
#else
|
|
return Success;
|
|
#endif
|
|
}
|
|
|
|
/// Get the cached witness table, if successful.
|
|
const WitnessTable *getWitnessTable() const {
|
|
assert(isSuccessful());
|
|
return (const WitnessTable *)Data;
|
|
}
|
|
|
|
/// Get the generation number under which this lookup failed.
|
|
unsigned getFailureGeneration() const {
|
|
assert(!isSuccessful());
|
|
return Data;
|
|
}
|
|
};
|
|
}
|
|
|
|
// Conformance Cache.
|
|
|
|
struct ConformanceState {
|
|
ConcurrentMap<size_t, ConformanceCacheEntry> Cache;
|
|
std::vector<ConformanceSection> SectionsToScan;
|
|
pthread_mutex_t SectionsToScanLock;
|
|
|
|
ConformanceState() {
|
|
SectionsToScan.reserve(16);
|
|
pthread_mutex_init(&SectionsToScanLock, nullptr);
|
|
}
|
|
};
|
|
|
|
static Lazy<ConformanceState> Conformances;
|
|
|
|
// This variable is used to signal when a cache was generated and
|
|
// it is correct to avoid a new scan.
|
|
static unsigned ConformanceCacheGeneration = 0;
|
|
|
|
void
|
|
swift::swift_registerProtocolConformances(const ProtocolConformanceRecord *begin,
|
|
const ProtocolConformanceRecord *end){
|
|
auto &C = Conformances.get();
|
|
|
|
pthread_mutex_lock(&C.SectionsToScanLock);
|
|
|
|
C.SectionsToScan.push_back(ConformanceSection{begin, end});
|
|
|
|
pthread_mutex_unlock(&C.SectionsToScanLock);
|
|
}
|
|
|
|
static void _addImageProtocolConformancesBlock(const uint8_t *conformances,
|
|
size_t conformancesSize) {
|
|
assert(conformancesSize % sizeof(ProtocolConformanceRecord) == 0
|
|
&& "weird-sized conformances section?!");
|
|
|
|
// If we have a section, enqueue the conformances for lookup.
|
|
auto recordsBegin
|
|
= reinterpret_cast<const ProtocolConformanceRecord*>(conformances);
|
|
auto recordsEnd
|
|
= reinterpret_cast<const ProtocolConformanceRecord*>
|
|
(conformances + conformancesSize);
|
|
swift_registerProtocolConformances(recordsBegin, recordsEnd);
|
|
}
|
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
static void _addImageProtocolConformances(const mach_header *mh,
|
|
intptr_t vmaddr_slide) {
|
|
#ifdef __LP64__
|
|
using mach_header_platform = mach_header_64;
|
|
assert(mh->magic == MH_MAGIC_64 && "loaded non-64-bit image?!");
|
|
#else
|
|
using mach_header_platform = mach_header;
|
|
#endif
|
|
|
|
// Look for a __swift2_proto section.
|
|
unsigned long conformancesSize;
|
|
const uint8_t *conformances =
|
|
getsectiondata(reinterpret_cast<const mach_header_platform *>(mh),
|
|
SEG_TEXT, SWIFT_PROTOCOL_CONFORMANCES_SECTION,
|
|
&conformancesSize);
|
|
|
|
if (!conformances)
|
|
return;
|
|
|
|
_addImageProtocolConformancesBlock(conformances, conformancesSize);
|
|
}
|
|
#elif defined(__ELF__)
|
|
static int _addImageProtocolConformances(struct dl_phdr_info *info,
|
|
size_t size, void * /*data*/) {
|
|
void *handle;
|
|
if (!info->dlpi_name || info->dlpi_name[0] == '\0') {
|
|
handle = dlopen(nullptr, RTLD_LAZY);
|
|
} else
|
|
handle = dlopen(info->dlpi_name, RTLD_LAZY | RTLD_NOLOAD);
|
|
auto conformances = reinterpret_cast<const uint8_t*>(
|
|
dlsym(handle, SWIFT_PROTOCOL_CONFORMANCES_SECTION));
|
|
|
|
if (!conformances) {
|
|
// if there are no conformances, don't hold this handle open.
|
|
dlclose(handle);
|
|
return 0;
|
|
}
|
|
|
|
// Extract the size of the conformances block from the head of the section
|
|
auto conformancesSize = *reinterpret_cast<const uint64_t*>(conformances);
|
|
conformances += sizeof(conformancesSize);
|
|
|
|
_addImageProtocolConformancesBlock(conformances, conformancesSize);
|
|
|
|
dlclose(handle);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static void installCallbacksToInspectDylib() {
|
|
static OnceToken_t token;
|
|
auto callback = [](void*) {
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
// Install our dyld callback if we haven't already.
|
|
// Dyld will invoke this on our behalf for all images that have already
|
|
// been loaded.
|
|
_dyld_register_func_for_add_image(_addImageProtocolConformances);
|
|
#elif defined(__ELF__)
|
|
// Search the loaded dls. Unlike the above, this only searches the already
|
|
// loaded ones.
|
|
// FIXME: Find a way to have this continue to happen after.
|
|
// rdar://problem/19045112
|
|
dl_iterate_phdr(_addImageProtocolConformances, nullptr);
|
|
#else
|
|
# error No known mechanism to inspect dynamic libraries on this platform.
|
|
#endif
|
|
};
|
|
|
|
SWIFT_ONCE_F(token, callback, nullptr);
|
|
}
|
|
|
|
static size_t hashTypeProtocolPair(const void *type,
|
|
const ProtocolDescriptor *protocol) {
|
|
// A simple hash function for the conformance pair.
|
|
return (size_t)type + ((size_t)protocol >> 2);
|
|
}
|
|
|
|
/// Search the witness table in the ConformanceCache. \returns a pair of the
|
|
/// WitnessTable pointer and a boolean value True if a definitive value is
|
|
/// found. \returns false if the type or its superclasses were not found in
|
|
/// the cache.
|
|
static
|
|
std::pair<const WitnessTable *, bool>
|
|
searchInConformanceCache(const Metadata *type,
|
|
const ProtocolDescriptor *protocol,
|
|
ConformanceCacheEntry *&foundEntry) {
|
|
auto &C = Conformances.get();
|
|
auto origType = type;
|
|
|
|
foundEntry = nullptr;
|
|
|
|
recur_inside_cache_lock:
|
|
|
|
// See if we have a cached conformance. Try the specific type first.
|
|
|
|
// Hash and lookup the type-protocol pair in the cache.
|
|
size_t hash = hashTypeProtocolPair(type, protocol);
|
|
ConcurrentList<ConformanceCacheEntry> &Bucket =
|
|
C.Cache.findOrAllocateNode(hash);
|
|
|
|
// Check if the type-protocol entry exists in the cache entry that we found.
|
|
for (auto &Entry : Bucket) {
|
|
if (!Entry.matches(type, protocol)) continue;
|
|
|
|
if (Entry.isSuccessful()) {
|
|
return std::make_pair(Entry.getWitnessTable(), true);
|
|
}
|
|
|
|
if (type == origType)
|
|
foundEntry = &Entry;
|
|
|
|
// If we got a cached negative response, check the generation number.
|
|
if (Entry.getFailureGeneration() == C.SectionsToScan.size()) {
|
|
// We found an entry with a negative value.
|
|
return std::make_pair(nullptr, true);
|
|
}
|
|
}
|
|
|
|
// If the type is generic, see if there's a shared nondependent witness table
|
|
// for its instances.
|
|
if (auto generic = type->getGenericPattern()) {
|
|
// Hash and lookup the type-protocol pair in the cache.
|
|
size_t hash = hashTypeProtocolPair(generic, protocol);
|
|
ConcurrentList<ConformanceCacheEntry> &Bucket =
|
|
C.Cache.findOrAllocateNode(hash);
|
|
|
|
for (auto &Entry : Bucket) {
|
|
if (!Entry.matches(generic, protocol)) continue;
|
|
if (Entry.isSuccessful()) {
|
|
return std::make_pair(Entry.getWitnessTable(), true);
|
|
}
|
|
// We don't try to cache negative responses for generic
|
|
// patterns.
|
|
}
|
|
}
|
|
|
|
// If the type is a class, try its superclass.
|
|
if (const ClassMetadata *classType = type->getClassObject()) {
|
|
if (classHasSuperclass(classType)) {
|
|
type = swift_getObjCClassMetadata(classType->SuperClass);
|
|
goto recur_inside_cache_lock;
|
|
}
|
|
}
|
|
|
|
// We did not find an entry.
|
|
return std::make_pair(nullptr, false);
|
|
}
|
|
|
|
/// Checks if a given candidate is a type itself, one of its
|
|
/// superclasses or a related generic type.
|
|
/// This check is supposed to use the same logic that is used
|
|
/// by searchInConformanceCache.
|
|
static
|
|
bool isRelatedType(const Metadata *type, const void *candidate) {
|
|
|
|
while (true) {
|
|
if (type == candidate)
|
|
return true;
|
|
|
|
// If the type is generic, see if there's a shared nondependent witness table
|
|
// for its instances.
|
|
if (auto generic = type->getGenericPattern()) {
|
|
if (generic == candidate)
|
|
return true;
|
|
}
|
|
|
|
// If the type is a class, try its superclass.
|
|
if (const ClassMetadata *classType = type->getClassObject()) {
|
|
if (classHasSuperclass(classType)) {
|
|
type = swift_getObjCClassMetadata(classType->SuperClass);
|
|
if (type == candidate)
|
|
return true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const WitnessTable *
|
|
swift::swift_conformsToProtocol(const Metadata *type,
|
|
const ProtocolDescriptor *protocol) {
|
|
auto &C = Conformances.get();
|
|
|
|
// Install callbacks for tracking when a new dylib is loaded so we can
|
|
// scan it.
|
|
installCallbacksToInspectDylib();
|
|
auto origType = type;
|
|
|
|
unsigned numSections = 0;
|
|
|
|
ConformanceCacheEntry *foundEntry;
|
|
|
|
recur:
|
|
// See if we have a cached conformance. The ConcurrentMap data structure
|
|
// allows us to insert and search the map concurrently without locking.
|
|
// We do lock the slow path because the SectionsToScan data structure is not
|
|
// concurrent.
|
|
auto FoundConformance = searchInConformanceCache(type, protocol, foundEntry);
|
|
// The negative answer does not always mean that there is no conformance,
|
|
// unless it is an exact match on the type. If it is not an exact match,
|
|
// it may mean that all of the superclasses do not have this conformance,
|
|
// but the actual type may still have this conformance.
|
|
if (FoundConformance.second) {
|
|
if (FoundConformance.first || foundEntry)
|
|
return FoundConformance.first;
|
|
}
|
|
|
|
unsigned failedGeneration = ConformanceCacheGeneration;
|
|
|
|
// If we didn't have an up-to-date cache entry, scan the conformance records.
|
|
pthread_mutex_lock(&C.SectionsToScanLock);
|
|
|
|
// If we have no new information to pull in (and nobody else pulled in
|
|
// new information while we waited on the lock), we're done.
|
|
if (C.SectionsToScan.size() == numSections) {
|
|
if (failedGeneration != ConformanceCacheGeneration) {
|
|
// Someone else pulled in new conformances while we were waiting.
|
|
// Start over with our newly-populated cache.
|
|
pthread_mutex_unlock(&C.SectionsToScanLock);
|
|
type = origType;
|
|
goto recur;
|
|
}
|
|
|
|
|
|
// Hash and lookup the type-protocol pair in the cache.
|
|
size_t hash = hashTypeProtocolPair(type, protocol);
|
|
ConcurrentList<ConformanceCacheEntry> &Bucket =
|
|
C.Cache.findOrAllocateNode(hash);
|
|
Bucket.push_front(ConformanceCacheEntry::createFailure(
|
|
type, protocol, C.SectionsToScan.size()));
|
|
pthread_mutex_unlock(&C.SectionsToScanLock);
|
|
return nullptr;
|
|
}
|
|
|
|
// Update the last known number of sections to scan.
|
|
numSections = C.SectionsToScan.size();
|
|
|
|
// Scan only sections that were not scanned yet.
|
|
unsigned sectionIdx = foundEntry ? foundEntry->getFailureGeneration() : 0;
|
|
unsigned endSectionIdx = C.SectionsToScan.size();
|
|
|
|
for (; sectionIdx < endSectionIdx; ++sectionIdx) {
|
|
auto §ion = C.SectionsToScan[sectionIdx];
|
|
// Eagerly pull records for nondependent witnesses into our cache.
|
|
for (const auto &record : section) {
|
|
// If the record applies to a specific type, cache it.
|
|
if (auto metadata = record.getCanonicalTypeMetadata()) {
|
|
auto P = record.getProtocol();
|
|
|
|
// Look for an exact match.
|
|
if (protocol != P)
|
|
continue;
|
|
|
|
if (!isRelatedType(type, metadata))
|
|
continue;
|
|
|
|
// Hash and lookup the type-protocol pair in the cache.
|
|
size_t hash = hashTypeProtocolPair(metadata, P);
|
|
ConcurrentList<ConformanceCacheEntry> &Bucket =
|
|
C.Cache.findOrAllocateNode(hash);
|
|
|
|
auto witness = record.getWitnessTable(metadata);
|
|
if (witness)
|
|
Bucket.push_front(
|
|
ConformanceCacheEntry::createSuccess(metadata, P, witness));
|
|
else
|
|
Bucket.push_front(ConformanceCacheEntry::createFailure(
|
|
metadata, P, C.SectionsToScan.size()));
|
|
|
|
// If the record provides a nondependent witness table for all instances
|
|
// of a generic type, cache it for the generic pattern.
|
|
// TODO: "Nondependent witness table" probably deserves its own flag.
|
|
// An accessor function might still be necessary even if the witness table
|
|
// can be shared.
|
|
} else if (record.getTypeKind()
|
|
== ProtocolConformanceTypeKind::UniqueGenericPattern
|
|
&& record.getConformanceKind()
|
|
== ProtocolConformanceReferenceKind::WitnessTable) {
|
|
|
|
auto R = record.getGenericPattern();
|
|
auto P = record.getProtocol();
|
|
|
|
// Look for an exact match.
|
|
if (protocol != P)
|
|
continue;
|
|
|
|
if (!isRelatedType(type, R))
|
|
continue;
|
|
|
|
// Hash and lookup the type-protocol pair in the cache.
|
|
size_t hash = hashTypeProtocolPair(R, P);
|
|
ConcurrentList<ConformanceCacheEntry> &Bucket =
|
|
C.Cache.findOrAllocateNode(hash);
|
|
Bucket.push_front(ConformanceCacheEntry::createSuccess(
|
|
R, P, record.getStaticWitnessTable()));
|
|
}
|
|
}
|
|
}
|
|
++ConformanceCacheGeneration;
|
|
|
|
pthread_mutex_unlock(&C.SectionsToScanLock);
|
|
// Start over with our newly-populated cache.
|
|
type = origType;
|
|
goto recur;
|
|
}
|
|
|
|
// The return type is incorrect. It is only important that it is
|
|
// passed using 'sret'.
|
|
extern "C" OpaqueExistentialContainer
|
|
_TFs24_injectValueIntoOptionalU__FQ_GSqQ__(OpaqueValue *value,
|
|
const Metadata *T);
|
|
// The return type is incorrect. It is only important that it is
|
|
// passed using 'sret'.
|
|
extern "C" OpaqueExistentialContainer
|
|
_TFs26_injectNothingIntoOptionalU__FT_GSqQ__(const Metadata *T);
|
|
|
|
static inline bool swift_isClassOrObjCExistentialImpl(const Metadata *T) {
|
|
auto kind = T->getKind();
|
|
// Classes.
|
|
if (Metadata::isAnyKindOfClass(kind))
|
|
return true;
|
|
#if SWIFT_OBJC_INTEROP
|
|
// ObjC existentials.
|
|
if (kind == MetadataKind::Existential &&
|
|
static_cast<const ExistentialTypeMetadata *>(T)->isObjC())
|
|
return true;
|
|
|
|
// Blocks are ObjC objects.
|
|
if (kind == MetadataKind::Function) {
|
|
auto fT = static_cast<const FunctionTypeMetadata *>(T);
|
|
return fT->getConvention() == FunctionMetadataConvention::Block;
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
//===----------------------------------------------------------------------===//
|
|
// Bridging to and from Objective-C
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
// protocol _ObjectiveCBridgeable {
|
|
struct _ObjectiveCBridgeableWitnessTable {
|
|
// typealias _ObjectiveCType : class
|
|
const Metadata *ObjectiveCType;
|
|
|
|
// class func _isBridgedToObjectiveC() -> bool
|
|
bool (*isBridgedToObjectiveC)(const Metadata *value, const Metadata *T);
|
|
|
|
// class func _getObjectiveCType() -> Any.Type
|
|
const Metadata *(*getObjectiveCType)(const Metadata *self,
|
|
const Metadata *selfType);
|
|
|
|
// func _bridgeToObjectiveC() -> _ObjectiveCType
|
|
HeapObject *(*bridgeToObjectiveC)(OpaqueValue *self, const Metadata *Self);
|
|
// class func _forceBridgeFromObjectiveC(x: _ObjectiveCType,
|
|
// inout result: Self?)
|
|
void (*forceBridgeFromObjectiveC)(HeapObject *sourceValue,
|
|
OpaqueValue *result,
|
|
const Metadata *self,
|
|
const Metadata *selfType);
|
|
|
|
// class func _conditionallyBridgeFromObjectiveC(x: _ObjectiveCType,
|
|
// inout result: Self?) -> Bool
|
|
bool (*conditionallyBridgeFromObjectiveC)(HeapObject *sourceValue,
|
|
OpaqueValue *result,
|
|
const Metadata *self,
|
|
const Metadata *selfType);
|
|
};
|
|
// }
|
|
|
|
} // unnamed namespace
|
|
|
|
extern "C" const ProtocolDescriptor _TMps21_ObjectiveCBridgeable;
|
|
|
|
/// Dynamic cast from a value type that conforms to the _ObjectiveCBridgeable
|
|
/// protocol to a class type, first by bridging the value to its Objective-C
|
|
/// object representation and then by dynamic casting that object to the
|
|
/// resulting target type.
|
|
static bool _dynamicCastValueToClassViaObjCBridgeable(
|
|
OpaqueValue *dest,
|
|
OpaqueValue *src,
|
|
const Metadata *srcType,
|
|
const Metadata *targetType,
|
|
const _ObjectiveCBridgeableWitnessTable *srcBridgeWitness,
|
|
DynamicCastFlags flags) {
|
|
// Check whether the source is bridged to Objective-C.
|
|
if (!srcBridgeWitness->isBridgedToObjectiveC(srcType, srcType)) {
|
|
return _fail(src, srcType, targetType, flags);
|
|
}
|
|
|
|
// Bridge the source value to an object.
|
|
auto srcBridgedObject = srcBridgeWitness->bridgeToObjectiveC(src, srcType);
|
|
|
|
// Dynamic cast the object to the resulting class type.
|
|
bool success;
|
|
if (auto cast = _dynamicCastUnknownClass(srcBridgedObject, targetType,
|
|
flags & DynamicCastFlags::Unconditional)) {
|
|
*reinterpret_cast<void **>(dest) = cast;
|
|
success = true;
|
|
} else {
|
|
success = false;
|
|
}
|
|
|
|
// Clean up the source if we're supposed to.
|
|
if (shouldDeallocateSource(success, flags)) {
|
|
srcType->vw_destroy(src);
|
|
}
|
|
|
|
// We're done.
|
|
return success;
|
|
}
|
|
|
|
/// Dynamic cast from a value type that conforms to the
|
|
/// _ObjectiveCBridgeable protocol to a class-bounded existential,
|
|
/// first by bridging the value to its Objective-C object
|
|
/// representation and then by dynamic-casting that object to the
|
|
/// resulting target type.
|
|
static bool _dynamicCastValueToClassExistentialViaObjCBridgeable(
|
|
OpaqueValue *dest,
|
|
OpaqueValue *src,
|
|
const Metadata *srcType,
|
|
const ExistentialTypeMetadata *targetType,
|
|
const _ObjectiveCBridgeableWitnessTable *srcBridgeWitness,
|
|
DynamicCastFlags flags) {
|
|
// Check whether the source is bridged to Objective-C.
|
|
if (!srcBridgeWitness->isBridgedToObjectiveC(srcType, srcType)) {
|
|
return _fail(src, srcType, targetType, flags);
|
|
}
|
|
|
|
// Bridge the source value to an object.
|
|
auto srcBridgedObject = srcBridgeWitness->bridgeToObjectiveC(src, srcType);
|
|
|
|
// Try to cast the object to the destination existential.
|
|
DynamicCastFlags subFlags = DynamicCastFlags::TakeOnSuccess
|
|
| DynamicCastFlags::DestroyOnFailure;
|
|
if (flags & DynamicCastFlags::Unconditional)
|
|
subFlags |= DynamicCastFlags::Unconditional;
|
|
bool success = _dynamicCastToExistential(
|
|
dest,
|
|
(OpaqueValue *)&srcBridgedObject,
|
|
swift_getObjectType(srcBridgedObject),
|
|
targetType,
|
|
subFlags);
|
|
|
|
// Clean up the source if we're supposed to.
|
|
if (shouldDeallocateSource(success, flags)) {
|
|
srcType->vw_destroy(src);
|
|
}
|
|
|
|
// We're done.
|
|
return success;
|
|
}
|
|
|
|
/// Dynamic cast from a class type to a value type that conforms to the
|
|
/// _ObjectiveCBridgeable, first by dynamic casting the object to the
|
|
/// Objective-C class to which the value type is bridged, and then bridging
|
|
/// from that object to the value type via the witness table.
|
|
static bool _dynamicCastClassToValueViaObjCBridgeable(
|
|
OpaqueValue *dest,
|
|
OpaqueValue *src,
|
|
const Metadata *srcType,
|
|
const Metadata *targetType,
|
|
const _ObjectiveCBridgeableWitnessTable *targetBridgeWitness,
|
|
DynamicCastFlags flags) {
|
|
// Check whether the target is bridged to Objective-C.
|
|
if (!targetBridgeWitness->isBridgedToObjectiveC(targetType, targetType)) {
|
|
return _fail(src, srcType, targetType, flags);
|
|
}
|
|
|
|
// Determine the class type to which the target value type is bridged.
|
|
auto targetBridgedClass = targetBridgeWitness->getObjectiveCType(targetType,
|
|
targetType);
|
|
|
|
// Dynamic cast the source object to the class type to which the target value
|
|
// type is bridged. If we succeed, we can bridge from there; if we fail,
|
|
// there's nothing more to do.
|
|
void *srcObject = *reinterpret_cast<void * const *>(src);
|
|
if (!_dynamicCastUnknownClass(srcObject,
|
|
targetBridgedClass,
|
|
flags & DynamicCastFlags::Unconditional)) {
|
|
return _fail(src, srcType, targetType, flags);
|
|
}
|
|
|
|
// Unless we're always supposed to consume the input, retain the
|
|
// object because the witness takes it at +1.
|
|
bool alwaysConsumeSrc = (flags & DynamicCastFlags::TakeOnSuccess) &&
|
|
(flags & DynamicCastFlags::DestroyOnFailure);
|
|
if (!alwaysConsumeSrc) {
|
|
swift_unknownRetain(srcObject);
|
|
}
|
|
|
|
// Object that frees a buffer when it goes out of scope.
|
|
struct FreeBuffer {
|
|
void *Buffer = nullptr;
|
|
~FreeBuffer() { free(Buffer); }
|
|
} freeBuffer;
|
|
|
|
// Allocate a buffer to store the T? returned by bridging.
|
|
// The extra byte is for the tag.
|
|
const std::size_t inlineValueSize = 3 * sizeof(void*);
|
|
alignas(swift_max_align_t) char inlineBuffer[inlineValueSize + 1];
|
|
void *optDestBuffer;
|
|
if (targetType->getValueWitnesses()->getStride() <= inlineValueSize) {
|
|
// Use the inline buffer.
|
|
optDestBuffer = inlineBuffer;
|
|
} else {
|
|
// Allocate a buffer.
|
|
optDestBuffer = malloc(targetType->getValueWitnesses()->size);
|
|
freeBuffer.Buffer = optDestBuffer;
|
|
}
|
|
|
|
// Initialize the buffer as an empty optional.
|
|
swift_storeEnumTagSinglePayload((OpaqueValue *)optDestBuffer, targetType,
|
|
0, 1);
|
|
|
|
// Perform the bridging operation.
|
|
bool success;
|
|
if (flags & DynamicCastFlags::Unconditional) {
|
|
// For an unconditional dynamic cast, use forceBridgeFromObjectiveC.
|
|
targetBridgeWitness->forceBridgeFromObjectiveC(
|
|
(HeapObject *)srcObject, (OpaqueValue *)optDestBuffer,
|
|
targetType, targetType);
|
|
success = true;
|
|
} else {
|
|
// For a conditional dynamic cast, use conditionallyBridgeFromObjectiveC.
|
|
success = targetBridgeWitness->conditionallyBridgeFromObjectiveC(
|
|
(HeapObject *)srcObject, (OpaqueValue *)optDestBuffer,
|
|
targetType, targetType);
|
|
}
|
|
|
|
// If we succeeded, take from the optional buffer into the
|
|
// destination buffer.
|
|
if (success) {
|
|
targetType->vw_initializeWithTake(dest, (OpaqueValue *)optDestBuffer);
|
|
}
|
|
|
|
// Unless we're always supposed to consume the input, release the
|
|
// input if we need to now.
|
|
if (!alwaysConsumeSrc && shouldDeallocateSource(success, flags)) {
|
|
swift_unknownRelease(srcObject);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
//===--- Bridging helpers for the Swift stdlib ----------------------------===//
|
|
// Functions that must discover and possibly use an arbitrary type's
|
|
// conformance to a given protocol. See ../core/BridgeObjectiveC.swift for
|
|
// documentation.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
extern "C" const _ObjectiveCBridgeableWitnessTable
|
|
_TWPVs19_BridgeableMetatypes21_ObjectiveCBridgeables;
|
|
|
|
static const _ObjectiveCBridgeableWitnessTable *
|
|
findBridgeWitness(const Metadata *T) {
|
|
auto w = swift_conformsToProtocol(T, &_TMps21_ObjectiveCBridgeable);
|
|
if (LLVM_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: {
|
|
auto metaTy = static_cast<const MetatypeMetadata *>(T);
|
|
if (metaTy->InstanceType->isAnyClass())
|
|
return &_TWPVs19_BridgeableMetatypes21_ObjectiveCBridgeables;
|
|
break;
|
|
}
|
|
case MetadataKind::ExistentialMetatype: {
|
|
auto existentialMetaTy =
|
|
static_cast<const ExistentialMetatypeMetadata *>(T);
|
|
if (existentialMetaTy->isObjC())
|
|
return &_TWPVs19_BridgeableMetatypes21_ObjectiveCBridgeables;
|
|
break;
|
|
}
|
|
|
|
case MetadataKind::Class:
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Tuple:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::Existential:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
case MetadataKind::ForeignClass:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
break;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/// \param value passed at +1, consumed.
|
|
extern "C" HeapObject *swift_bridgeNonVerbatimToObjectiveC(
|
|
OpaqueValue *value, const Metadata *T
|
|
) {
|
|
assert(!swift_isClassOrObjCExistentialImpl(T));
|
|
|
|
if (const auto *bridgeWitness = findBridgeWitness(T)) {
|
|
if (!bridgeWitness->isBridgedToObjectiveC(T, T)) {
|
|
// Witnesses take 'self' at +0, so we still need to consume the +1 argument.
|
|
T->vw_destroy(value);
|
|
return nullptr;
|
|
}
|
|
auto result = bridgeWitness->bridgeToObjectiveC(value, T);
|
|
// Witnesses take 'self' at +0, so we still need to consume the +1 argument.
|
|
T->vw_destroy(value);
|
|
return result;
|
|
}
|
|
|
|
// Consume the +1 argument.
|
|
T->vw_destroy(value);
|
|
return nullptr;
|
|
}
|
|
|
|
extern "C" const Metadata *swift_getBridgedNonVerbatimObjectiveCType(
|
|
const Metadata *value, const Metadata *T
|
|
) {
|
|
// Classes and Objective-C existentials bridge verbatim.
|
|
assert(!swift_isClassOrObjCExistentialImpl(T));
|
|
|
|
// Check if the type conforms to _BridgedToObjectiveC, in which case
|
|
// we'll extract its associated type.
|
|
if (const auto *bridgeWitness = findBridgeWitness(T)) {
|
|
return bridgeWitness->getObjectiveCType(T, T);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// @_silgen_name("swift_bridgeNonVerbatimFromObjectiveC")
|
|
// func _bridgeNonVerbatimFromObjectiveC<NativeType>(
|
|
// x: AnyObject,
|
|
// nativeType: NativeType.Type
|
|
// inout result: T?
|
|
// )
|
|
extern "C" void
|
|
swift_bridgeNonVerbatimFromObjectiveC(
|
|
HeapObject *sourceValue,
|
|
const Metadata *nativeType,
|
|
OpaqueValue *destValue,
|
|
const Metadata *nativeType_
|
|
) {
|
|
// Check if the type conforms to _BridgedToObjectiveC.
|
|
if (const auto *bridgeWitness = findBridgeWitness(nativeType)) {
|
|
// if the type also conforms to _ConditionallyBridgedToObjectiveC,
|
|
// make sure it bridges at runtime
|
|
if (bridgeWitness->isBridgedToObjectiveC(nativeType, nativeType)) {
|
|
// Check if sourceValue has the _ObjectiveCType type required by the
|
|
// protocol.
|
|
const Metadata *objectiveCType =
|
|
bridgeWitness->getObjectiveCType(nativeType, nativeType);
|
|
|
|
auto sourceValueAsObjectiveCType =
|
|
const_cast<void*>(swift_dynamicCastUnknownClass(sourceValue,
|
|
objectiveCType));
|
|
|
|
if (sourceValueAsObjectiveCType) {
|
|
// The type matches. _forceBridgeFromObjectiveC returns `Self`, so
|
|
// we can just return it directly.
|
|
bridgeWitness->forceBridgeFromObjectiveC(
|
|
static_cast<HeapObject*>(sourceValueAsObjectiveCType),
|
|
destValue, nativeType, nativeType);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fail.
|
|
swift::crash("value type is not bridged to Objective-C");
|
|
}
|
|
|
|
// @_silgen_name("swift_bridgeNonVerbatimFromObjectiveCConditional")
|
|
// func _bridgeNonVerbatimFromObjectiveCConditional<NativeType>(
|
|
// x: AnyObject,
|
|
// nativeType: T.Type,
|
|
// inout result: T?
|
|
// ) -> Bool
|
|
extern "C" bool
|
|
swift_bridgeNonVerbatimFromObjectiveCConditional(
|
|
HeapObject *sourceValue,
|
|
const Metadata *nativeType,
|
|
OpaqueValue *destValue,
|
|
const Metadata *nativeType_
|
|
) {
|
|
// Local function that releases the source and returns false.
|
|
auto fail = [&] () -> bool {
|
|
swift_unknownRelease(sourceValue);
|
|
return false;
|
|
};
|
|
|
|
// Check if the type conforms to _BridgedToObjectiveC.
|
|
const auto *bridgeWitness = findBridgeWitness(nativeType);
|
|
if (!bridgeWitness)
|
|
return fail();
|
|
|
|
// Dig out the Objective-C class type through which the native type
|
|
// is bridged.
|
|
const Metadata *objectiveCType =
|
|
bridgeWitness->getObjectiveCType(nativeType, nativeType);
|
|
|
|
// Check whether we can downcast the source value to the Objective-C
|
|
// type.
|
|
auto sourceValueAsObjectiveCType =
|
|
const_cast<void*>(swift_dynamicCastUnknownClass(sourceValue,
|
|
objectiveCType));
|
|
if (!sourceValueAsObjectiveCType)
|
|
return fail();
|
|
|
|
// If the type also conforms to _ConditionallyBridgedToObjectiveC,
|
|
// use conditional bridging.
|
|
return bridgeWitness->conditionallyBridgeFromObjectiveC(
|
|
static_cast<HeapObject*>(sourceValueAsObjectiveCType),
|
|
destValue, nativeType, nativeType);
|
|
}
|
|
|
|
// func isBridgedNonVerbatimToObjectiveC<T>(x: T.Type) -> Bool
|
|
extern "C" bool swift_isBridgedNonVerbatimToObjectiveC(
|
|
const Metadata *value, const Metadata *T
|
|
) {
|
|
assert(!swift_isClassOrObjCExistentialImpl(T));
|
|
|
|
auto bridgeWitness = findBridgeWitness(T);
|
|
return bridgeWitness && bridgeWitness->isBridgedToObjectiveC(value, T);
|
|
}
|
|
#endif
|
|
|
|
// func isClassOrObjCExistential<T>(x: T.Type) -> Bool
|
|
extern "C" bool swift_isClassOrObjCExistential(const Metadata *value,
|
|
const Metadata *T) {
|
|
return swift_isClassOrObjCExistentialImpl(T);
|
|
}
|
|
|
|
// func _swift_getSuperclass_nonNull(_: AnyClass) -> AnyClass?
|
|
extern "C" const Metadata *_swift_getSuperclass_nonNull(
|
|
const Metadata *theClass
|
|
) {
|
|
if (const ClassMetadata *classType = theClass->getClassObject())
|
|
if (classHasSuperclass(classType))
|
|
return swift_getObjCClassMetadata(classType->SuperClass);
|
|
return nullptr;
|
|
}
|
|
|
|
extern "C" bool swift_isClassType(const Metadata *type) {
|
|
return Metadata::isAnyKindOfClass(type->getKind());
|
|
}
|
|
|
|
extern "C" bool swift_isOptionalType(const Metadata *type) {
|
|
return type->getKind() == MetadataKind::Optional;
|
|
}
|