Skip to content

Commit 7acf945

Browse files
authored
Merge pull request #16773 from mikeash/fix-static-stripped-error-bridging-4.2
[Runtime] 4.2 - Redo the ErrorObject/dlsym interface to use a struct containing all the bridging points.
2 parents bd5adeb + 5501079 commit 7acf945

File tree

7 files changed

+143
-30
lines changed

7 files changed

+143
-30
lines changed

stdlib/public/SDK/Foundation/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ add_swift_library(swiftFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SD
2626
NSCoder.swift
2727
NSDate.swift
2828
NSDictionary.swift
29+
NSError.mm
2930
NSError.swift
3031
NSExpression.swift
3132
NSFastEnumeration.swift
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_FOUNDATION_NSERROR_H
14+
#define SWIFT_FOUNDATION_NSERROR_H
15+
16+
#include "swift/Runtime/Metadata.h"
17+
#include "../../runtime/SwiftHashableSupport.h"
18+
#include <Foundation/Foundation.h>
19+
20+
namespace swift {
21+
22+
/// The name of the symbol that ErrorObject.mm will look up using dlsym. It uses
23+
/// this to locate various items related to Error bridging to NS/CFError.
24+
#define ERROR_BRIDGING_SYMBOL_NAME swift_errorBridgingInfo
25+
#define ERROR_BRIDGING_SYMBOL_NAME_STRING "swift_errorBridgingInfo"
26+
27+
/// The items that ErrorObject.mm needs for bridging. The
28+
/// ERROR_BRIDGING_SYMBOL_NAME symbol will contain an instance of this struct.
29+
struct ErrorBridgingInfo {
30+
const SWIFT_CC(swift) WitnessTable *(*GetCFErrorErrorConformance)();
31+
32+
const SWIFT_CC(swift) hashable_support::HashableWitnessTable *
33+
(*GetNSObjectHashableConformance)();
34+
35+
SWIFT_CC(swift) NSDictionary *(*GetErrorDefaultUserInfo)(const OpaqueValue *error,
36+
const Metadata *T,
37+
const WitnessTable *Error);
38+
39+
SWIFT_CC(swift) bool (*BridgeErrorToNSError)(NSError *, OpaqueValue*, const Metadata *,
40+
const WitnessTable *);
41+
42+
const ProtocolDescriptor *ObjectiveCBridgeableError;
43+
};
44+
45+
}
46+
47+
#endif // SWIFT_FOUNDATION_NSERROR_H
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "NSError.h"
14+
15+
#include "swift/Demangling/ManglingMacros.h"
16+
17+
using namespace swift;
18+
19+
// Declare the mangled Swift symbols that we'll be putting in the bridging info.
20+
extern "C" const SWIFT_CC(swift) WitnessTable *
21+
MANGLE_SYM(So10CFErrorRefas5Error10FoundationWa)();
22+
23+
extern "C" const SWIFT_CC(swift) hashable_support::HashableWitnessTable *
24+
MANGLE_SYM(So8NSObjectCs8Hashable10ObjectiveCWa)();
25+
26+
extern "C" SWIFT_CC(swift)
27+
NSDictionary *MANGLE_SYM(10Foundation24_getErrorDefaultUserInfoyyXlSgxs0C0RzlF)(
28+
const OpaqueValue *error, const Metadata *T, const WitnessTable *Error);
29+
30+
extern "C" SWIFT_CC(swift) bool
31+
MANGLE_SYM(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF)(
32+
NSError *, OpaqueValue*, const Metadata *, const WitnessTable *);
33+
34+
extern "C" const ProtocolDescriptor
35+
MANGLE_SYM(10Foundation26_ObjectiveCBridgeableErrorMp);
36+
37+
// Define the bridging info struct.
38+
extern "C" ErrorBridgingInfo ERROR_BRIDGING_SYMBOL_NAME = {
39+
MANGLE_SYM(So10CFErrorRefas5Error10FoundationWa),
40+
MANGLE_SYM(So8NSObjectCs8Hashable10ObjectiveCWa),
41+
MANGLE_SYM(10Foundation24_getErrorDefaultUserInfoyyXlSgxs0C0RzlF),
42+
MANGLE_SYM(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF),
43+
&MANGLE_SYM(10Foundation26_ObjectiveCBridgeableErrorMp)
44+
};
45+
46+
// This directive ensures that the symbol is preserved even when statically
47+
// linked into an executable and stripped, so that the dlsym lookup from
48+
// ErrorObject.mm still works.
49+
asm(".desc _" ERROR_BRIDGING_SYMBOL_NAME_STRING ", 0x10");

stdlib/public/runtime/ErrorObject.mm

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <objc/message.h>
3838
#include <objc/objc.h>
3939
#include <Foundation/Foundation.h>
40+
#include "../SDK/Foundation/NSError.h"
4041

4142
using namespace swift;
4243
using namespace swift::hashable_support;
@@ -220,31 +221,40 @@ static Class getSwiftNativeNSErrorClass() {
220221
object_dispose((id)error);
221222
}
222223

224+
/// Get the error bridging info from the Foundation overlay. If it can't
225+
/// be loaded, return all NULLs.
226+
static ErrorBridgingInfo getErrorBridgingInfo() {
227+
auto *info = SWIFT_LAZY_CONSTANT(
228+
reinterpret_cast<ErrorBridgingInfo *>(
229+
dlsym(RTLD_DEFAULT, ERROR_BRIDGING_SYMBOL_NAME_STRING)));
230+
if (!info) {
231+
ErrorBridgingInfo nulls = {};
232+
return nulls;
233+
}
234+
return *info;
235+
}
236+
223237
static const WitnessTable *getNSErrorConformanceToError() {
224238
// CFError and NSError are toll-free-bridged, so we can use either type's
225239
// witness table interchangeably. CFError's is potentially slightly more
226240
// efficient since it doesn't need to dispatch for an unsubclassed NSCFError.
227-
// The witness table lives in the Foundation overlay, but it should be safe
228-
// to assume that that's been linked in if a user is using NSError in their
229-
// Swift source.
241+
// The error bridging info lives in the Foundation overlay, but it should be
242+
// safe to assume that that's been linked in if a user is using NSError in
243+
// their Swift source.
230244

231-
auto TheWitnessTable = SWIFT_LAZY_CONSTANT(dlsym(RTLD_DEFAULT,
232-
MANGLE_AS_STRING(MANGLE_SYM(So10CFErrorRefas5Error10FoundationWa))));
233-
assert(TheWitnessTable &&
245+
auto getter = getErrorBridgingInfo().GetCFErrorErrorConformance;
246+
assert(getter &&
234247
"Foundation overlay not loaded, or 'CFError : Error' conformance "
235248
"not available");
236-
237-
return reinterpret_cast<const SWIFT_CC(swift) WitnessTable *(*)()>(TheWitnessTable)();
249+
return getter();
238250
}
239251

240252
static const HashableWitnessTable *getNSErrorConformanceToHashable() {
241-
auto TheWitnessTable = SWIFT_LAZY_CONSTANT(dlsym(RTLD_DEFAULT,
242-
MANGLE_AS_STRING(MANGLE_SYM(So8NSObjectCs8Hashable10ObjectiveCWa))));
243-
assert(TheWitnessTable &&
253+
auto getter = getErrorBridgingInfo().GetNSObjectHashableConformance;
254+
assert(getter &&
244255
"ObjectiveC overlay not loaded, or 'NSObject : Hashable' conformance "
245256
"not available");
246-
247-
return reinterpret_cast<const SWIFT_CC(swift) HashableWitnessTable *(*)()>(TheWitnessTable)();
257+
return getter();
248258
}
249259

250260
bool SwiftError::isPureNSError() const {
@@ -371,16 +381,9 @@ NSInteger getErrorCode(const OpaqueValue *error,
371381
NSDictionary *_swift_stdlib_getErrorDefaultUserInfo(OpaqueValue *error,
372382
const Metadata *T,
373383
const WitnessTable *Error) {
374-
typedef SWIFT_CC(swift)
375-
NSDictionary *GetDefaultFn(const OpaqueValue *error,
376-
const Metadata *T,
377-
const WitnessTable *Error);
378-
379384
// public func Foundation._getErrorDefaultUserInfo<T: Error>(_ error: T)
380385
// -> AnyObject?
381-
auto foundationGetDefaultUserInfo = SWIFT_LAZY_CONSTANT(
382-
reinterpret_cast<GetDefaultFn*> (dlsym(RTLD_DEFAULT,
383-
MANGLE_AS_STRING(MANGLE_SYM(10Foundation24_getErrorDefaultUserInfoyyXlSgxs0C0RzlF)))));
386+
auto foundationGetDefaultUserInfo = getErrorBridgingInfo().GetErrorDefaultUserInfo;
384387
if (!foundationGetDefaultUserInfo) {
385388
SWIFT_CC_PLUSONE_GUARD(T->vw_destroy(error));
386389
return nullptr;
@@ -517,16 +520,9 @@ typedef SWIFT_CC(swift)
517520
// public func Foundation._bridgeNSErrorToError<
518521
// T : _ObjectiveCBridgeableError
519522
// >(error: NSError, out: UnsafeMutablePointer<T>) -> Bool {
520-
typedef SWIFT_CC(swift)
521-
bool BridgeFn(NSError *, OpaqueValue*, const Metadata *,
522-
const WitnessTable *);
523-
auto bridgeNSErrorToError = SWIFT_LAZY_CONSTANT(
524-
reinterpret_cast<BridgeFn*>(dlsym(RTLD_DEFAULT,
525-
MANGLE_AS_STRING(MANGLE_SYM(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF)))));
523+
auto bridgeNSErrorToError = getErrorBridgingInfo().BridgeErrorToNSError;
526524
// protocol _ObjectiveCBridgeableError
527-
auto TheObjectiveCBridgeableError = SWIFT_LAZY_CONSTANT(
528-
reinterpret_cast<const ProtocolDescriptor *>(dlsym(RTLD_DEFAULT,
529-
MANGLE_AS_STRING(MANGLE_SYM(10Foundation26_ObjectiveCBridgeableErrorMp)))));
525+
auto TheObjectiveCBridgeableError = getErrorBridgingInfo().ObjectiveCBridgeableError;
530526

531527
// If the Foundation overlay isn't loaded, then arbitrary NSErrors can't be
532528
// bridged.

test/stdlib/ErrorBridgedStatic.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ class Bar: Foo {
1717
override func foo(_ x: Int32) throws {
1818
try super.foo(5)
1919
}
20+
21+
override func foothrows(_ x: Int32) throws {
22+
try super.foothrows(5)
23+
}
2024
}
2125

2226
var ErrorBridgingStaticTests = TestSuite("ErrorBridging with static libs")
@@ -27,4 +31,14 @@ ErrorBridgingStaticTests.test("round-trip Swift override of ObjC method") {
2731
} catch { }
2832
}
2933

34+
ErrorBridgingStaticTests.test("round-trip Swift override of throwing ObjC method") {
35+
do {
36+
try (Bar() as Foo).foothrows(5)
37+
} catch {
38+
print(error)
39+
expectEqual(error._domain, "abcd")
40+
expectEqual(error._code, 1234)
41+
}
42+
}
43+
3044
runAllTests()

test/stdlib/Inputs/ErrorBridgedStaticImpl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
@interface Foo: NSObject
44

55
- (BOOL)foo:(int)x error:(NSError**)error;
6+
- (BOOL)foothrows:(int)x error:(NSError**)error;
67

78
@end

test/stdlib/Inputs/ErrorBridgedStaticImpl.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,10 @@ - (BOOL)foo:(int)x error:(NSError**)error {
77
return NO;
88
}
99

10+
- (BOOL)foothrows:(int)x error:(NSError**)error {
11+
*error = [NSError errorWithDomain: @"abcd" code: 1234 userInfo: nil];
12+
return NO;
13+
}
14+
1015
@end
1116

0 commit comments

Comments
 (0)