mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Dynamic casting: handle value-to-class-existential casts via bridging.
This allows one to cast a value type (say, [String]) to AnyObject or NSCoding, for example. It's another piece of <rdar://problem/17637959>. Swift SVN r20964
This commit is contained in:
@@ -114,6 +114,9 @@ inline bool operator&(DynamicCastFlags a, DynamicCastFlags b) {
|
|||||||
inline DynamicCastFlags operator|(DynamicCastFlags a, DynamicCastFlags b) {
|
inline DynamicCastFlags operator|(DynamicCastFlags a, DynamicCastFlags b) {
|
||||||
return DynamicCastFlags(size_t(a) | size_t(b));
|
return DynamicCastFlags(size_t(a) | size_t(b));
|
||||||
}
|
}
|
||||||
|
inline DynamicCastFlags operator-(DynamicCastFlags a, DynamicCastFlags b) {
|
||||||
|
return DynamicCastFlags(size_t(a) & ~size_t(b));
|
||||||
|
}
|
||||||
inline DynamicCastFlags &operator|=(DynamicCastFlags &a, DynamicCastFlags b) {
|
inline DynamicCastFlags &operator|=(DynamicCastFlags &a, DynamicCastFlags b) {
|
||||||
return a = (a | b);
|
return a = (a | b);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,30 @@ namespace {
|
|||||||
static const _ObjectiveCBridgeableWitnessTable *
|
static const _ObjectiveCBridgeableWitnessTable *
|
||||||
findBridgeWitness(const Metadata *T);
|
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);
|
||||||
|
|
||||||
/// A convenient method for failing out of a dynamic cast.
|
/// A convenient method for failing out of a dynamic cast.
|
||||||
static bool _fail(OpaqueValue *srcValue, const Metadata *srcType,
|
static bool _fail(OpaqueValue *srcValue, const Metadata *srcType,
|
||||||
const Metadata *targetType, DynamicCastFlags flags) {
|
const Metadata *targetType, DynamicCastFlags flags) {
|
||||||
@@ -255,7 +279,7 @@ static const OpaqueValue *
|
|||||||
_dynamicCastToExistential(const OpaqueValue *value,
|
_dynamicCastToExistential(const OpaqueValue *value,
|
||||||
const Metadata *sourceType,
|
const Metadata *sourceType,
|
||||||
const ExistentialTypeMetadata *targetType) {
|
const ExistentialTypeMetadata *targetType) {
|
||||||
for (unsigned i = 0, n = targetType->Protocols.NumProtocols; i != n; ++i) {
|
for (unsigned i = 0, n = targetType->Protocols.NumProtocols; i != n; ++i) {
|
||||||
auto *protocol = targetType->Protocols[i];
|
auto *protocol = targetType->Protocols[i];
|
||||||
if (!_conformsToProtocol(value, sourceType, protocol, nullptr))
|
if (!_conformsToProtocol(value, sourceType, protocol, nullptr))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -407,11 +431,59 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
|
|||||||
auto destExistential =
|
auto destExistential =
|
||||||
reinterpret_cast<ClassExistentialContainer*>(dest);
|
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::Class:
|
||||||
|
case MetadataKind::ObjCClassWrapper:
|
||||||
|
case MetadataKind::ForeignClass:
|
||||||
|
case MetadataKind::Existential:
|
||||||
|
case MetadataKind::ExistentialMetatype:
|
||||||
|
case MetadataKind::Metatype:
|
||||||
|
// Handle these cases below.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MetadataKind::Struct:
|
||||||
|
case MetadataKind::Enum:
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
if (src != srcDynamicValue && shouldDeallocateSource(success, flags)) {
|
||||||
|
deallocateDynamicValue(src, srcType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MetadataKind::Function:
|
||||||
|
case MetadataKind::Block:
|
||||||
|
case MetadataKind::HeapArray:
|
||||||
|
case MetadataKind::HeapLocalVariable:
|
||||||
|
case MetadataKind::Opaque:
|
||||||
|
case MetadataKind::PolyFunction:
|
||||||
|
case MetadataKind::Tuple:
|
||||||
|
// Will never succeed.
|
||||||
|
return _fail(src, srcType, targetType, flags);
|
||||||
|
}
|
||||||
|
|
||||||
// Check for protocol conformances and fill in the witness tables.
|
// Check for protocol conformances and fill in the witness tables.
|
||||||
if (!_conformsToProtocols(srcDynamicValue, srcDynamicType,
|
if (!_conformsToProtocols(srcDynamicValue, srcDynamicType,
|
||||||
targetType->Protocols,
|
targetType->Protocols,
|
||||||
destExistential->getWitnessTables()))
|
destExistential->getWitnessTables())) {
|
||||||
return _fail(src, srcType, targetType, flags);
|
return _fail(src, srcType, targetType, flags);
|
||||||
|
}
|
||||||
|
|
||||||
auto object = *(reinterpret_cast<HeapObject**>(srcDynamicValue));
|
auto object = *(reinterpret_cast<HeapObject**>(srcDynamicValue));
|
||||||
destExistential->Value = object;
|
destExistential->Value = object;
|
||||||
@@ -1008,22 +1080,6 @@ static bool _dynamicCastToExistentialMetatype(OpaqueValue *dest,
|
|||||||
_failCorruptType(srcType);
|
_failCorruptType(srcType);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _dynamicCastValueToClassViaObjCBridgeable(
|
|
||||||
OpaqueValue *dest,
|
|
||||||
OpaqueValue *src,
|
|
||||||
const Metadata *srcType,
|
|
||||||
const Metadata *targetType,
|
|
||||||
const _ObjectiveCBridgeableWitnessTable *srcBridgeWitness,
|
|
||||||
DynamicCastFlags flags);
|
|
||||||
|
|
||||||
static bool _dynamicCastClassToValueViaObjCBridgeable(
|
|
||||||
OpaqueValue *dest,
|
|
||||||
OpaqueValue *src,
|
|
||||||
const Metadata *srcType,
|
|
||||||
const Metadata *targetType,
|
|
||||||
const _ObjectiveCBridgeableWitnessTable *targetBridgeWitness,
|
|
||||||
DynamicCastFlags flags);
|
|
||||||
|
|
||||||
/// Perform a dynamic cast to an arbitrary type.
|
/// Perform a dynamic cast to an arbitrary type.
|
||||||
bool swift::swift_dynamicCast(OpaqueValue *dest,
|
bool swift::swift_dynamicCast(OpaqueValue *dest,
|
||||||
OpaqueValue *src,
|
OpaqueValue *src,
|
||||||
@@ -1665,7 +1721,7 @@ extern "C" const ProtocolDescriptor _TMpSs21_ObjectiveCBridgeable;
|
|||||||
|
|
||||||
/// Dynamic cast from a value type that conforms to the _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
|
/// protocol to a class type, first by bridging the value to its Objective-C
|
||||||
/// object represntation and then by dynamic casting that object to the
|
/// object representation and then by dynamic casting that object to the
|
||||||
/// resulting target type.
|
/// resulting target type.
|
||||||
static bool _dynamicCastValueToClassViaObjCBridgeable(
|
static bool _dynamicCastValueToClassViaObjCBridgeable(
|
||||||
OpaqueValue *dest,
|
OpaqueValue *dest,
|
||||||
@@ -1682,16 +1738,57 @@ static bool _dynamicCastValueToClassViaObjCBridgeable(
|
|||||||
// Bridge the source value to an object.
|
// Bridge the source value to an object.
|
||||||
auto srcBridgedObject = srcBridgeWitness->bridgeToObjectiveC(src, srcType);
|
auto srcBridgedObject = srcBridgeWitness->bridgeToObjectiveC(src, srcType);
|
||||||
|
|
||||||
// Dynamic cast the object to the resulting class type. We pass on
|
// Dynamic cast the object to the resulting class type. The
|
||||||
// responsibility
|
// additional flags essneitally make this call act as taking the
|
||||||
|
// source object at +1.
|
||||||
DynamicCastFlags classCastFlags = flags | DynamicCastFlags::TakeOnSuccess
|
DynamicCastFlags classCastFlags = flags | DynamicCastFlags::TakeOnSuccess
|
||||||
| DynamicCastFlags::DestroyOnFailure;
|
| DynamicCastFlags::DestroyOnFailure;
|
||||||
bool success = _dynamicCastUnknownClass(dest, srcBridgedObject, targetType,
|
bool success = _dynamicCastUnknownClass(dest, srcBridgedObject, targetType,
|
||||||
classCastFlags);
|
classCastFlags);
|
||||||
|
|
||||||
// Clean up the source if we're supposed to.
|
// Clean up the source if we're supposed to.
|
||||||
if ((success && (flags & DynamicCastFlags::TakeOnSuccess)) ||
|
if (shouldDeallocateSource(success, flags)) {
|
||||||
(!success && (flags & DynamicCastFlags::DestroyOnFailure))) {
|
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);
|
srcType->vw_destroy(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,19 @@ func testForcedValueToObjectBridging() {
|
|||||||
// CHECK-NEXT: )
|
// CHECK-NEXT: )
|
||||||
println(genericForcedCast(array) as NSObject)
|
println(genericForcedCast(array) as NSObject)
|
||||||
|
|
||||||
// FIXME: Forced bridging (AnyObject)
|
// Forced bridging (AnyObject)
|
||||||
// FIXME: Forced bridging (existential success)
|
// CHECK-NEXT: (
|
||||||
|
// CHECK-NEXT: Hello,
|
||||||
|
// CHECK-NEXT: World
|
||||||
|
// CHECK-NEXT: )
|
||||||
|
println(genericForcedCast(array) as NSObject)
|
||||||
|
|
||||||
|
// Forced bridging (existential success)
|
||||||
|
// CHECK-NEXT: (
|
||||||
|
// CHECK-NEXT: Hello,
|
||||||
|
// CHECK-NEXT: World
|
||||||
|
// CHECK-NEXT: )
|
||||||
|
println(genericForcedCast(array) as NSCoding)
|
||||||
|
|
||||||
println("Done")
|
println("Done")
|
||||||
}
|
}
|
||||||
@@ -68,9 +79,34 @@ func testConditionalValueToObjectBridging() {
|
|||||||
println("Not an NSObject")
|
println("Not an NSObject")
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Conditional bridging (AnyObject)
|
// Conditional bridging (AnyObject)
|
||||||
// FIXME: Conditional bridging (existential success)
|
// CHECK-NEXT: (
|
||||||
// FIXME: Conditional bridging (existential failure)
|
// CHECK-NEXT: Hello,
|
||||||
|
// CHECK-NEXT: World
|
||||||
|
// CHECK-NEXT: )
|
||||||
|
if let anyObject = (genericConditionalCast(array) as AnyObject?) {
|
||||||
|
println("\(anyObject)")
|
||||||
|
} else {
|
||||||
|
println("Not an AnyObject")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conditional bridging (existential success)
|
||||||
|
// CHECK-NEXT: (
|
||||||
|
// CHECK-NEXT: Hello,
|
||||||
|
// CHECK-NEXT: World
|
||||||
|
// CHECK-NEXT: )
|
||||||
|
if let coding = (genericConditionalCast(array) as NSCoding?) {
|
||||||
|
println("\(coding)")
|
||||||
|
} else {
|
||||||
|
println("Not an NSCoding")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-NEXT: NSXMLParserDelegate
|
||||||
|
if let delegate = (genericConditionalCast(array) as NSXMLParserDelegate?) {
|
||||||
|
println("\(delegate)")
|
||||||
|
} else {
|
||||||
|
println("Not an NSXMLParserDelegate")
|
||||||
|
}
|
||||||
|
|
||||||
// Conditional bridging (unrelated class)
|
// Conditional bridging (unrelated class)
|
||||||
// CHECK-NEXT: Not an NSString
|
// CHECK-NEXT: Not an NSString
|
||||||
|
|||||||
Reference in New Issue
Block a user