Runtime: Implement an opaque 'SwiftValue' ObjC class to hold bridged values.

If there's no better mapping for a Swift value into an Objective-C object for bridging purposes, we can fall back to boxing the value in a class. This class doesn't have any public interface beyond being `NSObject`-conforming in Objective-C, but is recognized by the Swift runtime so that it can be dynamically cast back to the boxed type.
This commit is contained in:
Joe Groff
2016-07-15 15:55:44 -07:00
parent 18406900ba
commit bc8433f186
9 changed files with 516 additions and 75 deletions

View File

@@ -31,6 +31,10 @@
#include "Private.h"
#include "../SwiftShims/RuntimeShims.h"
#include "stddef.h"
#if SWIFT_OBJC_INTEROP
#include "swift/Runtime/ObjCBridge.h"
#include "SwiftValue.h"
#endif
#include <cstring>
#include <type_traits>
@@ -2094,6 +2098,65 @@ checkDynamicCastFromOptional(OpaqueValue *dest,
return {false, payloadType};
}
#if SWIFT_OBJC_INTEROP
/// Try to unbox a SwiftValue box to perform a dynamic cast.
static bool tryDynamicCastBoxedSwiftValue(OpaqueValue *dest,
OpaqueValue *src,
const Metadata *srcType,
const Metadata *targetType,
DynamicCastFlags flags) {
// Swift type should be AnyObject or a class type.
if (!srcType->isAnyClass()) {
auto existential = dyn_cast<ExistentialTypeMetadata>(srcType);
if (!existential ||
existential->Flags.getSpecialProtocol()
!= SpecialProtocol::AnyObject)
return false;
}
id srcObject;
memcpy(&srcObject, src, sizeof(id));
// Do we have a SwiftValue?
SwiftValue *srcSwiftValue = getAsSwiftValue(srcObject);
if (!srcSwiftValue)
return false;
// If so, extract the boxed value and try to cast it.
const Metadata *boxedType;
const OpaqueValue *boxedValue;
std::tie(boxedType, boxedValue)
= getValueFromSwiftValue(srcSwiftValue);
// We can't touch the value from the box because it may be
// multiply-referenced.
// TODO: Check for uniqueness and consume if box is unique?
// Does the boxed type exactly match the target type we're looking for?
if (boxedType == targetType) {
targetType->vw_initializeWithCopy(dest,
const_cast<OpaqueValue*>(boxedValue));
// Release the box if we need to.
if (flags & DynamicCastFlags::TakeOnSuccess)
objc_release((id)srcSwiftValue);
return true;
}
// Maybe we can cast the boxed value to our destination type somehow.
auto innerFlags = flags - DynamicCastFlags::TakeOnSuccess
- DynamicCastFlags::DestroyOnFailure;
if (swift_dynamicCast(dest, const_cast<OpaqueValue*>(boxedValue),
boxedType, targetType, innerFlags)) {
// Release the box if we need to.
if (flags & DynamicCastFlags::TakeOnSuccess)
objc_release((id)srcSwiftValue);
return true;
}
return false;
}
#endif
/// Perform a dynamic cast to an arbitrary type.
SWIFT_RT_ENTRY_VISIBILITY
bool swift::swift_dynamicCast(OpaqueValue *dest,
@@ -2238,14 +2301,24 @@ bool swift::swift_dynamicCast(OpaqueValue *dest,
if (tryDynamicCastNSErrorToValue(dest, src, srcType, targetType, flags)) {
return true;
}
#endif
SWIFT_FALLTHROUGH;
}
case MetadataKind::Existential: {
#if SWIFT_OBJC_INTEROP
// A class or AnyObject reference may point at a boxed SwiftValue.
if (tryDynamicCastBoxedSwiftValue(dest, src, srcType,
targetType, flags)) {
return true;
}
#endif
break;
}
case MetadataKind::ExistentialMetatype:
case MetadataKind::Enum:
case MetadataKind::Optional:
case MetadataKind::Existential:
case MetadataKind::ExistentialMetatype:
case MetadataKind::Function:
case MetadataKind::HeapLocalVariable:
case MetadataKind::HeapGenericLocalVariable:
@@ -2557,7 +2630,7 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
if (consume) {
if (canTake) {
if (isOutOfLine) {
// Should only be true of opaque existentials.
// Should only be true of opaque existentials right now.
assert(srcExistentialTy->getRepresentation()
== ExistentialTypeRepresentation::Opaque);
auto container = reinterpret_cast<OpaqueExistentialContainer*>(src);
@@ -2589,8 +2662,8 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
return (id)srcBridgedObject;
}
// TODO: Fall back to boxing here.
crash("unimplemented boxing bridge");
// Fall back to boxing.
return (id)bridgeAnythingToSwiftValueObject(src, srcType, consume);
}
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE