1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
|
//===--- 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
|