Files
swift-mirror/stdlib/runtime/SwiftObject.mm
John McCall 3f46b30ca4 Add runtime functions to "pin" a native Swift object.
Pinning an object prevents it from being deallocated,
just like retaining it, but only one client can own the
pin at once.  Sensible "sharing" of the pin can occur
if attempts are perfectly nested.  It is efficient to
simultaneously query the pin state of an object in
conjunction with its strong reference count.

This combination of traits makes pinning suitable for
use in tracking whether a data structure backed by
an object is undergoing a non-structural modification:

- A structural change would require unique ownership
  of the object, but two non-structural changes (to
  different parts of the object) can occur at once
  without harm.  So a non-structural change can check
  for either uniqueness or a pin and then, if necessary,
  assert the pin for the duration of the change.
  Meanwhile, this act of asserting the pin prevents
  simultaneous structural changes.

- A very simple code-generation discipline leads to
  changes being perfectly nested as long as they're
  all performed by a single thread (or synchronously).
  Asynchrony can introduce imperfect nesting, but it's
  easy to write that off as a race condition and hence
  undefined behavior.

See Accessors.rst for more on both of these points.

Swift SVN r23761
2014-12-06 09:46:01 +00:00

1058 lines
32 KiB
Plaintext

//===--- SwiftObject.mm - Native Swift Object root class ------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This implements runtime support for bridging between Swift and Objective-C
// types in cases where they aren't trivial.
//
//===----------------------------------------------------------------------===//
#include "swift/Runtime/Config.h"
#if SWIFT_OBJC_INTEROP
#include <objc/NSObject.h>
#include <objc/runtime.h>
#include <objc/message.h>
#if __has_include(<objc/objc-internal.h>)
#include <objc/objc-abi.h>
#include <objc/objc-internal.h>
#endif
#endif
#include "swift/Runtime/Heap.h"
#include "swift/Runtime/HeapObject.h"
#include "swift/Runtime/Metadata.h"
#include "swift/Runtime/ObjCBridge.h"
#include "../shims/RuntimeShims.h"
#include "Private.h"
#include "Debug.h"
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <mutex>
#include <unordered_map>
#if SWIFT_OBJC_INTEROP
#import <CoreFoundation/CFBase.h> // for CFTypeID
#endif
#if SWIFT_OBJC_INTEROP
// Redeclare these just we check them.
extern "C" id objc_retain(id);
extern "C" void objc_release(id);
extern "C" id _objc_rootAutorelease(id);
extern "C" void objc_moveWeak(id*, id*);
extern "C" void objc_copyWeak(id*, id*);
extern "C" id objc_initWeak(id*, id);
extern "C" id objc_storeWeak(id*, id);
extern "C" void objc_destroyWeak(id*);
extern "C" id objc_loadWeakRetained(id*);
#endif
using namespace swift;
#if SWIFT_HAS_ISA_MASKING
extern "C" __attribute__((weak_import))
const uintptr_t objc_debug_isa_class_mask;
static uintptr_t computeISAMask() {
// The versions of the Objective-C runtime which use non-pointer
// ISAs also export this symbol.
if (auto runtimeSymbol = &objc_debug_isa_class_mask)
return *runtimeSymbol;
return ~uintptr_t(0);
}
uintptr_t swift::swift_isaMask = computeISAMask();
#endif
const ClassMetadata *swift::_swift_getClass(const void *object) {
#if SWIFT_OBJC_INTEROP
if (!isObjCTaggedPointer(object))
return _swift_getClassOfAllocated(object);
return reinterpret_cast<const ClassMetadata*>(object_getClass((id) object));
#else
return _swift_getClassOfAllocated(object);
#endif
}
#if SWIFT_OBJC_INTEROP
struct SwiftObject_s {
void *isa __attribute__((unavailable));
long refCount __attribute__((unavailable));
};
static_assert(std::is_trivially_constructible<SwiftObject_s>::value,
"SwiftObject must be trivially constructible");
static_assert(std::is_trivially_destructible<SwiftObject_s>::value,
"SwiftObject must be trivially destructible");
#if __has_attribute(objc_root_class)
__attribute__((objc_root_class))
#endif
@interface SwiftObject<NSObject> {
// FIXME: rdar://problem/18950072 Clang emits ObjC++ classes as having
// non-trivial structors if they contain any struct fields at all, regardless of
// whether they in fact have nontrivial default constructors. Dupe the body
// of SwiftObject_s into here as a workaround because we don't want to pay
// the cost of .cxx_destruct method dispatch at deallocation time.
void *magic_isa __attribute__((unavailable));
long magic_refCount __attribute__((unavailable));
}
- (BOOL)isEqual:(id)object;
- (NSUInteger)hash;
- (Class)superclass;
- (Class)class;
- (instancetype)self;
- (struct _NSZone *)zone;
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
- (BOOL)isProxy;
+ (BOOL)isSubclassOfClass:(Class)aClass;
- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
- (BOOL)respondsToSelector:(SEL)aSelector;
- (instancetype)retain;
- (oneway void)release;
- (instancetype)autorelease;
- (NSUInteger)retainCount;
- (NSString *)description;
- (NSString *)debugDescription;
@end
static SwiftObject *_allocHelper(Class cls) {
// XXX FIXME
// When we have layout information, do precise alignment rounding
// For now, assume someone is using hardware vector types
#if defined(__x86_64__) || defined(__i386__)
const size_t mask = 32 - 1;
#else
const size_t mask = 16 - 1;
#endif
return reinterpret_cast<SwiftObject *>(swift::swift_allocObject(
reinterpret_cast<HeapMetadata const *>(cls),
class_getInstanceSize(cls), mask));
}
// Helper from the standard library for stringizing an arbitrary object.
namespace {
struct String { void *x, *y, *z; };
}
extern "C" void swift_getSummary(String *out, OpaqueValue *value,
const Metadata *T);
static NSString *_getDescription(SwiftObject *obj) {
// Cached lookup of swift_convertStringToNSString, which is in Foundation.
static NSString *(*convertStringToNSString)(void *sx, void *sy, void *sz)
= nullptr;
if (!convertStringToNSString) {
convertStringToNSString = (decltype(convertStringToNSString))(uintptr_t)
dlsym(RTLD_DEFAULT, "swift_convertStringToNSString");
// If Foundation hasn't loaded yet, fall back to returning the static string
// "SwiftObject". The likelihood of someone invoking -description without
// ObjC interop is low.
if (!convertStringToNSString)
return @"SwiftObject";
}
String tmp;
swift_retain((HeapObject*)obj);
swift_getSummary(&tmp, (OpaqueValue*)&obj, _swift_getClassOfAllocated(obj));
return [convertStringToNSString(tmp.x, tmp.y, tmp.z) autorelease];
}
@implementation SwiftObject
+ (void)load {}
+ (void)initialize {}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
assert(zone == nullptr);
return _allocHelper(self);
}
+ (instancetype)alloc {
// we do not support "placement new" or zones,
// so there is no need to call allocWithZone
return _allocHelper(self);
}
+ (Class)class {
return self;
}
- (Class)class {
return (Class) _swift_getClassOfAllocated(self);
}
+ (Class)superclass {
return (Class) _swift_getSuperclass((const ClassMetadata*) self);
}
- (Class)superclass {
return (Class) _swift_getSuperclass(_swift_getClassOfAllocated(self));
}
+ (BOOL)isMemberOfClass:(Class)cls {
return cls == (Class) _swift_getClassOfAllocated(self);
}
- (BOOL)isMemberOfClass:(Class)cls {
return cls == (Class) _swift_getClassOfAllocated(self);
}
- (instancetype)self {
return self;
}
- (BOOL)isProxy {
return NO;
}
- (struct _NSZone *)zone {
return (struct _NSZone *)malloc_zone_from_ptr(self);
}
- (void)doesNotRecognizeSelector: (SEL) sel {
Class cls = (Class) _swift_getClassOfAllocated(self);
fatalError("Unrecognized selector %c[%s %s]\n",
class_isMetaClass(cls) ? '+' : '-',
class_getName(cls), sel_getName(sel));
}
- (id)retain {
auto SELF = reinterpret_cast<HeapObject *>(self);
swift_retain(SELF);
return self;
}
- (void)release {
auto SELF = reinterpret_cast<HeapObject *>(self);
swift_release(SELF);
}
- (id)autorelease {
return _objc_rootAutorelease(self);
}
- (NSUInteger)retainCount {
return swift::swift_retainCount(reinterpret_cast<HeapObject *>(self));
}
- (BOOL)_isDeallocating {
return swift_isDeallocating(reinterpret_cast<HeapObject *>(self));
}
- (BOOL)_tryRetain {
return swift_tryRetain(reinterpret_cast<HeapObject*>(self)) != nullptr;
}
- (BOOL)allowsWeakReference {
return !swift_isDeallocating(reinterpret_cast<HeapObject *>(self));
}
- (BOOL)retainWeakReference {
return swift_tryRetain(reinterpret_cast<HeapObject*>(self)) != nullptr;
}
// Retaining the class object itself is a no-op.
+ (id)retain {
return self;
}
+ (void)release {
/* empty */
}
+ (id)autorelease {
return self;
}
+ (NSUInteger)retainCount {
return ULONG_MAX;
}
+ (BOOL)_isDeallocating {
return NO;
}
+ (BOOL)_tryRetain {
return YES;
}
+ (BOOL)allowsWeakReference {
return YES;
}
+ (BOOL)retainWeakReference {
return YES;
}
- (void)dealloc {
_swift_deallocClassInstance(reinterpret_cast<HeapObject *>(self));
}
- (BOOL)isKindOfClass:(Class)someClass {
for (auto isa = _swift_getClassOfAllocated(self); isa != nullptr;
isa = _swift_getSuperclass(isa))
if (isa == (const ClassMetadata*) someClass)
return YES;
return NO;
}
+ (BOOL)isSubclassOfClass:(Class)someClass {
for (auto isa = (const ClassMetadata*) self; isa != nullptr;
isa = _swift_getSuperclass(isa))
if (isa == (const ClassMetadata*) someClass)
return YES;
return NO;
}
+ (BOOL)respondsToSelector:(SEL)sel {
if (!sel) return NO;
return class_respondsToSelector((Class) _swift_getClassOfAllocated(self), sel);
}
- (BOOL)respondsToSelector:(SEL)sel {
if (!sel) return NO;
return class_respondsToSelector((Class) _swift_getClassOfAllocated(self), sel);
}
- (BOOL)conformsToProtocol:(Protocol*)proto {
if (!proto) return NO;
return class_conformsToProtocol((Class) _swift_getClassOfAllocated(self), proto);
}
+ (BOOL)conformsToProtocol:(Protocol*)proto {
if (!proto) return NO;
return class_conformsToProtocol(self, proto);
}
- (NSUInteger)hash {
return (NSUInteger)self;
}
- (BOOL)isEqual:(id)object {
return self == object;
}
- (id)performSelector:(SEL)aSelector {
return ((id(*)(id, SEL))objc_msgSend)(self, aSelector);
}
- (id)performSelector:(SEL)aSelector withObject:(id)object {
return ((id(*)(id, SEL, id))objc_msgSend)(self, aSelector, object);
}
- (id)performSelector:(SEL)aSelector withObject:(id)object1
withObject:(id)object2 {
return ((id(*)(id, SEL, id, id))objc_msgSend)(self, aSelector, object1,
object2);
}
- (NSString *)description {
return _getDescription(self);
}
- (NSString *)debugDescription {
return _getDescription(self);
}
- (CFTypeID)_cfTypeID {
// Adopt the same CFTypeID as NSObject.
static CFTypeID result;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
id obj = [[NSObject alloc] init];
result = [obj _cfTypeID];
[obj release];
});
return result;
}
@end
/*****************************************************************************/
/****************************** WEAK REFERENCES ******************************/
/*****************************************************************************/
/// A side-table of shared weak references for use by the unowned entry.
///
/// FIXME: this needs to be integrated with the ObjC runtime so that
/// entries will actually get collected. Also, that would make this just
/// a simple manipulation of the internal structures there.
///
/// FIXME: this is not actually safe; if the ObjC runtime deallocates
/// the pointer, the keys in UnownedRefs will become dangling
/// references. rdar://16968733
namespace {
struct UnownedRefEntry {
id Value;
size_t Count;
};
}
// The ObjC runtime will hold a point into the UnownedRefEntry,
// so we require pointers to objects to be stable across rehashes.
// DenseMap doesn't guarantee that, but std::unordered_map does.
static std::unordered_map<const void*, UnownedRefEntry> UnownedRefs;
static std::mutex UnownedRefsMutex;
static void objc_rootRetainUnowned(id object) {
std::lock_guard<std::mutex> lock(UnownedRefsMutex);
auto it = UnownedRefs.find((const void*) object);
assert(it != UnownedRefs.end());
assert(it->second.Count > 0);
// Do an unbalanced retain.
id result = objc_loadWeakRetained(&it->second.Value);
// If that yielded null, abort.
if (!result) _swift_abortRetainUnowned((const void*) object);
}
static void objc_rootWeakRetain(id object) {
std::lock_guard<std::mutex> lock(UnownedRefsMutex);
auto ins = UnownedRefs.insert({ (const void*) object, UnownedRefEntry() });
if (!ins.second) {
ins.first->second.Count++;
} else {
objc_initWeak(&ins.first->second.Value, object);
ins.first->second.Count = 1;
}
}
static void objc_rootWeakRelease(id object) {
std::lock_guard<std::mutex> lock(UnownedRefsMutex);
auto it = UnownedRefs.find((const void*) object);
assert(it != UnownedRefs.end());
assert(it->second.Count > 0);
if (--it->second.Count == 0) {
objc_destroyWeak(&it->second.Value);
UnownedRefs.erase(it);
}
}
#endif
/// Decide dynamically whether the given object uses native Swift
/// reference-counting.
bool swift::usesNativeSwiftReferenceCounting(const ClassMetadata *theClass) {
#if SWIFT_OBJC_INTEROP
if (!theClass->isTypeMetadata()) return false;
return (theClass->getFlags() & ClassFlags::UsesSwift1Refcounting);
#else
return true;
#endif
}
// version for SwiftShims
bool
swift::_swift_usesNativeSwiftReferenceCounting_class(const void *theClass) {
#if SWIFT_OBJC_INTEROP
return usesNativeSwiftReferenceCounting((const ClassMetadata *)theClass);
#else
return true;
#endif
}
// These bits are all set in every non-tagged non-native BridgeObject
static auto const unTaggedNonNativeBridgeObjectBits
= heap_object_abi::SwiftSpareBitsMask
& ~heap_object_abi::ObjCReservedBitsMask;
#if SWIFT_OBJC_INTEROP
static bool usesNativeSwiftReferenceCounting_allocated(const void *object) {
assert(!isObjCTaggedPointerOrNull(object));
return usesNativeSwiftReferenceCounting(_swift_getClassOfAllocated(object));
}
void *swift::swift_unknownRetain(void *object) {
if (isObjCTaggedPointerOrNull(object)) return object;
if (usesNativeSwiftReferenceCounting_allocated(object))
return swift_retain((HeapObject*) object);
return objc_retain((id) object);
}
void swift::swift_unknownRelease(void *object) {
if (isObjCTaggedPointerOrNull(object)) return;
if (usesNativeSwiftReferenceCounting_allocated(object))
return swift_release((HeapObject*) object);
return objc_release((id) object);
}
/// Return true iff the given BridgeObject is not known to use native
/// reference-counting.
///
/// Requires: object does not encode a tagged pointer
static bool isNonNative_unTagged_bridgeObject(void *object) {
return (uintptr_t(object) & unTaggedNonNativeBridgeObjectBits)
== unTaggedNonNativeBridgeObjectBits;
}
#endif
// Mask out the spare bits in a bridgeObject, returning the object it
// encodes.
///
/// Requires: object does not encode a tagged pointer
static void* toPlainObject_unTagged_bridgeObject(void *object) {
return (void*)(uintptr_t(object) & ~unTaggedNonNativeBridgeObjectBits);
}
void *swift::swift_bridgeObjectRetain(void *object) {
#if SWIFT_OBJC_INTEROP
if (isObjCTaggedPointer(object))
return object;
#endif
auto const objectRef = toPlainObject_unTagged_bridgeObject(object);
#if SWIFT_OBJC_INTEROP
if (!isNonNative_unTagged_bridgeObject(object))
return swift_retain((HeapObject*) objectRef);
return objc_retain((id) objectRef);
#else
return swift_retain((HeapObject*) objectRef);
#endif
}
void swift::swift_bridgeObjectRelease(void *object) {
#if SWIFT_OBJC_INTEROP
if (isObjCTaggedPointer(object))
return;
#endif
auto const objectRef = toPlainObject_unTagged_bridgeObject(object);
#if SWIFT_OBJC_INTEROP
if (!isNonNative_unTagged_bridgeObject(object))
return swift_release((HeapObject*) objectRef);
return objc_release((id) objectRef);
#else
swift_release((HeapObject*) objectRef);
#endif
}
#if SWIFT_OBJC_INTEROP
void swift::swift_unknownRetainUnowned(void *object) {
if (isObjCTaggedPointerOrNull(object)) return;
if (usesNativeSwiftReferenceCounting_allocated(object))
return swift_retainUnowned((HeapObject*) object);
objc_rootRetainUnowned((id) object);
}
void swift::swift_unknownWeakRetain(void *object) {
if (isObjCTaggedPointerOrNull(object)) return;
if (usesNativeSwiftReferenceCounting_allocated(object))
return swift_weakRetain((HeapObject*) object);
objc_rootWeakRetain((id) object);
}
void swift::swift_unknownWeakRelease(void *object) {
if (isObjCTaggedPointerOrNull(object)) return;
if (usesNativeSwiftReferenceCounting_allocated(object))
return swift_weakRelease((HeapObject*) object);
objc_rootWeakRelease((id) object);
}
// FIXME: these are not really valid implementations; they assume too
// much about the implementation of ObjC weak references, and the
// loads from ->Value can race with clears by the runtime.
static void doWeakInit(WeakReference *addr, void *value, bool valueIsNative) {
assert(value != nullptr);
if (valueIsNative) {
swift_weakInit(addr, (HeapObject*) value);
} else {
#if SWIFT_OBJC_INTEROP
objc_initWeak((id*) &addr->Value, (id) value);
#else
assert(valueIsNative);
#endif
}
}
static void doWeakDestroy(WeakReference *addr, bool valueIsNative) {
if (valueIsNative) {
swift_weakDestroy(addr);
} else {
#if SWIFT_OBJC_INTEROP
objc_destroyWeak((id*) &addr->Value);
#else
assert(valueIsNative);
#endif
}
}
void swift::swift_unknownWeakInit(WeakReference *addr, void *value) {
if (isObjCTaggedPointerOrNull(value)) {
addr->Value = (HeapObject*) value;
return;
}
doWeakInit(addr, value, usesNativeSwiftReferenceCounting_allocated(value));
}
void swift::swift_unknownWeakAssign(WeakReference *addr, void *newValue) {
// If the incoming value is not allocated, this is just a destroy
// and re-initialize.
if (isObjCTaggedPointerOrNull(newValue)) {
swift_unknownWeakDestroy(addr);
addr->Value = (HeapObject*) newValue;
return;
}
bool newIsNative = usesNativeSwiftReferenceCounting_allocated(newValue);
// If the existing value is not allocated, this is just an initialize.
void *oldValue = addr->Value;
if (isObjCTaggedPointerOrNull(oldValue))
return doWeakInit(addr, newValue, newIsNative);
bool oldIsNative = usesNativeSwiftReferenceCounting_allocated(oldValue);
// If they're both native, we can use the native function.
if (oldIsNative && newIsNative)
return swift_weakAssign(addr, (HeapObject*) newValue);
// If neither is native, we can use the ObjC function.
if (!oldIsNative && !newIsNative)
return (void) objc_storeWeak((id*) &addr->Value, (id) newValue);
// Otherwise, destroy according to one set of semantics and
// re-initialize with the other.
doWeakDestroy(addr, oldIsNative);
doWeakInit(addr, newValue, newIsNative);
}
void *swift::swift_unknownWeakLoadStrong(WeakReference *addr) {
void *value = addr->Value;
if (isObjCTaggedPointerOrNull(value)) return value;
if (usesNativeSwiftReferenceCounting_allocated(value)) {
return swift_weakLoadStrong(addr);
} else {
return (void*) objc_loadWeakRetained((id*) &addr->Value);
}
}
void *swift::swift_unknownWeakTakeStrong(WeakReference *addr) {
void *value = addr->Value;
if (isObjCTaggedPointerOrNull(value)) return value;
if (usesNativeSwiftReferenceCounting_allocated(value)) {
return swift_weakTakeStrong(addr);
} else {
void *result = (void*) objc_loadWeakRetained((id*) &addr->Value);
objc_destroyWeak((id*) &addr->Value);
return result;
}
}
void swift::swift_unknownWeakDestroy(WeakReference *addr) {
id object = (id) addr->Value;
if (isObjCTaggedPointerOrNull(object)) return;
doWeakDestroy(addr, usesNativeSwiftReferenceCounting_allocated(object));
}
void swift::swift_unknownWeakCopyInit(WeakReference *dest, WeakReference *src) {
id object = (id) src->Value;
if (isObjCTaggedPointerOrNull(object)) {
dest->Value = (HeapObject*) object;
return;
}
if (usesNativeSwiftReferenceCounting_allocated(object))
return swift_weakCopyInit(dest, src);
objc_copyWeak((id*) &dest->Value, (id*) src);
}
void swift::swift_unknownWeakTakeInit(WeakReference *dest, WeakReference *src) {
id object = (id) src->Value;
if (isObjCTaggedPointerOrNull(object)) {
dest->Value = (HeapObject*) object;
return;
}
if (usesNativeSwiftReferenceCounting_allocated(object))
return swift_weakTakeInit(dest, src);
objc_moveWeak((id*) &dest->Value, (id*) &src->Value);
}
void swift::swift_unknownWeakCopyAssign(WeakReference *dest, WeakReference *src) {
if (dest == src) return;
swift_unknownWeakDestroy(dest);
swift_unknownWeakCopyInit(dest, src);
}
void swift::swift_unknownWeakTakeAssign(WeakReference *dest, WeakReference *src) {
if (dest == src) return;
swift_unknownWeakDestroy(dest);
swift_unknownWeakTakeInit(dest, src);
}
#endif
/*****************************************************************************/
/******************************* DYNAMIC CASTS *******************************/
/*****************************************************************************/
#if SWIFT_OBJC_INTEROP
const void *
swift::swift_dynamicCastObjCClass(const void *object,
const ClassMetadata *targetType) {
// FIXME: We need to decide if this is really how we want to treat 'nil'.
if (object == nullptr)
return nullptr;
if ([(id)object isKindOfClass:(Class)targetType]) {
return object;
}
return nullptr;
}
const void *
swift::swift_dynamicCastObjCClassUnconditional(const void *object,
const ClassMetadata *targetType) {
// FIXME: We need to decide if this is really how we want to treat 'nil'.
if (object == nullptr)
return nullptr;
if ([(id)object isKindOfClass:(Class)targetType]) {
return object;
}
Class sourceType = object_getClass((id)object);
swift_dynamicCastFailure(reinterpret_cast<const Metadata *>(sourceType),
targetType);
}
const void *
swift::swift_dynamicCastForeignClass(const void *object,
const ForeignClassMetadata *targetType) {
// FIXME: Actually compare CFTypeIDs, once they are available in the metadata.
return object;
}
const void *
swift::swift_dynamicCastForeignClassUnconditional(
const void *object,
const ForeignClassMetadata *targetType) {
// FIXME: Actual compare CFTypeIDs, once they are available in the metadata.
return object;
}
extern "C" bool swift_objcRespondsToSelector(id object, SEL selector) {
return [object respondsToSelector:selector];
}
extern "C" bool swift::_swift_objectConformsToObjCProtocol(const void *theObject,
const ProtocolDescriptor *protocol) {
return [((id) theObject) conformsToProtocol: (Protocol*) protocol];
}
extern "C" bool swift::_swift_classConformsToObjCProtocol(const void *theClass,
const ProtocolDescriptor *protocol) {
return [((Class) theClass) conformsToProtocol: (Protocol*) protocol];
}
extern "C" const Metadata *swift_dynamicCastTypeToObjCProtocolUnconditional(
const Metadata *type,
size_t numProtocols,
Protocol * const *protocols) {
Class classObject;
switch (type->getKind()) {
case MetadataKind::Class:
// Native class metadata is also the class object.
classObject = (Class)type;
break;
case MetadataKind::ObjCClassWrapper:
// Unwrap to get the class object.
classObject = (Class)static_cast<const ObjCClassWrapperMetadata *>(type)
->Class;
break;
// Other kinds of type can never conform to ObjC protocols.
case MetadataKind::Struct:
case MetadataKind::Enum:
case MetadataKind::Opaque:
case MetadataKind::Tuple:
case MetadataKind::Function:
case MetadataKind::Existential:
case MetadataKind::Metatype:
case MetadataKind::ExistentialMetatype:
case MetadataKind::ForeignClass:
case MetadataKind::Block:
swift_dynamicCastFailure(type, nameForMetadata(type).c_str(),
protocols[0], protocol_getName(protocols[0]));
case MetadataKind::PolyFunction:
case MetadataKind::HeapLocalVariable:
assert(false && "not type metadata");
break;
}
for (size_t i = 0; i < numProtocols; ++i) {
if (![classObject conformsToProtocol:protocols[i]]) {
swift_dynamicCastFailure(type, nameForMetadata(type).c_str(),
protocols[i], protocol_getName(protocols[i]));
}
}
return type;
}
extern "C" const Metadata *swift_dynamicCastTypeToObjCProtocolConditional(
const Metadata *type,
size_t numProtocols,
Protocol * const *protocols) {
Class classObject;
switch (type->getKind()) {
case MetadataKind::Class:
// Native class metadata is also the class object.
classObject = (Class)type;
break;
case MetadataKind::ObjCClassWrapper:
// Unwrap to get the class object.
classObject = (Class)static_cast<const ObjCClassWrapperMetadata *>(type)
->Class;
break;
// Other kinds of type can never conform to ObjC protocols.
case MetadataKind::Struct:
case MetadataKind::Enum:
case MetadataKind::Opaque:
case MetadataKind::Tuple:
case MetadataKind::Function:
case MetadataKind::Existential:
case MetadataKind::Metatype:
case MetadataKind::ExistentialMetatype:
case MetadataKind::ForeignClass:
case MetadataKind::Block:
return nullptr;
case MetadataKind::PolyFunction:
case MetadataKind::HeapLocalVariable:
assert(false && "not type metadata");
break;
}
for (size_t i = 0; i < numProtocols; ++i) {
if (![classObject conformsToProtocol:protocols[i]]) {
return nullptr;
}
}
return type;
}
extern "C" id swift_dynamicCastObjCProtocolUnconditional(id object,
size_t numProtocols,
Protocol * const *protocols) {
for (size_t i = 0; i < numProtocols; ++i) {
if (![object conformsToProtocol:protocols[i]]) {
Class sourceType = object_getClass(object);
swift_dynamicCastFailure(sourceType, class_getName(sourceType),
protocols[i], protocol_getName(protocols[i]));
}
}
return object;
}
extern "C" id swift_dynamicCastObjCProtocolConditional(id object,
size_t numProtocols,
Protocol * const *protocols) {
for (size_t i = 0; i < numProtocols; ++i) {
if (![object conformsToProtocol:protocols[i]]) {
return nil;
}
}
return object;
}
extern "C" void swift_instantiateObjCClass(Class c) {
static const objc_image_info ImageInfo = {0, 0};
// Register the class.
Class registered = objc_readClassPair(c, &ImageInfo);
assert(registered == c
&& "objc_readClassPair failed to instantiate the class in-place");
(void)registered;
}
extern "C" Class swift_getInitializedObjCClass(Class c) {
// Used when we have class metadata and we want to ensure a class has been
// initialized by the Objective C runtime. We need to do this because the
// class "c" might be valid metadata, but it hasn't been initialized yet.
return [c class];
}
const ClassMetadata *
swift::swift_dynamicCastObjCClassMetatype(const ClassMetadata *source,
const ClassMetadata *dest) {
if ([(Class)source isSubclassOfClass:(Class)dest])
return source;
return nil;
}
const ClassMetadata *
swift::swift_dynamicCastObjCClassMetatypeUnconditional(
const ClassMetadata *source,
const ClassMetadata *dest) {
if ([(Class)source isSubclassOfClass:(Class)dest])
return source;
swift_dynamicCastFailure(source, dest);
}
const ClassMetadata *
swift::swift_dynamicCastForeignClassMetatype(const ClassMetadata *sourceType,
const ClassMetadata *targetType) {
// FIXME: Actually compare CFTypeIDs, once they are available in
// the metadata.
return sourceType;
}
const ClassMetadata *
swift::swift_dynamicCastForeignClassMetatypeUnconditional(
const ClassMetadata *sourceType,
const ClassMetadata *targetType)
{
// FIXME: Actually compare CFTypeIDs, once they arae available in
// the metadata.
return sourceType;
}
extern "C" const char *swift_getGenericClassObjCName(const ClassMetadata *clas,
const char *basename) {
// FIXME: We should use a runtime mangler to form the real mangled name of the
// generic instance. Since we don't have a runtime mangler yet, just tack the
// address of the class onto the basename, which is totally lame but at least
// gives a unique name to the ObjC runtime.
size_t baseLen = strlen(basename);
size_t alignMask = alignof(char) - 1;
auto fullName = (char*)swift_slowAlloc(baseLen + 17, alignMask);
snprintf(fullName, baseLen + 17, "%s%016llX", basename,
(unsigned long long)clas);
return fullName;
}
#endif
// Given a non-nil object reference, return true iff the object uses
// native swift reference counting.
bool swift::_swift_usesNativeSwiftReferenceCounting_nonNull(
const void* object
) {
assert(object != nullptr);
#if SWIFT_OBJC_INTEROP
return !isObjCTaggedPointer(object) &&
usesNativeSwiftReferenceCounting_allocated(object);
#else
return true;
#endif
}
// Given a non-nil non-@objc object reference, return true iff the
// object has a strong reference count of 1.
bool swift::_swift_isUniquelyReferenced_nonNull_native(
const HeapObject* object
) {
assert(object != nullptr);
assert(!object->refCount.isDeallocating());
return object->refCount.isUniquelyReferenced();
}
// Given a non-@objc object reference, return true iff the
// object is non-nil and has a strong reference count of 1.
bool swift::_swift_isUniquelyReferenced_native(
const HeapObject* object
) {
return object != nullptr
&& _swift_isUniquelyReferenced_nonNull_native(object);
}
// Given a non-nil object reference, return true iff the object is a
// native swift object with strong reference count of 1.
bool swift::_swift_isUniquelyReferencedNonObjC_nonNull(
const void* object
) {
assert(object != nullptr);
return
#if SWIFT_OBJC_INTEROP
swift::_swift_usesNativeSwiftReferenceCounting_nonNull(object) &&
#endif
_swift_isUniquelyReferenced_nonNull_native((HeapObject*)object);
}
// Given an object reference, return true iff it is non-nil and refers
// to a native swift object with strong reference count of 1.
bool swift::_swift_isUniquelyReferencedNonObjC(
const void* object
) {
return object != nullptr
&& _swift_isUniquelyReferencedNonObjC_nonNull(object);
}
/// Return true if the given bits of a Builtin.BridgeObject refer to a
/// native swift object whose strong reference count is 1.
bool swift::_swift_isUniquelyReferencedNonObjC_nonNull_bridgeObject(
__swift_uintptr_t bits
) {
auto bridgeObject = (void*)bits;
if (isObjCTaggedPointer(bridgeObject))
return false;
const auto object = toPlainObject_unTagged_bridgeObject(bridgeObject);
// Note: we could just return false if all spare bits are set,
// but in that case the cost of a deeper check for a unique native
// object is going to be a negligible cost for a possible big win.
#if SWIFT_OBJC_INTEROP
return !isNonNative_unTagged_bridgeObject(bridgeObject)
? _swift_isUniquelyReferenced_nonNull_native((const HeapObject *)object)
: _swift_isUniquelyReferencedNonObjC_nonNull(object);
#else
return _swift_isUniquelyReferenced_nonNull_native((const HeapObject *)object);
#endif
}
/// Given a non-nil object reference, return true if the object is a
/// native swift object and either its strong reference count is 1 or
/// its pinned flag is set.
bool swift::_swift_isUniquelyReferencedOrPinnedNonObjC_nonNull(
const void *object) {
assert(object != nullptr);
return
#if SWIFT_OBJC_INTEROP
swift::_swift_usesNativeSwiftReferenceCounting_nonNull(object) &&
#endif
_swift_isUniquelyReferencedOrPinned_nonNull_native(
(const HeapObject*)object);
}
/// Given a non-nil native swift object reference, return true if
/// either the object has a strong reference count of 1 or its
/// pinned flag is set.
bool swift::_swift_isUniquelyReferencedOrPinned_nonNull_native(
const HeapObject* object) {
assert(object != nullptr);
assert(!object->refCount.isDeallocating());
return object->refCount.isUniquelyReferencedOrPinned();
}
/// Returns class_getInstanceSize(c)
///
/// That function is otherwise unavailable to the core stdlib.
size_t swift::_swift_class_getInstancePositiveExtentSize(const void* c) {
#if SWIFT_OBJC_INTEROP
return class_getInstanceSize((Class)c);
#else
auto metaData = static_cast<const ClassMetadata*>(c);
return metaData->getInstanceSize() - metaData->getInstanceAddressPoint();
#endif
}
const ClassMetadata *swift::getRootSuperclass() {
#if SWIFT_OBJC_INTEROP
return (const ClassMetadata *)[SwiftObject class];
#else
return nullptr;
#endif
}