//===--- ErrorObject.mm - Cocoa-interoperable recoverable error object ----===// // // 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 object representation of the standard Error // type, which represents recoverable errors in the language. This // implementation is designed to interoperate efficiently with Cocoa libraries // by: // - allowing for NSError and CFError objects to "toll-free bridge" to // Error existentials, which allows for cheap Cocoa to Swift interop // - allowing a native Swift error to lazily "become" an NSError when // passed into Cocoa, allowing for cheap Swift to Cocoa interop // //===----------------------------------------------------------------------===// #include "swift/Runtime/Config.h" #if SWIFT_OBJC_INTEROP #include "ErrorObject.h" #include "Private.h" #include "SwiftObject.h" #include "swift/Basic/Lazy.h" #include "swift/Demangling/ManglingMacros.h" #include "swift/Runtime/Casting.h" #include "swift/Runtime/Debug.h" #include "swift/Runtime/ObjCBridge.h" #include #include #include #include #include #include using namespace swift; using namespace swift::hashable_support; @interface NSObject (MakeTheCompilerHappy) + (id) dictionary; //declare this so we can call it below without warnings @end // Mimic the memory layout of NSError so things don't go haywire when we // switch superclasses to the real thing. @interface __SwiftNSErrorLayoutStandin : NSObject { @private void *_reserved; NSInteger _code; id _domain; id _userInfo; } @end @implementation __SwiftNSErrorLayoutStandin @end /// A subclass of NSError used to represent bridged native Swift errors. /// This type cannot be subclassed, and should not ever be instantiated /// except by the Swift runtime. /// /// NOTE: older runtimes called this _SwiftNativeNSError. The two must /// coexist, so it was renamed. The old name must not be used in the new /// runtime. @interface __SwiftNativeNSError : __SwiftNSErrorLayoutStandin @end @implementation __SwiftNativeNSError + (instancetype)allocWithZone:(NSZone *)zone { (void)zone; swift::crash("__SwiftNativeNSError cannot be instantiated"); } - (void)dealloc { // We must destroy the contained Swift value. auto error = (SwiftError*)self; error->getType()->vw_destroy(error->getValue()); [super dealloc]; } // Override the domain/code/userInfo accessors to follow our idea of NSError's // layout. This gives us a buffer in case NSError decides to change its stored // property order. - (id /* NSString */)domain { auto error = (const SwiftError*)self; // The domain string should not be nil; if it is, then this error box hasn't // been initialized yet as an NSError. auto domain = error->domain.load(SWIFT_MEMORY_ORDER_CONSUME); assert(domain && "Error box used as NSError before initialization"); // Don't need to .retain.autorelease since it's immutable. return cf_const_cast(domain); } - (id /* NSString */)description { auto error = (const SwiftError *)self; auto value = error->getValue(); auto type = error->type; // Copy the value, since it will be consumed by getDescription. ValueBuffer copyBuf; auto copy = type->allocateBufferIn(©Buf); error->type->vw_initializeWithCopy(copy, const_cast(value)); id description = getDescription(copy, type); type->deallocateBufferIn(©Buf); return description; } - (NSInteger)code { auto error = (const SwiftError*)self; return error->code.load(SWIFT_MEMORY_ORDER_CONSUME); } - (id /* NSDictionary */)userInfo { auto error = (const SwiftError*)self; auto userInfo = error->userInfo.load(SWIFT_MEMORY_ORDER_CONSUME); assert(userInfo && "Error box used as NSError before initialization"); // Don't need to .retain.autorelease since it's immutable. return cf_const_cast(userInfo); } - (id)copyWithZone:(NSZone *)zone { (void)zone; // __SwiftNativeNSError is immutable, so we can return the same instance back. return [self retain]; } - (Class)classForCoder { // This is a runtime-private subclass. When archiving or unarchiving, do so // as an NSError. return getNSErrorClass(); } // Note: We support comparing cases of `@objc` enums defined in Swift to // pure `NSError`s. They should compare equal as long as the domain and // code match. Equal values should have equal hash values. Thus, we can't // use the Swift hash value computation that comes from the `Hashable` // conformance if one exists, and we must use the `NSError` hashing // algorithm. // // So we are not overriding the `hash` method, even though we are // overriding `isEqual:`. - (BOOL)isEqual:(id)other { auto self_ = (const SwiftError *)self; auto other_ = (const SwiftError *)other; assert(!self_->isPureNSError()); if (self == other) { return YES; } if (!other) { return NO; } if (other_->isPureNSError()) { return [super isEqual:other]; } auto hashableBaseType = self_->getHashableBaseType(); if (!hashableBaseType || other_->getHashableBaseType() != hashableBaseType) { return [super isEqual:other]; } auto hashableConformance = self_->getHashableConformance(); if (!hashableConformance) { return [super isEqual:other]; } return _swift_stdlib_Hashable_isEqual_indirect( self_->getValue(), other_->getValue(), hashableBaseType, hashableConformance); } @end Class swift::getNSErrorClass() { return SWIFT_LAZY_CONSTANT(objc_lookUpClass("NSError")); } const Metadata *swift::getNSErrorMetadata() { return SWIFT_LAZY_CONSTANT( swift_getObjCClassMetadata((const ClassMetadata *)getNSErrorClass())); } extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error); const WitnessTable *swift::findErrorWitness(const Metadata *srcType) { return swift_conformsToProtocolCommon(srcType, &PROTOCOL_DESCR_SYM(s5Error)); } id swift::dynamicCastValueToNSError(OpaqueValue *src, const Metadata *srcType, const WitnessTable *srcErrorWitness, DynamicCastFlags flags) { // Check whether there is an embedded NSError. if (id embedded = getErrorEmbeddedNSErrorIndirect(src, srcType, srcErrorWitness)) { if (flags & DynamicCastFlags::TakeOnSuccess) srcType->vw_destroy(src); return embedded; } BoxPair errorBox = swift_allocError(srcType, srcErrorWitness, src, /*isTake*/ flags & DynamicCastFlags::TakeOnSuccess); auto *error = (SwiftError *)errorBox.object; return _swift_stdlib_bridgeErrorToNSError(error); } static Class getAndBridgeSwiftNativeNSErrorClass() { Class nsErrorClass = swift::getNSErrorClass(); Class ourClass = [__SwiftNativeNSError class]; // We want "err as AnyObject" to do *something* even without Foundation if (nsErrorClass) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" class_setSuperclass(ourClass, nsErrorClass); #pragma clang diagnostic pop } return ourClass; } static Class getSwiftNativeNSErrorClass() { return SWIFT_LAZY_CONSTANT(getAndBridgeSwiftNativeNSErrorClass()); } static id getEmptyNSDictionary() { return [objc_lookUpClass("NSDictionary") dictionary]; } /// Allocate a catchable error object. BoxPair swift::swift_allocError(const Metadata *type, const WitnessTable *errorConformance, OpaqueValue *initialValue, bool isTake) { auto TheSwiftNativeNSError = getSwiftNativeNSErrorClass(); assert(class_getInstanceSize(TheSwiftNativeNSError) == sizeof(SwiftErrorHeader) && "NSError layout changed!"); // Determine the extra allocated space necessary to carry the value. // TODO: If the error type is a simple enum with no associated values, we // could emplace it in the "code" slot of the NSError and save ourselves // some work. unsigned size = type->getValueWitnesses()->getSize(); unsigned alignMask = type->getValueWitnesses()->getAlignmentMask(); size_t alignmentPadding = -sizeof(SwiftError) & alignMask; size_t totalExtraSize = sizeof(SwiftError) - sizeof(SwiftErrorHeader) + alignmentPadding + size; size_t valueOffset = alignmentPadding + sizeof(SwiftError); // Allocate the instance as if it were a CFError. We won't really initialize // the CFError parts until forced to though. auto instance = (SwiftError *)class_createInstance(TheSwiftNativeNSError, totalExtraSize); // Leave the NSError bits zero-initialized. We'll lazily instantiate them when // needed. // Initialize the Swift type metadata. instance->type = type; instance->errorConformance = errorConformance; instance->hashableBaseType = nullptr; instance->hashableConformance = nullptr; auto valueBytePtr = reinterpret_cast(instance) + valueOffset; auto valuePtr = reinterpret_cast(valueBytePtr); // If an initial value was given, copy or take it in. if (initialValue) { if (isTake) type->vw_initializeWithTake(valuePtr, initialValue); else type->vw_initializeWithCopy(valuePtr, initialValue); } // Return the SwiftError reference and a pointer to the uninitialized value // inside. return BoxPair{reinterpret_cast(instance), valuePtr}; } /// Deallocate an error object whose contained object has already been /// destroyed. void swift::swift_deallocError(SwiftError *error, const Metadata *type) { object_dispose((id)error); } static const WitnessTable *getNSErrorConformanceToError() { // CFError and NSError are toll-free-bridged, so we can use either type's // witness table interchangeably. CFError's is potentially slightly more // efficient since it doesn't need to dispatch for an unsubclassed NSCFError. // The error bridging info lives in the Foundation overlay, but it should be // safe to assume that that's been linked in if a user is using NSError in // their Swift source. auto *conformance = SWIFT_LAZY_CONSTANT( reinterpret_cast( dlsym(RTLD_DEFAULT, MANGLE_AS_STRING(MANGLE_SYM(So10CFErrorRefas5Error10FoundationMc))))); assert(conformance && "Foundation overlay not loaded, or 'CFError : Error' conformance " "not available"); return swift_getWitnessTable(conformance, conformance->getCanonicalTypeMetadata(), nullptr); } static const HashableWitnessTable *getNSErrorConformanceToHashable() { auto *conformance = SWIFT_LAZY_CONSTANT( reinterpret_cast( dlsym(RTLD_DEFAULT, MANGLE_AS_STRING(MANGLE_SYM(So8NSObjectCSH10ObjectiveCMc))))); assert(conformance && "ObjectiveC overlay not loaded, or 'NSObject : Hashable' conformance " "not available"); return (const HashableWitnessTable *)swift_getWitnessTable( conformance, conformance->getCanonicalTypeMetadata(), nullptr); } bool SwiftError::isPureNSError() const { // We can do an exact type check; __SwiftNativeNSError shouldn't be subclassed // or proxied. return _swift_getClass(this) != (ClassMetadata *)getSwiftNativeNSErrorClass(); } const Metadata *SwiftError::getType() const { if (isPureNSError()) { id asError = reinterpret_cast(const_cast(this)); return swift_getObjCClassMetadata((ClassMetadata*)[asError class]); } return type; } const WitnessTable *SwiftError::getErrorConformance() const { if (isPureNSError()) { return getNSErrorConformanceToError(); } return errorConformance; } const Metadata *SwiftError::getHashableBaseType() const { if (isPureNSError()) { return getNSErrorMetadata(); } if (auto type = hashableBaseType.load(std::memory_order_acquire)) { if (reinterpret_cast(type) == 1) { return nullptr; } return type; } const Metadata *expectedType = nullptr; const Metadata *hashableBaseType = findHashableBaseType(type); this->hashableBaseType.compare_exchange_strong( expectedType, hashableBaseType ? hashableBaseType : reinterpret_cast(1), std::memory_order_acq_rel); return type; } const HashableWitnessTable *SwiftError::getHashableConformance() const { if (isPureNSError()) { return getNSErrorConformanceToHashable(); } if (auto wt = hashableConformance.load(std::memory_order_acquire)) { if (reinterpret_cast(wt) == 1) { return nullptr; } return wt; } const HashableWitnessTable *expectedWT = nullptr; const HashableWitnessTable *wt = reinterpret_cast( swift_conformsToProtocolCommon(type, &HashableProtocolDescriptor)); hashableConformance.compare_exchange_strong( expectedWT, wt ? wt : reinterpret_cast(1), std::memory_order_acq_rel); return wt; } /// Extract a pointer to the value, the type metadata, and the Error /// protocol witness from an error object. /// /// The "scratch" pointer should point to an uninitialized word-sized /// temporary buffer. The implementation may write a reference to itself to /// that buffer if the error object is a toll-free-bridged NSError instead of /// a native Swift error, in which case the object itself is the "boxed" value. /// /// This function is called by compiler-generated code. void swift::swift_getErrorValue(const SwiftError *errorObject, void **scratch, ErrorValueResult *out) { // TODO: Would be great if Clang had a return-three convention so we didn't // need the out parameter here. out->type = errorObject->getType(); // Check for a bridged Cocoa NSError. if (errorObject->isPureNSError()) { // Return a pointer to the scratch buffer. *scratch = (void*)errorObject; out->value = (const OpaqueValue *)scratch; out->errorConformance = getNSErrorConformanceToError(); } else { out->value = errorObject->getValue(); out->errorConformance = errorObject->errorConformance; } } // internal func _getErrorDomainNSString // (_ x: UnsafePointer) -> AnyObject #define getErrorDomainNSString \ MANGLE_SYM(s23_getErrorDomainNSStringyyXlSPyxGs0B0RzlF) SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL id getErrorDomainNSString(const OpaqueValue *error, const Metadata *T, const WitnessTable *Error); // internal func _getErrorCode(_ x: UnsafePointer) -> Int #define getErrorCode \ MANGLE_SYM(s13_getErrorCodeySiSPyxGs0B0RzlF) SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL NSInteger getErrorCode(const OpaqueValue *error, const Metadata *T, const WitnessTable *Error); // internal func _getErrorUserInfoNSDictionary(_ x: UnsafePointer) -> AnyObject #define getErrorUserInfoNSDictionary \ MANGLE_SYM(s29_getErrorUserInfoNSDictionaryyyXlSgSPyxGs0B0RzlF) SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL id getErrorUserInfoNSDictionary( const OpaqueValue *error, const Metadata *T, const WitnessTable *Error); // @_silgen_name("_swift_stdlib_getErrorDefaultUserInfo") // internal func _getErrorDefaultUserInfo(_ x: T) -> AnyObject SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL id _swift_stdlib_getErrorDefaultUserInfo(OpaqueValue *error, const Metadata *T, const WitnessTable *Error) { // public func Foundation._getErrorDefaultUserInfo(_ error: T) // -> AnyObject? typedef SWIFT_CC(swift) NSDictionary *(*GetErrorDefaultUserInfoFunction)( const OpaqueValue *error, const Metadata *T, const WitnessTable *Error); auto foundationGetDefaultUserInfo = SWIFT_LAZY_CONSTANT( reinterpret_cast( dlsym(RTLD_DEFAULT, MANGLE_AS_STRING(MANGLE_SYM(10Foundation24_getErrorDefaultUserInfoyyXlSgxs0C0RzlF))))); if (!foundationGetDefaultUserInfo) { return nullptr; } // +0 Convention: In the case where we have the +1 convention, this will // destroy the error for us, otherwise, it will take the value guaranteed. The // conclusion is that we can leave this alone. return foundationGetDefaultUserInfo(error, T, Error); } /// Take an Error box and turn it into a valid NSError instance. Error is passed /// at +1. id swift::_swift_stdlib_bridgeErrorToNSError(SwiftError *errorObject) { id ns = reinterpret_cast(errorObject); // If we already have a domain set, then we've already initialized. // If this is a real NSError, then Cocoa and Core Foundation's initializers // guarantee that the domain is never nil, so if this test fails, we can // assume we're working with a bridged error. (Note that Cocoa and CF // **will** allow the userInfo storage to be initialized to nil.) // // If this is a bridged error, then the domain, code, and user info are // lazily computed, and the domain will be nil if they haven't been computed // yet. The initialization is ordered in such a way that all other lazy // initialization of the object happens-before the domain initialization so // that the domain can be used alone as a flag for the initialization of the // object. if (errorObject->domain.load(std::memory_order_acquire)) { return ns; } // Otherwise, calculate the domain, code, and user info, and // initialize the NSError. auto value = SwiftError::getIndirectValue(&errorObject); auto type = errorObject->getType(); auto witness = errorObject->getErrorConformance(); id domain = getErrorDomainNSString(value, type, witness); NSInteger code = getErrorCode(value, type, witness); id userInfo = getErrorUserInfoNSDictionary(value, type, witness); // Never produce an empty userInfo dictionary. if (!userInfo) userInfo = SWIFT_LAZY_CONSTANT(getEmptyNSDictionary()); // The error code shouldn't change, so we can store it blindly, even if // somebody beat us to it. The store can be relaxed, since we'll do a // store(release) of the domain last thing to publish the initialized // NSError. errorObject->code.store(code, std::memory_order_relaxed); // However, we need to cmpxchg the userInfo; if somebody beat us to it, // we need to release. CFDictionaryRef expectedUserInfo = nullptr; if (!errorObject->userInfo.compare_exchange_strong(expectedUserInfo, (CFDictionaryRef)userInfo, std::memory_order_acq_rel)) objc_release(userInfo); // We also need to cmpxchg in the domain; if somebody beat us to it, // we need to release. // // Storing the domain must be the **LAST THING** we do, since it's // also the flag that the NSError has been initialized. CFStringRef expectedDomain = nullptr; if (!errorObject->domain.compare_exchange_strong(expectedDomain, (CFStringRef)domain, std::memory_order_acq_rel)) objc_release(domain); return ns; } extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error); static IMP getNSProxyLookupMethod() { Class NSProxyClass = objc_lookUpClass("NSProxy"); return class_getMethodImplementation(NSProxyClass, @selector(methodSignatureForSelector:)); } // A safer alternative to calling `isKindOfClass:` directly. static bool isKindOfClass(HeapObject *object, Class cls) { IMP NSProxyLookupMethod = SWIFT_LAZY_CONSTANT(getNSProxyLookupMethod()); // People sometimes fail to override `methodSignatureForSelector:` in their // NSProxy subclasses, which causes `isKindOfClass:` to crash. Avoid that... Class objectClass = object_getClass((id)object); IMP objectLookupMethod = class_getMethodImplementation(objectClass, @selector(methodSignatureForSelector:)); if (objectLookupMethod == NSProxyLookupMethod) { return false; } return [reinterpret_cast(object) isKindOfClass: cls]; } bool swift::tryDynamicCastNSErrorObjectToValue(HeapObject *object, OpaqueValue *dest, const Metadata *destType, DynamicCastFlags flags) { Class NSErrorClass = getNSErrorClass(); // The object must be an NSError subclass. if (isObjCTaggedPointerOrNull(object) || !isKindOfClass(object, NSErrorClass)) return false; id srcInstance = reinterpret_cast(object); // A __SwiftNativeNSError box can always be unwrapped to cast the value back // out as an Error existential. if (!reinterpret_cast(srcInstance)->isPureNSError()) { ProtocolDescriptorRef theErrorProtocol(&PROTOCOL_DESCR_SYM(s5Error), ProtocolDispatchStrategy::Swift); auto theErrorTy = swift_getExistentialTypeMetadata(ProtocolClassConstraint::Any, nullptr, 1, &theErrorProtocol); return swift_dynamicCast(dest, reinterpret_cast(&object), theErrorTy, destType, flags); } // public func Foundation._bridgeNSErrorToError< // T : _ObjectiveCBridgeableError // >(error: NSError, out: UnsafeMutablePointer) -> Bool { typedef SWIFT_CC(swift) bool (*BridgeErrorToNSErrorFunction)( NSError *, OpaqueValue*, const Metadata *, const WitnessTable *); auto bridgeNSErrorToError = SWIFT_LAZY_CONSTANT( reinterpret_cast( dlsym(RTLD_DEFAULT, MANGLE_AS_STRING(MANGLE_SYM(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF))))); // protocol _ObjectiveCBridgeableError auto TheObjectiveCBridgeableError = SWIFT_LAZY_CONSTANT( reinterpret_cast( dlsym(RTLD_DEFAULT, MANGLE_AS_STRING(MANGLE_SYM(10Foundation26_ObjectiveCBridgeableErrorMp))))); // If the Foundation overlay isn't loaded, then arbitrary NSErrors can't be // bridged. if (!bridgeNSErrorToError || !TheObjectiveCBridgeableError) return false; // Is the target type a bridgeable error? auto witness = swift_conformsToProtocolCommon(destType, TheObjectiveCBridgeableError); if (witness) { // If so, attempt the bridge. if (bridgeNSErrorToError(srcInstance, dest, destType, witness)) { if (flags & DynamicCastFlags::TakeOnSuccess) objc_release(srcInstance); return true; } } // If the destination is just an Error then we can bridge directly. auto *destTypeExistential = dyn_cast(destType); if (destTypeExistential && destTypeExistential->getRepresentation() == ExistentialTypeRepresentation::Error) { auto destBoxAddr = reinterpret_cast(dest); *destBoxAddr = objc_retain(srcInstance); return true; } return false; } bool swift::tryDynamicCastNSErrorToValue(OpaqueValue *dest, OpaqueValue *src, const Metadata *srcType, const Metadata *destType, DynamicCastFlags flags) { // NSError instances must be class instances, anything else automatically fails. switch (srcType->getKind()) { case MetadataKind::Class: case MetadataKind::ObjCClassWrapper: case MetadataKind::ForeignClass: return tryDynamicCastNSErrorObjectToValue(*reinterpret_cast(src), dest, destType, flags); // Not a class. // Foreign reference types don't support casting to parent/child types yet // (rdar://85881664&85881794). case MetadataKind::ForeignReferenceType: default: return false; } } SwiftError * swift::swift_errorRetain(SwiftError *error) { // For now, SwiftError is always objc-refcounted. return (SwiftError*)objc_retain((id)error); } void swift::swift_errorRelease(SwiftError *error) { // For now, SwiftError is always objc-refcounted. return objc_release((id)error); } #endif