| //===--- SwiftObject.mm - Native Swift Object root class ------------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This implements the Objective-C root class that provides basic `id`- |
| // compatibility and `NSObject` protocol conformance for pure Swift classes. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "swift/Runtime/Config.h" |
| |
| #if SWIFT_OBJC_INTEROP |
| #include <objc/NSObject.h> |
| #include <objc/runtime.h> |
| #include <objc/message.h> |
| #include <objc/objc.h> |
| #endif |
| #include "llvm/ADT/StringRef.h" |
| #include "swift/Basic/LLVM.h" |
| #include "swift/Basic/Lazy.h" |
| #include "swift/Runtime/Casting.h" |
| #include "swift/Runtime/Heap.h" |
| #include "swift/Runtime/HeapObject.h" |
| #include "swift/Runtime/Metadata.h" |
| #include "swift/Runtime/ObjCBridge.h" |
| #include "swift/Strings.h" |
| #include "../SwiftShims/RuntimeShims.h" |
| #include "Private.h" |
| #include "SwiftObject.h" |
| #include "WeakReference.h" |
| #include "swift/Runtime/Debug.h" |
| #if SWIFT_OBJC_INTEROP |
| #include <dlfcn.h> |
| #endif |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unordered_map> |
| #if SWIFT_OBJC_INTEROP |
| # import <CoreFoundation/CFBase.h> // for CFTypeID |
| # import <Foundation/Foundation.h> |
| # include <malloc/malloc.h> |
| # include <dispatch/dispatch.h> |
| #endif |
| |
| using namespace swift; |
| |
| #if SWIFT_HAS_ISA_MASKING |
| OBJC_EXPORT __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); |
| } |
| |
| SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_BEGIN |
| uintptr_t swift::swift_isaMask = computeISAMask(); |
| SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_END |
| #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_const_cast(object))); |
| #else |
| return _swift_getClassOfAllocated(object); |
| #endif |
| } |
| |
| #if SWIFT_OBJC_INTEROP |
| |
| /// \brief Replacement for ObjC object_isClass(), which is unavailable on |
| /// deployment targets macOS 10.9 and iOS 7. |
| static bool objcObjectIsClass(id object) { |
| // same as object_isClass(object) |
| return class_isMetaClass(object_getClass(object)); |
| } |
| |
| /// Same as _swift_getClassOfAllocated() but returns type Class. |
| static Class _swift_getObjCClassOfAllocated(const void *object) { |
| return class_const_cast(_swift_getClassOfAllocated(object)); |
| } |
| |
| #endif |
| |
| /// \brief Fetch the type metadata associated with the formal dynamic |
| /// type of the given (possibly Objective-C) object. The formal |
| /// dynamic type ignores dynamic subclasses such as those introduced |
| /// by KVO. |
| /// |
| /// The object pointer may be a tagged pointer, but cannot be null. |
| const Metadata *swift::swift_getObjectType(HeapObject *object) { |
| auto classAsMetadata = _swift_getClass(object); |
| |
| #if SWIFT_OBJC_INTEROP |
| // Walk up the superclass chain skipping over artifical Swift classes. |
| // If we find a non-Swift class use the result of [object class] instead. |
| |
| while (classAsMetadata && classAsMetadata->isTypeMetadata()) { |
| if (!classAsMetadata->isArtificialSubclass()) |
| return classAsMetadata; |
| classAsMetadata = classAsMetadata->SuperClass; |
| } |
| |
| id objcObject = reinterpret_cast<id>(object); |
| Class objcClass = [objcObject class]; |
| if (objcObjectIsClass(objcObject)) { |
| // Original object is a class. We want a |
| // metaclass but +class doesn't give that to us. |
| objcClass = object_getClass(objcClass); |
| } |
| classAsMetadata = reinterpret_cast<const ClassMetadata *>(objcClass); |
| return swift_getObjCClassMetadata(classAsMetadata); |
| #else |
| assert(classAsMetadata && |
| classAsMetadata->isTypeMetadata() && |
| !classAsMetadata->isArtificialSubclass()); |
| return classAsMetadata; |
| #endif |
| } |
| |
| #if SWIFT_OBJC_INTEROP |
| 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)); |
| } |
| |
| NSString *swift::convertStringToNSString(String *swiftString) { |
| // public func _convertStringToNSString(_ string: String) -> NSString |
| typedef SWIFT_CC(swift) NSString *ConversionFn(void *sx, void *sy, void *sz); |
| auto convertStringToNSString = SWIFT_LAZY_CONSTANT( |
| reinterpret_cast<ConversionFn*>(dlsym(RTLD_DEFAULT, |
| MANGLE_AS_STRING(MANGLE_SYM(10Foundation24_convertStringToNSStringySo0E0CSSF))))); |
| |
| // 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"; |
| |
| return convertStringToNSString(swiftString->x, |
| swiftString->y, |
| swiftString->z); |
| } |
| |
| static NSString *_getDescription(SwiftObject *obj) { |
| String tmp; |
| swift_retain((HeapObject*)obj); |
| swift_getSummary(&tmp, (OpaqueValue*)&obj, _swift_getClassOfAllocated(obj)); |
| return [convertStringToNSString(&tmp) autorelease]; |
| } |
| |
| static NSString *_getClassDescription(Class cls) { |
| return NSStringFromClass(cls); |
| } |
| |
| |
| @implementation SwiftObject |
| + (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 _swift_getObjCClassOfAllocated(self); |
| } |
| + (Class)superclass { |
| return (Class)((const ClassMetadata*) self)->SuperClass; |
| } |
| - (Class)superclass { |
| return (Class)_swift_getClassOfAllocated(self)->SuperClass; |
| } |
| |
| + (BOOL)isMemberOfClass:(Class)cls { |
| return cls == _swift_getObjCClassOfAllocated(self); |
| } |
| |
| - (BOOL)isMemberOfClass:(Class)cls { |
| return cls == _swift_getObjCClassOfAllocated(self); |
| } |
| |
| - (instancetype)self { |
| return self; |
| } |
| - (BOOL)isProxy { |
| return NO; |
| } |
| |
| - (struct _NSZone *)zone { |
| auto zone = malloc_zone_from_ptr(self); |
| return (struct _NSZone *)(zone ? zone : malloc_default_zone()); |
| } |
| |
| - (void)doesNotRecognizeSelector: (SEL) sel { |
| Class cls = _swift_getObjCClassOfAllocated(self); |
| fatalError(/* flags = */ 0, |
| "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_rootObjCDealloc(reinterpret_cast<HeapObject *>(self)); |
| } |
| |
| - (BOOL)isKindOfClass:(Class)someClass { |
| for (auto cls = _swift_getClassOfAllocated(self); cls != nullptr; |
| cls = cls->SuperClass) |
| if (cls == (const ClassMetadata*) someClass) |
| return YES; |
| |
| return NO; |
| } |
| |
| + (BOOL)isSubclassOfClass:(Class)someClass { |
| for (auto cls = (const ClassMetadata*) self; cls != nullptr; |
| cls = cls->SuperClass) |
| if (cls == (const ClassMetadata*) someClass) |
| return YES; |
| |
| return NO; |
| } |
| |
| + (BOOL)respondsToSelector:(SEL)sel { |
| if (!sel) return NO; |
| return class_respondsToSelector(_swift_getObjCClassOfAllocated(self), sel); |
| } |
| |
| - (BOOL)respondsToSelector:(SEL)sel { |
| if (!sel) return NO; |
| return class_respondsToSelector(_swift_getObjCClassOfAllocated(self), sel); |
| } |
| |
| + (BOOL)instancesRespondToSelector:(SEL)sel { |
| if (!sel) return NO; |
| return class_respondsToSelector(self, sel); |
| } |
| |
| |
| + (IMP)methodForSelector:(SEL)sel { |
| return class_getMethodImplementation(object_getClass((id)self), sel); |
| } |
| |
| - (IMP)methodForSelector:(SEL)sel { |
| return class_getMethodImplementation(object_getClass(self), sel); |
| } |
| |
| + (IMP)instanceMethodForSelector:(SEL)sel { |
| return class_getMethodImplementation(self, sel); |
| } |
| |
| |
| - (BOOL)conformsToProtocol:(Protocol*)proto { |
| if (!proto) return NO; |
| auto selfClass = _swift_getObjCClassOfAllocated(self); |
| |
| // Walk the superclass chain. |
| while (selfClass) { |
| if (class_conformsToProtocol(selfClass, proto)) |
| return YES; |
| selfClass = class_getSuperclass(selfClass); |
| } |
| |
| return NO; |
| } |
| |
| + (BOOL)conformsToProtocol:(Protocol*)proto { |
| if (!proto) return NO; |
| |
| // Walk the superclass chain. |
| Class selfClass = self; |
| while (selfClass) { |
| if (class_conformsToProtocol(selfClass, proto)) |
| return YES; |
| selfClass = class_getSuperclass(selfClass); |
| } |
| |
| return NO; |
| } |
| |
| - (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); |
| } |
| |
| + (NSString *)description { |
| return _getClassDescription(self); |
| } |
| + (NSString *)debugDescription { |
| return _getClassDescription(self); |
| } |
| |
| - (NSString *)_copyDescription { |
| // The NSObject version of this pushes an autoreleasepool in case -description |
| // autoreleases, but we're OK with leaking things if we're at the top level |
| // of the main thread with no autorelease pool. |
| return [[self description] retain]; |
| } |
| |
| - (CFTypeID)_cfTypeID { |
| // Adopt the same CFTypeID as NSObject. |
| static CFTypeID result; |
| static dispatch_once_t predicate; |
| dispatch_once_f(&predicate, &result, [](void *resultAddr) { |
| id obj = [[NSObject alloc] init]; |
| *(CFTypeID*)resultAddr = [obj _cfTypeID]; |
| [obj release]; |
| }); |
| return result; |
| } |
| |
| // Foundation collections expect these to be implemented. |
| - (BOOL)isNSArray__ { return NO; } |
| - (BOOL)isNSCFConstantString__ { return NO; } |
| - (BOOL)isNSData__ { return NO; } |
| - (BOOL)isNSDate__ { return NO; } |
| - (BOOL)isNSDictionary__ { return NO; } |
| - (BOOL)isNSObject__ { return NO; } |
| - (BOOL)isNSOrderedSet__ { return NO; } |
| - (BOOL)isNSNumber__ { return NO; } |
| - (BOOL)isNSSet__ { return NO; } |
| - (BOOL)isNSString__ { return NO; } |
| - (BOOL)isNSTimeZone__ { return NO; } |
| - (BOOL)isNSValue__ { return NO; } |
| |
| @end |
| |
| #endif |
| |
| /// Decide dynamically whether the given class uses native Swift |
| /// reference-counting. |
| bool swift::usesNativeSwiftReferenceCounting(const ClassMetadata *theClass) { |
| #if SWIFT_OBJC_INTEROP |
| if (!theClass->isTypeMetadata()) return false; |
| return (theClass->getFlags() & ClassFlags::UsesSwiftRefcounting); |
| #else |
| return true; |
| #endif |
| } |
| |
| /// Decide dynamically whether the given type metadata uses native Swift |
| /// reference-counting. The metadata is known to correspond to a class |
| /// type, but note that does not imply being known to be a ClassMetadata |
| /// due to the existence of ObjCClassWrapper. |
| SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL |
| bool |
| _objcClassUsesNativeSwiftReferenceCounting(const Metadata *theClass) { |
| #if SWIFT_OBJC_INTEROP |
| // If this is ObjC wrapper metadata, the class is definitely not using |
| // Swift ref-counting. |
| if (isa<ObjCClassWrapperMetadata>(theClass)) return false; |
| |
| // Otherwise, it's class metadata. |
| return usesNativeSwiftReferenceCounting(cast<ClassMetadata>(theClass)); |
| #else |
| return true; |
| #endif |
| } |
| |
| // The non-pointer bits, excluding the ObjC tag bits. |
| static auto const unTaggedNonNativeBridgeObjectBits |
| = heap_object_abi::SwiftSpareBitsMask |
| & ~heap_object_abi::ObjCReservedBitsMask; |
| |
| #if SWIFT_OBJC_INTEROP |
| |
| #if defined(__x86_64__) |
| static uintptr_t const objectPointerIsObjCBit = 0x4000000000000000ULL; |
| #elif defined(__arm64__) |
| static uintptr_t const objectPointerIsObjCBit = 0x4000000000000000ULL; |
| #else |
| static uintptr_t const objectPointerIsObjCBit = 0x00000002U; |
| #endif |
| |
| void *swift::swift_unknownRetain_n(void *object, int n) |
| SWIFT_CC(DefaultCC_IMPL) { |
| if (isObjCTaggedPointerOrNull(object)) return object; |
| if (objectUsesNativeSwiftReferenceCounting(object)) { |
| return swift_retain_n(static_cast<HeapObject *>(object), n); |
| } |
| for (int i = 0; i < n; ++i) |
| objc_retain(static_cast<id>(object)); |
| |
| return object; |
| } |
| |
| void swift::swift_unknownRelease_n(void *object, int n) |
| SWIFT_CC(DefaultCC_IMPL) { |
| if (isObjCTaggedPointerOrNull(object)) return; |
| if (objectUsesNativeSwiftReferenceCounting(object)) |
| return swift_release_n(static_cast<HeapObject *>(object), n); |
| for (int i = 0; i < n; ++i) |
| objc_release(static_cast<id>(object)); |
| } |
| |
| void *swift::swift_unknownRetain(void *object) |
| SWIFT_CC(DefaultCC_IMPL) { |
| if (isObjCTaggedPointerOrNull(object)) return object; |
| if (objectUsesNativeSwiftReferenceCounting(object)) { |
| return swift_retain(static_cast<HeapObject *>(object)); |
| } |
| return objc_retain(static_cast<id>(object)); |
| } |
| |
| void swift::swift_unknownRelease(void *object) |
| SWIFT_CC(DefaultCC_IMPL) { |
| if (isObjCTaggedPointerOrNull(object)) return; |
| if (objectUsesNativeSwiftReferenceCounting(object)) |
| return SWIFT_RT_ENTRY_CALL(swift_release)(static_cast<HeapObject *>(object)); |
| return objc_release(static_cast<id>(object)); |
| } |
| |
| void *swift::swift_nonatomic_unknownRetain_n(void *object, int n) |
| SWIFT_CC(DefaultCC_IMPL) { |
| if (isObjCTaggedPointerOrNull(object)) return object; |
| if (objectUsesNativeSwiftReferenceCounting(object)) { |
| return swift_nonatomic_retain_n(static_cast<HeapObject *>(object), n); |
| } |
| for (int i = 0; i < n; ++i) |
| objc_retain(static_cast<id>(object)); |
| return object; |
| } |
| |
| void swift::swift_nonatomic_unknownRelease_n(void *object, int n) |
| SWIFT_CC(DefaultCC_IMPL) { |
| if (isObjCTaggedPointerOrNull(object)) return; |
| if (objectUsesNativeSwiftReferenceCounting(object)) |
| return swift_nonatomic_release_n(static_cast<HeapObject *>(object), n); |
| for (int i = 0; i < n; ++i) |
| objc_release(static_cast<id>(object)); |
| } |
| |
| void *swift::swift_nonatomic_unknownRetain(void *object) |
| SWIFT_CC(DefaultCC_IMPL) { |
| if (isObjCTaggedPointerOrNull(object)) return object; |
| if (objectUsesNativeSwiftReferenceCounting(object)) { |
| return swift_nonatomic_retain(static_cast<HeapObject *>(object)); |
| } |
| return objc_retain(static_cast<id>(object)); |
| } |
| |
| void swift::swift_nonatomic_unknownRelease(void *object) |
| SWIFT_CC(DefaultCC_IMPL) { |
| if (isObjCTaggedPointerOrNull(object)) return; |
| if (objectUsesNativeSwiftReferenceCounting(object)) |
| return SWIFT_RT_ENTRY_CALL(swift_release)(static_cast<HeapObject *>(object)); |
| return objc_release(static_cast<id>(object)); |
| } |
| |
| |
| /// Return true iff the given BridgeObject is not known to use native |
| /// reference-counting. |
| /// |
| /// Precondition: object does not encode a tagged pointer |
| static bool isNonNative_unTagged_bridgeObject(void *object) { |
| static_assert((heap_object_abi::SwiftSpareBitsMask & objectPointerIsObjCBit) == |
| objectPointerIsObjCBit, |
| "isObjC bit not within spare bits"); |
| return (uintptr_t(object) & objectPointerIsObjCBit) != 0; |
| } |
| #endif |
| |
| // Mask out the spare bits in a bridgeObject, returning the object it |
| // encodes. |
| /// |
| /// Precondition: 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) |
| SWIFT_CC(DefaultCC_IMPL) { |
| #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)) { |
| swift_retain(static_cast<HeapObject *>(objectRef)); |
| return static_cast<HeapObject *>(objectRef); |
| } |
| return objc_retain(static_cast<id>(objectRef)); |
| #else |
| swift_retain(static_cast<HeapObject *>(objectRef)); |
| return static_cast<HeapObject *>(objectRef); |
| #endif |
| } |
| |
| SWIFT_RUNTIME_EXPORT |
| void *swift::swift_nonatomic_bridgeObjectRetain(void *object) |
| SWIFT_CC(DefaultCC_IMPL) { |
| #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)) { |
| swift_nonatomic_retain(static_cast<HeapObject *>(objectRef)); |
| return static_cast<HeapObject *>(objectRef); |
| } |
| return objc_retain(static_cast<id>(objectRef)); |
| #else |
| swift_nonatomic_retain(static_cast<HeapObject *>(objectRef)); |
| return static_cast<HeapObject *>(objectRef); |
| #endif |
| } |
| |
| SWIFT_RUNTIME_EXPORT |
| void swift::swift_bridgeObjectRelease(void *object) |
| SWIFT_CC(DefaultCC_IMPL) { |
| #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(static_cast<HeapObject *>(objectRef)); |
| return objc_release(static_cast<id>(objectRef)); |
| #else |
| swift_release(static_cast<HeapObject *>(objectRef)); |
| #endif |
| } |
| |
| void swift::swift_nonatomic_bridgeObjectRelease(void *object) |
| SWIFT_CC(DefaultCC_IMPL) { |
| #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_nonatomic_release(static_cast<HeapObject *>(objectRef)); |
| return objc_release(static_cast<id>(objectRef)); |
| #else |
| swift_nonatomic_release(static_cast<HeapObject *>(objectRef)); |
| #endif |
| } |
| |
| void *swift::swift_bridgeObjectRetain_n(void *object, int n) |
| SWIFT_CC(DefaultCC_IMPL) { |
| #if SWIFT_OBJC_INTEROP |
| if (isObjCTaggedPointer(object)) |
| return object; |
| #endif |
| |
| auto const objectRef = toPlainObject_unTagged_bridgeObject(object); |
| |
| #if SWIFT_OBJC_INTEROP |
| void *objc_ret = nullptr; |
| if (!isNonNative_unTagged_bridgeObject(object)) { |
| swift_retain_n(static_cast<HeapObject *>(objectRef), n); |
| return static_cast<HeapObject *>(objectRef); |
| } |
| for (int i = 0;i < n; ++i) |
| objc_ret = objc_retain(static_cast<id>(objectRef)); |
| return objc_ret; |
| #else |
| swift_retain_n(static_cast<HeapObject *>(objectRef), n); |
| return static_cast<HeapObject *>(objectRef); |
| #endif |
| } |
| |
| void swift::swift_bridgeObjectRelease_n(void *object, int n) |
| SWIFT_CC(DefaultCC_IMPL) { |
| #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_n(static_cast<HeapObject *>(objectRef), n); |
| for (int i = 0; i < n; ++i) |
| objc_release(static_cast<id>(objectRef)); |
| #else |
| swift_release_n(static_cast<HeapObject *>(objectRef), n); |
| #endif |
| } |
| |
| void *swift::swift_nonatomic_bridgeObjectRetain_n(void *object, int n) |
| SWIFT_CC(DefaultCC_IMPL) { |
| #if SWIFT_OBJC_INTEROP |
| if (isObjCTaggedPointer(object)) |
| return object; |
| #endif |
| |
| auto const objectRef = toPlainObject_unTagged_bridgeObject(object); |
| |
| #if SWIFT_OBJC_INTEROP |
| void *objc_ret = nullptr; |
| if (!isNonNative_unTagged_bridgeObject(object)) { |
| swift_nonatomic_retain_n(static_cast<HeapObject *>(objectRef), n); |
| return static_cast<HeapObject *>(objectRef); |
| } |
| for (int i = 0;i < n; ++i) |
| objc_ret = objc_retain(static_cast<id>(objectRef)); |
| return objc_ret; |
| #else |
| swift_nonatomic_retain_n(static_cast<HeapObject *>(objectRef), n); |
| return static_cast<HeapObject *>(objectRef); |
| #endif |
| } |
| |
| void swift::swift_nonatomic_bridgeObjectRelease_n(void *object, int n) |
| SWIFT_CC(DefaultCC_IMPL) { |
| #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_nonatomic_release_n(static_cast<HeapObject *>(objectRef), n); |
| for (int i = 0; i < n; ++i) |
| objc_release(static_cast<id>(objectRef)); |
| #else |
| swift_nonatomic_release_n(static_cast<HeapObject *>(objectRef), n); |
| #endif |
| } |
| |
| |
| #if SWIFT_OBJC_INTEROP |
| |
| /*****************************************************************************/ |
| /************************ UNKNOWN UNOWNED REFERENCES *************************/ |
| /*****************************************************************************/ |
| |
| // Swift's native unowned references are implemented purely with |
| // reference-counting: as long as an unowned reference is held to an object, |
| // it can be destroyed but never deallocated, being that it remains fully safe |
| // to pass around a pointer and perform further reference-counting operations. |
| // |
| // For imported class types (meaning ObjC, for now, but in principle any |
| // type which supports ObjC-style weak references but not directly Swift-style |
| // unowned references), we have to implement this on top of the weak-reference |
| // support, at least for now. But we'd like to be able to statically take |
| // advantage of Swift's representational advantages when we know that all the |
| // objects involved are Swift-native. That means that whatever scheme we use |
| // for unowned references needs to interoperate with code just doing naive |
| // loads and stores, at least when the ObjC case isn't triggered. |
| // |
| // We have to be sensitive about making unreasonable assumptions about the |
| // implementation of ObjC weak references, and we definitely cannot modify |
| // memory owned by the ObjC runtime. In the long run, direct support from |
| // the ObjC runtime can allow an efficient implementation that doesn't violate |
| // those requirements, both by allowing us to directly check whether a weak |
| // reference was cleared by deallocation vs. just initialized to nil and by |
| // guaranteeing a bit pattern that distinguishes Swift references. In the |
| // meantime, out-of-band allocation is inefficient but not ridiculously so. |
| // |
| // Note that unowned references need not provide guaranteed behavior in |
| // the presence of read/write or write/write races on the reference itself. |
| // Furthermore, and unlike weak references, they also do not need to be |
| // safe against races with the deallocation of the object. It is the user's |
| // responsibility to ensure that the reference remains valid at the time |
| // that the unowned reference is read. |
| |
| namespace { |
| /// An Objective-C unowned reference. Given an unknown unowned reference |
| /// in memory, it is an ObjC unowned reference if the IsObjCFlag bit |
| /// is set; if so, the pointer stored in the reference actually points |
| /// to out-of-line storage containing an ObjC weak reference. |
| /// |
| /// It is an invariant that this out-of-line storage is only ever |
| /// allocated and constructed for non-null object references, so if the |
| /// weak load yields null, it can only be because the object was deallocated. |
| struct ObjCUnownedReference : UnownedReference { |
| // Pretending that there's a subclass relationship here means that |
| // accesses to objects formally constructed as UnownedReferences will |
| // technically be aliasing violations. However, the language runtime |
| // will generally not see any such objects. |
| |
| enum : uintptr_t { IsObjCMask = 0x1, IsObjCFlag = 0x1 }; |
| |
| /// The out-of-line storage of an ObjC unowned reference. |
| struct Storage { |
| /// A weak reference registered with the ObjC runtime. |
| mutable id WeakRef; |
| |
| Storage(id ref) { |
| assert(ref && "creating storage for null reference?"); |
| objc_initWeak(&WeakRef, ref); |
| } |
| |
| Storage(const Storage &other) { |
| objc_copyWeak(&WeakRef, &other.WeakRef); |
| } |
| |
| Storage &operator=(const Storage &other) = delete; |
| |
| Storage &operator=(id ref) { |
| objc_storeWeak(&WeakRef, ref); |
| return *this; |
| } |
| |
| ~Storage() { |
| objc_destroyWeak(&WeakRef); |
| } |
| |
| // Don't use the C++ allocator. |
| void *operator new(size_t size) { return malloc(size); } |
| void operator delete(void *ptr) { free(ptr); } |
| }; |
| |
| Storage *storage() { |
| assert(isa<ObjCUnownedReference>(this)); |
| return reinterpret_cast<Storage*>( |
| reinterpret_cast<uintptr_t>(Value) & ~IsObjCMask); |
| } |
| |
| static void initialize(UnownedReference *dest, id value) { |
| initializeWithStorage(dest, new Storage(value)); |
| } |
| |
| static void initializeWithCopy(UnownedReference *dest, Storage *src) { |
| initializeWithStorage(dest, new Storage(*src)); |
| } |
| |
| static void initializeWithStorage(UnownedReference *dest, |
| Storage *storage) { |
| dest->Value = (HeapObject*) (uintptr_t(storage) | IsObjCFlag); |
| } |
| |
| static bool classof(const UnownedReference *ref) { |
| return (uintptr_t(ref->Value) & IsObjCMask) == IsObjCFlag; |
| } |
| }; |
| } |
| |
| static bool isObjCForUnownedReference(void *value) { |
| return (isObjCTaggedPointer(value) || |
| !objectUsesNativeSwiftReferenceCounting(value)); |
| } |
| |
| UnownedReference *swift::swift_unknownUnownedInit(UnownedReference *dest, |
| void *value) { |
| if (!value) { |
| dest->Value = nullptr; |
| } else if (isObjCForUnownedReference(value)) { |
| ObjCUnownedReference::initialize(dest, (id) value); |
| } else { |
| swift_unownedInit(dest, (HeapObject*) value); |
| } |
| return dest; |
| } |
| |
| UnownedReference *swift::swift_unknownUnownedAssign(UnownedReference *dest, |
| void *value) { |
| if (!value) { |
| swift_unknownUnownedDestroy(dest); |
| dest->Value = nullptr; |
| } else if (isObjCForUnownedReference(value)) { |
| if (auto objcDest = dyn_cast<ObjCUnownedReference>(dest)) { |
| objc_storeWeak(&objcDest->storage()->WeakRef, (id) value); |
| } else { |
| swift_unownedDestroy(dest); |
| ObjCUnownedReference::initialize(dest, (id) value); |
| } |
| } else { |
| if (auto objcDest = dyn_cast<ObjCUnownedReference>(dest)) { |
| delete objcDest->storage(); |
| swift_unownedInit(dest, (HeapObject*) value); |
| } else { |
| swift_unownedAssign(dest, (HeapObject*) value); |
| } |
| } |
| return dest; |
| } |
| |
| void *swift::swift_unknownUnownedLoadStrong(UnownedReference *ref) { |
| if (!ref->Value) { |
| return nullptr; |
| } else if (auto objcRef = dyn_cast<ObjCUnownedReference>(ref)) { |
| auto result = (void*) objc_loadWeakRetained(&objcRef->storage()->WeakRef); |
| if (result == nullptr) { |
| swift::swift_abortRetainUnowned(nullptr); |
| } |
| return result; |
| } else { |
| return swift_unownedLoadStrong(ref); |
| } |
| } |
| |
| void *swift::swift_unknownUnownedTakeStrong(UnownedReference *ref) { |
| if (!ref->Value) { |
| return nullptr; |
| } else if (auto objcRef = dyn_cast<ObjCUnownedReference>(ref)) { |
| auto storage = objcRef->storage(); |
| auto result = (void*) objc_loadWeakRetained(&objcRef->storage()->WeakRef); |
| if (result == nullptr) { |
| swift::swift_abortRetainUnowned(nullptr); |
| } |
| delete storage; |
| return result; |
| } else { |
| return swift_unownedTakeStrong(ref); |
| } |
| } |
| |
| void swift::swift_unknownUnownedDestroy(UnownedReference *ref) { |
| if (!ref->Value) { |
| // Nothing to do. |
| return; |
| } else if (auto objcRef = dyn_cast<ObjCUnownedReference>(ref)) { |
| delete objcRef->storage(); |
| } else { |
| swift_unownedDestroy(ref); |
| } |
| } |
| |
| UnownedReference *swift::swift_unknownUnownedCopyInit(UnownedReference *dest, |
| UnownedReference *src) { |
| assert(dest != src); |
| if (!src->Value) { |
| dest->Value = nullptr; |
| } else if (auto objcSrc = dyn_cast<ObjCUnownedReference>(src)) { |
| ObjCUnownedReference::initializeWithCopy(dest, objcSrc->storage()); |
| } else { |
| swift_unownedCopyInit(dest, src); |
| } |
| return dest; |
| } |
| |
| UnownedReference *swift::swift_unknownUnownedTakeInit(UnownedReference *dest, |
| UnownedReference *src) { |
| assert(dest != src); |
| dest->Value = src->Value; |
| return dest; |
| } |
| |
| UnownedReference *swift::swift_unknownUnownedCopyAssign(UnownedReference *dest, |
| UnownedReference *src) { |
| if (dest == src) return dest; |
| |
| if (auto objcSrc = dyn_cast<ObjCUnownedReference>(src)) { |
| if (auto objcDest = dyn_cast<ObjCUnownedReference>(dest)) { |
| // ObjC unfortunately doesn't expose a copy-assign operation. |
| objc_destroyWeak(&objcDest->storage()->WeakRef); |
| objc_copyWeak(&objcDest->storage()->WeakRef, |
| &objcSrc->storage()->WeakRef); |
| return dest; |
| } |
| |
| swift_unownedDestroy(dest); |
| ObjCUnownedReference::initializeWithCopy(dest, objcSrc->storage()); |
| } else { |
| if (auto objcDest = dyn_cast<ObjCUnownedReference>(dest)) { |
| delete objcDest->storage(); |
| swift_unownedCopyInit(dest, src); |
| } else { |
| swift_unownedCopyAssign(dest, src); |
| } |
| } |
| return dest; |
| } |
| |
| UnownedReference *swift::swift_unknownUnownedTakeAssign(UnownedReference *dest, |
| UnownedReference *src) { |
| assert(dest != src); |
| |
| // There's not really anything more efficient to do here than this. |
| swift_unknownUnownedDestroy(dest); |
| dest->Value = src->Value; |
| return dest; |
| } |
| |
| bool swift::swift_unknownUnownedIsEqual(UnownedReference *ref, void *value) { |
| if (!ref->Value) { |
| return value == nullptr; |
| } else if (auto objcRef = dyn_cast<ObjCUnownedReference>(ref)) { |
| id refValue = objc_loadWeakRetained(&objcRef->storage()->WeakRef); |
| bool isEqual = (void*)refValue == value; |
| // This ObjC case has no deliberate unowned check here, |
| // unlike the Swift case. |
| [refValue release]; |
| return isEqual; |
| } else { |
| return swift_unownedIsEqual(ref, (HeapObject *)value); |
| } |
| } |
| |
| /*****************************************************************************/ |
| /************************** UNKNOWN WEAK REFERENCES **************************/ |
| /*****************************************************************************/ |
| |
| WeakReference *swift::swift_unknownWeakInit(WeakReference *ref, void *value) { |
| ref->unknownInit(value); |
| return ref; |
| } |
| |
| WeakReference *swift::swift_unknownWeakAssign(WeakReference *ref, void *value) { |
| ref->unknownAssign(value); |
| return ref; |
| } |
| |
| void *swift::swift_unknownWeakLoadStrong(WeakReference *ref) { |
| return ref->unknownLoadStrong(); |
| } |
| |
| void *swift::swift_unknownWeakTakeStrong(WeakReference *ref) { |
| return ref->unknownTakeStrong(); |
| } |
| |
| void swift::swift_unknownWeakDestroy(WeakReference *ref) { |
| ref->unknownDestroy(); |
| } |
| |
| WeakReference *swift::swift_unknownWeakCopyInit(WeakReference *dest, |
| WeakReference *src) { |
| dest->unknownCopyInit(src); |
| return dest; |
| } |
| WeakReference *swift::swift_unknownWeakTakeInit(WeakReference *dest, |
| WeakReference *src) { |
| dest->unknownTakeInit(src); |
| return dest; |
| } |
| WeakReference *swift::swift_unknownWeakCopyAssign(WeakReference *dest, |
| WeakReference *src) { |
| dest->unknownCopyAssign(src); |
| return dest; |
| } |
| WeakReference *swift::swift_unknownWeakTakeAssign(WeakReference *dest, |
| WeakReference *src) { |
| dest->unknownTakeAssign(src); |
| return dest; |
| } |
| |
| // SWIFT_OBJC_INTEROP |
| #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_const_cast(object) isKindOfClass:class_const_cast(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_const_cast(object) isKindOfClass:class_const_cast(targetType)]) { |
| return object; |
| } |
| |
| Class sourceType = object_getClass(id_const_cast(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; |
| } |
| |
| bool swift::objectConformsToObjCProtocol(const void *theObject, |
| const ProtocolDescriptor *protocol) { |
| return [id_const_cast(theObject) |
| conformsToProtocol: protocol_const_cast(protocol)]; |
| } |
| |
| |
| bool swift::classConformsToObjCProtocol(const void *theClass, |
| const ProtocolDescriptor *protocol) { |
| return [class_const_cast(theClass) |
| conformsToProtocol: protocol_const_cast(protocol)]; |
| } |
| |
| SWIFT_RUNTIME_EXPORT |
| const Metadata *swift_dynamicCastTypeToObjCProtocolUnconditional( |
| const Metadata *type, |
| size_t numProtocols, |
| Protocol * const *protocols) { |
| Class classObject; |
| |
| switch (type->getKind()) { |
| case MetadataKind::Class: |
| case MetadataKind::ObjCClassWrapper: |
| // Native class metadata is also the class object. |
| // ObjC class wrappers get unwrapped. |
| classObject = type->getObjCClassObject(); |
| break; |
| |
| // Other kinds of type can never conform to ObjC protocols. |
| 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: |
| swift_dynamicCastFailure(type, nameForMetadata(type).c_str(), |
| protocols[0], protocol_getName(protocols[0])); |
| |
| case MetadataKind::HeapLocalVariable: |
| case MetadataKind::HeapGenericLocalVariable: |
| case MetadataKind::ErrorObject: |
| 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; |
| } |
| |
| SWIFT_RUNTIME_EXPORT |
| const Metadata *swift_dynamicCastTypeToObjCProtocolConditional( |
| const Metadata *type, |
| size_t numProtocols, |
| Protocol * const *protocols) { |
| Class classObject; |
| |
| switch (type->getKind()) { |
| case MetadataKind::Class: |
| case MetadataKind::ObjCClassWrapper: |
| // Native class metadata is also the class object. |
| // ObjC class wrappers get unwrapped. |
| classObject = type->getObjCClassObject(); |
| break; |
| |
| // Other kinds of type can never conform to ObjC protocols. |
| 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: |
| return nullptr; |
| |
| case MetadataKind::HeapLocalVariable: |
| case MetadataKind::HeapGenericLocalVariable: |
| case MetadataKind::ErrorObject: |
| assert(false && "not type metadata"); |
| break; |
| } |
| |
| for (size_t i = 0; i < numProtocols; ++i) { |
| if (![classObject conformsToProtocol:protocols[i]]) { |
| return nullptr; |
| } |
| } |
| |
| return type; |
| } |
| |
| SWIFT_RUNTIME_EXPORT |
| 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; |
| } |
| |
| SWIFT_RUNTIME_EXPORT |
| 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; |
| } |
| |
| void swift::swift_instantiateObjCClass(const ClassMetadata *_c) { |
| static const objc_image_info ImageInfo = {0, 0}; |
| |
| // Ensure the superclass is realized. |
| Class c = class_const_cast(_c); |
| [class_getSuperclass(c) class]; |
| |
| // Register the class. |
| Class registered = objc_readClassPair(c, &ImageInfo); |
| assert(registered == c |
| && "objc_readClassPair failed to instantiate the class in-place"); |
| (void)registered; |
| } |
| |
| SWIFT_RT_ENTRY_VISIBILITY |
| Class swift_getInitializedObjCClass(Class c) |
| SWIFT_CC(RegisterPreservingCC_IMPL) { |
| // 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_const_cast(source) isSubclassOfClass:class_const_cast(dest)]) |
| return source; |
| return nil; |
| } |
| |
| const ClassMetadata * |
| swift::swift_dynamicCastObjCClassMetatypeUnconditional( |
| const ClassMetadata *source, |
| const ClassMetadata *dest) { |
| if ([class_const_cast(source) isSubclassOfClass:class_const_cast(dest)]) |
| return source; |
| |
| swift_dynamicCastFailure(source, dest); |
| } |
| #endif |
| |
| 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; |
| } |
| |
| #if SWIFT_OBJC_INTEROP |
| // Given a non-nil object reference, return true iff the object uses |
| // native swift reference counting. |
| static bool usesNativeSwiftReferenceCounting_nonNull( |
| const void* object |
| ) { |
| assert(object != nullptr); |
| return !isObjCTaggedPointer(object) && |
| objectUsesNativeSwiftReferenceCounting(object); |
| } |
| #endif |
| |
| bool swift::swift_isUniquelyReferenced_nonNull_native(const HeapObject *object) |
| SWIFT_CC(RegisterPreservingCC_IMPL) { |
| assert(object != nullptr); |
| assert(!object->refCounts.isDeiniting()); |
| return object->refCounts.isUniquelyReferenced(); |
| } |
| |
| bool swift::swift_isUniquelyReferenced_native(const HeapObject* object) { |
| return object != nullptr |
| && swift::SWIFT_RT_ENTRY_CALL(swift_isUniquelyReferenced_nonNull_native)(object); |
| } |
| |
| bool swift::swift_isUniquelyReferencedNonObjC_nonNull(const void* object) { |
| assert(object != nullptr); |
| return |
| #if SWIFT_OBJC_INTEROP |
| usesNativeSwiftReferenceCounting_nonNull(object) && |
| #endif |
| SWIFT_RT_ENTRY_CALL(swift_isUniquelyReferenced_nonNull_native)( |
| (const 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( |
| 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_RT_ENTRY_CALL(swift_isUniquelyReferenced_nonNull_native)( |
| (const HeapObject *)object) |
| : swift_isUniquelyReferencedNonObjC_nonNull(object); |
| #else |
| return SWIFT_RT_ENTRY_CALL(swift_isUniquelyReferenced_nonNull_native)( |
| (const HeapObject *)object); |
| #endif |
| } |
| |
| /// 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_isUniquelyReferencedOrPinnedNonObjC_nonNull_bridgeObject( |
| 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 |
| if (isNonNative_unTagged_bridgeObject(bridgeObject)) |
| return swift_isUniquelyReferencedOrPinnedNonObjC_nonNull(object); |
| #endif |
| return swift_isUniquelyReferencedOrPinned_nonNull_native( |
| (const HeapObject *)object); |
| } |
| |
| |
| /// 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 |
| usesNativeSwiftReferenceCounting_nonNull(object) && |
| #endif |
| swift_isUniquelyReferencedOrPinned_nonNull_native( |
| (const HeapObject*)object); |
| } |
| |
| // Given a non-@objc object reference, return true iff the |
| // object is non-nil and either has a strong reference count of 1 |
| // or is pinned. |
| bool swift::swift_isUniquelyReferencedOrPinned_native(const HeapObject *object) |
| SWIFT_CC(RegisterPreservingCC_IMPL) { |
| return object != nullptr && |
| swift_isUniquelyReferencedOrPinned_nonNull_native(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) SWIFT_CC(RegisterPreservingCC_IMPL) { |
| assert(object != nullptr); |
| assert(!object->refCounts.isDeiniting()); |
| return object->refCounts.isUniquelyReferencedOrPinned(); |
| } |
| |
| struct ClassExtents { |
| size_t negative; |
| size_t positive; |
| }; |
| |
| SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL |
| ClassExtents |
| _getSwiftClassInstanceExtents(const Metadata *c) { |
| assert(c && c->isClassObject()); |
| auto metaData = c->getClassObject(); |
| return ClassExtents{ |
| metaData->getInstanceAddressPoint(), |
| metaData->getInstanceSize() - metaData->getInstanceAddressPoint() |
| }; |
| } |
| |
| #if SWIFT_OBJC_INTEROP |
| |
| SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL |
| ClassExtents |
| _getObjCClassInstanceExtents(const ClassMetadata* c) { |
| // Pure ObjC classes never have negative extents. |
| if (c->isPureObjC()) |
| return ClassExtents{0, class_getInstanceSize(class_const_cast(c))}; |
| |
| return _getSwiftClassInstanceExtents(c); |
| } |
| |
| SWIFT_CC(swift) |
| SWIFT_RUNTIME_EXPORT |
| void swift_objc_swift3ImplicitObjCEntrypoint(id self, SEL selector, |
| const char *filename, |
| size_t filenameLength, |
| size_t line, size_t column, |
| std::atomic<bool> *didLog) { |
| // Only log once. We should have been given a unique zero-initialized |
| // atomic flag for each entry point. |
| if (didLog->exchange(true)) |
| return; |
| |
| // Figure out how much reporting we want by querying the environment |
| // variable SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT. We have four meaningful |
| // levels: |
| // |
| // 0: Don't report anything |
| // 1: Complain about uses of implicit @objc entrypoints. |
| // 2: Complain about uses of implicit @objc entrypoints, with backtraces |
| // if possible. |
| // 3: Complain about uses of implicit @objc entrypoints, then abort(). |
| // |
| // The actual reportLevel is stored as the above values +1, so that |
| // 0 indicates we have not yet checked. It's fine to race through here. |
| // |
| // The default, if SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT is not set, is 2. |
| static int storedReportLevel = 0; |
| if (storedReportLevel == 0) { |
| auto reportLevelStr = getenv("SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT"); |
| if (reportLevelStr && |
| reportLevelStr[0] >= '0' && reportLevelStr[0] <= '3' && |
| reportLevelStr[1] == 0) |
| storedReportLevel = (reportLevelStr[0] - '0') + 1; |
| else |
| storedReportLevel = 3; |
| } |
| |
| int reportLevel = storedReportLevel - 1; |
| if (reportLevel < 1) return; |
| |
| // Report the error. |
| uint32_t flags = 0; |
| if (reportLevel >= 2) |
| flags |= 1 << 0; // Backtrace |
| bool isInstanceMethod = !class_isMetaClass(object_getClass(self)); |
| void (*reporter)(uint32_t, const char *, ...) = |
| reportLevel > 2 ? swift::fatalError : swift::warning; |
| |
| if (filenameLength > INT_MAX) |
| filenameLength = INT_MAX; |
| |
| char *message, *nullTerminatedFilename; |
| asprintf(&message, |
| "implicit Objective-C entrypoint %c[%s %s] is deprecated and will " |
| "be removed in Swift 4", |
| isInstanceMethod ? '-' : '+', |
| class_getName([self class]), |
| sel_getName(selector)); |
| asprintf(&nullTerminatedFilename, "%*s", (int)filenameLength, filename); |
| |
| RuntimeErrorDetails::FixIt fixit = { |
| .filename = nullTerminatedFilename, |
| .startLine = line, |
| .endLine = line, |
| .startColumn = column, |
| .endColumn = column, |
| .replacementText = "@objc " |
| }; |
| RuntimeErrorDetails::Note note = { |
| .description = "add '@objc' to expose this Swift declaration to Objective-C", |
| .numFixIts = 1, |
| .fixIts = &fixit |
| }; |
| RuntimeErrorDetails details = { |
| .version = RuntimeErrorDetails::currentVersion, |
| .errorType = "implicit-objc-entrypoint", |
| .framesToSkip = 1, |
| .numNotes = 1, |
| .notes = ¬e |
| }; |
| uintptr_t runtime_error_flags = RuntimeErrorFlagNone; |
| if (reporter == swift::fatalError) |
| runtime_error_flags = RuntimeErrorFlagFatal; |
| _swift_reportToDebugger(runtime_error_flags, message, &details); |
| |
| reporter(flags, |
| "*** %s:%zu:%zu: %s; add explicit '@objc' to the declaration to " |
| "emit the Objective-C entrypoint in Swift 4 and suppress this " |
| "message\n", |
| nullTerminatedFilename, line, column, message); |
| free(message); |
| free(nullTerminatedFilename); |
| } |
| |
| #endif |
| |
| const ClassMetadata *swift::getRootSuperclass() { |
| #if SWIFT_OBJC_INTEROP |
| static Lazy<const ClassMetadata *> SwiftObjectClass; |
| |
| return SwiftObjectClass.get([](void *ptr) { |
| *((const ClassMetadata **) ptr) = |
| (const ClassMetadata *)[SwiftObject class]; |
| }); |
| #else |
| return nullptr; |
| #endif |
| } |