1//===- NativeTypeEnum.cpp - info about enum type ----------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h"
10
11#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
12#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
13#include "llvm/DebugInfo/CodeView/TypeRecord.h"
14#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
15#include "llvm/DebugInfo/PDB/Native/NativeSymbolEnumerator.h"
16#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h"
17#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
18#include "llvm/DebugInfo/PDB/Native/SymbolCache.h"
19#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
20#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h"
21
22#include <cassert>
23#include <optional>
24
25using namespace llvm;
26using namespace llvm::codeview;
27using namespace llvm::pdb;
28
29namespace {
30// Yea, this is a pretty terrible class name.  But if we have an enum:
31//
32// enum Foo {
33//  A,
34//  B
35// };
36//
37// then A and B are the "enumerators" of the "enum" Foo.  And we need
38// to enumerate them.
39class NativeEnumEnumEnumerators : public IPDBEnumSymbols, TypeVisitorCallbacks {
40public:
41  NativeEnumEnumEnumerators(NativeSession &Session,
42                            const NativeTypeEnum &ClassParent);
43
44  uint32_t getChildCount() const override;
45  std::unique_ptr<PDBSymbol> getChildAtIndex(uint32_t Index) const override;
46  std::unique_ptr<PDBSymbol> getNext() override;
47  void reset() override;
48
49private:
50  Error visitKnownMember(CVMemberRecord &CVM,
51                         EnumeratorRecord &Record) override;
52  Error visitKnownMember(CVMemberRecord &CVM,
53                         ListContinuationRecord &Record) override;
54
55  NativeSession &Session;
56  const NativeTypeEnum &ClassParent;
57  std::vector<EnumeratorRecord> Enumerators;
58  std::optional<TypeIndex> ContinuationIndex;
59  uint32_t Index = 0;
60};
61} // namespace
62
63NativeEnumEnumEnumerators::NativeEnumEnumEnumerators(
64    NativeSession &Session, const NativeTypeEnum &ClassParent)
65    : Session(Session), ClassParent(ClassParent) {
66  TpiStream &Tpi = cantFail(Session.getPDBFile().getPDBTpiStream());
67  LazyRandomTypeCollection &Types = Tpi.typeCollection();
68
69  ContinuationIndex = ClassParent.getEnumRecord().FieldList;
70  while (ContinuationIndex) {
71    CVType FieldListCVT = Types.getType(*ContinuationIndex);
72    assert(FieldListCVT.kind() == LF_FIELDLIST);
73    ContinuationIndex.reset();
74    FieldListRecord FieldList;
75    cantFail(TypeDeserializer::deserializeAs<FieldListRecord>(FieldListCVT,
76                                                              FieldList));
77    cantFail(visitMemberRecordStream(FieldList.Data, *this));
78  }
79}
80
81Error NativeEnumEnumEnumerators::visitKnownMember(CVMemberRecord &CVM,
82                                                  EnumeratorRecord &Record) {
83  Enumerators.push_back(Record);
84  return Error::success();
85}
86
87Error NativeEnumEnumEnumerators::visitKnownMember(
88    CVMemberRecord &CVM, ListContinuationRecord &Record) {
89  ContinuationIndex = Record.ContinuationIndex;
90  return Error::success();
91}
92
93uint32_t NativeEnumEnumEnumerators::getChildCount() const {
94  return Enumerators.size();
95}
96
97std::unique_ptr<PDBSymbol>
98NativeEnumEnumEnumerators::getChildAtIndex(uint32_t Index) const {
99  if (Index >= getChildCount())
100    return nullptr;
101
102  SymIndexId Id = Session.getSymbolCache()
103                      .getOrCreateFieldListMember<NativeSymbolEnumerator>(
104                          ClassParent.getEnumRecord().FieldList, Index,
105                          ClassParent, Enumerators[Index]);
106  return Session.getSymbolCache().getSymbolById(Id);
107}
108
109std::unique_ptr<PDBSymbol> NativeEnumEnumEnumerators::getNext() {
110  if (Index >= getChildCount())
111    return nullptr;
112
113  return getChildAtIndex(Index++);
114}
115
116void NativeEnumEnumEnumerators::reset() { Index = 0; }
117
118NativeTypeEnum::NativeTypeEnum(NativeSession &Session, SymIndexId Id,
119                               TypeIndex Index, EnumRecord Record)
120    : NativeRawSymbol(Session, PDB_SymType::Enum, Id), Index(Index),
121      Record(std::move(Record)) {}
122
123NativeTypeEnum::NativeTypeEnum(NativeSession &Session, SymIndexId Id,
124                               NativeTypeEnum &UnmodifiedType,
125                               codeview::ModifierRecord Modifier)
126    : NativeRawSymbol(Session, PDB_SymType::Enum, Id),
127      UnmodifiedType(&UnmodifiedType), Modifiers(std::move(Modifier)) {}
128
129NativeTypeEnum::~NativeTypeEnum() = default;
130
131void NativeTypeEnum::dump(raw_ostream &OS, int Indent,
132                          PdbSymbolIdField ShowIdFields,
133                          PdbSymbolIdField RecurseIdFields) const {
134  NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields);
135
136  dumpSymbolField(OS, "baseType", static_cast<uint32_t>(getBuiltinType()),
137                  Indent);
138  dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session,
139                    PdbSymbolIdField::LexicalParent, ShowIdFields,
140                    RecurseIdFields);
141  dumpSymbolField(OS, "name", getName(), Indent);
142  dumpSymbolIdField(OS, "typeId", getTypeId(), Indent, Session,
143                    PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields);
144  if (Modifiers)
145    dumpSymbolIdField(OS, "unmodifiedTypeId", getUnmodifiedTypeId(), Indent,
146                      Session, PdbSymbolIdField::UnmodifiedType, ShowIdFields,
147                      RecurseIdFields);
148  dumpSymbolField(OS, "length", getLength(), Indent);
149  dumpSymbolField(OS, "constructor", hasConstructor(), Indent);
150  dumpSymbolField(OS, "constType", isConstType(), Indent);
151  dumpSymbolField(OS, "hasAssignmentOperator", hasAssignmentOperator(), Indent);
152  dumpSymbolField(OS, "hasCastOperator", hasCastOperator(), Indent);
153  dumpSymbolField(OS, "hasNestedTypes", hasNestedTypes(), Indent);
154  dumpSymbolField(OS, "overloadedOperator", hasOverloadedOperator(), Indent);
155  dumpSymbolField(OS, "isInterfaceUdt", isInterfaceUdt(), Indent);
156  dumpSymbolField(OS, "intrinsic", isIntrinsic(), Indent);
157  dumpSymbolField(OS, "nested", isNested(), Indent);
158  dumpSymbolField(OS, "packed", isPacked(), Indent);
159  dumpSymbolField(OS, "isRefUdt", isRefUdt(), Indent);
160  dumpSymbolField(OS, "scoped", isScoped(), Indent);
161  dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent);
162  dumpSymbolField(OS, "isValueUdt", isValueUdt(), Indent);
163  dumpSymbolField(OS, "volatileType", isVolatileType(), Indent);
164}
165
166std::unique_ptr<IPDBEnumSymbols>
167NativeTypeEnum::findChildren(PDB_SymType Type) const {
168  if (Type != PDB_SymType::Data)
169    return std::make_unique<NullEnumerator<PDBSymbol>>();
170
171  const NativeTypeEnum *ClassParent = nullptr;
172  if (!Modifiers)
173    ClassParent = this;
174  else
175    ClassParent = UnmodifiedType;
176  return std::make_unique<NativeEnumEnumEnumerators>(Session, *ClassParent);
177}
178
179PDB_SymType NativeTypeEnum::getSymTag() const { return PDB_SymType::Enum; }
180
181PDB_BuiltinType NativeTypeEnum::getBuiltinType() const {
182  if (UnmodifiedType)
183    return UnmodifiedType->getBuiltinType();
184
185  Session.getSymbolCache().findSymbolByTypeIndex(Record->getUnderlyingType());
186
187  codeview::TypeIndex Underlying = Record->getUnderlyingType();
188
189  // This indicates a corrupt record.
190  if (!Underlying.isSimple() ||
191      Underlying.getSimpleMode() != SimpleTypeMode::Direct) {
192    return PDB_BuiltinType::None;
193  }
194
195  switch (Underlying.getSimpleKind()) {
196  case SimpleTypeKind::Boolean128:
197  case SimpleTypeKind::Boolean64:
198  case SimpleTypeKind::Boolean32:
199  case SimpleTypeKind::Boolean16:
200  case SimpleTypeKind::Boolean8:
201    return PDB_BuiltinType::Bool;
202  case SimpleTypeKind::NarrowCharacter:
203  case SimpleTypeKind::UnsignedCharacter:
204  case SimpleTypeKind::SignedCharacter:
205    return PDB_BuiltinType::Char;
206  case SimpleTypeKind::WideCharacter:
207    return PDB_BuiltinType::WCharT;
208  case SimpleTypeKind::Character16:
209    return PDB_BuiltinType::Char16;
210  case SimpleTypeKind::Character32:
211    return PDB_BuiltinType::Char32;
212  case SimpleTypeKind::Character8:
213    return PDB_BuiltinType::Char8;
214  case SimpleTypeKind::Int128:
215  case SimpleTypeKind::Int128Oct:
216  case SimpleTypeKind::Int16:
217  case SimpleTypeKind::Int16Short:
218  case SimpleTypeKind::Int32:
219  case SimpleTypeKind::Int32Long:
220  case SimpleTypeKind::Int64:
221  case SimpleTypeKind::Int64Quad:
222    return PDB_BuiltinType::Int;
223  case SimpleTypeKind::UInt128:
224  case SimpleTypeKind::UInt128Oct:
225  case SimpleTypeKind::UInt16:
226  case SimpleTypeKind::UInt16Short:
227  case SimpleTypeKind::UInt32:
228  case SimpleTypeKind::UInt32Long:
229  case SimpleTypeKind::UInt64:
230  case SimpleTypeKind::UInt64Quad:
231    return PDB_BuiltinType::UInt;
232  case SimpleTypeKind::HResult:
233    return PDB_BuiltinType::HResult;
234  case SimpleTypeKind::Complex16:
235  case SimpleTypeKind::Complex32:
236  case SimpleTypeKind::Complex32PartialPrecision:
237  case SimpleTypeKind::Complex64:
238  case SimpleTypeKind::Complex80:
239  case SimpleTypeKind::Complex128:
240    return PDB_BuiltinType::Complex;
241  case SimpleTypeKind::Float16:
242  case SimpleTypeKind::Float32:
243  case SimpleTypeKind::Float32PartialPrecision:
244  case SimpleTypeKind::Float48:
245  case SimpleTypeKind::Float64:
246  case SimpleTypeKind::Float80:
247  case SimpleTypeKind::Float128:
248    return PDB_BuiltinType::Float;
249  default:
250    return PDB_BuiltinType::None;
251  }
252  llvm_unreachable("Unreachable");
253}
254
255SymIndexId NativeTypeEnum::getUnmodifiedTypeId() const {
256  return UnmodifiedType ? UnmodifiedType->getSymIndexId() : 0;
257}
258
259bool NativeTypeEnum::hasConstructor() const {
260  if (UnmodifiedType)
261    return UnmodifiedType->hasConstructor();
262
263  return bool(Record->getOptions() &
264              codeview::ClassOptions::HasConstructorOrDestructor);
265}
266
267bool NativeTypeEnum::hasAssignmentOperator() const {
268  if (UnmodifiedType)
269    return UnmodifiedType->hasAssignmentOperator();
270
271  return bool(Record->getOptions() &
272              codeview::ClassOptions::HasOverloadedAssignmentOperator);
273}
274
275bool NativeTypeEnum::hasNestedTypes() const {
276  if (UnmodifiedType)
277    return UnmodifiedType->hasNestedTypes();
278
279  return bool(Record->getOptions() &
280              codeview::ClassOptions::ContainsNestedClass);
281}
282
283bool NativeTypeEnum::isIntrinsic() const {
284  if (UnmodifiedType)
285    return UnmodifiedType->isIntrinsic();
286
287  return bool(Record->getOptions() & codeview::ClassOptions::Intrinsic);
288}
289
290bool NativeTypeEnum::hasCastOperator() const {
291  if (UnmodifiedType)
292    return UnmodifiedType->hasCastOperator();
293
294  return bool(Record->getOptions() &
295              codeview::ClassOptions::HasConversionOperator);
296}
297
298uint64_t NativeTypeEnum::getLength() const {
299  if (UnmodifiedType)
300    return UnmodifiedType->getLength();
301
302  const auto Id = Session.getSymbolCache().findSymbolByTypeIndex(
303      Record->getUnderlyingType());
304  const auto UnderlyingType =
305      Session.getConcreteSymbolById<PDBSymbolTypeBuiltin>(Id);
306  return UnderlyingType ? UnderlyingType->getLength() : 0;
307}
308
309std::string NativeTypeEnum::getName() const {
310  if (UnmodifiedType)
311    return UnmodifiedType->getName();
312
313  return std::string(Record->getName());
314}
315
316bool NativeTypeEnum::isNested() const {
317  if (UnmodifiedType)
318    return UnmodifiedType->isNested();
319
320  return bool(Record->getOptions() & codeview::ClassOptions::Nested);
321}
322
323bool NativeTypeEnum::hasOverloadedOperator() const {
324  if (UnmodifiedType)
325    return UnmodifiedType->hasOverloadedOperator();
326
327  return bool(Record->getOptions() &
328              codeview::ClassOptions::HasOverloadedOperator);
329}
330
331bool NativeTypeEnum::isPacked() const {
332  if (UnmodifiedType)
333    return UnmodifiedType->isPacked();
334
335  return bool(Record->getOptions() & codeview::ClassOptions::Packed);
336}
337
338bool NativeTypeEnum::isScoped() const {
339  if (UnmodifiedType)
340    return UnmodifiedType->isScoped();
341
342  return bool(Record->getOptions() & codeview::ClassOptions::Scoped);
343}
344
345SymIndexId NativeTypeEnum::getTypeId() const {
346  if (UnmodifiedType)
347    return UnmodifiedType->getTypeId();
348
349  return Session.getSymbolCache().findSymbolByTypeIndex(
350      Record->getUnderlyingType());
351}
352
353bool NativeTypeEnum::isRefUdt() const { return false; }
354
355bool NativeTypeEnum::isValueUdt() const { return false; }
356
357bool NativeTypeEnum::isInterfaceUdt() const { return false; }
358
359bool NativeTypeEnum::isConstType() const {
360  if (!Modifiers)
361    return false;
362  return ((Modifiers->getModifiers() & ModifierOptions::Const) !=
363          ModifierOptions::None);
364}
365
366bool NativeTypeEnum::isVolatileType() const {
367  if (!Modifiers)
368    return false;
369  return ((Modifiers->getModifiers() & ModifierOptions::Volatile) !=
370          ModifierOptions::None);
371}
372
373bool NativeTypeEnum::isUnalignedType() const {
374  if (!Modifiers)
375    return false;
376  return ((Modifiers->getModifiers() & ModifierOptions::Unaligned) !=
377          ModifierOptions::None);
378}
379
380const NativeTypeBuiltin &NativeTypeEnum::getUnderlyingBuiltinType() const {
381  if (UnmodifiedType)
382    return UnmodifiedType->getUnderlyingBuiltinType();
383
384  return Session.getSymbolCache().getNativeSymbolById<NativeTypeBuiltin>(
385      getTypeId());
386}
387