llvm-project/llvm/lib/Demangle/MicrosoftDemangleNodes.cpp
Nathan Sidwell d3b10150b6 [demangler] Simplify OutputBuffer initialization
Every non-testcase use of OutputBuffer contains code to allocate an
initial buffer (using either 128 or 1024 as initial guesses). There's
now no need to do that, given recent changes to the buffer extension
heuristics -- it allocates a 1k(ish) buffer on first need.

Just pass in a buffer (if any) to the constructor.  Thus the
OutputBuffer's ownership of the buffer starts at its own lifetime
start. We can reduce the lifetime of this object in several cases.

That new constructor takes a 'size_t *' for the size argument, as all
uses with a non-null buffer are passing through a malloc'd buffer from
their own caller in this manner.

The buffer reset member function is never used, and is deleted.

Some adjustment to a couple of uses is needed, due to the lazy buffer
creation of this patch.

a) the Microsoft demangler can demangle empty strings to nothing,
which it then memoizes.  We need to avoid the UB of passing nullptr to
memcpy.

b) a unit test checks insertion of no characters into an empty buffer.
We need to avoid UB when converting that to std::string.

The original buffer initialization code would return a failure code if
that first malloc failed.  Existing code either ignored that, called
std::terminate with a FIXME, or returned an error code.

But that's not foolproof anyway, as a subsequent buffer extension
failure ends up calling std::terminate. I am working on addressing
that unfortunate failure mode in a manner more consistent with the C++
ABI design.

Reviewed By: dblaikie

Differential Revision: https://reviews.llvm.org/D122604
2022-10-17 04:23:16 -07:00

659 lines
22 KiB
C++

//===- MicrosoftDemangle.cpp ----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines a demangler for MSVC-style mangled symbols.
//
//===----------------------------------------------------------------------===//
#include "llvm/Demangle/MicrosoftDemangleNodes.h"
#include "llvm/Demangle/Utility.h"
#include <cctype>
#include <string>
using namespace llvm;
using namespace ms_demangle;
#define OUTPUT_ENUM_CLASS_VALUE(Enum, Value, Desc) \
case Enum::Value: \
OB << Desc; \
break;
// Writes a space if the last token does not end with a punctuation.
static void outputSpaceIfNecessary(OutputBuffer &OB) {
if (OB.empty())
return;
char C = OB.back();
if (std::isalnum(C) || C == '>')
OB << " ";
}
static void outputSingleQualifier(OutputBuffer &OB, Qualifiers Q) {
switch (Q) {
case Q_Const:
OB << "const";
break;
case Q_Volatile:
OB << "volatile";
break;
case Q_Restrict:
OB << "__restrict";
break;
default:
break;
}
}
static bool outputQualifierIfPresent(OutputBuffer &OB, Qualifiers Q,
Qualifiers Mask, bool NeedSpace) {
if (!(Q & Mask))
return NeedSpace;
if (NeedSpace)
OB << " ";
outputSingleQualifier(OB, Mask);
return true;
}
static void outputQualifiers(OutputBuffer &OB, Qualifiers Q, bool SpaceBefore,
bool SpaceAfter) {
if (Q == Q_None)
return;
size_t Pos1 = OB.getCurrentPosition();
SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Const, SpaceBefore);
SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Volatile, SpaceBefore);
SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Restrict, SpaceBefore);
size_t Pos2 = OB.getCurrentPosition();
if (SpaceAfter && Pos2 > Pos1)
OB << " ";
}
static void outputCallingConvention(OutputBuffer &OB, CallingConv CC) {
outputSpaceIfNecessary(OB);
switch (CC) {
case CallingConv::Cdecl:
OB << "__cdecl";
break;
case CallingConv::Fastcall:
OB << "__fastcall";
break;
case CallingConv::Pascal:
OB << "__pascal";
break;
case CallingConv::Regcall:
OB << "__regcall";
break;
case CallingConv::Stdcall:
OB << "__stdcall";
break;
case CallingConv::Thiscall:
OB << "__thiscall";
break;
case CallingConv::Eabi:
OB << "__eabi";
break;
case CallingConv::Vectorcall:
OB << "__vectorcall";
break;
case CallingConv::Clrcall:
OB << "__clrcall";
break;
case CallingConv::Swift:
OB << "__attribute__((__swiftcall__)) ";
break;
case CallingConv::SwiftAsync:
OB << "__attribute__((__swiftasynccall__)) ";
break;
default:
break;
}
}
std::string Node::toString(OutputFlags Flags) const {
OutputBuffer OB;
this->output(OB, Flags);
StringView SV = OB;
std::string Owned(SV.begin(), SV.end());
std::free(OB.getBuffer());
return Owned;
}
void PrimitiveTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
switch (PrimKind) {
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Void, "void");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Bool, "bool");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char, "char");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Schar, "signed char");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uchar, "unsigned char");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char8, "char8_t");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char16, "char16_t");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char32, "char32_t");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Short, "short");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ushort, "unsigned short");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Int, "int");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uint, "unsigned int");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Long, "long");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ulong, "unsigned long");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Int64, "__int64");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uint64, "unsigned __int64");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Wchar, "wchar_t");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Float, "float");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Double, "double");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ldouble, "long double");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Nullptr, "std::nullptr_t");
}
outputQualifiers(OB, Quals, true, false);
}
void NodeArrayNode::output(OutputBuffer &OB, OutputFlags Flags) const {
output(OB, Flags, ", ");
}
void NodeArrayNode::output(OutputBuffer &OB, OutputFlags Flags,
StringView Separator) const {
if (Count == 0)
return;
if (Nodes[0])
Nodes[0]->output(OB, Flags);
for (size_t I = 1; I < Count; ++I) {
OB << Separator;
Nodes[I]->output(OB, Flags);
}
}
void EncodedStringLiteralNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
switch (Char) {
case CharKind::Wchar:
OB << "L\"";
break;
case CharKind::Char:
OB << "\"";
break;
case CharKind::Char16:
OB << "u\"";
break;
case CharKind::Char32:
OB << "U\"";
break;
}
OB << DecodedString << "\"";
if (IsTruncated)
OB << "...";
}
void IntegerLiteralNode::output(OutputBuffer &OB, OutputFlags Flags) const {
if (IsNegative)
OB << '-';
OB << Value;
}
void TemplateParameterReferenceNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
if (ThunkOffsetCount > 0)
OB << "{";
else if (Affinity == PointerAffinity::Pointer)
OB << "&";
if (Symbol) {
Symbol->output(OB, Flags);
if (ThunkOffsetCount > 0)
OB << ", ";
}
if (ThunkOffsetCount > 0)
OB << ThunkOffsets[0];
for (int I = 1; I < ThunkOffsetCount; ++I) {
OB << ", " << ThunkOffsets[I];
}
if (ThunkOffsetCount > 0)
OB << "}";
}
void IdentifierNode::outputTemplateParameters(OutputBuffer &OB,
OutputFlags Flags) const {
if (!TemplateParams)
return;
OB << "<";
TemplateParams->output(OB, Flags);
OB << ">";
}
void DynamicStructorIdentifierNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
if (IsDestructor)
OB << "`dynamic atexit destructor for ";
else
OB << "`dynamic initializer for ";
if (Variable) {
OB << "`";
Variable->output(OB, Flags);
OB << "''";
} else {
OB << "'";
Name->output(OB, Flags);
OB << "''";
}
}
void NamedIdentifierNode::output(OutputBuffer &OB, OutputFlags Flags) const {
OB << Name;
outputTemplateParameters(OB, Flags);
}
void IntrinsicFunctionIdentifierNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
switch (Operator) {
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, New, "operator new");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Delete, "operator delete");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Assign, "operator=");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, RightShift, "operator>>");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LeftShift, "operator<<");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalNot, "operator!");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Equals, "operator==");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, NotEquals, "operator!=");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArraySubscript,
"operator[]");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Pointer, "operator->");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Increment, "operator++");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Decrement, "operator--");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Minus, "operator-");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Plus, "operator+");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Dereference, "operator*");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseAnd, "operator&");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, MemberPointer,
"operator->*");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Divide, "operator/");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Modulus, "operator%");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LessThan, "operator<");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LessThanEqual, "operator<=");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, GreaterThan, "operator>");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, GreaterThanEqual,
"operator>=");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Comma, "operator,");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Parens, "operator()");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseNot, "operator~");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseXor, "operator^");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseOr, "operator|");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalAnd, "operator&&");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalOr, "operator||");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, TimesEqual, "operator*=");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, PlusEqual, "operator+=");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, MinusEqual, "operator-=");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, DivEqual, "operator/=");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ModEqual, "operator%=");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, RshEqual, "operator>>=");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LshEqual, "operator<<=");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseAndEqual,
"operator&=");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseOrEqual,
"operator|=");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseXorEqual,
"operator^=");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VbaseDtor, "`vbase dtor'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecDelDtor,
"`vector deleting dtor'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, DefaultCtorClosure,
"`default ctor closure'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ScalarDelDtor,
"`scalar deleting dtor'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecCtorIter,
"`vector ctor iterator'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecDtorIter,
"`vector dtor iterator'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecVbaseCtorIter,
"`vector vbase ctor iterator'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VdispMap,
"`virtual displacement map'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecCtorIter,
"`eh vector ctor iterator'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecDtorIter,
"`eh vector dtor iterator'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecVbaseCtorIter,
"`eh vector vbase ctor iterator'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, CopyCtorClosure,
"`copy ctor closure'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LocalVftableCtorClosure,
"`local vftable ctor closure'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArrayNew, "operator new[]");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArrayDelete,
"operator delete[]");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorCtorIter,
"`managed vector ctor iterator'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorDtorIter,
"`managed vector dtor iterator'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVectorCopyCtorIter,
"`EH vector copy ctor iterator'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVectorVbaseCopyCtorIter,
"`EH vector vbase copy ctor iterator'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VectorCopyCtorIter,
"`vector copy ctor iterator'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VectorVbaseCopyCtorIter,
"`vector vbase copy constructor iterator'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorVbaseCopyCtorIter,
"`managed vector vbase copy constructor iterator'");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, CoAwait,
"operator co_await");
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Spaceship, "operator<=>");
case IntrinsicFunctionKind::MaxIntrinsic:
case IntrinsicFunctionKind::None:
break;
}
outputTemplateParameters(OB, Flags);
}
void LocalStaticGuardIdentifierNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
if (IsThread)
OB << "`local static thread guard'";
else
OB << "`local static guard'";
if (ScopeIndex > 0)
OB << "{" << ScopeIndex << "}";
}
void ConversionOperatorIdentifierNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
OB << "operator";
outputTemplateParameters(OB, Flags);
OB << " ";
TargetType->output(OB, Flags);
}
void StructorIdentifierNode::output(OutputBuffer &OB, OutputFlags Flags) const {
if (IsDestructor)
OB << "~";
Class->output(OB, Flags);
outputTemplateParameters(OB, Flags);
}
void LiteralOperatorIdentifierNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
OB << "operator \"\"" << Name;
outputTemplateParameters(OB, Flags);
}
void FunctionSignatureNode::outputPre(OutputBuffer &OB,
OutputFlags Flags) const {
if (!(Flags & OF_NoAccessSpecifier)) {
if (FunctionClass & FC_Public)
OB << "public: ";
if (FunctionClass & FC_Protected)
OB << "protected: ";
if (FunctionClass & FC_Private)
OB << "private: ";
}
if (!(Flags & OF_NoMemberType)) {
if (!(FunctionClass & FC_Global)) {
if (FunctionClass & FC_Static)
OB << "static ";
}
if (FunctionClass & FC_Virtual)
OB << "virtual ";
if (FunctionClass & FC_ExternC)
OB << "extern \"C\" ";
}
if (!(Flags & OF_NoReturnType) && ReturnType) {
ReturnType->outputPre(OB, Flags);
OB << " ";
}
if (!(Flags & OF_NoCallingConvention))
outputCallingConvention(OB, CallConvention);
}
void FunctionSignatureNode::outputPost(OutputBuffer &OB,
OutputFlags Flags) const {
if (!(FunctionClass & FC_NoParameterList)) {
OB << "(";
if (Params)
Params->output(OB, Flags);
else
OB << "void";
if (IsVariadic) {
if (OB.back() != '(')
OB << ", ";
OB << "...";
}
OB << ")";
}
if (Quals & Q_Const)
OB << " const";
if (Quals & Q_Volatile)
OB << " volatile";
if (Quals & Q_Restrict)
OB << " __restrict";
if (Quals & Q_Unaligned)
OB << " __unaligned";
if (IsNoexcept)
OB << " noexcept";
if (RefQualifier == FunctionRefQualifier::Reference)
OB << " &";
else if (RefQualifier == FunctionRefQualifier::RValueReference)
OB << " &&";
if (!(Flags & OF_NoReturnType) && ReturnType)
ReturnType->outputPost(OB, Flags);
}
void ThunkSignatureNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
OB << "[thunk]: ";
FunctionSignatureNode::outputPre(OB, Flags);
}
void ThunkSignatureNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {
if (FunctionClass & FC_StaticThisAdjust) {
OB << "`adjustor{" << ThisAdjust.StaticOffset << "}'";
} else if (FunctionClass & FC_VirtualThisAdjust) {
if (FunctionClass & FC_VirtualThisAdjustEx) {
OB << "`vtordispex{" << ThisAdjust.VBPtrOffset << ", "
<< ThisAdjust.VBOffsetOffset << ", " << ThisAdjust.VtordispOffset
<< ", " << ThisAdjust.StaticOffset << "}'";
} else {
OB << "`vtordisp{" << ThisAdjust.VtordispOffset << ", "
<< ThisAdjust.StaticOffset << "}'";
}
}
FunctionSignatureNode::outputPost(OB, Flags);
}
void PointerTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
if (Pointee->kind() == NodeKind::FunctionSignature) {
// If this is a pointer to a function, don't output the calling convention.
// It needs to go inside the parentheses.
const FunctionSignatureNode *Sig =
static_cast<const FunctionSignatureNode *>(Pointee);
Sig->outputPre(OB, OF_NoCallingConvention);
} else
Pointee->outputPre(OB, Flags);
outputSpaceIfNecessary(OB);
if (Quals & Q_Unaligned)
OB << "__unaligned ";
if (Pointee->kind() == NodeKind::ArrayType) {
OB << "(";
} else if (Pointee->kind() == NodeKind::FunctionSignature) {
OB << "(";
const FunctionSignatureNode *Sig =
static_cast<const FunctionSignatureNode *>(Pointee);
outputCallingConvention(OB, Sig->CallConvention);
OB << " ";
}
if (ClassParent) {
ClassParent->output(OB, Flags);
OB << "::";
}
switch (Affinity) {
case PointerAffinity::Pointer:
OB << "*";
break;
case PointerAffinity::Reference:
OB << "&";
break;
case PointerAffinity::RValueReference:
OB << "&&";
break;
default:
assert(false);
}
outputQualifiers(OB, Quals, false, false);
}
void PointerTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {
if (Pointee->kind() == NodeKind::ArrayType ||
Pointee->kind() == NodeKind::FunctionSignature)
OB << ")";
Pointee->outputPost(OB, Flags);
}
void TagTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
if (!(Flags & OF_NoTagSpecifier)) {
switch (Tag) {
OUTPUT_ENUM_CLASS_VALUE(TagKind, Class, "class");
OUTPUT_ENUM_CLASS_VALUE(TagKind, Struct, "struct");
OUTPUT_ENUM_CLASS_VALUE(TagKind, Union, "union");
OUTPUT_ENUM_CLASS_VALUE(TagKind, Enum, "enum");
}
OB << " ";
}
QualifiedName->output(OB, Flags);
outputQualifiers(OB, Quals, true, false);
}
void TagTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {}
void ArrayTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
ElementType->outputPre(OB, Flags);
outputQualifiers(OB, Quals, true, false);
}
void ArrayTypeNode::outputOneDimension(OutputBuffer &OB, OutputFlags Flags,
Node *N) const {
assert(N->kind() == NodeKind::IntegerLiteral);
IntegerLiteralNode *ILN = static_cast<IntegerLiteralNode *>(N);
if (ILN->Value != 0)
ILN->output(OB, Flags);
}
void ArrayTypeNode::outputDimensionsImpl(OutputBuffer &OB,
OutputFlags Flags) const {
if (Dimensions->Count == 0)
return;
outputOneDimension(OB, Flags, Dimensions->Nodes[0]);
for (size_t I = 1; I < Dimensions->Count; ++I) {
OB << "][";
outputOneDimension(OB, Flags, Dimensions->Nodes[I]);
}
}
void ArrayTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {
OB << "[";
outputDimensionsImpl(OB, Flags);
OB << "]";
ElementType->outputPost(OB, Flags);
}
void SymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
Name->output(OB, Flags);
}
void FunctionSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
Signature->outputPre(OB, Flags);
outputSpaceIfNecessary(OB);
Name->output(OB, Flags);
Signature->outputPost(OB, Flags);
}
void VariableSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
const char *AccessSpec = nullptr;
bool IsStatic = true;
switch (SC) {
case StorageClass::PrivateStatic:
AccessSpec = "private";
break;
case StorageClass::PublicStatic:
AccessSpec = "public";
break;
case StorageClass::ProtectedStatic:
AccessSpec = "protected";
break;
default:
IsStatic = false;
break;
}
if (!(Flags & OF_NoAccessSpecifier) && AccessSpec)
OB << AccessSpec << ": ";
if (!(Flags & OF_NoMemberType) && IsStatic)
OB << "static ";
if (!(Flags & OF_NoVariableType) && Type) {
Type->outputPre(OB, Flags);
outputSpaceIfNecessary(OB);
}
Name->output(OB, Flags);
if (!(Flags & OF_NoVariableType) && Type)
Type->outputPost(OB, Flags);
}
void CustomTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
Identifier->output(OB, Flags);
}
void CustomTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {}
void QualifiedNameNode::output(OutputBuffer &OB, OutputFlags Flags) const {
Components->output(OB, Flags, "::");
}
void RttiBaseClassDescriptorNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
OB << "`RTTI Base Class Descriptor at (";
OB << NVOffset << ", " << VBPtrOffset << ", " << VBTableOffset << ", "
<< this->Flags;
OB << ")'";
}
void LocalStaticGuardVariableNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
Name->output(OB, Flags);
}
void VcallThunkIdentifierNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
OB << "`vcall'{" << OffsetInVTable << ", {flat}}";
}
void SpecialTableSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
outputQualifiers(OB, Quals, false, true);
Name->output(OB, Flags);
if (TargetName) {
OB << "{for `";
TargetName->output(OB, Flags);
OB << "'}";
}
}