mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Add swift_dynamicCast.
This is the most general dynamic cast operation, permitting arbitary source and destination types and handling arbitrary changes in representation. A value of the destination type is constructed in an address provided by the caller; flags control the behavior w.r.t. the source value. Not yet used; probably buggy in various particulars. Swift SVN r18815
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "swift/Basic/LLVM.h"
|
||||
#include "swift/Basic/Fallthrough.h"
|
||||
#include "swift/Runtime/Enum.h"
|
||||
#include "swift/Runtime/HeapObject.h"
|
||||
@@ -21,6 +22,7 @@
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "Debug.h"
|
||||
#include "ExistentialMetadataImpl.h"
|
||||
#include "Private.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
@@ -35,6 +37,7 @@ using namespace metadataimpl;
|
||||
extern "C" const ClassMetadata* object_getClass(const void *);
|
||||
extern "C" const char* class_getName(const ClassMetadata*);
|
||||
extern "C" const ClassMetadata* class_getSuperclass(const ClassMetadata*);
|
||||
extern "C" const Metadata *swift_getObjectType(const void *);
|
||||
|
||||
// Aliases for Swift runtime entry points for Objective-C types.
|
||||
extern "C" const void *swift_dynamicCastObjCProtocolConditional(
|
||||
@@ -42,6 +45,31 @@ extern "C" const void *swift_dynamicCastObjCProtocolConditional(
|
||||
size_t numProtocols,
|
||||
const ProtocolDescriptor * const *protocols);
|
||||
|
||||
/// Report a dynamic cast failure.
|
||||
LLVM_ATTRIBUTE_NORETURN
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE // Minimize trashed registers
|
||||
static void _dynamicCastFailure(const Metadata *sourceType,
|
||||
const Metadata *targetType) {
|
||||
swift::crash("Swift dynamic cast failure");
|
||||
}
|
||||
|
||||
/// 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");
|
||||
}
|
||||
|
||||
/// 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)
|
||||
_dynamicCastFailure(srcType, targetType);
|
||||
if (flags & DynamicCastFlags::DestroyOnFailure)
|
||||
srcType->vw_destroy(srcValue);
|
||||
return false;
|
||||
}
|
||||
|
||||
static size_t
|
||||
_setupClassMask() {
|
||||
void *handle = dlopen(nullptr, RTLD_LAZY);
|
||||
@@ -101,72 +129,40 @@ swift::swift_dynamicCastClassUnconditional(const void *object,
|
||||
return value;
|
||||
}
|
||||
|
||||
static const OpaqueValue *
|
||||
_dynamicCastToExistential(const OpaqueValue *value,
|
||||
const Metadata *sourceType,
|
||||
const ExistentialTypeMetadata *targetType) {
|
||||
for (unsigned i = 0, n = targetType->Protocols.NumProtocols; i != n; ++i) {
|
||||
auto *protocol = targetType->Protocols[i];
|
||||
|
||||
// Handle AnyObject directly.
|
||||
// FIXME: strcmp here is horribly slow.
|
||||
if (strcmp(protocol->Name, "_TtPSs9AnyObject_") == 0) {
|
||||
switch (sourceType->getKind()) {
|
||||
case MetadataKind::Class:
|
||||
case MetadataKind::ObjCClassWrapper:
|
||||
case MetadataKind::ForeignClass: // FIXME
|
||||
// Classes conform to AnyObject.
|
||||
break;
|
||||
|
||||
case MetadataKind::Existential: {
|
||||
auto sourceExistential
|
||||
= static_cast<const ExistentialTypeMetadata*>(sourceType);
|
||||
// The existential conforms to AnyObject if it's class-constrained.
|
||||
if (sourceExistential->Flags.getClassConstraint()
|
||||
!= ProtocolClassConstraint::Class)
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
case MetadataKind::ExistentialMetatype: // FIXME
|
||||
case MetadataKind::Function:
|
||||
case MetadataKind::Block: // FIXME
|
||||
case MetadataKind::HeapArray:
|
||||
case MetadataKind::HeapLocalVariable:
|
||||
case MetadataKind::Metatype:
|
||||
case MetadataKind::Enum:
|
||||
case MetadataKind::Opaque:
|
||||
case MetadataKind::PolyFunction:
|
||||
case MetadataKind::Struct:
|
||||
case MetadataKind::Tuple:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// FIXME: Can't handle protocols that require witness tables.
|
||||
if (protocol->Flags.needsWitnessTable())
|
||||
return nullptr;
|
||||
|
||||
// For Objective-C protocols, check whether we have a class that
|
||||
// conforms to the given protocol.
|
||||
switch (sourceType->getKind()) {
|
||||
case MetadataKind::Class:
|
||||
case MetadataKind::ObjCClassWrapper: {
|
||||
const void *object
|
||||
= *reinterpret_cast<const void * const *>(value);
|
||||
if (swift_dynamicCastObjCProtocolConditional(object, 1, &protocol)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
static bool _unknownClassConformsToObjCProtocol(const OpaqueValue *value,
|
||||
const ProtocolDescriptor *protocol) {
|
||||
const void *object
|
||||
= *reinterpret_cast<const void * const *>(value);
|
||||
return swift_dynamicCastObjCProtocolConditional(object, 1, &protocol);
|
||||
}
|
||||
|
||||
/// 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 void **conformance) {
|
||||
// Handle AnyObject directly.
|
||||
// FIXME: strcmp here is horribly slow.
|
||||
if (strcmp(protocol->Name, "_TtPSs9AnyObject_") == 0) {
|
||||
switch (type->getKind()) {
|
||||
case MetadataKind::Class:
|
||||
case MetadataKind::ObjCClassWrapper:
|
||||
case MetadataKind::ForeignClass:
|
||||
return nullptr;
|
||||
// Classes conform to AnyObject.
|
||||
return true;
|
||||
|
||||
case MetadataKind::Existential: // FIXME
|
||||
case MetadataKind::Existential: {
|
||||
auto sourceExistential = cast<ExistentialTypeMetadata>(type);
|
||||
// The existential conforms to AnyObject if it's class-constrained.
|
||||
return sourceExistential->isClassBounded();
|
||||
}
|
||||
|
||||
case MetadataKind::ExistentialMetatype: // FIXME
|
||||
case MetadataKind::Function:
|
||||
case MetadataKind::Block: // FIXME
|
||||
@@ -178,15 +174,204 @@ _dynamicCastToExistential(const OpaqueValue *value,
|
||||
case MetadataKind::PolyFunction:
|
||||
case MetadataKind::Struct:
|
||||
case MetadataKind::Tuple:
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
_failCorruptType(type);
|
||||
}
|
||||
|
||||
// FIXME: Can't handle protocols that require witness tables.
|
||||
if (protocol->Flags.needsWitnessTable())
|
||||
return false;
|
||||
|
||||
// For Objective-C protocols, check whether we have a class that
|
||||
// conforms to the given protocol.
|
||||
switch (type->getKind()) {
|
||||
case MetadataKind::Class:
|
||||
if (value) {
|
||||
return _unknownClassConformsToObjCProtocol(value, protocol);
|
||||
} else {
|
||||
return _swift_classConformsToObjCProtocol(type, protocol);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
case MetadataKind::ObjCClassWrapper: {
|
||||
if (value) {
|
||||
return _unknownClassConformsToObjCProtocol(value, protocol);
|
||||
} else {
|
||||
auto wrapper = cast<ObjCClassWrapperMetadata>(type);
|
||||
return _swift_classConformsToObjCProtocol(wrapper->Class, protocol);
|
||||
}
|
||||
}
|
||||
|
||||
case MetadataKind::ForeignClass:
|
||||
if (value)
|
||||
return _unknownClassConformsToObjCProtocol(value, protocol);
|
||||
return false;
|
||||
|
||||
case MetadataKind::Existential: // FIXME
|
||||
case MetadataKind::ExistentialMetatype: // FIXME
|
||||
case MetadataKind::Function:
|
||||
case MetadataKind::Block: // FIXME
|
||||
case MetadataKind::HeapArray:
|
||||
case MetadataKind::HeapLocalVariable:
|
||||
case MetadataKind::Metatype:
|
||||
case MetadataKind::Enum:
|
||||
case MetadataKind::Opaque:
|
||||
case MetadataKind::PolyFunction:
|
||||
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 void **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 const OpaqueValue *
|
||||
_dynamicCastToExistential(const OpaqueValue *value,
|
||||
const Metadata *sourceType,
|
||||
const ExistentialTypeMetadata *targetType) {
|
||||
for (unsigned i = 0, n = targetType->Protocols.NumProtocols; i != n; ++i) {
|
||||
auto *protocol = targetType->Protocols[i];
|
||||
if (!_conformsToProtocol(value, sourceType, protocol, nullptr))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
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<void**>(value));
|
||||
return;
|
||||
}
|
||||
|
||||
case MetadataKind::Existential: {
|
||||
auto existentialType = cast<ExistentialTypeMetadata>(type);
|
||||
if (existentialType->isClassBounded()) {
|
||||
auto existential =
|
||||
reinterpret_cast<ClassExistentialContainer*>(value);
|
||||
outValue = (OpaqueValue*) &existential->Value;
|
||||
outType = swift_getObjectType(existential->Value);
|
||||
return;
|
||||
} else {
|
||||
auto existential =
|
||||
reinterpret_cast<OpaqueExistentialContainer*>(value);
|
||||
OpaqueValue *existentialValue =
|
||||
existential->Type->vw_projectBuffer(&existential->Buffer);
|
||||
findDynamicValueAndType(existentialValue, existential->Type,
|
||||
outValue, outType);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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::Block:
|
||||
case MetadataKind::HeapArray:
|
||||
case MetadataKind::HeapLocalVariable:
|
||||
case MetadataKind::Enum:
|
||||
case MetadataKind::Opaque:
|
||||
case MetadataKind::PolyFunction:
|
||||
case MetadataKind::Struct:
|
||||
case MetadataKind::Tuple:
|
||||
outValue = value;
|
||||
outType = type;
|
||||
return;
|
||||
}
|
||||
_failCorruptType(type);
|
||||
}
|
||||
|
||||
/// Perform a dynamic cast to an existential type.
|
||||
static bool _dynamicCastToExistential(OpaqueValue *dest,
|
||||
OpaqueValue *src,
|
||||
const Metadata *srcType,
|
||||
const ExistentialTypeMetadata *targetType,
|
||||
DynamicCastFlags flags) {
|
||||
// Find the actual type of the source.
|
||||
OpaqueValue *srcDynamicValue;
|
||||
const Metadata *srcDynamicType;
|
||||
findDynamicValueAndType(src, srcType, srcDynamicValue, srcDynamicType);
|
||||
|
||||
// The representation of an existential is different for
|
||||
// class-bounded protocols.
|
||||
if (targetType->isClassBounded()) {
|
||||
auto destExistential =
|
||||
reinterpret_cast<ClassExistentialContainer*>(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);
|
||||
|
||||
auto object = *(reinterpret_cast<HeapObject**>(srcDynamicValue));
|
||||
destExistential->Value = object;
|
||||
if (!(flags & DynamicCastFlags::TakeOnSuccess)) {
|
||||
swift_retain_noresult(object);
|
||||
}
|
||||
return true;
|
||||
|
||||
} else {
|
||||
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 (flags & DynamicCastFlags::TakeOnSuccess) {
|
||||
srcDynamicType->vw_initializeBufferWithTake(&destExistential->Buffer,
|
||||
srcDynamicValue);
|
||||
} else {
|
||||
srcDynamicType->vw_initializeBufferWithCopy(&destExistential->Buffer,
|
||||
srcDynamicValue);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
@@ -221,6 +406,8 @@ swift::swift_dynamicCastUnknownClass(const void *object,
|
||||
swift::crash("bad metadata kind!");
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
@@ -363,7 +550,7 @@ swift::swift_dynamicCastMetatypeUnconditional(const Metadata *sourceType,
|
||||
case MetadataKind::PolyFunction:
|
||||
case MetadataKind::Struct:
|
||||
case MetadataKind::Tuple:
|
||||
swift::crash("Swift dynamic cast failed");
|
||||
_dynamicCastFailure(sourceType, targetType);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -383,11 +570,435 @@ swift::swift_dynamicCastMetatypeUnconditional(const Metadata *sourceType,
|
||||
// The cast succeeds only if the metadata pointers are statically
|
||||
// equivalent.
|
||||
if (sourceType != targetType)
|
||||
swift::crash("Swift dynamic cast failed");
|
||||
_dynamicCastFailure(sourceType, targetType);
|
||||
return origSourceType;
|
||||
}
|
||||
}
|
||||
|
||||
/// Do a dynamic cast to the target class.
|
||||
static bool _dynamicCastUnknownClass(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)) {
|
||||
#if SWIFT_OBJC_INTEROP
|
||||
swift_unknownRetain(result);
|
||||
#else
|
||||
swift_retain(result);
|
||||
#endif
|
||||
}
|
||||
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) {
|
||||
#if SWIFT_OBJC_INTEROP
|
||||
swift_unknownRelease(object);
|
||||
#else
|
||||
swift_release(object);
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, store to the destination and return true.
|
||||
*destSlot = result;
|
||||
if (!(flags & DynamicCastFlags::TakeOnSuccess)) {
|
||||
#if SWIFT_OBJC_INTEROP
|
||||
swift_unknownRetain(result);
|
||||
#else
|
||||
swift_retain(result);
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
if (srcType->isClassBounded()) {
|
||||
auto classContainer =
|
||||
reinterpret_cast<ClassExistentialContainer*>(src);
|
||||
void *obj = classContainer->Value;
|
||||
return _dynamicCastUnknownClass(dest, obj, targetType, flags);
|
||||
} else {
|
||||
auto opaqueContainer =
|
||||
reinterpret_cast<OpaqueExistentialContainer*>(src);
|
||||
auto srcCapturedType = opaqueContainer->Type;
|
||||
return swift_dynamicCast(dest,
|
||||
srcCapturedType->vw_projectBuffer(&opaqueContainer->Buffer),
|
||||
srcCapturedType,
|
||||
targetType,
|
||||
flags);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
if (srcType->isClassBounded()) {
|
||||
auto classContainer =
|
||||
reinterpret_cast<const ClassExistentialContainer*>(src);
|
||||
srcValue = (OpaqueValue*) &classContainer->Value;
|
||||
void *obj = classContainer->Value;
|
||||
srcCapturedType = swift_unknownTypeOf(reinterpret_cast<HeapObject*>(obj));
|
||||
} else {
|
||||
auto opaqueContainer = reinterpret_cast<OpaqueExistentialContainer*>(src);
|
||||
srcCapturedType = opaqueContainer->Type;
|
||||
srcValue = srcCapturedType->vw_projectBuffer(&opaqueContainer->Buffer);
|
||||
}
|
||||
|
||||
return swift_dynamicCast(dest, srcValue, srcCapturedType,
|
||||
targetType, flags);
|
||||
}
|
||||
|
||||
/// Perform a dynamic cast of a metatype to a metatype.
|
||||
static bool _dynamicCastMetatypeToMetatype(OpaqueValue *dest,
|
||||
const Metadata *metatype,
|
||||
const Metadata *targetType,
|
||||
DynamicCastFlags flags) {
|
||||
const Metadata *result;
|
||||
if (flags & DynamicCastFlags::Unconditional) {
|
||||
result = swift_dynamicCastMetatypeUnconditional(metatype, targetType);
|
||||
} else {
|
||||
result = swift_dynamicCastMetatype(metatype, targetType);
|
||||
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) {
|
||||
// Class values are currently never metatypes (?).
|
||||
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)
|
||||
_dynamicCastFailure(swift_getObjectType(object), targetType);
|
||||
if (flags & DynamicCastFlags::DestroyOnFailure)
|
||||
swift_release((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);
|
||||
if (srcExistentialType->isClassBounded()) {
|
||||
auto srcExistential = (ClassExistentialContainer*) src;
|
||||
return _dynamicCastUnknownClassToMetatype(dest,
|
||||
srcExistential->Value,
|
||||
targetType, flags);
|
||||
} else {
|
||||
auto srcExistential = (OpaqueExistentialContainer*) src;
|
||||
auto srcValueType = srcExistential->Type;
|
||||
auto srcValue = srcValueType->vw_projectBuffer(&srcExistential->Buffer);
|
||||
return _dynamicCastToMetatype(dest, srcValue, srcValueType,
|
||||
targetType, flags);
|
||||
}
|
||||
}
|
||||
|
||||
case MetadataKind::Class:
|
||||
case MetadataKind::ObjCClassWrapper:
|
||||
case MetadataKind::ForeignClass: {
|
||||
auto object = reinterpret_cast<void**>(src);
|
||||
return _dynamicCastUnknownClassToMetatype(dest, object, targetType, flags);
|
||||
}
|
||||
|
||||
case MetadataKind::Function:
|
||||
case MetadataKind::Block:
|
||||
case MetadataKind::HeapArray:
|
||||
case MetadataKind::HeapLocalVariable:
|
||||
case MetadataKind::Enum:
|
||||
case MetadataKind::Opaque:
|
||||
case MetadataKind::PolyFunction:
|
||||
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.
|
||||
|
||||
// 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;
|
||||
for (unsigned i = 0, n = protocols.NumProtocols; i != n; ++i) {
|
||||
const ProtocolDescriptor *protocol = protocols[i];
|
||||
if (!_conformsToProtocol(nullptr, srcMetatype, protocol, nullptr)) {
|
||||
if (flags & DynamicCastFlags::Unconditional)
|
||||
_dynamicCastFailure(srcMetatype, targetType);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (writeDestMetatype)
|
||||
*((const Metadata **) dest) = 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)
|
||||
_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-recurse,
|
||||
// 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)
|
||||
_dynamicCastFailure(swift_getObjectType(object), targetType);
|
||||
if (flags & DynamicCastFlags::DestroyOnFailure)
|
||||
swift_release((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);
|
||||
if (srcExistentialType->isClassBounded()) {
|
||||
auto srcExistential = (ClassExistentialContainer*) src;
|
||||
return _dynamicCastUnknownClassToExistentialMetatype(dest,
|
||||
srcExistential->Value,
|
||||
targetType, flags);
|
||||
} else {
|
||||
auto srcExistential = (OpaqueExistentialContainer*) src;
|
||||
auto srcValueType = srcExistential->Type;
|
||||
auto srcValue = srcValueType->vw_projectBuffer(&srcExistential->Buffer);
|
||||
return _dynamicCastToExistentialMetatype(dest, srcValue, srcValueType,
|
||||
targetType, flags);
|
||||
}
|
||||
}
|
||||
|
||||
case MetadataKind::Class:
|
||||
case MetadataKind::ObjCClassWrapper:
|
||||
case MetadataKind::ForeignClass:
|
||||
case MetadataKind::Function:
|
||||
case MetadataKind::Block:
|
||||
case MetadataKind::HeapArray:
|
||||
case MetadataKind::HeapLocalVariable:
|
||||
case MetadataKind::Enum:
|
||||
case MetadataKind::Opaque:
|
||||
case MetadataKind::PolyFunction:
|
||||
case MetadataKind::Struct:
|
||||
case MetadataKind::Tuple:
|
||||
if (flags & DynamicCastFlags::Unconditional) {
|
||||
_dynamicCastFailure(srcType, targetType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
_failCorruptType(srcType);
|
||||
}
|
||||
|
||||
/// Perform a dynamic cast to an arbitrary type.
|
||||
bool swift::swift_dynamicCast(OpaqueValue *dest,
|
||||
OpaqueValue *src,
|
||||
const Metadata *srcType,
|
||||
const Metadata *targetType,
|
||||
DynamicCastFlags flags) {
|
||||
switch (targetType->getKind()) {
|
||||
|
||||
// Casts to class type.
|
||||
case MetadataKind::Class:
|
||||
case MetadataKind::ObjCClassWrapper:
|
||||
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 _dynamicCastUnknownClass(dest, object,
|
||||
targetType, flags);
|
||||
}
|
||||
|
||||
case MetadataKind::Existential: {
|
||||
auto srcExistentialType = cast<ExistentialTypeMetadata>(srcType);
|
||||
return _dynamicCastToUnknownClassFromExistential(dest, src,
|
||||
srcExistentialType,
|
||||
targetType, flags);
|
||||
}
|
||||
|
||||
case MetadataKind::ExistentialMetatype:
|
||||
case MetadataKind::Function:
|
||||
case MetadataKind::Block:
|
||||
case MetadataKind::HeapArray:
|
||||
case MetadataKind::HeapLocalVariable:
|
||||
case MetadataKind::Metatype:
|
||||
case MetadataKind::Enum:
|
||||
case MetadataKind::Opaque:
|
||||
case MetadataKind::PolyFunction:
|
||||
case MetadataKind::Struct:
|
||||
case MetadataKind::Tuple:
|
||||
return nullptr;
|
||||
}
|
||||
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);
|
||||
|
||||
// The non-polymorphic types.
|
||||
case MetadataKind::Function:
|
||||
case MetadataKind::Block:
|
||||
case MetadataKind::HeapArray:
|
||||
case MetadataKind::HeapLocalVariable:
|
||||
case MetadataKind::Enum:
|
||||
case MetadataKind::Opaque:
|
||||
case MetadataKind::PolyFunction:
|
||||
case MetadataKind::Struct:
|
||||
case MetadataKind::Tuple:
|
||||
// If there's an exact type match, we're done.
|
||||
if (srcType == targetType) {
|
||||
if (flags & DynamicCastFlags::TakeOnSuccess) {
|
||||
srcType->vw_initializeWithTake(dest, src);
|
||||
} else {
|
||||
srcType->vw_initializeWithCopy(dest, src);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
const OpaqueValue *
|
||||
swift::swift_dynamicCastIndirect(const OpaqueValue *value,
|
||||
const Metadata *sourceType,
|
||||
|
||||
Reference in New Issue
Block a user