[PDB] Make the native reader support enumerators.

Previously we would dump the names of enum types, but not their
enumerator values.  This adds support for enumerator values.  In
doing so, we have to introduce a general purpose mechanism for
caching symbol indices of field list members.  Unlike global
types, FieldList members do not have a TypeIndex.  So instead,
we identify them by the pair {TypeIndexOfFieldList, IndexInFieldList}.

llvm-svn: 342415
This commit is contained in:
Zachary Turner 2018-09-17 21:08:11 +00:00
parent 1ff1310572
commit 210ae53b0e
15 changed files with 1402 additions and 258 deletions

View File

@ -29,6 +29,21 @@ public:
virtual void reset() = 0;
};
template <typename ChildType>
class NullEnumerator : public IPDBEnumChildren<ChildType> {
virtual uint32_t getChildCount() const override { return 0; }
virtual std::unique_ptr<ChildType>
getChildAtIndex(uint32_t Index) const override {
assert(false);
return nullptr;
}
virtual std::unique_ptr<ChildType> getNext() override {
assert(false);
return nullptr;
}
virtual void reset() override {}
};
} // end namespace pdb
} // end namespace llvm

View File

@ -0,0 +1,50 @@
//===- NativeSymbolEnumerator.h - info about enumerator values --*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_DEBUGINFO_PDB_NATIVE_NATIVESYMBOLENUMERATOR_H
#define LLVM_DEBUGINFO_PDB_NATIVE_NATIVESYMBOLENUMERATOR_H
#include "llvm/DebugInfo/CodeView/CodeView.h"
#include "llvm/DebugInfo/PDB/Native/NativeRawSymbol.h"
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
namespace llvm {
namespace pdb {
class NativeTypeEnum;
class NativeSymbolEnumerator : public NativeRawSymbol {
public:
NativeSymbolEnumerator(NativeSession &Session, SymIndexId Id,
const NativeTypeEnum &Parent,
codeview::EnumeratorRecord Record);
~NativeSymbolEnumerator() override;
void dump(raw_ostream &OS, int Indent) const override;
SymIndexId getClassParentId() const override;
SymIndexId getLexicalParentId() const override;
std::string getName() const override;
SymIndexId getTypeId() const override;
PDB_DataKind getDataKind() const override;
PDB_LocType getLocationType() const override;
bool isConstType() const override;
bool isVolatileType() const override;
bool isUnalignedType() const override;
Variant getValue() const override;
protected:
const NativeTypeEnum &Parent;
codeview::EnumeratorRecord Record;
};
} // namespace pdb
} // namespace llvm
#endif // LLVM_DEBUGINFO_PDB_NATIVE_NATIVETYPEENUM_H

View File

@ -18,6 +18,8 @@
namespace llvm {
namespace pdb {
class NativeTypeBuiltin;
class NativeTypeEnum : public NativeRawSymbol {
public:
NativeTypeEnum(NativeSession &Session, SymIndexId Id, codeview::TypeIndex TI,
@ -56,6 +58,8 @@ public:
bool isValueUdt() const override;
bool isInterfaceUdt() const override;
const NativeTypeBuiltin &getUnderlyingBuiltinType() const;
protected:
codeview::TypeIndex Index;
codeview::EnumRecord Record;

View File

@ -31,6 +31,8 @@ class SymbolCache {
std::vector<std::unique_ptr<NativeRawSymbol>> Cache;
DenseMap<codeview::TypeIndex, SymIndexId> TypeIndexToSymbolId;
DenseMap<std::pair<codeview::TypeIndex, uint32_t>, SymIndexId>
FieldListMembersToSymbolId;
std::vector<SymIndexId> Compilands;
SymIndexId createSymbolPlaceholder() {
@ -77,6 +79,23 @@ public:
SymIndexId findSymbolByTypeIndex(codeview::TypeIndex TI);
template <typename ConcreteSymbolT, typename... Args>
SymIndexId getOrCreateFieldListMember(codeview::TypeIndex FieldListTI,
uint32_t Index,
Args &&... ConstructorArgs) {
SymIndexId SymId = Cache.size();
std::pair<codeview::TypeIndex, uint32_t> Key{FieldListTI, Index};
auto Result = FieldListMembersToSymbolId.try_emplace(Key, SymId);
if (Result.second) {
auto NewSymbol = llvm::make_unique<ConcreteSymbolT>(
Session, SymId, std::forward<Args>(ConstructorArgs)...);
Cache.push_back(std::move(NewSymbol));
} else {
SymId = Result.first->second;
}
return SymId;
}
std::unique_ptr<PDBSymbolCompiland> getOrCreateCompiland(uint32_t Index);
uint32_t getNumCompilands() const;

View File

@ -336,6 +336,36 @@ enum PDB_VariantType {
struct Variant {
Variant() = default;
explicit Variant(bool V) : Type(PDB_VariantType::Bool) { Value.Bool = V; }
explicit Variant(int8_t V) : Type(PDB_VariantType::Int8) { Value.Int8 = V; }
explicit Variant(int16_t V) : Type(PDB_VariantType::Int16) {
Value.Int16 = V;
}
explicit Variant(int32_t V) : Type(PDB_VariantType::Int32) {
Value.Int32 = V;
}
explicit Variant(int64_t V) : Type(PDB_VariantType::Int64) {
Value.Int64 = V;
}
explicit Variant(float V) : Type(PDB_VariantType::Single) {
Value.Single = V;
}
explicit Variant(double V) : Type(PDB_VariantType::Double) {
Value.Double = V;
}
explicit Variant(uint8_t V) : Type(PDB_VariantType::UInt8) {
Value.UInt8 = V;
}
explicit Variant(uint16_t V) : Type(PDB_VariantType::UInt16) {
Value.UInt16 = V;
}
explicit Variant(uint32_t V) : Type(PDB_VariantType::UInt32) {
Value.UInt32 = V;
}
explicit Variant(uint64_t V) : Type(PDB_VariantType::UInt64) {
Value.UInt64 = V;
}
Variant(const Variant &Other) {
*this = Other;
}

View File

@ -51,6 +51,7 @@ add_pdb_impl_folder(Native
Native/NativeEnumTypes.cpp
Native/NativeExeSymbol.cpp
Native/NativeRawSymbol.cpp
Native/NativeSymbolEnumerator.cpp
Native/NativeTypeBuiltin.cpp
Native/NativeTypeEnum.cpp
Native/NativeTypePointer.cpp

View File

@ -141,6 +141,15 @@ PrivateGetDIAValue(IDiaSymbol *Symbol,
return IdResult;
}
template <typename PrintType, typename ArgType>
void DumpDIAValueAs(llvm::raw_ostream &OS, int Indent, StringRef Name,
IDiaSymbol *Symbol,
HRESULT (__stdcall IDiaSymbol::*Method)(ArgType *)) {
ArgType Value;
if (S_OK == (Symbol->*Method)(&Value))
dumpSymbolField(OS, Name, static_cast<PrintType>(Value), Indent);
}
template <typename ArgType>
void DumpDIAValue(llvm::raw_ostream &OS, int Indent, StringRef Name,
IDiaSymbol *Symbol,
@ -194,6 +203,10 @@ DIARawSymbol::DIARawSymbol(const DIASession &PDBSession,
DumpDIAValue(Stream, Indent, StringRef{#Method}, Symbol, \
&IDiaSymbol::get_##Method);
#define RAW_METHOD_DUMP_AS(Stream, Method, Type) \
DumpDIAValueAs<Type>(Stream, Indent, StringRef{#Method}, Symbol, \
&IDiaSymbol::get_##Method);
void DIARawSymbol::dump(raw_ostream &OS, int Indent) const {
RAW_METHOD_DUMP(OS, symIndexId);
RAW_METHOD_DUMP(OS, symTag);
@ -267,12 +280,12 @@ void DIARawSymbol::dump(raw_ostream &OS, int Indent) const {
RAW_METHOD_DUMP(OS, virtualBaseDispIndex);
RAW_METHOD_DUMP(OS, virtualBaseOffset);
RAW_METHOD_DUMP(OS, virtualTableShapeId);
RAW_METHOD_DUMP(OS, dataKind);
RAW_METHOD_DUMP_AS(OS, dataKind, PDB_DataKind);
RAW_METHOD_DUMP(OS, guid);
RAW_METHOD_DUMP(OS, offset);
RAW_METHOD_DUMP(OS, thisAdjust);
RAW_METHOD_DUMP(OS, virtualBasePointerOffset);
RAW_METHOD_DUMP(OS, locationType);
RAW_METHOD_DUMP_AS(OS, locationType, PDB_LocType);
RAW_METHOD_DUMP(OS, machineType);
RAW_METHOD_DUMP(OS, thunkOrdinal);
RAW_METHOD_DUMP(OS, length);

View File

@ -0,0 +1,116 @@
//===- NativeSymbolEnumerator.cpp - info about enumerators ------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/PDB/Native/NativeSymbolEnumerator.h"
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h"
#include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h"
using namespace llvm;
using namespace llvm::codeview;
using namespace llvm::pdb;
NativeSymbolEnumerator::NativeSymbolEnumerator(
NativeSession &Session, SymIndexId Id, const NativeTypeEnum &Parent,
codeview::EnumeratorRecord Record)
: NativeRawSymbol(Session, PDB_SymType::Data, Id), Parent(Parent),
Record(std::move(Record)) {}
NativeSymbolEnumerator::~NativeSymbolEnumerator() {}
void NativeSymbolEnumerator::dump(raw_ostream &OS, int Indent) const {
NativeRawSymbol::dump(OS, Indent);
dumpSymbolField(OS, "classParentId", getClassParentId(), Indent);
dumpSymbolField(OS, "lexicalParentId", getLexicalParentId(), Indent);
dumpSymbolField(OS, "name", getName(), Indent);
dumpSymbolField(OS, "typeId", getTypeId(), Indent);
dumpSymbolField(OS, "dataKind", getDataKind(), Indent);
dumpSymbolField(OS, "locationType", getLocationType(), Indent);
dumpSymbolField(OS, "constType", isConstType(), Indent);
dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent);
dumpSymbolField(OS, "volatileType", isVolatileType(), Indent);
dumpSymbolField(OS, "value", getValue(), Indent);
}
SymIndexId NativeSymbolEnumerator::getClassParentId() const {
return Parent.getSymIndexId();
}
SymIndexId NativeSymbolEnumerator::getLexicalParentId() const { return 0; }
std::string NativeSymbolEnumerator::getName() const { return Record.Name; }
SymIndexId NativeSymbolEnumerator::getTypeId() const {
return Parent.getTypeId();
}
PDB_DataKind NativeSymbolEnumerator::getDataKind() const {
return PDB_DataKind::Constant;
}
PDB_LocType NativeSymbolEnumerator::getLocationType() const {
return PDB_LocType::Constant;
}
bool NativeSymbolEnumerator::isConstType() const { return false; }
bool NativeSymbolEnumerator::isVolatileType() const { return false; }
bool NativeSymbolEnumerator::isUnalignedType() const { return false; }
Variant NativeSymbolEnumerator::getValue() const {
const NativeTypeBuiltin &BT = Parent.getUnderlyingBuiltinType();
switch (BT.getBuiltinType()) {
case PDB_BuiltinType::Int:
case PDB_BuiltinType::Long:
case PDB_BuiltinType::Char: {
assert(Record.Value.isSignedIntN(BT.getLength() * 8));
int64_t N = Record.Value.getSExtValue();
switch (BT.getLength()) {
case 1:
return Variant{static_cast<int8_t>(N)};
case 2:
return Variant{static_cast<int16_t>(N)};
case 4:
return Variant{static_cast<int32_t>(N)};
case 8:
return Variant{static_cast<int64_t>(N)};
}
break;
}
case PDB_BuiltinType::UInt:
case PDB_BuiltinType::ULong: {
assert(Record.Value.isIntN(BT.getLength() * 8));
uint64_t U = Record.Value.getZExtValue();
switch (BT.getLength()) {
case 1:
return Variant{static_cast<uint8_t>(U)};
case 2:
return Variant{static_cast<uint16_t>(U)};
case 4:
return Variant{static_cast<uint32_t>(U)};
case 8:
return Variant{static_cast<uint64_t>(U)};
}
break;
}
case PDB_BuiltinType::Bool: {
assert(Record.Value.isIntN(BT.getLength() * 8));
uint64_t U = Record.Value.getZExtValue();
return Variant{static_cast<bool>(U)};
}
default:
assert(false && "Invalid enumeration type");
break;
}
return Variant{Record.Value.getSExtValue()};
}

View File

@ -12,7 +12,11 @@
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h"
#include "llvm/DebugInfo/PDB/Native/NativeSymbolEnumerator.h"
#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/SymbolCache.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h"
#include "llvm/Support/FormatVariadic.h"
@ -23,6 +27,95 @@ using namespace llvm;
using namespace llvm::codeview;
using namespace llvm::pdb;
namespace {
// Yea, this is a pretty terrible class name. But if we have an enum:
//
// enum Foo {
// A,
// B
// };
//
// then A and B are the "enumerators" of the "enum" Foo. And we need
// to enumerate them.
class NativeEnumEnumEnumerators : public IPDBEnumSymbols, TypeVisitorCallbacks {
public:
NativeEnumEnumEnumerators(NativeSession &Session,
const NativeTypeEnum &ClassParent,
const codeview::EnumRecord &CVEnum);
uint32_t getChildCount() const override;
std::unique_ptr<PDBSymbol> getChildAtIndex(uint32_t Index) const override;
std::unique_ptr<PDBSymbol> getNext() override;
void reset() override;
private:
Error visitKnownMember(CVMemberRecord &CVM,
EnumeratorRecord &Record) override;
Error visitKnownMember(CVMemberRecord &CVM,
ListContinuationRecord &Record) override;
NativeSession &Session;
const NativeTypeEnum &ClassParent;
const codeview::EnumRecord &CVEnum;
std::vector<EnumeratorRecord> Enumerators;
Optional<TypeIndex> ContinuationIndex;
uint32_t Index = 0;
};
} // namespace
NativeEnumEnumEnumerators::NativeEnumEnumEnumerators(
NativeSession &Session, const NativeTypeEnum &ClassParent,
const codeview::EnumRecord &CVEnum)
: Session(Session), ClassParent(ClassParent), CVEnum(CVEnum) {
TpiStream &Tpi = cantFail(Session.getPDBFile().getPDBTpiStream());
LazyRandomTypeCollection &Types = Tpi.typeCollection();
ContinuationIndex = CVEnum.FieldList;
while (ContinuationIndex) {
CVType FieldList = Types.getType(*ContinuationIndex);
assert(FieldList.kind() == LF_FIELDLIST);
ContinuationIndex.reset();
cantFail(visitMemberRecordStream(FieldList.data(), *this));
}
}
Error NativeEnumEnumEnumerators::visitKnownMember(CVMemberRecord &CVM,
EnumeratorRecord &Record) {
Enumerators.push_back(Record);
return Error::success();
}
Error NativeEnumEnumEnumerators::visitKnownMember(
CVMemberRecord &CVM, ListContinuationRecord &Record) {
ContinuationIndex = Record.ContinuationIndex;
return Error::success();
}
uint32_t NativeEnumEnumEnumerators::getChildCount() const {
return Enumerators.size();
}
std::unique_ptr<PDBSymbol>
NativeEnumEnumEnumerators::getChildAtIndex(uint32_t Index) const {
if (Index >= getChildCount())
return nullptr;
SymIndexId Id =
Session.getSymbolCache()
.getOrCreateFieldListMember<NativeSymbolEnumerator>(
CVEnum.FieldList, Index, ClassParent, Enumerators[Index]);
return Session.getSymbolCache().getSymbolById(Id);
}
std::unique_ptr<PDBSymbol> NativeEnumEnumEnumerators::getNext() {
if (Index >= getChildCount())
return nullptr;
return getChildAtIndex(Index++);
}
void NativeEnumEnumEnumerators::reset() { Index = 0; }
NativeTypeEnum::NativeTypeEnum(NativeSession &Session, SymIndexId Id,
TypeIndex Index, EnumRecord Record)
: NativeRawSymbol(Session, PDB_SymType::Enum, Id), Index(Index),
@ -67,14 +160,20 @@ void NativeTypeEnum::dump(raw_ostream &OS, int Indent) const {
std::unique_ptr<IPDBEnumSymbols>
NativeTypeEnum::findChildren(PDB_SymType Type) const {
switch (Type) {
case PDB_SymType::Data: {
// TODO(amccarth) : Provide an actual implementation.
return nullptr;
}
default:
return nullptr;
if (Type != PDB_SymType::Data)
return llvm::make_unique<NullEnumerator<PDBSymbol>>();
const NativeTypeEnum *ClassParent = nullptr;
if (!Modifiers)
ClassParent = this;
else {
NativeRawSymbol &NRS =
Session.getSymbolCache().getNativeSymbolById(getUnmodifiedTypeId());
assert(NRS.getSymTag() == PDB_SymType::Enum);
ClassParent = static_cast<NativeTypeEnum *>(&NRS);
}
return llvm::make_unique<NativeEnumEnumEnumerators>(Session, *ClassParent,
Record);
}
PDB_SymType NativeTypeEnum::getSymTag() const { return PDB_SymType::Enum; }
@ -86,8 +185,9 @@ PDB_BuiltinType NativeTypeEnum::getBuiltinType() const {
// This indicates a corrupt record.
if (!Underlying.isSimple() ||
Underlying.getSimpleMode() != SimpleTypeMode::Direct)
Underlying.getSimpleMode() != SimpleTypeMode::Direct) {
return PDB_BuiltinType::None;
}
switch (Underlying.getSimpleKind()) {
case SimpleTypeKind::Boolean128:
@ -98,6 +198,7 @@ PDB_BuiltinType NativeTypeEnum::getBuiltinType() const {
return PDB_BuiltinType::Bool;
case SimpleTypeKind::NarrowCharacter:
case SimpleTypeKind::UnsignedCharacter:
case SimpleTypeKind::SignedCharacter:
return PDB_BuiltinType::Char;
case SimpleTypeKind::WideCharacter:
return PDB_BuiltinType::WCharT;
@ -149,6 +250,7 @@ PDB_BuiltinType NativeTypeEnum::getBuiltinType() const {
SymIndexId NativeTypeEnum::getUnmodifiedTypeId() const {
if (!Modifiers)
return 0;
return Session.getSymbolCache().findSymbolByTypeIndex(
Modifiers->ModifiedType);
}
@ -235,3 +337,8 @@ bool NativeTypeEnum::isUnalignedType() const {
return ((Modifiers->getModifiers() & ModifierOptions::Unaligned) !=
ModifierOptions::None);
}
const NativeTypeBuiltin &NativeTypeEnum::getUnderlyingBuiltinType() const {
return Session.getSymbolCache().getNativeSymbolById<NativeTypeBuiltin>(
getTypeId());
}

View File

@ -26,14 +26,17 @@ static const struct BuiltinTypeEntry {
PDB_BuiltinType Type;
uint32_t Size;
} BuiltinTypes[] = {
{codeview::SimpleTypeKind::Int16Short, PDB_BuiltinType::Int, 2},
{codeview::SimpleTypeKind::UInt16Short, PDB_BuiltinType::UInt, 2},
{codeview::SimpleTypeKind::Int32, PDB_BuiltinType::Int, 4},
{codeview::SimpleTypeKind::UInt32, PDB_BuiltinType::UInt, 4},
{codeview::SimpleTypeKind::Int32Long, PDB_BuiltinType::Int, 4},
{codeview::SimpleTypeKind::UInt32Long, PDB_BuiltinType::UInt, 4},
{codeview::SimpleTypeKind::Int64Quad, PDB_BuiltinType::Int, 8},
{codeview::SimpleTypeKind::UInt64Quad, PDB_BuiltinType::UInt, 8},
{codeview::SimpleTypeKind::NarrowCharacter, PDB_BuiltinType::Char, 1},
{codeview::SimpleTypeKind::SignedCharacter, PDB_BuiltinType::Char, 1},
{codeview::SimpleTypeKind::UnsignedCharacter, PDB_BuiltinType::UInt, 1},
{codeview::SimpleTypeKind::UInt16Short, PDB_BuiltinType::UInt, 2},
{codeview::SimpleTypeKind::Boolean8, PDB_BuiltinType::Bool, 1}
// This table can be grown as necessary, but these are the only types we've
// needed so far.
@ -169,6 +172,8 @@ SymIndexId SymbolCache::findSymbolByTypeIndex(codeview::TypeIndex Index) {
std::unique_ptr<PDBSymbol>
SymbolCache::getSymbolById(SymIndexId SymbolId) const {
assert(SymbolId < Cache.size());
// Id 0 is reserved.
if (SymbolId == 0)
return nullptr;

View File

@ -293,7 +293,7 @@ raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, const Variant &Value) {
OS << Value.Value.Single;
break;
case PDB_VariantType::UInt16:
OS << Value.Value.Double;
OS << Value.Value.UInt16;
break;
case PDB_VariantType::UInt32:
OS << Value.Value.UInt32;

View File

@ -0,0 +1,110 @@
// Build with "cl.exe /Zi /GR- /GX- every-enum.cpp /link /debug /nodefaultlib /incremental:no /entry:main"
#include <stdint.h>
// clang-format off
void *__purecall = 0;
void __cdecl operator delete(void *,unsigned int) {}
void __cdecl operator delete(void *,unsigned __int64) {}
enum I8 : int8_t {
I8A = INT8_MIN,
I8B = 0,
I8C = INT8_MAX
};
enum I16 : int16_t {
I16A = INT16_MIN,
I16B = 0,
I16C = INT16_MAX,
};
enum I32 : int32_t {
I32A = INT32_MIN,
I32B = 0,
I32C = INT32_MAX,
};
enum I64 : int64_t {
I64A = INT64_MIN,
I64B = 0,
I64C = INT64_MAX,
};
enum U8 : uint8_t {
U8A = 0,
U8B = UINT8_MAX
};
enum U16 : uint16_t {
U16A = 0,
U16B = UINT16_MAX,
};
enum U32 : uint32_t {
U32A = 0,
U32B = UINT32_MAX,
};
enum U64 : uint64_t {
U64A = 0,
U64B = UINT64_MAX,
};
enum Char16 : char16_t {
C16A = u'a',
C16B = u'b',
};
enum Char32 : char32_t {
C32A = U'a',
C32B = U'b',
};
enum WChar : wchar_t {
WCA = L'a',
WCB = L'b',
};
enum Bool : bool {
BA = true,
BB = false
};
enum class EC {
A = 1,
B = 2
};
struct Struct {
enum Nested {
A = 1,
B = 2
};
};
template<typename T> void f(T t) {}
int main(int argc, char **argv) {
f(I8A);
f(I16A);
f(I32A);
f(I64A);
f(U8A);
f(U16A);
f(U32A);
f(U64A);
f(C16A);
f(C32A);
f(WCA);
f(BA);
f(EC::A);
f(Struct::A);
f<const volatile EC>(EC::A);
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -970,6 +970,14 @@ static void dumpDia(StringRef Path) {
while (auto Child = Children->getNext()) {
outs() << "{";
Child->defaultDump(outs(), 2);
if (auto Enum = dyn_cast<PDBSymbolTypeEnum>(Child.get())) {
auto Enumerators = Enum->findAllChildren<PDBSymbolData>();
while (auto Enumerator = Enumerators->getNext()) {
outs() << " {";
Enumerator->defaultDump(outs(), 4);
outs() << "\n }\n";
}
}
outs() << "\n}\n";
}
}