-
Notifications
You must be signed in to change notification settings - Fork 10.5k
/
Copy pathErrorObject.mm
676 lines (578 loc) · 24.3 KB
/
ErrorObject.mm
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
//===--- 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