[ItaniumDemangle] Add customizable printLeft/printRight APIs to OutputBuffer (#133249)
This patch includes the necessary changes for the LLDB feature proposed in https://discourse.llvm.org/t/rfc-lldb-highlighting-function-names-in-lldb-backtraces/85309. The TL;DR is that we want to track where certain parts of a demangled name begin/end so we can highlight them in backtraces. We introduce a new `printLeft`/`printRight` API on `OutputBuffer` that a client (in our case LLDB) can implement to track state while printing the demangle tree. This requires redirecting all calls to to `printLeft`/`printRight` to the `OutputBuffer`. One quirk with the new API is that `Utility.h` would now depend on `ItaniumDemangle.h` and vice-versa. To keep these files header-only I made the definitions `inline` and implement the new APIs in `ItaniumDemangle.h` (so the definition of `Node` is available to them). Also introduces `notifyInsertion`/`notifyDeletion` APIs that a client can override to respond to cases where the `OutputBuffer` changes arbitrary parts of the name.
This commit is contained in:
@@ -283,20 +283,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void print(OutputBuffer &OB) const {
|
void print(OutputBuffer &OB) const {
|
||||||
printLeft(OB);
|
OB.printLeft(*this);
|
||||||
if (RHSComponentCache != Cache::No)
|
if (RHSComponentCache != Cache::No)
|
||||||
printRight(OB);
|
OB.printRight(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the "left" side of this Node into OutputBuffer.
|
|
||||||
virtual void printLeft(OutputBuffer &) const = 0;
|
|
||||||
|
|
||||||
// Print the "right". This distinction is necessary to represent C++ types
|
|
||||||
// that appear on the RHS of their subtype, such as arrays or functions.
|
|
||||||
// Since most types don't have such a component, provide a default
|
|
||||||
// implementation.
|
|
||||||
virtual void printRight(OutputBuffer &) const {}
|
|
||||||
|
|
||||||
// Print an initializer list of this type. Returns true if we printed a custom
|
// Print an initializer list of this type. Returns true if we printed a custom
|
||||||
// representation, false if nothing has been printed and the default
|
// representation, false if nothing has been printed and the default
|
||||||
// representation should be used.
|
// representation should be used.
|
||||||
@@ -312,6 +303,24 @@ public:
|
|||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
DEMANGLE_DUMP_METHOD void dump() const;
|
DEMANGLE_DUMP_METHOD void dump() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class OutputBuffer;
|
||||||
|
|
||||||
|
// Print the "left" side of this Node into OutputBuffer.
|
||||||
|
//
|
||||||
|
// Note, should only be called from OutputBuffer implementations.
|
||||||
|
// Call \ref OutputBuffer::printLeft instead.
|
||||||
|
virtual void printLeft(OutputBuffer &) const = 0;
|
||||||
|
|
||||||
|
// Print the "right". This distinction is necessary to represent C++ types
|
||||||
|
// that appear on the RHS of their subtype, such as arrays or functions.
|
||||||
|
// Since most types don't have such a component, provide a default
|
||||||
|
// implementation.
|
||||||
|
//
|
||||||
|
// Note, should only be called from OutputBuffer implementations.
|
||||||
|
// Call \ref OutputBuffer::printRight instead.
|
||||||
|
virtual void printRight(OutputBuffer &) const {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class NodeArray {
|
class NodeArray {
|
||||||
@@ -460,11 +469,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
Child->printLeft(OB);
|
OB.printLeft(*Child);
|
||||||
printQuals(OB);
|
printQuals(OB);
|
||||||
}
|
}
|
||||||
|
|
||||||
void printRight(OutputBuffer &OB) const override { Child->printRight(OB); }
|
void printRight(OutputBuffer &OB) const override { OB.printRight(*Child); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConversionOperatorType final : public Node {
|
class ConversionOperatorType final : public Node {
|
||||||
@@ -493,7 +502,7 @@ public:
|
|||||||
template<typename Fn> void match(Fn F) const { F(Ty, Postfix); }
|
template<typename Fn> void match(Fn F) const { F(Ty, Postfix); }
|
||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
Ty->printLeft(OB);
|
OB.printLeft(*Ty);
|
||||||
OB += Postfix;
|
OB += Postfix;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -579,7 +588,7 @@ struct AbiTagAttr : Node {
|
|||||||
std::string_view getBaseName() const override { return Base->getBaseName(); }
|
std::string_view getBaseName() const override { return Base->getBaseName(); }
|
||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
Base->printLeft(OB);
|
OB.printLeft(*Base);
|
||||||
OB += "[abi:";
|
OB += "[abi:";
|
||||||
OB += Tag;
|
OB += Tag;
|
||||||
OB += "]";
|
OB += "]";
|
||||||
@@ -646,7 +655,7 @@ public:
|
|||||||
// We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>.
|
// We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>.
|
||||||
if (Pointee->getKind() != KObjCProtoName ||
|
if (Pointee->getKind() != KObjCProtoName ||
|
||||||
!static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
|
!static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
|
||||||
Pointee->printLeft(OB);
|
OB.printLeft(*Pointee);
|
||||||
if (Pointee->hasArray(OB))
|
if (Pointee->hasArray(OB))
|
||||||
OB += " ";
|
OB += " ";
|
||||||
if (Pointee->hasArray(OB) || Pointee->hasFunction(OB))
|
if (Pointee->hasArray(OB) || Pointee->hasFunction(OB))
|
||||||
@@ -665,7 +674,7 @@ public:
|
|||||||
!static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
|
!static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
|
||||||
if (Pointee->hasArray(OB) || Pointee->hasFunction(OB))
|
if (Pointee->hasArray(OB) || Pointee->hasFunction(OB))
|
||||||
OB += ")";
|
OB += ")";
|
||||||
Pointee->printRight(OB);
|
OB.printRight(*Pointee);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -731,7 +740,7 @@ public:
|
|||||||
std::pair<ReferenceKind, const Node *> Collapsed = collapse(OB);
|
std::pair<ReferenceKind, const Node *> Collapsed = collapse(OB);
|
||||||
if (!Collapsed.second)
|
if (!Collapsed.second)
|
||||||
return;
|
return;
|
||||||
Collapsed.second->printLeft(OB);
|
OB.printLeft(*Collapsed.second);
|
||||||
if (Collapsed.second->hasArray(OB))
|
if (Collapsed.second->hasArray(OB))
|
||||||
OB += " ";
|
OB += " ";
|
||||||
if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB))
|
if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB))
|
||||||
@@ -748,7 +757,7 @@ public:
|
|||||||
return;
|
return;
|
||||||
if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB))
|
if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB))
|
||||||
OB += ")";
|
OB += ")";
|
||||||
Collapsed.second->printRight(OB);
|
OB.printRight(*Collapsed.second);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -768,7 +777,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
MemberType->printLeft(OB);
|
OB.printLeft(*MemberType);
|
||||||
if (MemberType->hasArray(OB) || MemberType->hasFunction(OB))
|
if (MemberType->hasArray(OB) || MemberType->hasFunction(OB))
|
||||||
OB += "(";
|
OB += "(";
|
||||||
else
|
else
|
||||||
@@ -780,7 +789,7 @@ public:
|
|||||||
void printRight(OutputBuffer &OB) const override {
|
void printRight(OutputBuffer &OB) const override {
|
||||||
if (MemberType->hasArray(OB) || MemberType->hasFunction(OB))
|
if (MemberType->hasArray(OB) || MemberType->hasFunction(OB))
|
||||||
OB += ")";
|
OB += ")";
|
||||||
MemberType->printRight(OB);
|
OB.printRight(*MemberType);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -800,7 +809,7 @@ public:
|
|||||||
bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }
|
bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }
|
||||||
bool hasArraySlow(OutputBuffer &) const override { return true; }
|
bool hasArraySlow(OutputBuffer &) const override { return true; }
|
||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override { Base->printLeft(OB); }
|
void printLeft(OutputBuffer &OB) const override { OB.printLeft(*Base); }
|
||||||
|
|
||||||
void printRight(OutputBuffer &OB) const override {
|
void printRight(OutputBuffer &OB) const override {
|
||||||
if (OB.back() != ']')
|
if (OB.back() != ']')
|
||||||
@@ -809,7 +818,7 @@ public:
|
|||||||
if (Dimension)
|
if (Dimension)
|
||||||
Dimension->print(OB);
|
Dimension->print(OB);
|
||||||
OB += "]";
|
OB += "]";
|
||||||
Base->printRight(OB);
|
OB.printRight(*Base);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool printInitListAsType(OutputBuffer &OB,
|
bool printInitListAsType(OutputBuffer &OB,
|
||||||
@@ -853,7 +862,7 @@ public:
|
|||||||
// by printing out the return types's left, then print our parameters, then
|
// by printing out the return types's left, then print our parameters, then
|
||||||
// finally print right of the return type.
|
// finally print right of the return type.
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
Ret->printLeft(OB);
|
OB.printLeft(*Ret);
|
||||||
OB += " ";
|
OB += " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -861,7 +870,7 @@ public:
|
|||||||
OB.printOpen();
|
OB.printOpen();
|
||||||
Params.printWithComma(OB);
|
Params.printWithComma(OB);
|
||||||
OB.printClose();
|
OB.printClose();
|
||||||
Ret->printRight(OB);
|
OB.printRight(*Ret);
|
||||||
|
|
||||||
if (CVQuals & QualConst)
|
if (CVQuals & QualConst)
|
||||||
OB += " const";
|
OB += " const";
|
||||||
@@ -966,6 +975,8 @@ public:
|
|||||||
FunctionRefQual getRefQual() const { return RefQual; }
|
FunctionRefQual getRefQual() const { return RefQual; }
|
||||||
NodeArray getParams() const { return Params; }
|
NodeArray getParams() const { return Params; }
|
||||||
const Node *getReturnType() const { return Ret; }
|
const Node *getReturnType() const { return Ret; }
|
||||||
|
const Node *getAttrs() const { return Attrs; }
|
||||||
|
const Node *getRequires() const { return Requires; }
|
||||||
|
|
||||||
bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }
|
bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }
|
||||||
bool hasFunctionSlow(OutputBuffer &) const override { return true; }
|
bool hasFunctionSlow(OutputBuffer &) const override { return true; }
|
||||||
@@ -974,10 +985,11 @@ public:
|
|||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
if (Ret) {
|
if (Ret) {
|
||||||
Ret->printLeft(OB);
|
OB.printLeft(*Ret);
|
||||||
if (!Ret->hasRHSComponent(OB))
|
if (!Ret->hasRHSComponent(OB))
|
||||||
OB += " ";
|
OB += " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
Name->print(OB);
|
Name->print(OB);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -985,8 +997,9 @@ public:
|
|||||||
OB.printOpen();
|
OB.printOpen();
|
||||||
Params.printWithComma(OB);
|
Params.printWithComma(OB);
|
||||||
OB.printClose();
|
OB.printClose();
|
||||||
|
|
||||||
if (Ret)
|
if (Ret)
|
||||||
Ret->printRight(OB);
|
OB.printRight(*Ret);
|
||||||
|
|
||||||
if (CVQuals & QualConst)
|
if (CVQuals & QualConst)
|
||||||
OB += " const";
|
OB += " const";
|
||||||
@@ -1326,14 +1339,14 @@ public:
|
|||||||
template<typename Fn> void match(Fn F) const { F(Name, Type); }
|
template<typename Fn> void match(Fn F) const { F(Name, Type); }
|
||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
Type->printLeft(OB);
|
OB.printLeft(*Type);
|
||||||
if (!Type->hasRHSComponent(OB))
|
if (!Type->hasRHSComponent(OB))
|
||||||
OB += " ";
|
OB += " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
void printRight(OutputBuffer &OB) const override {
|
void printRight(OutputBuffer &OB) const override {
|
||||||
Name->print(OB);
|
Name->print(OB);
|
||||||
Type->printRight(OB);
|
OB.printRight(*Type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1378,11 +1391,11 @@ public:
|
|||||||
template<typename Fn> void match(Fn F) const { F(Param); }
|
template<typename Fn> void match(Fn F) const { F(Param); }
|
||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
Param->printLeft(OB);
|
OB.printLeft(*Param);
|
||||||
OB += "...";
|
OB += "...";
|
||||||
}
|
}
|
||||||
|
|
||||||
void printRight(OutputBuffer &OB) const override { Param->printRight(OB); }
|
void printRight(OutputBuffer &OB) const override { OB.printRight(*Param); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An unexpanded parameter pack (either in the expression or type context). If
|
/// An unexpanded parameter pack (either in the expression or type context). If
|
||||||
@@ -1447,13 +1460,13 @@ public:
|
|||||||
initializePackExpansion(OB);
|
initializePackExpansion(OB);
|
||||||
size_t Idx = OB.CurrentPackIndex;
|
size_t Idx = OB.CurrentPackIndex;
|
||||||
if (Idx < Data.size())
|
if (Idx < Data.size())
|
||||||
Data[Idx]->printLeft(OB);
|
OB.printLeft(*Data[Idx]);
|
||||||
}
|
}
|
||||||
void printRight(OutputBuffer &OB) const override {
|
void printRight(OutputBuffer &OB) const override {
|
||||||
initializePackExpansion(OB);
|
initializePackExpansion(OB);
|
||||||
size_t Idx = OB.CurrentPackIndex;
|
size_t Idx = OB.CurrentPackIndex;
|
||||||
if (Idx < Data.size())
|
if (Idx < Data.size())
|
||||||
Data[Idx]->printRight(OB);
|
OB.printRight(*Data[Idx]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1611,13 +1624,13 @@ struct ForwardTemplateReference : Node {
|
|||||||
if (Printing)
|
if (Printing)
|
||||||
return;
|
return;
|
||||||
ScopedOverride<bool> SavePrinting(Printing, true);
|
ScopedOverride<bool> SavePrinting(Printing, true);
|
||||||
Ref->printLeft(OB);
|
OB.printLeft(*Ref);
|
||||||
}
|
}
|
||||||
void printRight(OutputBuffer &OB) const override {
|
void printRight(OutputBuffer &OB) const override {
|
||||||
if (Printing)
|
if (Printing)
|
||||||
return;
|
return;
|
||||||
ScopedOverride<bool> SavePrinting(Printing, true);
|
ScopedOverride<bool> SavePrinting(Printing, true);
|
||||||
Ref->printRight(OB);
|
OB.printRight(*Ref);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1769,7 +1782,7 @@ public:
|
|||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
OB += "~";
|
OB += "~";
|
||||||
Base->printLeft(OB);
|
OB.printLeft(*Base);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2049,7 +2062,7 @@ public:
|
|||||||
{
|
{
|
||||||
ScopedOverride<unsigned> LT(OB.GtIsGt, 0);
|
ScopedOverride<unsigned> LT(OB.GtIsGt, 0);
|
||||||
OB += "<";
|
OB += "<";
|
||||||
To->printLeft(OB);
|
OB.printLeft(*To);
|
||||||
OB += ">";
|
OB += ">";
|
||||||
}
|
}
|
||||||
OB.printOpen();
|
OB.printOpen();
|
||||||
@@ -6180,6 +6193,10 @@ struct ManglingParser : AbstractManglingParser<ManglingParser<Alloc>, Alloc> {
|
|||||||
Alloc>::AbstractManglingParser;
|
Alloc>::AbstractManglingParser;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline void OutputBuffer::printLeft(const Node &N) { N.printLeft(*this); }
|
||||||
|
|
||||||
|
inline void OutputBuffer::printRight(const Node &N) { N.printRight(*this); }
|
||||||
|
|
||||||
DEMANGLE_NAMESPACE_END
|
DEMANGLE_NAMESPACE_END
|
||||||
|
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
|
|||||||
@@ -27,6 +27,8 @@
|
|||||||
|
|
||||||
DEMANGLE_NAMESPACE_BEGIN
|
DEMANGLE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class Node;
|
||||||
|
|
||||||
// Stream that AST nodes write their string representation into after the AST
|
// Stream that AST nodes write their string representation into after the AST
|
||||||
// has been parsed.
|
// has been parsed.
|
||||||
class OutputBuffer {
|
class OutputBuffer {
|
||||||
@@ -79,10 +81,24 @@ public:
|
|||||||
OutputBuffer(const OutputBuffer &) = delete;
|
OutputBuffer(const OutputBuffer &) = delete;
|
||||||
OutputBuffer &operator=(const OutputBuffer &) = delete;
|
OutputBuffer &operator=(const OutputBuffer &) = delete;
|
||||||
|
|
||||||
|
virtual ~OutputBuffer() {}
|
||||||
|
|
||||||
operator std::string_view() const {
|
operator std::string_view() const {
|
||||||
return std::string_view(Buffer, CurrentPosition);
|
return std::string_view(Buffer, CurrentPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called by the demangler when printing the demangle tree. By
|
||||||
|
/// default calls into \c Node::print{Left|Right} but can be overriden
|
||||||
|
/// by clients to track additional state when printing the demangled name.
|
||||||
|
virtual void printLeft(const Node &N);
|
||||||
|
virtual void printRight(const Node &N);
|
||||||
|
|
||||||
|
/// Called when we write to this object anywhere other than the end.
|
||||||
|
virtual void notifyInsertion(size_t /*Position*/, size_t /*Count*/) {}
|
||||||
|
|
||||||
|
/// Called when we make the \c CurrentPosition of this object smaller.
|
||||||
|
virtual void notifyDeletion(size_t /*OldPos*/, size_t /*NewPos*/) {}
|
||||||
|
|
||||||
/// If a ParameterPackExpansion (or similar type) is encountered, the offset
|
/// If a ParameterPackExpansion (or similar type) is encountered, the offset
|
||||||
/// into the pack that we're currently printing.
|
/// into the pack that we're currently printing.
|
||||||
unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
|
unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
|
||||||
@@ -126,6 +142,8 @@ public:
|
|||||||
std::memcpy(Buffer, &*R.begin(), Size);
|
std::memcpy(Buffer, &*R.begin(), Size);
|
||||||
CurrentPosition += Size;
|
CurrentPosition += Size;
|
||||||
|
|
||||||
|
notifyInsertion(/*Position=*/0, /*Count=*/Size);
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,14 +179,20 @@ public:
|
|||||||
DEMANGLE_ASSERT(Pos <= CurrentPosition, "");
|
DEMANGLE_ASSERT(Pos <= CurrentPosition, "");
|
||||||
if (N == 0)
|
if (N == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
grow(N);
|
grow(N);
|
||||||
std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos);
|
std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos);
|
||||||
std::memcpy(Buffer + Pos, S, N);
|
std::memcpy(Buffer + Pos, S, N);
|
||||||
CurrentPosition += N;
|
CurrentPosition += N;
|
||||||
|
|
||||||
|
notifyInsertion(Pos, N);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t getCurrentPosition() const { return CurrentPosition; }
|
size_t getCurrentPosition() const { return CurrentPosition; }
|
||||||
void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; }
|
void setCurrentPosition(size_t NewPos) {
|
||||||
|
notifyDeletion(CurrentPosition, NewPos);
|
||||||
|
CurrentPosition = NewPos;
|
||||||
|
}
|
||||||
|
|
||||||
char back() const {
|
char back() const {
|
||||||
DEMANGLE_ASSERT(CurrentPosition, "");
|
DEMANGLE_ASSERT(CurrentPosition, "");
|
||||||
|
|||||||
@@ -283,20 +283,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void print(OutputBuffer &OB) const {
|
void print(OutputBuffer &OB) const {
|
||||||
printLeft(OB);
|
OB.printLeft(*this);
|
||||||
if (RHSComponentCache != Cache::No)
|
if (RHSComponentCache != Cache::No)
|
||||||
printRight(OB);
|
OB.printRight(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the "left" side of this Node into OutputBuffer.
|
|
||||||
virtual void printLeft(OutputBuffer &) const = 0;
|
|
||||||
|
|
||||||
// Print the "right". This distinction is necessary to represent C++ types
|
|
||||||
// that appear on the RHS of their subtype, such as arrays or functions.
|
|
||||||
// Since most types don't have such a component, provide a default
|
|
||||||
// implementation.
|
|
||||||
virtual void printRight(OutputBuffer &) const {}
|
|
||||||
|
|
||||||
// Print an initializer list of this type. Returns true if we printed a custom
|
// Print an initializer list of this type. Returns true if we printed a custom
|
||||||
// representation, false if nothing has been printed and the default
|
// representation, false if nothing has been printed and the default
|
||||||
// representation should be used.
|
// representation should be used.
|
||||||
@@ -312,6 +303,24 @@ public:
|
|||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
DEMANGLE_DUMP_METHOD void dump() const;
|
DEMANGLE_DUMP_METHOD void dump() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class OutputBuffer;
|
||||||
|
|
||||||
|
// Print the "left" side of this Node into OutputBuffer.
|
||||||
|
//
|
||||||
|
// Note, should only be called from OutputBuffer implementations.
|
||||||
|
// Call \ref OutputBuffer::printLeft instead.
|
||||||
|
virtual void printLeft(OutputBuffer &) const = 0;
|
||||||
|
|
||||||
|
// Print the "right". This distinction is necessary to represent C++ types
|
||||||
|
// that appear on the RHS of their subtype, such as arrays or functions.
|
||||||
|
// Since most types don't have such a component, provide a default
|
||||||
|
// implementation.
|
||||||
|
//
|
||||||
|
// Note, should only be called from OutputBuffer implementations.
|
||||||
|
// Call \ref OutputBuffer::printRight instead.
|
||||||
|
virtual void printRight(OutputBuffer &) const {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class NodeArray {
|
class NodeArray {
|
||||||
@@ -460,11 +469,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
Child->printLeft(OB);
|
OB.printLeft(*Child);
|
||||||
printQuals(OB);
|
printQuals(OB);
|
||||||
}
|
}
|
||||||
|
|
||||||
void printRight(OutputBuffer &OB) const override { Child->printRight(OB); }
|
void printRight(OutputBuffer &OB) const override { OB.printRight(*Child); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConversionOperatorType final : public Node {
|
class ConversionOperatorType final : public Node {
|
||||||
@@ -493,7 +502,7 @@ public:
|
|||||||
template<typename Fn> void match(Fn F) const { F(Ty, Postfix); }
|
template<typename Fn> void match(Fn F) const { F(Ty, Postfix); }
|
||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
Ty->printLeft(OB);
|
OB.printLeft(*Ty);
|
||||||
OB += Postfix;
|
OB += Postfix;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -579,7 +588,7 @@ struct AbiTagAttr : Node {
|
|||||||
std::string_view getBaseName() const override { return Base->getBaseName(); }
|
std::string_view getBaseName() const override { return Base->getBaseName(); }
|
||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
Base->printLeft(OB);
|
OB.printLeft(*Base);
|
||||||
OB += "[abi:";
|
OB += "[abi:";
|
||||||
OB += Tag;
|
OB += Tag;
|
||||||
OB += "]";
|
OB += "]";
|
||||||
@@ -646,7 +655,7 @@ public:
|
|||||||
// We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>.
|
// We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>.
|
||||||
if (Pointee->getKind() != KObjCProtoName ||
|
if (Pointee->getKind() != KObjCProtoName ||
|
||||||
!static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
|
!static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
|
||||||
Pointee->printLeft(OB);
|
OB.printLeft(*Pointee);
|
||||||
if (Pointee->hasArray(OB))
|
if (Pointee->hasArray(OB))
|
||||||
OB += " ";
|
OB += " ";
|
||||||
if (Pointee->hasArray(OB) || Pointee->hasFunction(OB))
|
if (Pointee->hasArray(OB) || Pointee->hasFunction(OB))
|
||||||
@@ -665,7 +674,7 @@ public:
|
|||||||
!static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
|
!static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
|
||||||
if (Pointee->hasArray(OB) || Pointee->hasFunction(OB))
|
if (Pointee->hasArray(OB) || Pointee->hasFunction(OB))
|
||||||
OB += ")";
|
OB += ")";
|
||||||
Pointee->printRight(OB);
|
OB.printRight(*Pointee);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -731,7 +740,7 @@ public:
|
|||||||
std::pair<ReferenceKind, const Node *> Collapsed = collapse(OB);
|
std::pair<ReferenceKind, const Node *> Collapsed = collapse(OB);
|
||||||
if (!Collapsed.second)
|
if (!Collapsed.second)
|
||||||
return;
|
return;
|
||||||
Collapsed.second->printLeft(OB);
|
OB.printLeft(*Collapsed.second);
|
||||||
if (Collapsed.second->hasArray(OB))
|
if (Collapsed.second->hasArray(OB))
|
||||||
OB += " ";
|
OB += " ";
|
||||||
if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB))
|
if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB))
|
||||||
@@ -748,7 +757,7 @@ public:
|
|||||||
return;
|
return;
|
||||||
if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB))
|
if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB))
|
||||||
OB += ")";
|
OB += ")";
|
||||||
Collapsed.second->printRight(OB);
|
OB.printRight(*Collapsed.second);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -768,7 +777,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
MemberType->printLeft(OB);
|
OB.printLeft(*MemberType);
|
||||||
if (MemberType->hasArray(OB) || MemberType->hasFunction(OB))
|
if (MemberType->hasArray(OB) || MemberType->hasFunction(OB))
|
||||||
OB += "(";
|
OB += "(";
|
||||||
else
|
else
|
||||||
@@ -780,7 +789,7 @@ public:
|
|||||||
void printRight(OutputBuffer &OB) const override {
|
void printRight(OutputBuffer &OB) const override {
|
||||||
if (MemberType->hasArray(OB) || MemberType->hasFunction(OB))
|
if (MemberType->hasArray(OB) || MemberType->hasFunction(OB))
|
||||||
OB += ")";
|
OB += ")";
|
||||||
MemberType->printRight(OB);
|
OB.printRight(*MemberType);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -800,7 +809,7 @@ public:
|
|||||||
bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }
|
bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }
|
||||||
bool hasArraySlow(OutputBuffer &) const override { return true; }
|
bool hasArraySlow(OutputBuffer &) const override { return true; }
|
||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override { Base->printLeft(OB); }
|
void printLeft(OutputBuffer &OB) const override { OB.printLeft(*Base); }
|
||||||
|
|
||||||
void printRight(OutputBuffer &OB) const override {
|
void printRight(OutputBuffer &OB) const override {
|
||||||
if (OB.back() != ']')
|
if (OB.back() != ']')
|
||||||
@@ -809,7 +818,7 @@ public:
|
|||||||
if (Dimension)
|
if (Dimension)
|
||||||
Dimension->print(OB);
|
Dimension->print(OB);
|
||||||
OB += "]";
|
OB += "]";
|
||||||
Base->printRight(OB);
|
OB.printRight(*Base);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool printInitListAsType(OutputBuffer &OB,
|
bool printInitListAsType(OutputBuffer &OB,
|
||||||
@@ -853,7 +862,7 @@ public:
|
|||||||
// by printing out the return types's left, then print our parameters, then
|
// by printing out the return types's left, then print our parameters, then
|
||||||
// finally print right of the return type.
|
// finally print right of the return type.
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
Ret->printLeft(OB);
|
OB.printLeft(*Ret);
|
||||||
OB += " ";
|
OB += " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -861,7 +870,7 @@ public:
|
|||||||
OB.printOpen();
|
OB.printOpen();
|
||||||
Params.printWithComma(OB);
|
Params.printWithComma(OB);
|
||||||
OB.printClose();
|
OB.printClose();
|
||||||
Ret->printRight(OB);
|
OB.printRight(*Ret);
|
||||||
|
|
||||||
if (CVQuals & QualConst)
|
if (CVQuals & QualConst)
|
||||||
OB += " const";
|
OB += " const";
|
||||||
@@ -966,6 +975,8 @@ public:
|
|||||||
FunctionRefQual getRefQual() const { return RefQual; }
|
FunctionRefQual getRefQual() const { return RefQual; }
|
||||||
NodeArray getParams() const { return Params; }
|
NodeArray getParams() const { return Params; }
|
||||||
const Node *getReturnType() const { return Ret; }
|
const Node *getReturnType() const { return Ret; }
|
||||||
|
const Node *getAttrs() const { return Attrs; }
|
||||||
|
const Node *getRequires() const { return Requires; }
|
||||||
|
|
||||||
bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }
|
bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }
|
||||||
bool hasFunctionSlow(OutputBuffer &) const override { return true; }
|
bool hasFunctionSlow(OutputBuffer &) const override { return true; }
|
||||||
@@ -974,10 +985,11 @@ public:
|
|||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
if (Ret) {
|
if (Ret) {
|
||||||
Ret->printLeft(OB);
|
OB.printLeft(*Ret);
|
||||||
if (!Ret->hasRHSComponent(OB))
|
if (!Ret->hasRHSComponent(OB))
|
||||||
OB += " ";
|
OB += " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
Name->print(OB);
|
Name->print(OB);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -985,8 +997,9 @@ public:
|
|||||||
OB.printOpen();
|
OB.printOpen();
|
||||||
Params.printWithComma(OB);
|
Params.printWithComma(OB);
|
||||||
OB.printClose();
|
OB.printClose();
|
||||||
|
|
||||||
if (Ret)
|
if (Ret)
|
||||||
Ret->printRight(OB);
|
OB.printRight(*Ret);
|
||||||
|
|
||||||
if (CVQuals & QualConst)
|
if (CVQuals & QualConst)
|
||||||
OB += " const";
|
OB += " const";
|
||||||
@@ -1326,14 +1339,14 @@ public:
|
|||||||
template<typename Fn> void match(Fn F) const { F(Name, Type); }
|
template<typename Fn> void match(Fn F) const { F(Name, Type); }
|
||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
Type->printLeft(OB);
|
OB.printLeft(*Type);
|
||||||
if (!Type->hasRHSComponent(OB))
|
if (!Type->hasRHSComponent(OB))
|
||||||
OB += " ";
|
OB += " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
void printRight(OutputBuffer &OB) const override {
|
void printRight(OutputBuffer &OB) const override {
|
||||||
Name->print(OB);
|
Name->print(OB);
|
||||||
Type->printRight(OB);
|
OB.printRight(*Type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1378,11 +1391,11 @@ public:
|
|||||||
template<typename Fn> void match(Fn F) const { F(Param); }
|
template<typename Fn> void match(Fn F) const { F(Param); }
|
||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
Param->printLeft(OB);
|
OB.printLeft(*Param);
|
||||||
OB += "...";
|
OB += "...";
|
||||||
}
|
}
|
||||||
|
|
||||||
void printRight(OutputBuffer &OB) const override { Param->printRight(OB); }
|
void printRight(OutputBuffer &OB) const override { OB.printRight(*Param); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An unexpanded parameter pack (either in the expression or type context). If
|
/// An unexpanded parameter pack (either in the expression or type context). If
|
||||||
@@ -1447,13 +1460,13 @@ public:
|
|||||||
initializePackExpansion(OB);
|
initializePackExpansion(OB);
|
||||||
size_t Idx = OB.CurrentPackIndex;
|
size_t Idx = OB.CurrentPackIndex;
|
||||||
if (Idx < Data.size())
|
if (Idx < Data.size())
|
||||||
Data[Idx]->printLeft(OB);
|
OB.printLeft(*Data[Idx]);
|
||||||
}
|
}
|
||||||
void printRight(OutputBuffer &OB) const override {
|
void printRight(OutputBuffer &OB) const override {
|
||||||
initializePackExpansion(OB);
|
initializePackExpansion(OB);
|
||||||
size_t Idx = OB.CurrentPackIndex;
|
size_t Idx = OB.CurrentPackIndex;
|
||||||
if (Idx < Data.size())
|
if (Idx < Data.size())
|
||||||
Data[Idx]->printRight(OB);
|
OB.printRight(*Data[Idx]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1611,13 +1624,13 @@ struct ForwardTemplateReference : Node {
|
|||||||
if (Printing)
|
if (Printing)
|
||||||
return;
|
return;
|
||||||
ScopedOverride<bool> SavePrinting(Printing, true);
|
ScopedOverride<bool> SavePrinting(Printing, true);
|
||||||
Ref->printLeft(OB);
|
OB.printLeft(*Ref);
|
||||||
}
|
}
|
||||||
void printRight(OutputBuffer &OB) const override {
|
void printRight(OutputBuffer &OB) const override {
|
||||||
if (Printing)
|
if (Printing)
|
||||||
return;
|
return;
|
||||||
ScopedOverride<bool> SavePrinting(Printing, true);
|
ScopedOverride<bool> SavePrinting(Printing, true);
|
||||||
Ref->printRight(OB);
|
OB.printRight(*Ref);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1769,7 +1782,7 @@ public:
|
|||||||
|
|
||||||
void printLeft(OutputBuffer &OB) const override {
|
void printLeft(OutputBuffer &OB) const override {
|
||||||
OB += "~";
|
OB += "~";
|
||||||
Base->printLeft(OB);
|
OB.printLeft(*Base);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2049,7 +2062,7 @@ public:
|
|||||||
{
|
{
|
||||||
ScopedOverride<unsigned> LT(OB.GtIsGt, 0);
|
ScopedOverride<unsigned> LT(OB.GtIsGt, 0);
|
||||||
OB += "<";
|
OB += "<";
|
||||||
To->printLeft(OB);
|
OB.printLeft(*To);
|
||||||
OB += ">";
|
OB += ">";
|
||||||
}
|
}
|
||||||
OB.printOpen();
|
OB.printOpen();
|
||||||
@@ -6180,6 +6193,10 @@ struct ManglingParser : AbstractManglingParser<ManglingParser<Alloc>, Alloc> {
|
|||||||
Alloc>::AbstractManglingParser;
|
Alloc>::AbstractManglingParser;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline void OutputBuffer::printLeft(const Node &N) { N.printLeft(*this); }
|
||||||
|
|
||||||
|
inline void OutputBuffer::printRight(const Node &N) { N.printRight(*this); }
|
||||||
|
|
||||||
DEMANGLE_NAMESPACE_END
|
DEMANGLE_NAMESPACE_END
|
||||||
|
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
|
|||||||
@@ -27,6 +27,8 @@
|
|||||||
|
|
||||||
DEMANGLE_NAMESPACE_BEGIN
|
DEMANGLE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class Node;
|
||||||
|
|
||||||
// Stream that AST nodes write their string representation into after the AST
|
// Stream that AST nodes write their string representation into after the AST
|
||||||
// has been parsed.
|
// has been parsed.
|
||||||
class OutputBuffer {
|
class OutputBuffer {
|
||||||
@@ -79,10 +81,24 @@ public:
|
|||||||
OutputBuffer(const OutputBuffer &) = delete;
|
OutputBuffer(const OutputBuffer &) = delete;
|
||||||
OutputBuffer &operator=(const OutputBuffer &) = delete;
|
OutputBuffer &operator=(const OutputBuffer &) = delete;
|
||||||
|
|
||||||
|
virtual ~OutputBuffer() {}
|
||||||
|
|
||||||
operator std::string_view() const {
|
operator std::string_view() const {
|
||||||
return std::string_view(Buffer, CurrentPosition);
|
return std::string_view(Buffer, CurrentPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called by the demangler when printing the demangle tree. By
|
||||||
|
/// default calls into \c Node::print{Left|Right} but can be overriden
|
||||||
|
/// by clients to track additional state when printing the demangled name.
|
||||||
|
virtual void printLeft(const Node &N);
|
||||||
|
virtual void printRight(const Node &N);
|
||||||
|
|
||||||
|
/// Called when we write to this object anywhere other than the end.
|
||||||
|
virtual void notifyInsertion(size_t /*Position*/, size_t /*Count*/) {}
|
||||||
|
|
||||||
|
/// Called when we make the \c CurrentPosition of this object smaller.
|
||||||
|
virtual void notifyDeletion(size_t /*OldPos*/, size_t /*NewPos*/) {}
|
||||||
|
|
||||||
/// If a ParameterPackExpansion (or similar type) is encountered, the offset
|
/// If a ParameterPackExpansion (or similar type) is encountered, the offset
|
||||||
/// into the pack that we're currently printing.
|
/// into the pack that we're currently printing.
|
||||||
unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
|
unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
|
||||||
@@ -126,6 +142,8 @@ public:
|
|||||||
std::memcpy(Buffer, &*R.begin(), Size);
|
std::memcpy(Buffer, &*R.begin(), Size);
|
||||||
CurrentPosition += Size;
|
CurrentPosition += Size;
|
||||||
|
|
||||||
|
notifyInsertion(/*Position=*/0, /*Count=*/Size);
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,14 +179,20 @@ public:
|
|||||||
DEMANGLE_ASSERT(Pos <= CurrentPosition, "");
|
DEMANGLE_ASSERT(Pos <= CurrentPosition, "");
|
||||||
if (N == 0)
|
if (N == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
grow(N);
|
grow(N);
|
||||||
std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos);
|
std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos);
|
||||||
std::memcpy(Buffer + Pos, S, N);
|
std::memcpy(Buffer + Pos, S, N);
|
||||||
CurrentPosition += N;
|
CurrentPosition += N;
|
||||||
|
|
||||||
|
notifyInsertion(Pos, N);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t getCurrentPosition() const { return CurrentPosition; }
|
size_t getCurrentPosition() const { return CurrentPosition; }
|
||||||
void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; }
|
void setCurrentPosition(size_t NewPos) {
|
||||||
|
notifyDeletion(CurrentPosition, NewPos);
|
||||||
|
CurrentPosition = NewPos;
|
||||||
|
}
|
||||||
|
|
||||||
char back() const {
|
char back() const {
|
||||||
DEMANGLE_ASSERT(CurrentPosition, "");
|
DEMANGLE_ASSERT(CurrentPosition, "");
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ TEST(ItaniumDemangle, HalfType) {
|
|||||||
Node *parseType() {
|
Node *parseType() {
|
||||||
OutputBuffer OB;
|
OutputBuffer OB;
|
||||||
Node *N = AbstractManglingParser<TestParser, TestAllocator>::parseType();
|
Node *N = AbstractManglingParser<TestParser, TestAllocator>::parseType();
|
||||||
N->printLeft(OB);
|
OB.printLeft(*N);
|
||||||
std::string_view Name = N->getBaseName();
|
std::string_view Name = N->getBaseName();
|
||||||
if (!Name.empty())
|
if (!Name.empty())
|
||||||
Types.push_back(std::string(Name.begin(), Name.end()));
|
Types.push_back(std::string(Name.begin(), Name.end()));
|
||||||
|
|||||||
@@ -93,3 +93,40 @@ TEST(OutputBufferTest, Extend) {
|
|||||||
|
|
||||||
std::free(OB.getBuffer());
|
std::free(OB.getBuffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(OutputBufferTest, Notifications) {
|
||||||
|
struct MyOutputBuffer : public OutputBuffer {
|
||||||
|
size_t Inserted = 0;
|
||||||
|
size_t LatestPos = 0;
|
||||||
|
|
||||||
|
void notifyDeletion(size_t OldPos, size_t NewPos) override {
|
||||||
|
LatestPos = NewPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyInsertion(size_t Position, size_t Count) override {
|
||||||
|
Inserted += Count;
|
||||||
|
LatestPos = Position;
|
||||||
|
}
|
||||||
|
} OB;
|
||||||
|
|
||||||
|
OB.prepend("n");
|
||||||
|
EXPECT_EQ(OB.Inserted, 1U);
|
||||||
|
EXPECT_EQ(OB.LatestPos, 0U);
|
||||||
|
|
||||||
|
OB.prepend("");
|
||||||
|
EXPECT_EQ(OB.Inserted, 1U);
|
||||||
|
EXPECT_EQ(OB.LatestPos, 0U);
|
||||||
|
|
||||||
|
OB.prepend("abc");
|
||||||
|
EXPECT_EQ(OB.Inserted, 4U);
|
||||||
|
EXPECT_EQ(OB.LatestPos, 0U);
|
||||||
|
|
||||||
|
OB.insert(2, "abc", 3U);
|
||||||
|
EXPECT_EQ(OB.Inserted, 7U);
|
||||||
|
EXPECT_EQ(OB.LatestPos, 2U);
|
||||||
|
|
||||||
|
OB.setCurrentPosition(3U);
|
||||||
|
EXPECT_EQ(OB.LatestPos, 3U);
|
||||||
|
|
||||||
|
std::free(OB.getBuffer());
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user