1 | #include "UdtRecordCompleter.h" |
2 | |
3 | #include "PdbAstBuilder.h" |
4 | #include "PdbIndex.h" |
5 | #include "PdbSymUid.h" |
6 | #include "PdbUtil.h" |
7 | |
8 | #include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" |
9 | #include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" |
10 | #include "Plugins/ExpressionParser/Clang/ClangUtil.h" |
11 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
12 | #include "SymbolFileNativePDB.h" |
13 | #include "lldb/Core/Address.h" |
14 | #include "lldb/Symbol/Type.h" |
15 | #include "lldb/Utility/LLDBAssert.h" |
16 | #include "lldb/Utility/LLDBLog.h" |
17 | #include "lldb/lldb-enumerations.h" |
18 | #include "lldb/lldb-forward.h" |
19 | |
20 | #include "llvm/ADT/STLExtras.h" |
21 | #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" |
22 | #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" |
23 | #include "llvm/DebugInfo/CodeView/TypeIndex.h" |
24 | #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" |
25 | #include "llvm/DebugInfo/PDB/Native/TpiStream.h" |
26 | #include "llvm/DebugInfo/PDB/PDBTypes.h" |
27 | #include <optional> |
28 | |
29 | using namespace llvm::codeview; |
30 | using namespace llvm::pdb; |
31 | using namespace lldb; |
32 | using namespace lldb_private; |
33 | using namespace lldb_private::npdb; |
34 | |
35 | using Error = llvm::Error; |
36 | |
37 | UdtRecordCompleter::UdtRecordCompleter( |
38 | PdbTypeSymId id, CompilerType &derived_ct, clang::TagDecl &tag_decl, |
39 | PdbAstBuilder &ast_builder, PdbIndex &index, |
40 | llvm::DenseMap<clang::Decl *, DeclStatus> &decl_to_status, |
41 | llvm::DenseMap<lldb::opaque_compiler_type_t, |
42 | llvm::SmallSet<std::pair<llvm::StringRef, CompilerType>, 8>> |
43 | &cxx_record_map) |
44 | : m_id(id), m_derived_ct(derived_ct), m_tag_decl(tag_decl), |
45 | m_ast_builder(ast_builder), m_index(index), |
46 | m_decl_to_status(decl_to_status), m_cxx_record_map(cxx_record_map) { |
47 | CVType cvt = m_index.tpi().getType(Index: m_id.index); |
48 | switch (cvt.kind()) { |
49 | case LF_ENUM: |
50 | m_cvr.er.Options = ClassOptions::None; |
51 | llvm::cantFail(Err: TypeDeserializer::deserializeAs<EnumRecord>(CVT&: cvt, Record&: m_cvr.er)); |
52 | break; |
53 | case LF_UNION: |
54 | m_cvr.ur.Options = ClassOptions::None; |
55 | llvm::cantFail(Err: TypeDeserializer::deserializeAs<UnionRecord>(CVT&: cvt, Record&: m_cvr.ur)); |
56 | m_layout.bit_size = m_cvr.ur.getSize() * 8; |
57 | m_record.record.kind = Member::Union; |
58 | break; |
59 | case LF_CLASS: |
60 | case LF_STRUCTURE: |
61 | m_cvr.cr.Options = ClassOptions::None; |
62 | llvm::cantFail(Err: TypeDeserializer::deserializeAs<ClassRecord>(CVT&: cvt, Record&: m_cvr.cr)); |
63 | m_layout.bit_size = m_cvr.cr.getSize() * 8; |
64 | m_record.record.kind = Member::Struct; |
65 | break; |
66 | default: |
67 | llvm_unreachable("unreachable!" ); |
68 | } |
69 | } |
70 | |
71 | clang::QualType UdtRecordCompleter::AddBaseClassForTypeIndex( |
72 | llvm::codeview::TypeIndex ti, llvm::codeview::MemberAccess access, |
73 | std::optional<uint64_t> vtable_idx) { |
74 | PdbTypeSymId type_id(ti); |
75 | clang::QualType qt = m_ast_builder.GetOrCreateType(type: type_id); |
76 | |
77 | CVType udt_cvt = m_index.tpi().getType(Index: ti); |
78 | |
79 | std::unique_ptr<clang::CXXBaseSpecifier> base_spec = |
80 | m_ast_builder.clang().CreateBaseClassSpecifier( |
81 | type: qt.getAsOpaquePtr(), access: TranslateMemberAccess(access), |
82 | is_virtual: vtable_idx.has_value(), base_of_class: udt_cvt.kind() == LF_CLASS); |
83 | if (!base_spec) |
84 | return {}; |
85 | |
86 | m_bases.push_back( |
87 | x: std::make_pair(x: vtable_idx.value_or(u: 0), y: std::move(base_spec))); |
88 | |
89 | return qt; |
90 | } |
91 | |
92 | void UdtRecordCompleter::AddMethod(llvm::StringRef name, TypeIndex type_idx, |
93 | MemberAccess access, MethodOptions options, |
94 | MemberAttributes attrs) { |
95 | clang::QualType method_qt = |
96 | m_ast_builder.GetOrCreateType(type: PdbTypeSymId(type_idx)); |
97 | if (method_qt.isNull()) |
98 | return; |
99 | CompilerType method_ct = m_ast_builder.ToCompilerType(qt: method_qt); |
100 | TypeSystemClang::RequireCompleteType(type: method_ct); |
101 | lldb::opaque_compiler_type_t derived_opaque_ty = |
102 | m_derived_ct.GetOpaqueQualType(); |
103 | auto iter = m_cxx_record_map.find(Val: derived_opaque_ty); |
104 | if (iter != m_cxx_record_map.end()) { |
105 | if (iter->getSecond().contains(V: {name, method_ct})) { |
106 | return; |
107 | } |
108 | } |
109 | |
110 | lldb::AccessType access_type = TranslateMemberAccess(access); |
111 | bool is_artificial = (options & MethodOptions::CompilerGenerated) == |
112 | MethodOptions::CompilerGenerated; |
113 | m_ast_builder.clang().AddMethodToCXXRecordType( |
114 | type: derived_opaque_ty, name: name.data(), mangled_name: nullptr, method_type: method_ct, |
115 | access: access_type, is_virtual: attrs.isVirtual(), is_static: attrs.isStatic(), is_inline: false, is_explicit: false, is_attr_used: false, |
116 | is_artificial); |
117 | |
118 | m_cxx_record_map[derived_opaque_ty].insert(V: {name, method_ct}); |
119 | } |
120 | |
121 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
122 | BaseClassRecord &base) { |
123 | clang::QualType base_qt = |
124 | AddBaseClassForTypeIndex(ti: base.Type, access: base.getAccess()); |
125 | |
126 | if (base_qt.isNull()) |
127 | return llvm::Error::success(); |
128 | auto decl = |
129 | m_ast_builder.clang().GetAsCXXRecordDecl(type: base_qt.getAsOpaquePtr()); |
130 | lldbassert(decl); |
131 | |
132 | auto offset = clang::CharUnits::fromQuantity(Quantity: base.getBaseOffset()); |
133 | m_layout.base_offsets.insert(KV: std::make_pair(x&: decl, y&: offset)); |
134 | |
135 | return llvm::Error::success(); |
136 | } |
137 | |
138 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
139 | VirtualBaseClassRecord &base) { |
140 | AddBaseClassForTypeIndex(ti: base.BaseType, access: base.getAccess(), vtable_idx: base.VTableIndex); |
141 | |
142 | return Error::success(); |
143 | } |
144 | |
145 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
146 | ListContinuationRecord &cont) { |
147 | return Error::success(); |
148 | } |
149 | |
150 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
151 | VFPtrRecord &vfptr) { |
152 | return Error::success(); |
153 | } |
154 | |
155 | Error UdtRecordCompleter::visitKnownMember( |
156 | CVMemberRecord &cvr, StaticDataMemberRecord &static_data_member) { |
157 | clang::QualType member_type = |
158 | m_ast_builder.GetOrCreateType(type: PdbTypeSymId(static_data_member.Type)); |
159 | if (member_type.isNull()) |
160 | return llvm::Error::success(); |
161 | |
162 | CompilerType member_ct = m_ast_builder.ToCompilerType(qt: member_type); |
163 | |
164 | lldb::AccessType access = |
165 | TranslateMemberAccess(access: static_data_member.getAccess()); |
166 | auto decl = TypeSystemClang::AddVariableToRecordType( |
167 | type: m_derived_ct, name: static_data_member.Name, var_type: member_ct, access); |
168 | |
169 | // Static constant members may be a const[expr] declaration. |
170 | // Query the symbol's value as the variable initializer if valid. |
171 | if (member_ct.IsConst() && member_ct.IsCompleteType()) { |
172 | std::string qual_name = decl->getQualifiedNameAsString(); |
173 | |
174 | auto results = |
175 | m_index.globals().findRecordsByName(Name: qual_name, Symbols: m_index.symrecords()); |
176 | |
177 | for (const auto &result : results) { |
178 | if (result.second.kind() == SymbolKind::S_CONSTANT) { |
179 | ConstantSym constant(SymbolRecordKind::ConstantSym); |
180 | cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(result.second, |
181 | constant)); |
182 | |
183 | clang::QualType qual_type = decl->getType(); |
184 | unsigned type_width = decl->getASTContext().getIntWidth(qual_type); |
185 | unsigned constant_width = constant.Value.getBitWidth(); |
186 | |
187 | if (qual_type->isIntegralOrEnumerationType()) { |
188 | if (type_width >= constant_width) { |
189 | TypeSystemClang::SetIntegerInitializerForVariable( |
190 | decl, constant.Value.extOrTrunc(type_width)); |
191 | } else { |
192 | LLDB_LOG(GetLog(LLDBLog::AST), |
193 | "Class '{0}' has a member '{1}' of type '{2}' ({3} bits) " |
194 | "which resolves to a wider constant value ({4} bits). " |
195 | "Ignoring constant." , |
196 | m_derived_ct.GetTypeName(), static_data_member.Name, |
197 | member_ct.GetTypeName(), type_width, constant_width); |
198 | } |
199 | } else { |
200 | lldb::BasicType basic_type_enum = member_ct.GetBasicTypeEnumeration(); |
201 | switch (basic_type_enum) { |
202 | case lldb::eBasicTypeFloat: |
203 | case lldb::eBasicTypeDouble: |
204 | case lldb::eBasicTypeLongDouble: |
205 | if (type_width == constant_width) { |
206 | TypeSystemClang::SetFloatingInitializerForVariable( |
207 | decl, basic_type_enum == lldb::eBasicTypeFloat |
208 | ? llvm::APFloat(constant.Value.bitsToFloat()) |
209 | : llvm::APFloat(constant.Value.bitsToDouble())); |
210 | decl->setConstexpr(true); |
211 | } else { |
212 | LLDB_LOG( |
213 | GetLog(LLDBLog::AST), |
214 | "Class '{0}' has a member '{1}' of type '{2}' ({3} bits) " |
215 | "which resolves to a constant value of mismatched width " |
216 | "({4} bits). Ignoring constant." , |
217 | m_derived_ct.GetTypeName(), static_data_member.Name, |
218 | member_ct.GetTypeName(), type_width, constant_width); |
219 | } |
220 | break; |
221 | default: |
222 | break; |
223 | } |
224 | } |
225 | break; |
226 | } |
227 | } |
228 | } |
229 | |
230 | // FIXME: Add a PdbSymUid namespace for field list members and update |
231 | // the m_uid_to_decl map with this decl. |
232 | return Error::success(); |
233 | } |
234 | |
235 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
236 | NestedTypeRecord &nested) { |
237 | return Error::success(); |
238 | } |
239 | |
240 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
241 | DataMemberRecord &data_member) { |
242 | |
243 | uint64_t offset = data_member.FieldOffset * 8; |
244 | uint32_t bitfield_width = 0; |
245 | |
246 | TypeIndex ti(data_member.Type); |
247 | if (!ti.isSimple()) { |
248 | CVType cvt = m_index.tpi().getType(Index: ti); |
249 | if (cvt.kind() == LF_BITFIELD) { |
250 | BitFieldRecord bfr; |
251 | llvm::cantFail(Err: TypeDeserializer::deserializeAs<BitFieldRecord>(CVT&: cvt, Record&: bfr)); |
252 | offset += bfr.BitOffset; |
253 | bitfield_width = bfr.BitSize; |
254 | ti = bfr.Type; |
255 | } |
256 | } |
257 | |
258 | clang::QualType member_qt = m_ast_builder.GetOrCreateType(type: PdbTypeSymId(ti)); |
259 | if (member_qt.isNull()) |
260 | return Error::success(); |
261 | TypeSystemClang::RequireCompleteType(type: m_ast_builder.ToCompilerType(qt: member_qt)); |
262 | lldb::AccessType access = TranslateMemberAccess(access: data_member.getAccess()); |
263 | size_t field_size = |
264 | bitfield_width ? bitfield_width : GetSizeOfType(id: ti, tpi&: m_index.tpi()) * 8; |
265 | if (field_size == 0) |
266 | return Error::success(); |
267 | m_record.CollectMember(data_member.Name, offset, field_size, member_qt, access, |
268 | bitfield_width); |
269 | return Error::success(); |
270 | } |
271 | |
272 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
273 | OneMethodRecord &one_method) { |
274 | AddMethod(name: one_method.Name, type_idx: one_method.Type, access: one_method.getAccess(), |
275 | options: one_method.getOptions(), attrs: one_method.Attrs); |
276 | |
277 | return Error::success(); |
278 | } |
279 | |
280 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
281 | OverloadedMethodRecord &overloaded) { |
282 | TypeIndex method_list_idx = overloaded.MethodList; |
283 | |
284 | CVType method_list_type = m_index.tpi().getType(Index: method_list_idx); |
285 | assert(method_list_type.kind() == LF_METHODLIST); |
286 | |
287 | MethodOverloadListRecord method_list; |
288 | llvm::cantFail(Err: TypeDeserializer::deserializeAs<MethodOverloadListRecord>( |
289 | CVT&: method_list_type, Record&: method_list)); |
290 | |
291 | for (const OneMethodRecord &method : method_list.Methods) |
292 | AddMethod(name: overloaded.Name, type_idx: method.Type, access: method.getAccess(), |
293 | options: method.getOptions(), attrs: method.Attrs); |
294 | |
295 | return Error::success(); |
296 | } |
297 | |
298 | Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, |
299 | EnumeratorRecord &enumerator) { |
300 | Declaration decl; |
301 | llvm::StringRef name = DropNameScope(name: enumerator.getName()); |
302 | |
303 | m_ast_builder.clang().AddEnumerationValueToEnumerationType( |
304 | enum_type: m_derived_ct, decl, name: name.str().c_str(), value: enumerator.Value); |
305 | return Error::success(); |
306 | } |
307 | |
308 | void UdtRecordCompleter::complete() { |
309 | // Ensure the correct order for virtual bases. |
310 | llvm::stable_sort(Range&: m_bases, C: llvm::less_first()); |
311 | |
312 | std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> bases; |
313 | bases.reserve(n: m_bases.size()); |
314 | for (auto &ib : m_bases) |
315 | bases.push_back(x: std::move(ib.second)); |
316 | |
317 | TypeSystemClang &clang = m_ast_builder.clang(); |
318 | // Make sure all base classes refer to complete types and not forward |
319 | // declarations. If we don't do this, clang will crash with an |
320 | // assertion in the call to clang_type.TransferBaseClasses() |
321 | for (const auto &base_class : bases) { |
322 | clang::TypeSourceInfo *type_source_info = |
323 | base_class->getTypeSourceInfo(); |
324 | if (type_source_info) { |
325 | TypeSystemClang::RequireCompleteType( |
326 | type: clang.GetType(qt: type_source_info->getType())); |
327 | } |
328 | } |
329 | |
330 | clang.TransferBaseClasses(type: m_derived_ct.GetOpaqueQualType(), bases: std::move(bases)); |
331 | |
332 | clang.AddMethodOverridesForCXXRecordType(type: m_derived_ct.GetOpaqueQualType()); |
333 | FinishRecord(); |
334 | TypeSystemClang::BuildIndirectFields(type: m_derived_ct); |
335 | TypeSystemClang::CompleteTagDeclarationDefinition(type: m_derived_ct); |
336 | |
337 | if (auto *record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(Val: &m_tag_decl)) { |
338 | m_ast_builder.GetClangASTImporter().SetRecordLayout(record_decl, m_layout); |
339 | } |
340 | } |
341 | |
342 | uint64_t |
343 | UdtRecordCompleter::AddMember(TypeSystemClang &clang, Member *field, |
344 | uint64_t bit_offset, CompilerType parent_ct, |
345 | ClangASTImporter::LayoutInfo &parent_layout, |
346 | clang::DeclContext *parent_decl_ctx) { |
347 | SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( |
348 | clang.GetSymbolFile()->GetBackingSymbolFile()); |
349 | clang::FieldDecl *field_decl = nullptr; |
350 | uint64_t bit_size = 0; |
351 | switch (field->kind) { |
352 | case Member::Field: { |
353 | field_decl = TypeSystemClang::AddFieldToRecordType( |
354 | type: parent_ct, name: field->name, field_type: m_ast_builder.ToCompilerType(qt: field->qt), |
355 | access: field->access, bitfield_bit_size: field->bitfield_width); |
356 | bit_size = field->bit_size; |
357 | break; |
358 | }; |
359 | case Member::Struct: |
360 | case Member::Union: { |
361 | clang::TagTypeKind kind = field->kind == Member::Struct |
362 | ? clang::TagTypeKind::Struct |
363 | : clang::TagTypeKind::Union; |
364 | ClangASTMetadata metadata; |
365 | metadata.SetUserID(pdb->anonymous_id); |
366 | metadata.SetIsDynamicCXXType(false); |
367 | CompilerType record_ct = clang.CreateRecordType( |
368 | decl_ctx: parent_decl_ctx, owning_module: OptionalClangModuleID(), access_type: lldb::eAccessPublic, name: "" , |
369 | kind: llvm::to_underlying(E: kind), language: lldb::eLanguageTypeC_plus_plus, metadata); |
370 | TypeSystemClang::StartTagDeclarationDefinition(type: record_ct); |
371 | ClangASTImporter::LayoutInfo layout; |
372 | clang::DeclContext *decl_ctx = clang.GetDeclContextForType(type: record_ct); |
373 | for (const auto &member : field->fields) { |
374 | uint64_t member_offset = field->kind == Member::Struct |
375 | ? member->bit_offset - field->base_offset |
376 | : 0; |
377 | uint64_t member_bit_size = AddMember(clang, field: member.get(), bit_offset: member_offset, |
378 | parent_ct: record_ct, parent_layout&: layout, parent_decl_ctx: decl_ctx); |
379 | if (field->kind == Member::Struct) |
380 | bit_size = std::max(a: bit_size, b: member_offset + member_bit_size); |
381 | else |
382 | bit_size = std::max(a: bit_size, b: member_bit_size); |
383 | } |
384 | layout.bit_size = bit_size; |
385 | TypeSystemClang::CompleteTagDeclarationDefinition(type: record_ct); |
386 | clang::RecordDecl *record_decl = clang.GetAsRecordDecl(type: record_ct); |
387 | m_ast_builder.GetClangASTImporter().SetRecordLayout(decl: record_decl, layout); |
388 | field_decl = TypeSystemClang::AddFieldToRecordType( |
389 | type: parent_ct, name: "" , field_type: record_ct, access: lldb::eAccessPublic, bitfield_bit_size: 0); |
390 | // Mark this record decl as completed. |
391 | DeclStatus status; |
392 | status.resolved = true; |
393 | status.uid = pdb->anonymous_id--; |
394 | m_decl_to_status.insert({record_decl, status}); |
395 | break; |
396 | }; |
397 | } |
398 | // FIXME: Add a PdbSymUid namespace for field list members and update |
399 | // the m_uid_to_decl map with this decl. |
400 | parent_layout.field_offsets.insert(KV: {field_decl, bit_offset}); |
401 | return bit_size; |
402 | } |
403 | |
404 | void UdtRecordCompleter::FinishRecord() { |
405 | TypeSystemClang &clang = m_ast_builder.clang(); |
406 | clang::DeclContext *decl_ctx = |
407 | m_ast_builder.GetOrCreateDeclContextForUid(uid: m_id); |
408 | m_record.ConstructRecord(); |
409 | // Maybe we should check the construsted record size with the size in pdb. If |
410 | // they mismatch, it might be pdb has fields info missing. |
411 | for (const auto &field : m_record.record.fields) { |
412 | AddMember(clang, field.get(), field->bit_offset, m_derived_ct, m_layout, |
413 | decl_ctx); |
414 | } |
415 | } |
416 | |
417 | void UdtRecordCompleter::Record::CollectMember( |
418 | llvm::StringRef name, uint64_t offset, uint64_t field_size, |
419 | clang::QualType qt, lldb::AccessType access, uint64_t bitfield_width) { |
420 | fields_map[offset].push_back(Elt: std::make_unique<Member>( |
421 | args&: name, args&: offset, args&: field_size, args&: qt, args&: access, args&: bitfield_width)); |
422 | if (start_offset > offset) |
423 | start_offset = offset; |
424 | } |
425 | |
426 | void UdtRecordCompleter::Record::ConstructRecord() { |
427 | // For anonymous unions in a struct, msvc generated pdb doesn't have the |
428 | // entity for that union. So, we need to construct anonymous union and struct |
429 | // based on field offsets. The final AST is likely not matching the exact |
430 | // original AST, but the memory layout is preseved. |
431 | // After we collecting all fields in visitKnownMember, we have all fields in |
432 | // increasing offset order in m_fields. Since we are iterating in increase |
433 | // offset order, if the current offset is equal to m_start_offset, we insert |
434 | // it as direct field of top level record. If the current offset is greater |
435 | // than m_start_offset, we should be able to find a field in end_offset_map |
436 | // whose end offset is less than or equal to current offset. (if not, it might |
437 | // be missing field info. We will ignore the field in this case. e.g. Field A |
438 | // starts at 0 with size 4 bytes, and Field B starts at 2 with size 4 bytes. |
439 | // Normally, there must be something which ends at/before 2.) Then we will |
440 | // append current field to the end of parent record. If parent is struct, we |
441 | // can just grow it. If parent is a field, it's a field inside an union. We |
442 | // convert it into an anonymous struct containing old field and new field. |
443 | |
444 | // The end offset to a vector of field/struct that ends at the offset. |
445 | std::map<uint64_t, std::vector<Member *>> end_offset_map; |
446 | for (auto &pair : fields_map) { |
447 | uint64_t offset = pair.first; |
448 | auto &fields = pair.second; |
449 | lldbassert(offset >= start_offset); |
450 | Member *parent = &record; |
451 | if (offset > start_offset) { |
452 | // Find the field with largest end offset that is <= offset. If it's less |
453 | // than offset, it indicates there are padding bytes between end offset |
454 | // and offset. |
455 | lldbassert(!end_offset_map.empty()); |
456 | auto iter = end_offset_map.lower_bound(x: offset); |
457 | if (iter == end_offset_map.end()) |
458 | --iter; |
459 | else if (iter->first > offset) { |
460 | if (iter == end_offset_map.begin()) |
461 | continue; |
462 | --iter; |
463 | } |
464 | if (iter->second.empty()) |
465 | continue; |
466 | parent = iter->second.back(); |
467 | iter->second.pop_back(); |
468 | } |
469 | // If it's a field, then the field is inside a union, so we can safely |
470 | // increase its size by converting it to a struct to hold multiple fields. |
471 | if (parent->kind == Member::Field) |
472 | parent->ConvertToStruct(); |
473 | |
474 | if (fields.size() == 1) { |
475 | uint64_t end_offset = offset + fields.back()->bit_size; |
476 | parent->fields.push_back(Elt: std::move(fields.back())); |
477 | if (parent->kind == Member::Struct) { |
478 | end_offset_map[end_offset].push_back(x: parent); |
479 | } else { |
480 | lldbassert(parent == &record && |
481 | "If parent is union, it must be the top level record." ); |
482 | end_offset_map[end_offset].push_back(x: parent->fields.back().get()); |
483 | } |
484 | } else { |
485 | if (parent->kind == Member::Struct) { |
486 | parent->fields.push_back(Elt: std::make_unique<Member>(args: Member::Union)); |
487 | parent = parent->fields.back().get(); |
488 | parent->bit_offset = offset; |
489 | } else { |
490 | lldbassert(parent == &record && |
491 | "If parent is union, it must be the top level record." ); |
492 | } |
493 | for (auto &field : fields) { |
494 | int64_t bit_size = field->bit_size; |
495 | parent->fields.push_back(Elt: std::move(field)); |
496 | end_offset_map[offset + bit_size].push_back( |
497 | x: parent->fields.back().get()); |
498 | } |
499 | } |
500 | } |
501 | } |
502 | |