Skip to content

Commit 4910b58

Browse files
authored
Merge pull request #3581 from jckarter/swiftvalue-description
2 parents 2b732d0 + 9cba385 commit 4910b58

File tree

4 files changed

+76
-29
lines changed

4 files changed

+76
-29
lines changed

stdlib/public/runtime/SwiftObject.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@ SWIFT_RUNTIME_EXPORT @interface SwiftObject<NSObject> {
8383

8484
namespace swift {
8585

86+
struct String { void *x, *y, *z; };
87+
88+
/// Helper from the standard library for stringizing an arbitrary object.
89+
extern "C" SWIFT_CC(swift)
90+
void swift_getSummary(String *out, OpaqueValue *value, const Metadata *T);
91+
92+
// Convert a Swift String to an NSString.
93+
NSString *convertStringToNSString(String *swiftString);
94+
8695
#endif
8796

8897
}

stdlib/public/runtime/SwiftObject.mm

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <unordered_map>
4444
#if SWIFT_OBJC_INTEROP
4545
# import <CoreFoundation/CFBase.h> // for CFTypeID
46+
# import <Foundation/Foundation.h>
4647
# include <malloc/malloc.h>
4748
# include <dispatch/dispatch.h>
4849
#endif
@@ -91,20 +92,13 @@ static uintptr_t computeISAMask() {
9192
class_getInstanceSize(cls), mask));
9293
}
9394

94-
// Helper from the standard library for stringizing an arbitrary object.
95-
namespace {
96-
struct String { void *x, *y, *z; };
97-
}
98-
99-
extern "C" void swift_getSummary(String *out, OpaqueValue *value,
100-
const Metadata *T);
101-
102-
static NSString *_getDescription(SwiftObject *obj) {
95+
NSString *swift::convertStringToNSString(String *swiftString) {
10396
typedef SWIFT_CC(swift) NSString *ConversionFn(void *sx, void *sy, void *sz);
10497

10598
// Cached lookup of swift_convertStringToNSString, which is in Foundation.
106-
static ConversionFn *convertStringToNSString = nullptr;
107-
99+
static std::atomic<ConversionFn *> TheConvertStringToNSString(nullptr);
100+
auto convertStringToNSString =
101+
TheConvertStringToNSString.load(std::memory_order_relaxed);
108102
if (!convertStringToNSString) {
109103
convertStringToNSString = (ConversionFn *)(uintptr_t)
110104
dlsym(RTLD_DEFAULT, "swift_convertStringToNSString");
@@ -113,31 +107,25 @@ static uintptr_t computeISAMask() {
113107
// ObjC interop is low.
114108
if (!convertStringToNSString)
115109
return @"SwiftObject";
110+
111+
TheConvertStringToNSString.store(convertStringToNSString,
112+
std::memory_order_relaxed);
116113
}
117114

115+
return convertStringToNSString(swiftString->x,
116+
swiftString->y,
117+
swiftString->z);
118+
}
119+
120+
static NSString *_getDescription(SwiftObject *obj) {
118121
String tmp;
119122
swift_retain((HeapObject*)obj);
120123
swift_getSummary(&tmp, (OpaqueValue*)&obj, _swift_getClassOfAllocated(obj));
121-
return [convertStringToNSString(tmp.x, tmp.y, tmp.z) autorelease];
124+
return [convertStringToNSString(&tmp) autorelease];
122125
}
123126

124127
static NSString *_getClassDescription(Class cls) {
125-
typedef SWIFT_CC(swift) NSString *ConversionFn(Class cls);
126-
127-
// Cached lookup of NSStringFromClass, which is in Foundation.
128-
static ConversionFn *NSStringFromClass_fn = nullptr;
129-
130-
if (!NSStringFromClass_fn) {
131-
NSStringFromClass_fn = (ConversionFn *)(uintptr_t)
132-
dlsym(RTLD_DEFAULT, "NSStringFromClass");
133-
// If Foundation hasn't loaded yet, fall back to returning the static string
134-
// "SwiftObject". The likelihood of someone invoking +description without
135-
// ObjC interop is low.
136-
if (!NSStringFromClass_fn)
137-
return @"SwiftObject";
138-
}
139-
140-
return NSStringFromClass_fn(cls);
128+
return NSStringFromClass(cls);
141129
}
142130

143131

stdlib/public/runtime/SwiftValue.mm

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,28 @@ - (void)dealloc {
180180
}
181181
#pragma clang diagnostic pop
182182

183+
static NSString *getValueDescription(SwiftValue *self) {
184+
String tmp;
185+
const Metadata *type;
186+
const OpaqueValue *value;
187+
std::tie(type, value) = getValueFromSwiftValue(self);
188+
189+
// Copy the value, since it will be consumed by getSummary.
190+
ValueBuffer copyBuf;
191+
auto copy = type->vw_initializeBufferWithCopy(&copyBuf,
192+
const_cast<OpaqueValue*>(value));
193+
swift_getSummary(&tmp, copy, type);
194+
type->vw_deallocateBuffer(&copyBuf);
195+
return convertStringToNSString(&tmp);
196+
}
197+
198+
- (NSString *)description {
199+
return getValueDescription(self);
200+
}
201+
- (NSString *)debugDescription {
202+
return getValueDescription(self);
203+
}
204+
183205
// Private methods for debugging purposes.
184206

185207
- (const Metadata *)_swiftTypeMetadata {
@@ -189,7 +211,7 @@ - (const OpaqueValue *)_swiftValue {
189211
return getValueFromSwiftValue(self).second;
190212
}
191213

192-
// TODO: Forward description, debugDescription, isEqual:, hash, etc. to
214+
// TODO: Forward isEqual: and hash to
193215
// corresponding operations on the boxed Swift type.
194216

195217
@end

test/1_stdlib/BridgeIdAsAny.swift.gyb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,24 @@ func ==(a: KnownUnbridged, b: KnownUnbridged) -> Bool {
3232
return a.x === b.x && a.y === b.y
3333
}
3434

35+
struct KnownUnbridgedWithDescription: CustomStringConvertible,
36+
CustomDebugStringConvertible {
37+
var x, y: LifetimeTracked
38+
39+
init() {
40+
x = LifetimeTracked(17)
41+
y = LifetimeTracked(38)
42+
}
43+
44+
var description: String {
45+
return "\(x)\(y), baby, hey"
46+
}
47+
48+
var debugDescription: String {
49+
return "KnownUnbridgedWithDescription(\"\(x)\(y)\" /* baby, hey */)"
50+
}
51+
}
52+
3553
func bridgedObjectPreservesIdentity(original: LifetimeTracked,
3654
bridged: AnyObject) {
3755
expectTrue(original === bridged)
@@ -115,4 +133,14 @@ BridgeAnything.test("${testName}") {
115133
}
116134
% end
117135

136+
BridgeAnything.test("description of boxed values") {
137+
for x in [KnownUnbridged(), KnownUnbridgedWithDescription()] as [Any] {
138+
let summary = String(reflecting: x)
139+
expectEqual(summary, _bridgeAnythingToObjectiveC(x).description)
140+
expectEqual(summary, _bridgeAnythingToObjectiveC(x).debugDescription)
141+
}
142+
143+
expectEqual(0, LifetimeTracked.instances)
144+
}
145+
118146
runAllTests()

0 commit comments

Comments
 (0)