Skip to content

Commit 3344448

Browse files
committed
Add _mangledTypeName to allow round trips T->mangledName->T
1 parent 685d35b commit 3344448

File tree

4 files changed

+128
-10
lines changed

4 files changed

+128
-10
lines changed

include/swift/Runtime/HeapObject.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1082,7 +1082,14 @@ struct TypeNamePair {
10821082
/// -> (UnsafePointer<UInt8>, Int)
10831083
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
10841084
TypeNamePair
1085-
swift_getTypeName(const Metadata *type, bool qualified);
1085+
swift_getTypeName(const Metadata *type, bool qualified);
1086+
1087+
/// Return the mangled name of a Swift type represented by a metadata object.
1088+
/// func _getMangledTypeName(_ type: Any.Type)
1089+
/// -> (UnsafePointer<UInt8>, Int)
1090+
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
1091+
TypeNamePair
1092+
swift_getMangledTypeName(const Metadata *type);
10861093

10871094
} // end namespace swift
10881095

stdlib/public/core/Misc.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,23 @@ func _typeName(_ type: Any.Type, qualified: Bool = true) -> String {
7878
UnsafeBufferPointer(start: stringPtr, count: count)).0
7979
}
8080

81+
@_silgen_name("swift_getMangledTypeName")
82+
public func _getMangledTypeName(_ type: Any.Type)
83+
-> (UnsafePointer<UInt8>, Int)
84+
85+
/// Returns the mangled name for a given type.
86+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
87+
public // SPI
88+
func _mangledTypeName<T>(_ type: T.Type) -> String? {
89+
let (stringPtr, count) = _getMangledTypeName(type)
90+
guard count > 0 else {
91+
return nil
92+
}
93+
94+
return String._fromUTF8Repairing(
95+
UnsafeBufferPointer(start: stringPtr, count: count)).0
96+
}
97+
8198
/// Lookup a class given a name. Until the demangled encoding of type
8299
/// names is stabilized, this is limited to top-level class names (Foo.bar).
83100
public // SPI(Foundation)

stdlib/public/runtime/Casting.cpp

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -126,17 +126,28 @@ std::string swift::nameForMetadata(const Metadata *type,
126126
return result;
127127
}
128128

129+
130+
/// Used as part of cache key for `TypeNameCache`.
131+
enum class TypeNameKind {
132+
NotQualified,
133+
Qualified,
134+
Mangled,
135+
};
136+
137+
using TypeNameCacheKey = llvm::PointerIntPair<const Metadata *, 2, TypeNameKind>;
138+
139+
#if SWIFT_CASTING_SUPPORTS_MUTEX
140+
static StaticReadWriteLock TypeNameCacheLock;
141+
#endif
142+
143+
/// Cache containing rendered names for Metadata.
144+
/// Access MUST be protected using `TypeNameCacheLock`.
145+
static Lazy<llvm::DenseMap<TypeNameCacheKey, std::pair<const char *, size_t>>>
146+
TypeNameCache;
147+
129148
TypeNamePair
130149
swift::swift_getTypeName(const Metadata *type, bool qualified) {
131-
using Key = llvm::PointerIntPair<const Metadata *, 1, bool>;
132-
133-
#if SWIFT_CASTING_SUPPORTS_MUTEX
134-
static StaticReadWriteLock TypeNameCacheLock;
135-
#endif
136-
static Lazy<llvm::DenseMap<Key, std::pair<const char *, size_t>>>
137-
TypeNameCache;
138-
139-
Key key(type, qualified);
150+
TypeNameCacheKey key = TypeNameCacheKey(type, qualified ? TypeNameKind::Qualified: TypeNameKind::NotQualified);
140151
auto &cache = TypeNameCache.get();
141152

142153
// Attempt read-only lookup of cache entry.
@@ -179,6 +190,60 @@ swift::swift_getTypeName(const Metadata *type, bool qualified) {
179190
}
180191
}
181192

193+
/// Return mangled name for the given type.
194+
TypeNamePair
195+
swift::swift_getMangledTypeName(const Metadata *type) {
196+
TypeNameCacheKey key(type, TypeNameKind::Mangled);
197+
auto &cache = TypeNameCache.get();
198+
199+
// Attempt read-only lookup of cache entry.
200+
{
201+
#if SWIFT_CASTING_SUPPORTS_MUTEX
202+
StaticScopedReadLock guard(TypeNameCacheLock);
203+
#endif
204+
205+
auto found = cache.find(key);
206+
if (found != cache.end()) {
207+
auto result = found->second;
208+
return TypeNamePair{result.first, result.second};
209+
}
210+
}
211+
212+
// Read-only cache lookup failed, we may need to create it.
213+
{
214+
#if SWIFT_CASTING_SUPPORTS_MUTEX
215+
StaticScopedWriteLock guard(TypeNameCacheLock);
216+
#endif
217+
218+
// Do lookup again just to make sure it wasn't created by another
219+
// thread before we acquired the write lock.
220+
auto found = cache.find(key);
221+
if (found != cache.end()) {
222+
auto result = found->second;
223+
return TypeNamePair{result.first, result.second};
224+
}
225+
226+
// Build the mangled name.
227+
Demangle::Demangler Dem;
228+
auto demangling = _swift_buildDemanglingForMetadata(type, Dem);
229+
230+
if (demangling == nullptr) {
231+
return TypeNamePair{NULL, 0};
232+
}
233+
auto name = Demangle::mangleNode(demangling);
234+
235+
// Copy it to memory we can reference forever.
236+
auto size = name.size();
237+
auto result = (char *)malloc(size + 1);
238+
memcpy(result, name.data(), size);
239+
result[size] = 0;
240+
241+
cache.insert({key, {result, size}});
242+
243+
return TypeNamePair{result, size};
244+
}
245+
}
246+
182247
/// Report a dynamic cast failure.
183248
// This is noinline to preserve this frame in stack traces.
184249
// We want "dynamicCastFailure" to appear in crash logs even we crash

test/Runtime/demangleToMetadata.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,5 +441,34 @@ DemangleToMetadataTests.test("Nested types in same-type-constrained extensions")
441441
// V !: P3 in InnerTEqualsConformsToP1
442442
}
443443

444+
if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) {
445+
DemangleToMetadataTests.test("Round-trip with _mangledTypeName and _typeByName") {
446+
func roundTrip<T>(_ type: T.Type) {
447+
let mangledName: String? = _mangledTypeName(type)
448+
let recoveredType: Any.Type? = _typeByName(mangledName!)
449+
expectEqual(String(reflecting: type), String(reflecting: recoveredType!))
450+
expectEqual(type, recoveredType! as! T.Type)
451+
}
452+
453+
roundTrip(Int.self)
454+
roundTrip(String.self)
455+
roundTrip(ConformsToP2AndP3.self)
456+
roundTrip(SG12<ConformsToP1AndP2, ConformsToP1AndP2>.self)
457+
roundTrip(SG12<ConformsToP1AndP2, ConformsToP1AndP2>.InnerTEqualsU<ConformsToP3>.self)
458+
roundTrip(SG12<ConformsToP1, ConformsToP2>.InnerTEqualsConformsToP1<ConformsToP3>.self)
459+
roundTrip(SG12<ConformsToP1, ConformsToP2>.InnerUEqualsConformsToP2<ConformsToP3>.self)
460+
}
461+
462+
DemangleToMetadataTests.test("Check _mangledTypeName, _typeName use appropriate cache keys") {
463+
// sanity check that name functions use the right keys to store cached names:
464+
for _ in 1...2 {
465+
expectEqual("Si", _mangledTypeName(Int.self)!)
466+
expectEqual("Swift.Int", _typeName(Int.self, qualified: true))
467+
expectEqual("Int", _typeName(Int.self, qualified: false))
468+
}
469+
}
470+
}
471+
472+
444473
runAllTests()
445474

0 commit comments

Comments
 (0)