mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
677 lines
24 KiB
Plaintext
677 lines
24 KiB
Plaintext
//===--- 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 <Foundation/Foundation.h>
|
|
#include <dlfcn.h>
|
|
#include <objc/NSObject.h>
|
|
#include <objc/message.h>
|
|
#include <objc/objc.h>
|
|
#include <objc/runtime.h>
|
|
|
|
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<id>(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<OpaqueValue *>(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<id>(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<char*>(instance) + valueOffset;
|
|
auto valuePtr = reinterpret_cast<OpaqueValue*>(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<HeapObject*>(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<const ProtocolConformanceDescriptor *>(
|
|
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<const ProtocolConformanceDescriptor *>(
|
|
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<id>(const_cast<SwiftError *>(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<uintptr_t>(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<const Metadata *>(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<uintptr_t>(wt) == 1) {
|
|
return nullptr;
|
|
}
|
|
return wt;
|
|
}
|
|
|
|
const HashableWitnessTable *expectedWT = nullptr;
|
|
const HashableWitnessTable *wt =
|
|
reinterpret_cast<const HashableWitnessTable *>(
|
|
swift_conformsToProtocolCommon(type, &HashableProtocolDescriptor));
|
|
hashableConformance.compare_exchange_strong(
|
|
expectedWT, wt ? wt : reinterpret_cast<const HashableWitnessTable *>(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<T : Error>
|
|
// (_ x: UnsafePointer<T>) -> 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<T : Error>(_ x: UnsafePointer<T>) -> 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<T : Error>(_ x: UnsafePointer<T>) -> 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<T : Error>(_ 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<T: Error>(_ 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<GetErrorDefaultUserInfoFunction>(
|
|
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<id>(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<id>(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<id>(object);
|
|
|
|
// A __SwiftNativeNSError box can always be unwrapped to cast the value back
|
|
// out as an Error existential.
|
|
if (!reinterpret_cast<SwiftError*>(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<OpaqueValue *>(&object),
|
|
theErrorTy, destType, flags);
|
|
}
|
|
|
|
// public func Foundation._bridgeNSErrorToError<
|
|
// T : _ObjectiveCBridgeableError
|
|
// >(error: NSError, out: UnsafeMutablePointer<T>) -> Bool {
|
|
typedef SWIFT_CC(swift) bool (*BridgeErrorToNSErrorFunction)(
|
|
NSError *, OpaqueValue*, const Metadata *,
|
|
const WitnessTable *);
|
|
auto bridgeNSErrorToError = SWIFT_LAZY_CONSTANT(
|
|
reinterpret_cast<BridgeErrorToNSErrorFunction>(
|
|
dlsym(RTLD_DEFAULT,
|
|
MANGLE_AS_STRING(MANGLE_SYM(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF)))));
|
|
|
|
// protocol _ObjectiveCBridgeableError
|
|
auto TheObjectiveCBridgeableError = SWIFT_LAZY_CONSTANT(
|
|
reinterpret_cast<ProtocolDescriptor *>(
|
|
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<ExistentialTypeMetadata>(destType);
|
|
if (destTypeExistential &&
|
|
destTypeExistential->getRepresentation() == ExistentialTypeRepresentation::Error) {
|
|
auto destBoxAddr = reinterpret_cast<id*>(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<HeapObject **>(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
|
|
|