Skip to content

Commit d0dd054

Browse files
Michael137IanWood1
authored andcommitted
[lldb] Implement TrackingOutputBuffer to track demangled name information (llvm#131836)
This patch implements a new `TrackingOutputBuffer` which tracks where the scope/basename/arguments begin and end in the demangled string. The idea that a function name can be decomposed into <scope, base, arguments>. The assumption is that given the ranges of those three elements and the demangled name, LLDB will be able to to reconstruct the full demangled name. The tracking of those ranges is pretty simple. We don’t ever deal with nesting, so whenever we recurse into a template argument list or another function type, we just stop tracking any positions. Once we recursed out of those, and are back to printing the top-level function name, we continue tracking the positions. We introduce a new structure `FunctionNameInfo` that holds all this information and is stored in the new `TrackingOutputBuffer` class. Tests are in `ItaniumDemangleTest.cpp`. llvm#131836
1 parent c0a20fd commit d0dd054

File tree

4 files changed

+510
-0
lines changed

4 files changed

+510
-0
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
//===-- DemangledNameInfo.h -------------------------------------*- C++ -*-===//
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+
#ifndef LLDB_CORE_DEMANGLEDNAMEINFO_H
10+
#define LLDB_CORE_DEMANGLEDNAMEINFO_H
11+
12+
#include "llvm/Demangle/ItaniumDemangle.h"
13+
#include "llvm/Demangle/Utility.h"
14+
15+
#include <cstddef>
16+
#include <utility>
17+
18+
namespace lldb_private {
19+
20+
/// Stores information about where certain portions of a demangled
21+
/// function name begin and end.
22+
struct DemangledNameInfo {
23+
/// A [start, end) pair for the function basename.
24+
/// The basename is the name without scope qualifiers
25+
/// and without template parameters. E.g.,
26+
/// \code{.cpp}
27+
/// void foo::bar<int>::someFunc<float>(int) const &&
28+
/// ^ ^
29+
/// start end
30+
/// \endcode
31+
std::pair<size_t, size_t> BasenameRange;
32+
33+
/// A [start, end) pair for the function scope qualifiers.
34+
/// E.g., for
35+
/// \code{.cpp}
36+
/// void foo::bar<int>::qux<float>(int) const &&
37+
/// ^ ^
38+
/// start end
39+
/// \endcode
40+
std::pair<size_t, size_t> ScopeRange;
41+
42+
/// Indicates the [start, end) of the function argument lits.
43+
/// E.g.,
44+
/// \code{.cpp}
45+
/// int (*getFunc<float>(float, double))(int, int)
46+
/// ^ ^
47+
/// start end
48+
/// \endcode
49+
std::pair<size_t, size_t> ArgumentsRange;
50+
51+
/// Returns \c true if this object holds a valid basename range.
52+
bool hasBasename() const {
53+
return BasenameRange.first != BasenameRange.second &&
54+
BasenameRange.second > 0;
55+
}
56+
57+
friend bool operator==(const DemangledNameInfo &lhs,
58+
const DemangledNameInfo &rhs) {
59+
return std::tie(lhs.BasenameRange, lhs.ArgumentsRange, lhs.ScopeRange,
60+
lhs.QualifiersRange) ==
61+
std::tie(rhs.BasenameRange, rhs.ArgumentsRange, rhs.ScopeRange,
62+
lhs.QualifiersRange);
63+
}
64+
65+
friend bool operator!=(const DemangledNameInfo &lhs,
66+
const DemangledNameInfo &rhs) {
67+
return !(lhs == rhs);
68+
}
69+
};
70+
71+
/// An OutputBuffer which keeps a record of where certain parts of a
72+
/// demangled name begin/end (e.g., basename, scope, argument list, etc.).
73+
/// The tracking occurs during printing of the Itanium demangle tree.
74+
///
75+
/// Usage:
76+
/// \code{.cpp}
77+
///
78+
/// Node *N = mangling_parser.parseType();
79+
///
80+
/// TrackingOutputBuffer buffer;
81+
/// N->printLeft(OB);
82+
///
83+
/// assert (buffer.NameInfo.hasBasename());
84+
///
85+
/// \endcode
86+
struct TrackingOutputBuffer : public llvm::itanium_demangle::OutputBuffer {
87+
using OutputBuffer::OutputBuffer;
88+
89+
/// Holds information about the demangled name that is
90+
/// being printed into this buffer.
91+
DemangledNameInfo NameInfo;
92+
93+
void printLeft(const llvm::itanium_demangle::Node &N) override;
94+
void printRight(const llvm::itanium_demangle::Node &N) override;
95+
96+
private:
97+
void printLeftImpl(const llvm::itanium_demangle::FunctionType &N);
98+
void printRightImpl(const llvm::itanium_demangle::FunctionType &N);
99+
100+
void printLeftImpl(const llvm::itanium_demangle::FunctionEncoding &N);
101+
void printRightImpl(const llvm::itanium_demangle::FunctionEncoding &N);
102+
103+
void printLeftImpl(const llvm::itanium_demangle::NestedName &N);
104+
void printLeftImpl(const llvm::itanium_demangle::NameWithTemplateArgs &N);
105+
106+
/// Called whenever we start printing a function type in the Itanium
107+
/// mangling scheme. Examples include \ref FunctionEncoding, \ref
108+
/// FunctionType, etc.
109+
///
110+
/// \returns A ScopedOverride which will update the nesting depth of
111+
/// currently printed function types on destruction.
112+
[[nodiscard]] llvm::itanium_demangle::ScopedOverride<unsigned>
113+
enterFunctionTypePrinting();
114+
115+
/// Returns \c true if we're not printing any nested function types,
116+
/// just a \ref FunctionEncoding in the Itanium mangling scheme.
117+
bool isPrintingTopLevelFunctionType() const;
118+
119+
/// If this object \ref shouldTrack, then update the end of
120+
/// the basename range to the current \c OB position.
121+
void updateBasenameEnd();
122+
123+
/// If this object \ref shouldTrack, then update the beginning
124+
/// of the scope range to the current \c OB position.
125+
void updateScopeStart();
126+
127+
/// If this object \ref shouldTrack, then update the end of
128+
/// the scope range to the current \c OB position.
129+
void updateScopeEnd();
130+
131+
/// Returns \c true if the members of this object can be
132+
/// updated. E.g., when we're printing nested template
133+
/// arguments, we don't need to be tracking basename
134+
/// locations.
135+
bool shouldTrack() const;
136+
137+
/// Helpers called to track beginning and end of the function
138+
/// arguments.
139+
void finalizeArgumentEnd();
140+
void finalizeStart();
141+
void finalizeEnd();
142+
143+
/// Helper used in the finalize APIs.
144+
bool canFinalize() const;
145+
146+
/// Incremented each time we start printing a function type node
147+
/// in the Itanium mangling scheme (e.g., \ref FunctionEncoding
148+
/// or \ref FunctionType).
149+
unsigned FunctionPrintingDepth = 0;
150+
};
151+
} // namespace lldb_private
152+
153+
#endif // LLDB_CORE_DEMANGLEDNAMEINFO_H

lldb/source/Core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ add_lldb_library(lldbCore NO_PLUGIN_DEPENDENCIES
2727
Debugger.cpp
2828
DebuggerEvents.cpp
2929
Declaration.cpp
30+
DemangledNameInfo.cpp
3031
Disassembler.cpp
3132
DumpDataExtractor.cpp
3233
DumpRegisterValue.cpp
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
//===-- DemangledNameInfo.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/DemangledNameInfo.h"
10+
11+
using namespace llvm::itanium_demangle;
12+
13+
namespace lldb_private {
14+
15+
bool TrackingOutputBuffer::shouldTrack() const {
16+
if (!isPrintingTopLevelFunctionType())
17+
return false;
18+
19+
if (isGtInsideTemplateArgs())
20+
return false;
21+
22+
if (NameInfo.ArgumentsRange.first > 0)
23+
return false;
24+
25+
return true;
26+
}
27+
28+
bool TrackingOutputBuffer::canFinalize() const {
29+
if (!isPrintingTopLevelFunctionType())
30+
return false;
31+
32+
if (isGtInsideTemplateArgs())
33+
return false;
34+
35+
if (NameInfo.ArgumentsRange.first == 0)
36+
return false;
37+
38+
return true;
39+
}
40+
41+
void TrackingOutputBuffer::updateBasenameEnd() {
42+
if (!shouldTrack())
43+
return;
44+
45+
NameInfo.BasenameRange.second = getCurrentPosition();
46+
}
47+
48+
void TrackingOutputBuffer::updateScopeStart() {
49+
if (!shouldTrack())
50+
return;
51+
52+
NameInfo.ScopeRange.first = getCurrentPosition();
53+
}
54+
55+
void TrackingOutputBuffer::updateScopeEnd() {
56+
if (!shouldTrack())
57+
return;
58+
59+
NameInfo.ScopeRange.second = getCurrentPosition();
60+
}
61+
62+
void TrackingOutputBuffer::finalizeArgumentEnd() {
63+
if (!canFinalize())
64+
return;
65+
66+
NameInfo.ArgumentsRange.second = getCurrentPosition();
67+
}
68+
69+
void TrackingOutputBuffer::finalizeStart() {
70+
if (!shouldTrack())
71+
return;
72+
73+
NameInfo.ArgumentsRange.first = getCurrentPosition();
74+
75+
// If nothing has set the end of the basename yet (for example when
76+
// printing templates), then the beginning of the arguments is the end of
77+
// the basename.
78+
if (NameInfo.BasenameRange.second == 0)
79+
NameInfo.BasenameRange.second = getCurrentPosition();
80+
81+
assert(!shouldTrack());
82+
assert(canFinalize());
83+
}
84+
85+
void TrackingOutputBuffer::finalizeEnd() {
86+
if (!canFinalize())
87+
return;
88+
89+
if (NameInfo.ScopeRange.first > NameInfo.ScopeRange.second)
90+
NameInfo.ScopeRange.second = NameInfo.ScopeRange.first;
91+
NameInfo.BasenameRange.first = NameInfo.ScopeRange.second;
92+
}
93+
94+
ScopedOverride<unsigned> TrackingOutputBuffer::enterFunctionTypePrinting() {
95+
return {FunctionPrintingDepth, FunctionPrintingDepth + 1};
96+
}
97+
98+
bool TrackingOutputBuffer::isPrintingTopLevelFunctionType() const {
99+
return FunctionPrintingDepth == 1;
100+
}
101+
102+
void TrackingOutputBuffer::printLeft(const Node &N) {
103+
switch (N.getKind()) {
104+
case Node::KFunctionType:
105+
printLeftImpl(static_cast<const FunctionType &>(N));
106+
break;
107+
case Node::KFunctionEncoding:
108+
printLeftImpl(static_cast<const FunctionEncoding &>(N));
109+
break;
110+
case Node::KNestedName:
111+
printLeftImpl(static_cast<const NestedName &>(N));
112+
break;
113+
case Node::KNameWithTemplateArgs:
114+
printLeftImpl(static_cast<const NameWithTemplateArgs &>(N));
115+
break;
116+
default:
117+
OutputBuffer::printLeft(N);
118+
}
119+
}
120+
121+
void TrackingOutputBuffer::printRight(const Node &N) {
122+
switch (N.getKind()) {
123+
case Node::KFunctionType:
124+
printRightImpl(static_cast<const FunctionType &>(N));
125+
break;
126+
case Node::KFunctionEncoding:
127+
printRightImpl(static_cast<const FunctionEncoding &>(N));
128+
break;
129+
default:
130+
OutputBuffer::printRight(N);
131+
}
132+
}
133+
134+
void TrackingOutputBuffer::printLeftImpl(const FunctionType &N) {
135+
auto Scoped = enterFunctionTypePrinting();
136+
OutputBuffer::printLeft(N);
137+
}
138+
139+
void TrackingOutputBuffer::printRightImpl(const FunctionType &N) {
140+
auto Scoped = enterFunctionTypePrinting();
141+
OutputBuffer::printRight(N);
142+
}
143+
144+
void TrackingOutputBuffer::printLeftImpl(const FunctionEncoding &N) {
145+
auto Scoped = enterFunctionTypePrinting();
146+
147+
const Node *Ret = N.getReturnType();
148+
if (Ret) {
149+
printLeft(*Ret);
150+
if (!Ret->hasRHSComponent(*this))
151+
*this += " ";
152+
}
153+
154+
updateScopeStart();
155+
156+
N.getName()->print(*this);
157+
}
158+
159+
void TrackingOutputBuffer::printRightImpl(const FunctionEncoding &N) {
160+
auto Scoped = enterFunctionTypePrinting();
161+
finalizeStart();
162+
163+
printOpen();
164+
N.getParams().printWithComma(*this);
165+
printClose();
166+
167+
finalizeArgumentEnd();
168+
169+
const Node *Ret = N.getReturnType();
170+
171+
if (Ret)
172+
printRight(*Ret);
173+
174+
auto CVQuals = N.getCVQuals();
175+
auto RefQual = N.getRefQual();
176+
auto *Attrs = N.getAttrs();
177+
auto *Requires = N.getRequires();
178+
179+
if (CVQuals & QualConst)
180+
*this += " const";
181+
if (CVQuals & QualVolatile)
182+
*this += " volatile";
183+
if (CVQuals & QualRestrict)
184+
*this += " restrict";
185+
if (RefQual == FrefQualLValue)
186+
*this += " &";
187+
else if (RefQual == FrefQualRValue)
188+
*this += " &&";
189+
if (Attrs != nullptr)
190+
Attrs->print(*this);
191+
if (Requires != nullptr) {
192+
*this += " requires ";
193+
Requires->print(*this);
194+
}
195+
196+
finalizeEnd();
197+
}
198+
199+
void TrackingOutputBuffer::printLeftImpl(const NestedName &N) {
200+
N.Qual->print(*this);
201+
*this += "::";
202+
updateScopeEnd();
203+
N.Name->print(*this);
204+
updateBasenameEnd();
205+
}
206+
207+
void TrackingOutputBuffer::printLeftImpl(const NameWithTemplateArgs &N) {
208+
N.Name->print(*this);
209+
updateBasenameEnd();
210+
N.TemplateArgs->print(*this);
211+
}
212+
213+
} // namespace lldb_private

0 commit comments

Comments
 (0)