1 | //===-- Mangled.cpp -------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "lldb/Core/Mangled.h" |
10 | |
11 | #include "lldb/Core/DataFileCache.h" |
12 | #include "lldb/Core/DemangledNameInfo.h" |
13 | #include "lldb/Core/RichManglingContext.h" |
14 | #include "lldb/Target/Language.h" |
15 | #include "lldb/Utility/ConstString.h" |
16 | #include "lldb/Utility/DataEncoder.h" |
17 | #include "lldb/Utility/LLDBLog.h" |
18 | #include "lldb/Utility/Log.h" |
19 | #include "lldb/Utility/RegularExpression.h" |
20 | #include "lldb/Utility/Stream.h" |
21 | #include "lldb/lldb-enumerations.h" |
22 | |
23 | #include "llvm/ADT/StringExtras.h" |
24 | #include "llvm/ADT/StringRef.h" |
25 | #include "llvm/Demangle/Demangle.h" |
26 | #include "llvm/Support/Compiler.h" |
27 | |
28 | #include <mutex> |
29 | #include <string> |
30 | #include <string_view> |
31 | #include <utility> |
32 | |
33 | #include <cstdlib> |
34 | #include <cstring> |
35 | using namespace lldb_private; |
36 | |
37 | #pragma mark Mangled |
38 | |
39 | bool Mangled::IsMangledName(llvm::StringRef name) { |
40 | return Mangled::GetManglingScheme(name) != Mangled::eManglingSchemeNone; |
41 | } |
42 | |
43 | Mangled::ManglingScheme Mangled::GetManglingScheme(llvm::StringRef const name) { |
44 | if (name.empty()) |
45 | return Mangled::eManglingSchemeNone; |
46 | |
47 | if (name.starts_with(Prefix: "?" )) |
48 | return Mangled::eManglingSchemeMSVC; |
49 | |
50 | if (name.starts_with(Prefix: "_R" )) |
51 | return Mangled::eManglingSchemeRustV0; |
52 | |
53 | if (name.starts_with(Prefix: "_D" )) { |
54 | // A dlang mangled name begins with `_D`, followed by a numeric length. One |
55 | // known exception is the symbol `_Dmain`. |
56 | // See `SymbolName` and `LName` in |
57 | // https://dlang.org/spec/abi.html#name_mangling |
58 | llvm::StringRef buf = name.drop_front(N: 2); |
59 | if (!buf.empty() && (llvm::isDigit(C: buf.front()) || name == "_Dmain" )) |
60 | return Mangled::eManglingSchemeD; |
61 | } |
62 | |
63 | if (name.starts_with(Prefix: "_Z" )) |
64 | return Mangled::eManglingSchemeItanium; |
65 | |
66 | // ___Z is a clang extension of block invocations |
67 | if (name.starts_with(Prefix: "___Z" )) |
68 | return Mangled::eManglingSchemeItanium; |
69 | |
70 | // Swift's older style of mangling used "_T" as a mangling prefix. This can |
71 | // lead to false positives with other symbols that just so happen to start |
72 | // with "_T". To minimize the chance of that happening, we only return true |
73 | // for select old-style swift mangled names. The known cases are ObjC classes |
74 | // and protocols. Classes are either prefixed with "_TtC" or "_TtGC". |
75 | // Protocols are prefixed with "_TtP". |
76 | if (name.starts_with(Prefix: "_TtC" ) || name.starts_with(Prefix: "_TtGC" ) || |
77 | name.starts_with(Prefix: "_TtP" )) |
78 | return Mangled::eManglingSchemeSwift; |
79 | |
80 | // Swift 4.2 used "$S" and "_$S". |
81 | // Swift 5 and onward uses "$s" and "_$s". |
82 | // Swift also uses "@__swiftmacro_" as a prefix for mangling filenames. |
83 | // Embedded Swift introduced "$e" and "_$e" as Swift mangling prefixes. |
84 | if (name.starts_with(Prefix: "$S" ) || name.starts_with(Prefix: "_$S" ) || |
85 | name.starts_with(Prefix: "$s" ) || name.starts_with(Prefix: "_$s" ) || |
86 | name.starts_with(Prefix: "$e" ) || name.starts_with(Prefix: "_$e" ) || |
87 | name.starts_with(Prefix: "@__swiftmacro_" )) |
88 | return Mangled::eManglingSchemeSwift; |
89 | |
90 | return Mangled::eManglingSchemeNone; |
91 | } |
92 | |
93 | Mangled::Mangled(ConstString s) : m_mangled(), m_demangled() { |
94 | if (s) |
95 | SetValue(s); |
96 | } |
97 | |
98 | Mangled::Mangled(llvm::StringRef name) { |
99 | if (!name.empty()) |
100 | SetValue(ConstString(name)); |
101 | } |
102 | |
103 | // Convert to bool operator. This allows code to check any Mangled objects |
104 | // to see if they contain anything valid using code such as: |
105 | // |
106 | // Mangled mangled(...); |
107 | // if (mangled) |
108 | // { ... |
109 | Mangled::operator bool() const { return m_mangled || m_demangled; } |
110 | |
111 | // Clear the mangled and demangled values. |
112 | void Mangled::Clear() { |
113 | m_mangled.Clear(); |
114 | m_demangled.Clear(); |
115 | m_demangled_info.reset(); |
116 | } |
117 | |
118 | // Compare the string values. |
119 | int Mangled::Compare(const Mangled &a, const Mangled &b) { |
120 | return ConstString::Compare(lhs: a.GetName(preference: ePreferMangled), |
121 | rhs: b.GetName(preference: ePreferMangled)); |
122 | } |
123 | |
124 | void Mangled::SetValue(ConstString name) { |
125 | if (name) { |
126 | if (IsMangledName(name: name.GetStringRef())) { |
127 | m_demangled.Clear(); |
128 | m_mangled = name; |
129 | m_demangled_info.reset(); |
130 | } else { |
131 | m_demangled = name; |
132 | m_mangled.Clear(); |
133 | m_demangled_info.reset(); |
134 | } |
135 | } else { |
136 | m_demangled.Clear(); |
137 | m_mangled.Clear(); |
138 | m_demangled_info.reset(); |
139 | } |
140 | } |
141 | |
142 | // Local helpers for different demangling implementations. |
143 | static char *GetMSVCDemangledStr(llvm::StringRef M) { |
144 | char *demangled_cstr = llvm::microsoftDemangle( |
145 | mangled_name: M, n_read: nullptr, status: nullptr, |
146 | Flags: llvm::MSDemangleFlags( |
147 | llvm::MSDF_NoAccessSpecifier | llvm::MSDF_NoCallingConvention | |
148 | llvm::MSDF_NoMemberType | llvm::MSDF_NoVariableType)); |
149 | |
150 | if (Log *log = GetLog(mask: LLDBLog::Demangle)) { |
151 | if (demangled_cstr && demangled_cstr[0]) |
152 | LLDB_LOGF(log, "demangled msvc: %s -> \"%s\"" , M.data(), demangled_cstr); |
153 | else |
154 | LLDB_LOGF(log, "demangled msvc: %s -> error" , M.data()); |
155 | } |
156 | |
157 | return demangled_cstr; |
158 | } |
159 | |
160 | static std::pair<char *, DemangledNameInfo> |
161 | GetItaniumDemangledStr(const char *M) { |
162 | char *demangled_cstr = nullptr; |
163 | |
164 | DemangledNameInfo info; |
165 | llvm::ItaniumPartialDemangler ipd; |
166 | bool err = ipd.partialDemangle(MangledName: M); |
167 | if (!err) { |
168 | // Default buffer and size (OutputBuffer will realloc in case it's too |
169 | // small). |
170 | size_t demangled_size = 80; |
171 | demangled_cstr = static_cast<char *>(std::malloc(size: 80)); |
172 | |
173 | TrackingOutputBuffer OB(demangled_cstr, demangled_size); |
174 | demangled_cstr = ipd.finishDemangle(OB: &OB); |
175 | OB.NameInfo.SuffixRange.first = OB.NameInfo.QualifiersRange.second; |
176 | OB.NameInfo.SuffixRange.second = std::string_view(OB).size(); |
177 | info = std::move(OB.NameInfo); |
178 | |
179 | assert(demangled_cstr && |
180 | "finishDemangle must always succeed if partialDemangle did" ); |
181 | assert(demangled_cstr[OB.getCurrentPosition() - 1] == '\0' && |
182 | "Expected demangled_size to return length including trailing null" ); |
183 | } |
184 | |
185 | if (Log *log = GetLog(mask: LLDBLog::Demangle)) { |
186 | if (demangled_cstr) |
187 | LLDB_LOGF(log, "demangled itanium: %s -> \"%s\"" , M, demangled_cstr); |
188 | else |
189 | LLDB_LOGF(log, "demangled itanium: %s -> error: failed to demangle" , M); |
190 | |
191 | if (!info.hasBasename()) |
192 | LLDB_LOGF(log, |
193 | "demangled itanium: %s -> error: failed to retrieve name info" , |
194 | M); |
195 | } |
196 | |
197 | return {demangled_cstr, std::move(info)}; |
198 | } |
199 | |
200 | static char *GetRustV0DemangledStr(llvm::StringRef M) { |
201 | char *demangled_cstr = llvm::rustDemangle(MangledName: M); |
202 | |
203 | if (Log *log = GetLog(mask: LLDBLog::Demangle)) { |
204 | if (demangled_cstr && demangled_cstr[0]) |
205 | LLDB_LOG(log, "demangled rustv0: {0} -> \"{1}\"" , M, demangled_cstr); |
206 | else |
207 | LLDB_LOG(log, "demangled rustv0: {0} -> error: failed to demangle" , |
208 | static_cast<std::string_view>(M)); |
209 | } |
210 | |
211 | return demangled_cstr; |
212 | } |
213 | |
214 | static char *GetDLangDemangledStr(llvm::StringRef M) { |
215 | char *demangled_cstr = llvm::dlangDemangle(MangledName: M); |
216 | |
217 | if (Log *log = GetLog(mask: LLDBLog::Demangle)) { |
218 | if (demangled_cstr && demangled_cstr[0]) |
219 | LLDB_LOG(log, "demangled dlang: {0} -> \"{1}\"" , M, demangled_cstr); |
220 | else |
221 | LLDB_LOG(log, "demangled dlang: {0} -> error: failed to demangle" , |
222 | static_cast<std::string_view>(M)); |
223 | } |
224 | |
225 | return demangled_cstr; |
226 | } |
227 | |
228 | // Explicit demangling for scheduled requests during batch processing. This |
229 | // makes use of ItaniumPartialDemangler's rich demangle info |
230 | bool Mangled::GetRichManglingInfo(RichManglingContext &context, |
231 | SkipMangledNameFn *skip_mangled_name) { |
232 | // Others are not meant to arrive here. ObjC names or C's main() for example |
233 | // have their names stored in m_demangled, while m_mangled is empty. |
234 | assert(m_mangled); |
235 | |
236 | // Check whether or not we are interested in this name at all. |
237 | ManglingScheme scheme = GetManglingScheme(name: m_mangled.GetStringRef()); |
238 | if (skip_mangled_name && skip_mangled_name(m_mangled.GetStringRef(), scheme)) |
239 | return false; |
240 | |
241 | switch (scheme) { |
242 | case eManglingSchemeNone: |
243 | // The current mangled_name_filter would allow llvm_unreachable here. |
244 | return false; |
245 | |
246 | case eManglingSchemeItanium: |
247 | // We want the rich mangling info here, so we don't care whether or not |
248 | // there is a demangled string in the pool already. |
249 | return context.FromItaniumName(mangled: m_mangled); |
250 | |
251 | case eManglingSchemeMSVC: { |
252 | // We have no rich mangling for MSVC-mangled names yet, so first try to |
253 | // demangle it if necessary. |
254 | if (!m_demangled && !m_mangled.GetMangledCounterpart(counterpart&: m_demangled)) { |
255 | if (char *d = GetMSVCDemangledStr(M: m_mangled)) { |
256 | // Without the rich mangling info we have to demangle the full name. |
257 | // Copy it to string pool and connect the counterparts to accelerate |
258 | // later access in GetDemangledName(). |
259 | m_demangled.SetStringWithMangledCounterpart(demangled: llvm::StringRef(d), |
260 | mangled: m_mangled); |
261 | ::free(ptr: d); |
262 | } else { |
263 | m_demangled.SetCString("" ); |
264 | } |
265 | } |
266 | |
267 | if (m_demangled.IsEmpty()) { |
268 | // Cannot demangle it, so don't try parsing. |
269 | return false; |
270 | } else { |
271 | // Demangled successfully, we can try and parse it with |
272 | // CPlusPlusLanguage::CxxMethodName. |
273 | return context.FromCxxMethodName(demangled: m_demangled); |
274 | } |
275 | } |
276 | |
277 | case eManglingSchemeRustV0: |
278 | case eManglingSchemeD: |
279 | case eManglingSchemeSwift: |
280 | // Rich demangling scheme is not supported |
281 | return false; |
282 | } |
283 | llvm_unreachable("Fully covered switch above!" ); |
284 | } |
285 | |
286 | ConstString Mangled::GetDemangledName() const { |
287 | return GetDemangledNameImpl(/*force=*/false); |
288 | } |
289 | |
290 | std::optional<DemangledNameInfo> const &Mangled::GetDemangledInfo() const { |
291 | if (!m_demangled_info) |
292 | GetDemangledNameImpl(/*force=*/true); |
293 | |
294 | return m_demangled_info; |
295 | } |
296 | |
297 | // Generate the demangled name on demand using this accessor. Code in this |
298 | // class will need to use this accessor if it wishes to decode the demangled |
299 | // name. The result is cached and will be kept until a new string value is |
300 | // supplied to this object, or until the end of the object's lifetime. |
301 | ConstString Mangled::GetDemangledNameImpl(bool force) const { |
302 | if (!m_mangled) |
303 | return m_demangled; |
304 | |
305 | // Re-use previously demangled names. |
306 | if (!force && !m_demangled.IsNull()) |
307 | return m_demangled; |
308 | |
309 | if (!force && m_mangled.GetMangledCounterpart(counterpart&: m_demangled) && |
310 | !m_demangled.IsNull()) |
311 | return m_demangled; |
312 | |
313 | // We didn't already mangle this name, demangle it and if all goes well |
314 | // add it to our map. |
315 | char *demangled_name = nullptr; |
316 | switch (GetManglingScheme(name: m_mangled.GetStringRef())) { |
317 | case eManglingSchemeMSVC: |
318 | demangled_name = GetMSVCDemangledStr(M: m_mangled); |
319 | break; |
320 | case eManglingSchemeItanium: { |
321 | std::pair<char *, DemangledNameInfo> demangled = |
322 | GetItaniumDemangledStr(M: m_mangled.GetCString()); |
323 | demangled_name = demangled.first; |
324 | m_demangled_info.emplace(args: std::move(demangled.second)); |
325 | break; |
326 | } |
327 | case eManglingSchemeRustV0: |
328 | demangled_name = GetRustV0DemangledStr(M: m_mangled); |
329 | break; |
330 | case eManglingSchemeD: |
331 | demangled_name = GetDLangDemangledStr(M: m_mangled); |
332 | break; |
333 | case eManglingSchemeSwift: |
334 | // Demangling a swift name requires the swift compiler. This is |
335 | // explicitly unsupported on llvm.org. |
336 | break; |
337 | case eManglingSchemeNone: |
338 | // Don't bother demangling anything that isn't mangled. |
339 | break; |
340 | } |
341 | |
342 | if (demangled_name) { |
343 | m_demangled.SetStringWithMangledCounterpart(demangled: demangled_name, mangled: m_mangled); |
344 | free(ptr: demangled_name); |
345 | } |
346 | |
347 | if (m_demangled.IsNull()) { |
348 | // Set the demangled string to the empty string to indicate we tried to |
349 | // parse it once and failed. |
350 | m_demangled.SetCString("" ); |
351 | } |
352 | |
353 | return m_demangled; |
354 | } |
355 | |
356 | ConstString Mangled::GetDisplayDemangledName() const { |
357 | if (Language *lang = Language::FindPlugin(language: GuessLanguage())) |
358 | return lang->GetDisplayDemangledName(mangled: *this); |
359 | return GetDemangledName(); |
360 | } |
361 | |
362 | bool Mangled::NameMatches(const RegularExpression ®ex) const { |
363 | if (m_mangled && regex.Execute(string: m_mangled.GetStringRef())) |
364 | return true; |
365 | |
366 | ConstString demangled = GetDemangledName(); |
367 | return demangled && regex.Execute(string: demangled.GetStringRef()); |
368 | } |
369 | |
370 | // Get the demangled name if there is one, else return the mangled name. |
371 | ConstString Mangled::GetName(Mangled::NamePreference preference) const { |
372 | if (preference == ePreferMangled && m_mangled) |
373 | return m_mangled; |
374 | |
375 | // Call the accessor to make sure we get a demangled name in case it hasn't |
376 | // been demangled yet... |
377 | ConstString demangled = GetDemangledName(); |
378 | |
379 | if (preference == ePreferDemangledWithoutArguments) { |
380 | if (Language *lang = Language::FindPlugin(language: GuessLanguage())) { |
381 | return lang->GetDemangledFunctionNameWithoutArguments(mangled: *this); |
382 | } |
383 | } |
384 | if (preference == ePreferDemangled) { |
385 | if (demangled) |
386 | return demangled; |
387 | return m_mangled; |
388 | } |
389 | return demangled; |
390 | } |
391 | |
392 | // Dump a Mangled object to stream "s". We don't force our demangled name to be |
393 | // computed currently (we don't use the accessor). |
394 | void Mangled::Dump(Stream *s) const { |
395 | if (m_mangled) { |
396 | *s << ", mangled = " << m_mangled; |
397 | } |
398 | if (m_demangled) { |
399 | const char *demangled = m_demangled.AsCString(); |
400 | s->Printf(format: ", demangled = %s" , demangled[0] ? demangled : "<error>" ); |
401 | } |
402 | } |
403 | |
404 | // Dumps a debug version of this string with extra object and state information |
405 | // to stream "s". |
406 | void Mangled::DumpDebug(Stream *s) const { |
407 | s->Printf(format: "%*p: Mangled mangled = " , static_cast<int>(sizeof(void *) * 2), |
408 | static_cast<const void *>(this)); |
409 | m_mangled.DumpDebug(s); |
410 | s->Printf(format: ", demangled = " ); |
411 | m_demangled.DumpDebug(s); |
412 | } |
413 | |
414 | // Return the size in byte that this object takes in memory. The size includes |
415 | // the size of the objects it owns, and not the strings that it references |
416 | // because they are shared strings. |
417 | size_t Mangled::MemorySize() const { |
418 | return m_mangled.MemorySize() + m_demangled.MemorySize(); |
419 | } |
420 | |
421 | // We "guess" the language because we can't determine a symbol's language from |
422 | // it's name. For example, a Pascal symbol can be mangled using the C++ |
423 | // Itanium scheme, and defined in a compilation unit within the same module as |
424 | // other C++ units. In addition, different targets could have different ways |
425 | // of mangling names from a given language, likewise the compilation units |
426 | // within those targets. |
427 | lldb::LanguageType Mangled::GuessLanguage() const { |
428 | lldb::LanguageType result = lldb::eLanguageTypeUnknown; |
429 | // Ask each language plugin to check if the mangled name belongs to it. |
430 | Language::ForEach(callback: [this, &result](Language *l) { |
431 | if (l->SymbolNameFitsToLanguage(name: *this)) { |
432 | result = l->GetLanguageType(); |
433 | return false; |
434 | } |
435 | return true; |
436 | }); |
437 | return result; |
438 | } |
439 | |
440 | // Dump OBJ to the supplied stream S. |
441 | Stream &operator<<(Stream &s, const Mangled &obj) { |
442 | if (obj.GetMangledName()) |
443 | s << "mangled = '" << obj.GetMangledName() << "'" ; |
444 | |
445 | ConstString demangled = obj.GetDemangledName(); |
446 | if (demangled) |
447 | s << ", demangled = '" << demangled << '\''; |
448 | else |
449 | s << ", demangled = <error>" ; |
450 | return s; |
451 | } |
452 | |
453 | // When encoding Mangled objects we can get away with encoding as little |
454 | // information as is required. The enumeration below helps us to efficiently |
455 | // encode Mangled objects. |
456 | enum MangledEncoding { |
457 | /// If the Mangled object has neither a mangled name or demangled name we can |
458 | /// encode the object with one zero byte using the Empty enumeration. |
459 | Empty = 0u, |
460 | /// If the Mangled object has only a demangled name and no mangled named, we |
461 | /// can encode only the demangled name. |
462 | DemangledOnly = 1u, |
463 | /// If the mangle name can calculate the demangled name (it is the |
464 | /// mangled/demangled counterpart), then we only need to encode the mangled |
465 | /// name as the demangled name can be recomputed. |
466 | MangledOnly = 2u, |
467 | /// If we have a Mangled object with two different names that are not related |
468 | /// then we need to save both strings. This can happen if we have a name that |
469 | /// isn't a true mangled name, but we want to be able to lookup a symbol by |
470 | /// name and type in the symbol table. We do this for Objective C symbols like |
471 | /// "OBJC_CLASS_$_NSValue" where the mangled named will be set to |
472 | /// "OBJC_CLASS_$_NSValue" and the demangled name will be manually set to |
473 | /// "NSValue". If we tried to demangled the name "OBJC_CLASS_$_NSValue" it |
474 | /// would fail, but in these cases we want these unrelated names to be |
475 | /// preserved. |
476 | MangledAndDemangled = 3u |
477 | }; |
478 | |
479 | bool Mangled::(const DataExtractor &data, lldb::offset_t *offset_ptr, |
480 | const StringTableReader &strtab) { |
481 | m_mangled.Clear(); |
482 | m_demangled.Clear(); |
483 | m_demangled_info.reset(); |
484 | MangledEncoding encoding = (MangledEncoding)data.GetU8(offset_ptr); |
485 | switch (encoding) { |
486 | case Empty: |
487 | return true; |
488 | |
489 | case DemangledOnly: |
490 | m_demangled.SetString(strtab.Get(offset: data.GetU32(offset_ptr))); |
491 | return true; |
492 | |
493 | case MangledOnly: |
494 | m_mangled.SetString(strtab.Get(offset: data.GetU32(offset_ptr))); |
495 | return true; |
496 | |
497 | case MangledAndDemangled: |
498 | m_mangled.SetString(strtab.Get(offset: data.GetU32(offset_ptr))); |
499 | m_demangled.SetString(strtab.Get(offset: data.GetU32(offset_ptr))); |
500 | return true; |
501 | } |
502 | return false; |
503 | } |
504 | /// The encoding format for the Mangled object is as follows: |
505 | /// |
506 | /// uint8_t encoding; |
507 | /// char str1[]; (only if DemangledOnly, MangledOnly) |
508 | /// char str2[]; (only if MangledAndDemangled) |
509 | /// |
510 | /// The strings are stored as NULL terminated UTF8 strings and str1 and str2 |
511 | /// are only saved if we need them based on the encoding. |
512 | /// |
513 | /// Some mangled names have a mangled name that can be demangled by the built |
514 | /// in demanglers. These kinds of mangled objects know when the mangled and |
515 | /// demangled names are the counterparts for each other. This is done because |
516 | /// demangling is very expensive and avoiding demangling the same name twice |
517 | /// saves us a lot of compute time. For these kinds of names we only need to |
518 | /// save the mangled name and have the encoding set to "MangledOnly". |
519 | /// |
520 | /// If a mangled obejct has only a demangled name, then we save only that string |
521 | /// and have the encoding set to "DemangledOnly". |
522 | /// |
523 | /// Some mangled objects have both mangled and demangled names, but the |
524 | /// demangled name can not be computed from the mangled name. This is often used |
525 | /// for runtime named, like Objective C runtime V2 and V3 names. Both these |
526 | /// names must be saved and the encoding is set to "MangledAndDemangled". |
527 | /// |
528 | /// For a Mangled object with no names, we only need to set the encoding to |
529 | /// "Empty" and not store any string values. |
530 | void Mangled::Encode(DataEncoder &file, ConstStringTable &strtab) const { |
531 | MangledEncoding encoding = Empty; |
532 | if (m_mangled) { |
533 | encoding = MangledOnly; |
534 | if (m_demangled) { |
535 | // We have both mangled and demangled names. If the demangled name is the |
536 | // counterpart of the mangled name, then we only need to save the mangled |
537 | // named. If they are different, we need to save both. |
538 | ConstString s; |
539 | if (!(m_mangled.GetMangledCounterpart(counterpart&: s) && s == m_demangled)) |
540 | encoding = MangledAndDemangled; |
541 | } |
542 | } else if (m_demangled) { |
543 | encoding = DemangledOnly; |
544 | } |
545 | file.AppendU8(value: encoding); |
546 | switch (encoding) { |
547 | case Empty: |
548 | break; |
549 | case DemangledOnly: |
550 | file.AppendU32(value: strtab.Add(s: m_demangled)); |
551 | break; |
552 | case MangledOnly: |
553 | file.AppendU32(value: strtab.Add(s: m_mangled)); |
554 | break; |
555 | case MangledAndDemangled: |
556 | file.AppendU32(value: strtab.Add(s: m_mangled)); |
557 | file.AppendU32(value: strtab.Add(s: m_demangled)); |
558 | break; |
559 | } |
560 | } |
561 | |