mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 10:42:39 +01:00
Enable llvm-pdbutil to list enumerations using native PDB reader
This extends the native reader to enable llvm-pdbutil to list the enums in a PDB and it includes a simple test. It does not yet list the values in the enumerations, which requires an actual implementation of NativeEnumSymbol::FindChildren. To exercise this code, use a command like: llvm-pdbutil pretty -native -enums foo.pdb Differential Revision: https://reviews.llvm.org/D35738 llvm-svn: 310144
This commit is contained in:
parent
23bd4a9364
commit
ee6fb7079a
60
include/llvm/DebugInfo/PDB/Native/NativeEnumSymbol.h
Normal file
60
include/llvm/DebugInfo/PDB/Native/NativeEnumSymbol.h
Normal file
@ -0,0 +1,60 @@
|
||||
//===- NativeEnumSymbol.h - info about enum type ----------------*- 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_NATIVEENUMSYMBOL_H
|
||||
#define LLVM_DEBUGINFO_PDB_NATIVE_NATIVEENUMSYMBOL_H
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeRawSymbol.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace pdb {
|
||||
|
||||
class NativeEnumSymbol : public NativeRawSymbol,
|
||||
public codeview::TypeVisitorCallbacks {
|
||||
public:
|
||||
NativeEnumSymbol(NativeSession &Session, SymIndexId Id,
|
||||
const codeview::CVType &CV);
|
||||
~NativeEnumSymbol() override;
|
||||
|
||||
std::unique_ptr<NativeRawSymbol> clone() const override;
|
||||
|
||||
std::unique_ptr<IPDBEnumSymbols>
|
||||
findChildren(PDB_SymType Type) const override;
|
||||
|
||||
Error visitKnownRecord(codeview::CVType &CVR,
|
||||
codeview::EnumRecord &Record) override;
|
||||
Error visitKnownMember(codeview::CVMemberRecord &CVM,
|
||||
codeview::EnumeratorRecord &Record) override;
|
||||
|
||||
PDB_SymType getSymTag() const override;
|
||||
uint32_t getClassParentId() const override;
|
||||
uint32_t getUnmodifiedTypeId() const override;
|
||||
bool hasConstructor() const override;
|
||||
bool hasAssignmentOperator() const override;
|
||||
bool hasCastOperator() const override;
|
||||
uint64_t getLength() const override;
|
||||
std::string getName() const override;
|
||||
bool isNested() const override;
|
||||
bool hasOverloadedOperator() const override;
|
||||
bool isPacked() const override;
|
||||
bool isScoped() const override;
|
||||
uint32_t getTypeId() const override;
|
||||
|
||||
protected:
|
||||
codeview::CVType CV;
|
||||
codeview::EnumRecord Record;
|
||||
};
|
||||
|
||||
} // namespace pdb
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_PDB_NATIVE_NATIVEENUMSYMBOL_H
|
51
include/llvm/DebugInfo/PDB/Native/NativeEnumTypes.h
Normal file
51
include/llvm/DebugInfo/PDB/Native/NativeEnumTypes.h
Normal file
@ -0,0 +1,51 @@
|
||||
//==- NativeEnumTypes.h - Native Type Enumerator impl ------------*- 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_NATIVEENUMTYPES_H
|
||||
#define LLVM_DEBUGINFO_PDB_NATIVE_NATIVEENUMTYPES_H
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbol.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
namespace pdb {
|
||||
|
||||
class NativeSession;
|
||||
|
||||
class NativeEnumTypes : public IPDBEnumChildren<PDBSymbol> {
|
||||
public:
|
||||
NativeEnumTypes(NativeSession &Session,
|
||||
codeview::LazyRandomTypeCollection &TypeCollection,
|
||||
codeview::TypeLeafKind Kind);
|
||||
|
||||
uint32_t getChildCount() const override;
|
||||
std::unique_ptr<PDBSymbol> getChildAtIndex(uint32_t Index) const override;
|
||||
std::unique_ptr<PDBSymbol> getNext() override;
|
||||
void reset() override;
|
||||
NativeEnumTypes *clone() const override;
|
||||
|
||||
private:
|
||||
NativeEnumTypes(NativeSession &Session,
|
||||
const std::vector<codeview::TypeIndex> &Matches,
|
||||
codeview::TypeLeafKind Kind);
|
||||
|
||||
std::vector<codeview::TypeIndex> Matches;
|
||||
uint32_t Index;
|
||||
NativeSession &Session;
|
||||
codeview::TypeLeafKind Kind;
|
||||
};
|
||||
|
||||
} // namespace pdb
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
@ -39,6 +39,12 @@ public:
|
||||
std::unique_ptr<PDBSymbolCompiland>
|
||||
createCompilandSymbol(DbiModuleDescriptor MI);
|
||||
|
||||
std::unique_ptr<PDBSymbolTypeEnum>
|
||||
createEnumSymbol(codeview::TypeIndex Index);
|
||||
|
||||
std::unique_ptr<IPDBEnumSymbols>
|
||||
createTypeEnumerator(codeview::TypeLeafKind Kind);
|
||||
|
||||
SymIndexId findSymbolByTypeIndex(codeview::TypeIndex TI);
|
||||
|
||||
uint64_t getLoadAddress() const override;
|
||||
|
@ -7,8 +7,8 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_DEBUGINFO_PDB_IPDBSYMBOL_H
|
||||
#define LLVM_DEBUGINFO_PDB_IPDBSYMBOL_H
|
||||
#ifndef LLVM_DEBUGINFO_PDB_PDBSYMBOL_H
|
||||
#define LLVM_DEBUGINFO_PDB_PDBSYMBOL_H
|
||||
|
||||
#include "ConcreteSymbolEnumerator.h"
|
||||
#include "IPDBRawSymbol.h"
|
||||
|
@ -44,6 +44,8 @@ add_pdb_impl_folder(Native
|
||||
Native/NativeBuiltinSymbol.cpp
|
||||
Native/NativeCompilandSymbol.cpp
|
||||
Native/NativeEnumModules.cpp
|
||||
Native/NativeEnumSymbol.cpp
|
||||
Native/NativeEnumTypes.cpp
|
||||
Native/NativeExeSymbol.cpp
|
||||
Native/NativeRawSymbol.cpp
|
||||
Native/NamedStreamMap.cpp
|
||||
|
108
lib/DebugInfo/PDB/Native/NativeEnumSymbol.cpp
Normal file
108
lib/DebugInfo/PDB/Native/NativeEnumSymbol.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
//===- NativeEnumSymbol.cpp - info about enum type --------------*- 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/NativeEnumSymbol.h"
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::pdb;
|
||||
|
||||
NativeEnumSymbol::NativeEnumSymbol(NativeSession &Session, SymIndexId Id,
|
||||
const codeview::CVType &CVT)
|
||||
: NativeRawSymbol(Session, Id), CV(CVT),
|
||||
Record(codeview::TypeRecordKind::Enum) {
|
||||
assert(CV.kind() == codeview::TypeLeafKind::LF_ENUM);
|
||||
cantFail(visitTypeRecord(CV, *this));
|
||||
}
|
||||
|
||||
NativeEnumSymbol::~NativeEnumSymbol() {}
|
||||
|
||||
std::unique_ptr<NativeRawSymbol> NativeEnumSymbol::clone() const {
|
||||
return llvm::make_unique<NativeEnumSymbol>(Session, SymbolId, CV);
|
||||
}
|
||||
|
||||
std::unique_ptr<IPDBEnumSymbols>
|
||||
NativeEnumSymbol::findChildren(PDB_SymType Type) const {
|
||||
switch (Type) {
|
||||
case PDB_SymType::Data: {
|
||||
// TODO(amccarth): Provide an actual implementation.
|
||||
return nullptr;
|
||||
}
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Error NativeEnumSymbol::visitKnownRecord(codeview::CVType &CVR,
|
||||
codeview::EnumRecord &ER) {
|
||||
Record = ER;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error NativeEnumSymbol::visitKnownMember(codeview::CVMemberRecord &CVM,
|
||||
codeview::EnumeratorRecord &R) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
PDB_SymType NativeEnumSymbol::getSymTag() const { return PDB_SymType::Enum; }
|
||||
|
||||
uint32_t NativeEnumSymbol::getClassParentId() const { return 0xFFFFFFFF; }
|
||||
|
||||
uint32_t NativeEnumSymbol::getUnmodifiedTypeId() const { return 0; }
|
||||
|
||||
bool NativeEnumSymbol::hasConstructor() const {
|
||||
return bool(Record.getOptions() &
|
||||
codeview::ClassOptions::HasConstructorOrDestructor);
|
||||
}
|
||||
|
||||
bool NativeEnumSymbol::hasAssignmentOperator() const {
|
||||
return bool(Record.getOptions() &
|
||||
codeview::ClassOptions::HasOverloadedAssignmentOperator);
|
||||
}
|
||||
|
||||
bool NativeEnumSymbol::hasCastOperator() const {
|
||||
return bool(Record.getOptions() &
|
||||
codeview::ClassOptions::HasConversionOperator);
|
||||
}
|
||||
|
||||
uint64_t NativeEnumSymbol::getLength() const {
|
||||
const auto Id = Session.findSymbolByTypeIndex(Record.getUnderlyingType());
|
||||
const auto UnderlyingType =
|
||||
Session.getConcreteSymbolById<PDBSymbolTypeBuiltin>(Id);
|
||||
return UnderlyingType ? UnderlyingType->getLength() : 0;
|
||||
}
|
||||
|
||||
std::string NativeEnumSymbol::getName() const { return Record.getName(); }
|
||||
|
||||
bool NativeEnumSymbol::isNested() const {
|
||||
return bool(Record.getOptions() & codeview::ClassOptions::Nested);
|
||||
}
|
||||
|
||||
bool NativeEnumSymbol::hasOverloadedOperator() const {
|
||||
return bool(Record.getOptions() &
|
||||
codeview::ClassOptions::HasOverloadedOperator);
|
||||
}
|
||||
|
||||
bool NativeEnumSymbol::isPacked() const {
|
||||
return bool(Record.getOptions() & codeview::ClassOptions::Packed);
|
||||
}
|
||||
|
||||
bool NativeEnumSymbol::isScoped() const {
|
||||
return bool(Record.getOptions() & codeview::ClassOptions::Scoped);
|
||||
}
|
||||
|
||||
uint32_t NativeEnumSymbol::getTypeId() const {
|
||||
return Session.findSymbolByTypeIndex(Record.getUnderlyingType());
|
||||
}
|
59
lib/DebugInfo/PDB/Native/NativeEnumTypes.cpp
Normal file
59
lib/DebugInfo/PDB/Native/NativeEnumTypes.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
//==- NativeEnumTypes.cpp - Native Type Enumerator impl ----------*- 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/NativeEnumTypes.h"
|
||||
|
||||
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeEnumSymbol.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbol.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace pdb {
|
||||
|
||||
NativeEnumTypes::NativeEnumTypes(NativeSession &PDBSession,
|
||||
codeview::LazyRandomTypeCollection &Types,
|
||||
codeview::TypeLeafKind Kind)
|
||||
: Matches(), Index(0), Session(PDBSession), Kind(Kind) {
|
||||
for (auto Index = Types.getFirst(); Index;
|
||||
Index = Types.getNext(Index.getValue())) {
|
||||
if (Types.getType(Index.getValue()).kind() == Kind)
|
||||
Matches.push_back(Index.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
NativeEnumTypes::NativeEnumTypes(
|
||||
NativeSession &PDBSession, const std::vector<codeview::TypeIndex> &Matches,
|
||||
codeview::TypeLeafKind Kind)
|
||||
: Matches(Matches), Index(0), Session(PDBSession), Kind(Kind) {}
|
||||
|
||||
uint32_t NativeEnumTypes::getChildCount() const {
|
||||
return static_cast<uint32_t>(Matches.size());
|
||||
}
|
||||
|
||||
std::unique_ptr<PDBSymbol>
|
||||
NativeEnumTypes::getChildAtIndex(uint32_t Index) const {
|
||||
if (Index < Matches.size())
|
||||
return Session.createEnumSymbol(Matches[Index]);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<PDBSymbol> NativeEnumTypes::getNext() {
|
||||
return getChildAtIndex(Index++);
|
||||
}
|
||||
|
||||
void NativeEnumTypes::reset() { Index = 0; }
|
||||
|
||||
NativeEnumTypes *NativeEnumTypes::clone() const {
|
||||
return new NativeEnumTypes(Session, Matches, Kind);
|
||||
}
|
||||
|
||||
} // namespace pdb
|
||||
} // namespace llvm
|
@ -13,7 +13,9 @@
|
||||
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeEnumModules.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace pdb {
|
||||
@ -38,6 +40,8 @@ NativeExeSymbol::findChildren(PDB_SymType Type) const {
|
||||
consumeError(Dbi.takeError());
|
||||
break;
|
||||
}
|
||||
case PDB_SymType::Enum:
|
||||
return Session.createTypeEnumerator(codeview::LF_ENUM);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -16,11 +16,15 @@
|
||||
#include "llvm/DebugInfo/PDB/IPDBSourceFile.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeBuiltinSymbol.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeEnumSymbol.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeExeSymbol.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/RawError.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/BinaryByteStream.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
@ -28,6 +32,7 @@
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
@ -102,6 +107,25 @@ NativeSession::createCompilandSymbol(DbiModuleDescriptor MI) {
|
||||
*this, std::unique_ptr<IPDBRawSymbol>(SymbolCache[Id]->clone()));
|
||||
}
|
||||
|
||||
std::unique_ptr<PDBSymbolTypeEnum>
|
||||
NativeSession::createEnumSymbol(codeview::TypeIndex Index) {
|
||||
const auto Id = findSymbolByTypeIndex(Index);
|
||||
return llvm::make_unique<PDBSymbolTypeEnum>(
|
||||
*this, std::unique_ptr<IPDBRawSymbol>(SymbolCache[Id]->clone()));
|
||||
}
|
||||
|
||||
std::unique_ptr<IPDBEnumSymbols>
|
||||
NativeSession::createTypeEnumerator(codeview::TypeLeafKind Kind) {
|
||||
auto Tpi = Pdb->getPDBTpiStream();
|
||||
if (!Tpi) {
|
||||
consumeError(Tpi.takeError());
|
||||
return nullptr;
|
||||
}
|
||||
auto &Types = Tpi->typeCollection();
|
||||
return std::unique_ptr<IPDBEnumSymbols>(
|
||||
new NativeEnumTypes(*this, Types, codeview::LF_ENUM));
|
||||
}
|
||||
|
||||
SymIndexId NativeSession::findSymbolByTypeIndex(codeview::TypeIndex Index) {
|
||||
// First see if it's already in our cache.
|
||||
const auto Entry = TypeIndexToSymbolId.find(Index);
|
||||
@ -129,9 +153,20 @@ SymIndexId NativeSession::findSymbolByTypeIndex(codeview::TypeIndex Index) {
|
||||
return Id;
|
||||
}
|
||||
|
||||
// TODO: Look up PDB type by type index
|
||||
|
||||
return 0;
|
||||
// We need to instantiate and cache the desired type symbol.
|
||||
auto Tpi = Pdb->getPDBTpiStream();
|
||||
if (!Tpi) {
|
||||
consumeError(Tpi.takeError());
|
||||
return 0;
|
||||
}
|
||||
auto &Types = Tpi->typeCollection();
|
||||
const auto &I = Types.getType(Index);
|
||||
const auto Id = static_cast<SymIndexId>(SymbolCache.size());
|
||||
// TODO(amccarth): Make this handle all types, not just LF_ENUMs.
|
||||
assert(I.kind() == codeview::LF_ENUM);
|
||||
SymbolCache.emplace_back(llvm::make_unique<NativeEnumSymbol>(*this, Id, I));
|
||||
TypeIndexToSymbolId[Index] = Id;
|
||||
return Id;
|
||||
}
|
||||
|
||||
uint64_t NativeSession::getLoadAddress() const { return 0; }
|
||||
|
6
test/DebugInfo/PDB/Native/pdb-native-enums.test
Normal file
6
test/DebugInfo/PDB/Native/pdb-native-enums.test
Normal file
@ -0,0 +1,6 @@
|
||||
; Test that the native PDB reader can enumerate the enum types.
|
||||
; RUN: llvm-pdbutil pretty -native -enums %p/../Inputs/every-type.pdb \
|
||||
; RUN: | FileCheck -check-prefix=ENUMS %s
|
||||
|
||||
ENUMS: enum FooClass::NestedEnum {
|
||||
ENUMS-NEXT: }
|
@ -26,25 +26,29 @@ void EnumDumper::start(const PDBSymbolTypeEnum &Symbol) {
|
||||
WithColor(Printer, PDB_ColorItem::Keyword).get() << "enum ";
|
||||
WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
|
||||
if (!opts::pretty::NoEnumDefs) {
|
||||
auto BuiltinType = Symbol.getUnderlyingType();
|
||||
if (BuiltinType->getBuiltinType() != PDB_BuiltinType::Int ||
|
||||
BuiltinType->getLength() != 4) {
|
||||
auto UnderlyingType = Symbol.getUnderlyingType();
|
||||
if (!UnderlyingType)
|
||||
return;
|
||||
if (UnderlyingType->getBuiltinType() != PDB_BuiltinType::Int ||
|
||||
UnderlyingType->getLength() != 4) {
|
||||
Printer << " : ";
|
||||
BuiltinDumper Dumper(Printer);
|
||||
Dumper.start(*BuiltinType);
|
||||
Dumper.start(*UnderlyingType);
|
||||
}
|
||||
auto EnumValues = Symbol.findAllChildren<PDBSymbolData>();
|
||||
Printer << " {";
|
||||
Printer.Indent();
|
||||
auto EnumValues = Symbol.findAllChildren<PDBSymbolData>();
|
||||
while (auto EnumValue = EnumValues->getNext()) {
|
||||
if (EnumValue->getDataKind() != PDB_DataKind::Constant)
|
||||
continue;
|
||||
Printer.NewLine();
|
||||
WithColor(Printer, PDB_ColorItem::Identifier).get()
|
||||
<< EnumValue->getName();
|
||||
Printer << " = ";
|
||||
WithColor(Printer, PDB_ColorItem::LiteralValue).get()
|
||||
<< EnumValue->getValue();
|
||||
if (EnumValues && EnumValues->getChildCount() > 0) {
|
||||
while (auto EnumValue = EnumValues->getNext()) {
|
||||
if (EnumValue->getDataKind() != PDB_DataKind::Constant)
|
||||
continue;
|
||||
Printer.NewLine();
|
||||
WithColor(Printer, PDB_ColorItem::Identifier).get()
|
||||
<< EnumValue->getName();
|
||||
Printer << " = ";
|
||||
WithColor(Printer, PDB_ColorItem::LiteralValue).get()
|
||||
<< EnumValue->getValue();
|
||||
}
|
||||
}
|
||||
Printer.Unindent();
|
||||
Printer.NewLine();
|
||||
|
Loading…
Reference in New Issue
Block a user