Skip to main content

pdb/tpi/
data.rs

1// Copyright 2017 pdb Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8#![allow(missing_docs)]
9
10use crate::common::*;
11use crate::tpi::constants::*;
12use crate::tpi::primitive::*;
13
14/// Encapsulates parsed data about a `Type`.
15#[non_exhaustive]
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum TypeData<'t> {
18    Primitive(PrimitiveType),
19    Class(ClassType<'t>),
20    Member(MemberType<'t>),
21    MemberFunction(MemberFunctionType),
22    OverloadedMethod(OverloadedMethodType<'t>),
23    Method(MethodType<'t>),
24    StaticMember(StaticMemberType<'t>),
25    Nested(NestedType<'t>),
26    BaseClass(BaseClassType),
27    VirtualBaseClass(VirtualBaseClassType),
28    VirtualFunctionTablePointer(VirtualFunctionTablePointerType),
29    Procedure(ProcedureType),
30    Pointer(PointerType),
31    Modifier(ModifierType),
32    Enumeration(EnumerationType<'t>),
33    Enumerate(EnumerateType<'t>),
34    Array(ArrayType),
35    Union(UnionType<'t>),
36    Bitfield(BitfieldType),
37    FieldList(FieldList<'t>),
38    ArgumentList(ArgumentList),
39    MethodList(MethodList),
40}
41
42impl<'t> TypeData<'t> {
43    /// Return the name of this TypeData, if any
44    pub fn name(&self) -> Option<RawString<'t>> {
45        let name = match self {
46            Self::Class(ClassType { ref name, .. })
47            | Self::Member(MemberType { ref name, .. })
48            | Self::OverloadedMethod(OverloadedMethodType { ref name, .. })
49            | Self::StaticMember(StaticMemberType { ref name, .. })
50            | Self::Nested(NestedType { ref name, .. })
51            | Self::Enumeration(EnumerationType { ref name, .. })
52            | Self::Enumerate(EnumerateType { ref name, .. })
53            | Self::Union(UnionType { ref name, .. }) => name,
54            _ => return None,
55        };
56
57        Some(*name)
58    }
59}
60
61/// Parse a type out of a `ParseBuffer`.
62pub(crate) fn parse_type_data<'t>(buf: &mut ParseBuffer<'t>) -> Result<TypeData<'t>> {
63    let leaf = buf.parse_u16()?;
64
65    match leaf {
66        // Basic types
67        // -----------
68
69        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L1631-L1642
70        LF_CLASS | LF_CLASS_ST | LF_STRUCTURE | LF_STRUCTURE_ST | LF_INTERFACE => {
71            let mut class = ClassType {
72                kind: match leaf {
73                    LF_CLASS | LF_CLASS_ST => ClassKind::Class,
74                    LF_STRUCTURE | LF_STRUCTURE_ST => ClassKind::Struct,
75                    LF_INTERFACE => ClassKind::Interface,
76                    _ => unreachable!(),
77                },
78                count: buf.parse_u16()?,
79                properties: TypeProperties(buf.parse_u16()?),
80                fields: parse_optional_type_index(buf)?,
81                derived_from: parse_optional_type_index(buf)?,
82                vtable_shape: parse_optional_type_index(buf)?,
83                size: parse_unsigned(buf)?,
84                name: parse_string(leaf, buf)?,
85                unique_name: None,
86            };
87
88            if class.properties.has_unique_name() {
89                class.unique_name = Some(parse_string(leaf, buf)?);
90            }
91
92            Ok(TypeData::Class(class))
93        }
94
95        // https://github.com/microsoft/microsoft-pdb/issues/50#issuecomment-737890766
96        LF_STRUCTURE19 => {
97            let mut class = ClassType {
98                kind: ClassKind::Struct,
99                properties: TypeProperties(buf.parse_u32()? as u16),
100                fields: parse_optional_type_index(buf)?,
101                derived_from: parse_optional_type_index(buf)?,
102                vtable_shape: parse_optional_type_index(buf)?,
103                count: buf.parse_u16()?,
104                size: parse_unsigned(buf)?,
105                name: parse_string(leaf, buf)?,
106                unique_name: None,
107            };
108
109            if class.properties.has_unique_name() {
110                class.unique_name = Some(parse_string(leaf, buf)?);
111            }
112
113            Ok(TypeData::Class(class))
114        }
115
116        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L2580-L2586
117        LF_MEMBER | LF_MEMBER_ST => Ok(TypeData::Member(MemberType {
118            attributes: FieldAttributes(buf.parse_u16()?),
119            field_type: buf.parse()?,
120            offset: parse_unsigned(buf)?,
121            name: parse_string(leaf, buf)?,
122        })),
123
124        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L2699-L2714
125        LF_NESTTYPE | LF_NESTTYPE_ST | LF_NESTTYPEEX | LF_NESTTYPEEX_ST => {
126            // These structs differ in their use of the first 16 bits
127            let raw_attr = match leaf {
128                LF_NESTTYPEEX | LF_NESTTYPEEX_ST => buf.parse_u16()?,
129                _ => {
130                    // discard padding
131                    buf.parse_u16()?;
132                    // assume zero
133                    0
134                }
135            };
136
137            Ok(TypeData::Nested(NestedType {
138                attributes: FieldAttributes(raw_attr),
139                nested_type: buf.parse()?,
140                name: parse_string(leaf, buf)?,
141            }))
142        }
143
144        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L1801-L1811
145        LF_MFUNCTION => Ok(TypeData::MemberFunction(MemberFunctionType {
146            return_type: buf.parse()?,
147            class_type: buf.parse()?,
148            this_pointer_type: parse_optional_type_index(buf)?,
149            attributes: FunctionAttributes(buf.parse_u16()?),
150            parameter_count: buf.parse_u16()?,
151            argument_list: buf.parse()?,
152            this_adjustment: buf.parse_u32()?,
153        })),
154
155        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L2650-L2655
156        LF_METHOD | LF_METHOD_ST => Ok(TypeData::OverloadedMethod(OverloadedMethodType {
157            count: buf.parse_u16()?,
158            method_list: buf.parse()?,
159            name: parse_string(leaf, buf)?,
160        })),
161
162        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L2671-L2678
163        LF_ONEMETHOD | LF_ONEMETHOD_ST => {
164            let attr = FieldAttributes(buf.parse_u16()?);
165            Ok(TypeData::Method(MethodType {
166                attributes: attr,
167                method_type: buf.parse()?,
168                vtable_offset: if attr.is_intro_virtual() {
169                    Some(buf.parse_u32()? as u32)
170                } else {
171                    // yes, this is variable length
172                    None
173                },
174                name: parse_string(leaf, buf)?,
175            }))
176        }
177
178        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L2499-L2505
179        LF_BCLASS | LF_BINTERFACE => Ok(TypeData::BaseClass(BaseClassType {
180            kind: match leaf {
181                LF_BCLASS => ClassKind::Class,
182                LF_BINTERFACE => ClassKind::Interface,
183                _ => unreachable!(),
184            },
185            attributes: FieldAttributes(buf.parse_u16()?),
186            base_class: buf.parse()?,
187            offset: parse_unsigned(buf)? as u32,
188        })),
189
190        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L2615-L2619
191        LF_VFUNCTAB => {
192            // padding is supposed to be zero always, but… let's not check
193            buf.parse_u16()?;
194            Ok(TypeData::VirtualFunctionTablePointer(
195                VirtualFunctionTablePointerType {
196                    table: buf.parse()?,
197                },
198            ))
199        }
200
201        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L2599-L2604
202        LF_STMEMBER | LF_STMEMBER_ST => Ok(TypeData::StaticMember(StaticMemberType {
203            attributes: FieldAttributes(buf.parse_u16()?),
204            field_type: buf.parse()?,
205            name: parse_string(leaf, buf)?,
206        })),
207
208        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L1469-L1506
209        LF_POINTER => {
210            let underlying_type = buf.parse()?;
211            let attributes = PointerAttributes(buf.parse()?);
212
213            let containing_class = if attributes.pointer_to_member() {
214                Some(buf.parse()?)
215            } else {
216                None
217            };
218
219            Ok(TypeData::Pointer(PointerType {
220                underlying_type,
221                attributes,
222                containing_class,
223            }))
224        }
225
226        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L1775-L1782
227        LF_PROCEDURE => Ok(TypeData::Procedure(ProcedureType {
228            return_type: parse_optional_type_index(buf)?,
229            attributes: FunctionAttributes(buf.parse_u16()?),
230            parameter_count: buf.parse_u16()?,
231            argument_list: buf.parse()?,
232        })),
233
234        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L1460-L1464
235        LF_MODIFIER => {
236            let type_index = buf.parse()?;
237
238            // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L1090-L1095
239            let flags = buf.parse_u16()?;
240
241            Ok(TypeData::Modifier(ModifierType {
242                underlying_type: type_index,
243                constant: (flags & 0x01) != 0,
244                volatile: (flags & 0x02) != 0,
245                unaligned: (flags & 0x04) != 0,
246            }))
247        }
248
249        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L1752-L1759
250        LF_ENUM | LF_ENUM_ST => {
251            let mut enumeration = EnumerationType {
252                count: buf.parse_u16()?,
253                properties: TypeProperties(buf.parse_u16()?),
254                underlying_type: buf.parse()?,
255                fields: buf.parse()?,
256                name: parse_string(leaf, buf)?,
257                unique_name: None,
258            };
259
260            if enumeration.properties.has_unique_name() {
261                enumeration.unique_name = Some(parse_string(leaf, buf)?);
262            }
263
264            Ok(TypeData::Enumeration(enumeration))
265        }
266
267        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L2683-L2688
268        LF_ENUMERATE | LF_ENUMERATE_ST => Ok(TypeData::Enumerate(EnumerateType {
269            attributes: FieldAttributes(buf.parse_u16()?),
270            value: buf.parse()?,
271            name: parse_string(leaf, buf)?,
272        })),
273
274        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L1564-L1579
275        LF_ARRAY | LF_ARRAY_ST | LF_STRIDED_ARRAY => {
276            let element_type = buf.parse()?;
277            let indexing_type = buf.parse()?;
278            let stride: Option<u32> = if leaf == LF_STRIDED_ARRAY {
279                Some(buf.parse_u32()?)
280            } else {
281                None
282            };
283
284            let mut dimensions: Vec<u32> = Vec::new();
285
286            loop {
287                let dim = parse_unsigned(buf)?;
288                if dim > u64::from(u32::max_value()) {
289                    return Err(Error::UnimplementedFeature("u64 array sizes"));
290                }
291                dimensions.push(dim as u32);
292
293                if buf.is_empty() {
294                    // shouldn't run out here
295                    return Err(Error::UnexpectedEof);
296                }
297
298                if buf.peek_u8()? == 0x00 {
299                    // end of dimensions
300                    buf.parse_u8()?;
301                    break;
302                }
303            }
304
305            // eat any padding
306            parse_padding(buf)?;
307
308            //println!("array: {:x}", buf);
309            //println!("dimensions: {:?}", dimensions);
310
311            assert!(buf.is_empty());
312
313            Ok(TypeData::Array(ArrayType {
314                element_type,
315                indexing_type,
316                stride,
317                dimensions,
318            }))
319        }
320
321        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L1657-L1664
322        LF_UNION | LF_UNION_ST => {
323            let mut union = UnionType {
324                count: buf.parse_u16()?,
325                properties: TypeProperties(buf.parse_u16()?),
326                fields: buf.parse()?,
327                size: parse_unsigned(buf)?,
328                name: parse_string(leaf, buf)?,
329                unique_name: None,
330            };
331
332            if union.properties.has_unique_name() {
333                union.unique_name = Some(parse_string(leaf, buf)?);
334            }
335
336            Ok(TypeData::Union(union))
337        }
338
339        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L2164-L2170
340        LF_BITFIELD => Ok(TypeData::Bitfield(BitfieldType {
341            underlying_type: buf.parse()?,
342            length: buf.parse_u8()?,
343            position: buf.parse_u8()?,
344        })),
345
346        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L1819-L1823
347        LF_VTSHAPE => {
348            // TODO
349            Err(Error::UnimplementedTypeKind(leaf))
350        }
351
352        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L1825-L1837
353        LF_VFTABLE => {
354            // TODO
355            Err(Error::UnimplementedTypeKind(leaf))
356        }
357
358        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L2521-L2528
359        LF_VBCLASS | LF_IVBCLASS => Ok(TypeData::VirtualBaseClass(VirtualBaseClassType {
360            direct: leaf == LF_VBCLASS,
361            attributes: FieldAttributes(buf.parse_u16()?),
362            base_class: buf.parse()?,
363            base_pointer: buf.parse()?,
364            base_pointer_offset: parse_unsigned(buf)? as u32,
365            virtual_base_offset: parse_unsigned(buf)? as u32,
366        })),
367
368        // List types
369        // ----------
370
371        // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L2112-L2115
372        LF_FIELDLIST => {
373            let mut fields: Vec<TypeData<'t>> = Vec::new();
374            let mut continuation: Option<TypeIndex> = None;
375
376            while !buf.is_empty() {
377                match buf.peek_u16()? {
378                    LF_INDEX => {
379                        // continuation record
380                        // eat the leaf value
381                        buf.parse_u16()?;
382
383                        // parse the TypeIndex where we continue
384                        continuation = Some(buf.parse()?);
385                    }
386                    _ => {
387                        // other type
388                        // recurse because recursion is endless fun because [STACK OVERFLOW]
389                        fields.push(parse_type_data(buf)?);
390                    }
391                }
392
393                // consume any padding
394                parse_padding(buf)?;
395            }
396
397            Ok(TypeData::FieldList(FieldList {
398                fields,
399                continuation,
400            }))
401        }
402
403        LF_ARGLIST => {
404            let count = buf.parse_u32()?;
405            let mut arglist: Vec<TypeIndex> = Vec::with_capacity(count as usize);
406            for _ in 0..count {
407                arglist.push(buf.parse()?);
408            }
409            Ok(TypeData::ArgumentList(ArgumentList { arguments: arglist }))
410        }
411
412        LF_METHODLIST => {
413            let mut methods: Vec<MethodListEntry> = Vec::new();
414
415            while !buf.is_empty() {
416                // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L2131-L2136
417                let attr = FieldAttributes(buf.parse_u16()?);
418                buf.parse_u16()?; // padding
419
420                methods.push(MethodListEntry {
421                    attributes: attr,
422                    method_type: buf.parse()?,
423                    vtable_offset: if attr.is_intro_virtual() {
424                        Some(buf.parse_u32()?)
425                    } else {
426                        None
427                    },
428                });
429            }
430
431            Ok(TypeData::MethodList(MethodList { methods }))
432        }
433
434        _ => Err(Error::UnimplementedTypeKind(leaf)),
435    }
436}
437
438#[inline]
439fn parse_optional_type_index(buf: &mut ParseBuffer<'_>) -> Result<Option<TypeIndex>> {
440    let index = buf.parse()?;
441    if index == TypeIndex(0) || index == TypeIndex(0xffff) {
442        Ok(None)
443    } else {
444        Ok(Some(index))
445    }
446}
447
448#[inline]
449fn parse_string<'t>(leaf: u16, buf: &mut ParseBuffer<'t>) -> Result<RawString<'t>> {
450    if leaf > LF_ST_MAX {
451        buf.parse_cstring()
452    } else {
453        buf.parse_u8_pascal_string()
454    }
455}
456
457#[inline]
458fn parse_padding(buf: &mut ParseBuffer<'_>) -> Result<()> {
459    while !buf.is_empty() && buf.peek_u8()? >= 0xf0 {
460        let padding = buf.parse_u8()?;
461        if padding > 0xf0 {
462            // low four bits indicate amount of padding
463            // (don't ask me what 0xf0 means, then)
464            buf.take((padding & 0x0f) as usize - 1)?;
465        }
466    }
467    Ok(())
468}
469
470// https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/pdbdump/pdbdump.cpp#L2417-L2456
471fn parse_unsigned(buf: &mut ParseBuffer<'_>) -> Result<u64> {
472    let leaf = buf.parse_u16()?;
473    if leaf < LF_NUMERIC {
474        // the u16 directly encodes a value
475        return Ok(u64::from(leaf));
476    }
477
478    match leaf {
479        LF_CHAR => Ok(u64::from(buf.parse_u8()?)),
480        LF_USHORT => Ok(u64::from(buf.parse_u16()?)),
481        LF_ULONG => Ok(u64::from(buf.parse_u32()?)),
482        LF_UQUADWORD => Ok(buf.parse_u64()?),
483        _ => {
484            if cfg!(debug_assertions) {
485                unreachable!();
486            } else {
487                Err(Error::UnexpectedNumericPrefix(leaf))
488            }
489        }
490    }
491}
492
493/*
494typedef struct CV_prop_t {
495unsigned short  packed      :1;     // true if structure is packed
496unsigned short  ctor        :1;     // true if constructors or destructors present
497unsigned short  ovlops      :1;     // true if overloaded operators present
498unsigned short  isnested    :1;     // true if this is a nested class
499unsigned short  cnested     :1;     // true if this class contains nested types
500unsigned short  opassign    :1;     // true if overloaded assignment (=)
501unsigned short  opcast      :1;     // true if casting methods
502unsigned short  fwdref      :1;     // true if forward reference (incomplete defn)
503unsigned short  scoped      :1;     // scoped definition
504unsigned short  hasuniquename :1;   // true if there is a decorated name following the regular name
505unsigned short  sealed      :1;     // true if class cannot be used as a base class
506unsigned short  hfa         :2;     // CV_HFA_e
507unsigned short  intrinsic   :1;     // true if class is an intrinsic type (e.g. __m128d)
508unsigned short  mocom       :2;     // CV_MOCOM_UDT_e
509} CV_prop_t;
510*/
511#[derive(Debug, Copy, Clone, PartialEq, Eq)]
512pub struct TypeProperties(u16);
513impl TypeProperties {
514    /// Indicates if a type is packed via `#pragma pack` or similar.
515    pub fn packed(self) -> bool {
516        self.0 & 0x0001 != 0
517    }
518
519    /// Indicates if a type has constructors or destructors.
520    pub fn constructors(self) -> bool {
521        self.0 & 0x0002 != 0
522    }
523
524    /// Indicates if a type has any overloaded operators.
525    pub fn overloaded_operators(self) -> bool {
526        self.0 & 0x0004 != 0
527    }
528
529    /// Indicates if a type is a nested type, e.g. a `union` defined inside a `class`.
530    pub fn is_nested_type(self) -> bool {
531        self.0 & 0x0008 != 0
532    }
533
534    /// Indicates if a type contains nested types.
535    pub fn contains_nested_types(self) -> bool {
536        self.0 & 0x0010 != 0
537    }
538
539    /// Indicates if a class has overloaded the assignment operator.
540    pub fn overloaded_assignment(self) -> bool {
541        self.0 & 0x0020 != 0
542    }
543    pub fn overloaded_casting(self) -> bool {
544        self.0 & 0x0040 != 0
545    }
546
547    /// Indicates if a type is a forward reference, i.e. an incomplete Type that serves as a
548    /// placeholder until a complete Type can be built. This is necessary for e.g. self-referential
549    /// data structures, but other more common declaration/definition idioms can cause forward
550    /// references too.
551    pub fn forward_reference(self) -> bool {
552        self.0 & 0x0080 != 0
553    }
554
555    pub fn scoped_definition(self) -> bool {
556        self.0 & 0x0100 != 0
557    }
558    pub fn has_unique_name(self) -> bool {
559        self.0 & 0x0200 != 0
560    }
561    pub fn sealed(self) -> bool {
562        self.0 & 0x0400 != 0
563    }
564    pub fn hfa(self) -> u8 {
565        ((self.0 & 0x1800) >> 11) as u8
566    }
567    pub fn intrinsic_type(self) -> bool {
568        self.0 & 0x1000 != 0
569    }
570    pub fn mocom(self) -> u8 {
571        ((self.0 & 0x6000) >> 14) as u8
572    }
573}
574
575/*
576typedef struct CV_fldattr_t {
577    unsigned short  access      :2;     // access protection CV_access_t
578    unsigned short  mprop       :3;     // method properties CV_methodprop_t
579    unsigned short  pseudo      :1;     // compiler generated fcn and does not exist
580    unsigned short  noinherit   :1;     // true if class cannot be inherited
581    unsigned short  noconstruct :1;     // true if class cannot be constructed
582    unsigned short  compgenx    :1;     // compiler generated fcn and does exist
583    unsigned short  sealed      :1;     // true if method cannot be overridden
584    unsigned short  unused      :6;     // unused
585} CV_fldattr_t;
586
587typedef enum CV_methodprop_e {
588    CV_MTvanilla        = 0x00,
589    CV_MTvirtual        = 0x01,
590    CV_MTstatic         = 0x02,
591    CV_MTfriend         = 0x03,
592    CV_MTintro          = 0x04,
593    CV_MTpurevirt       = 0x05,
594    CV_MTpureintro      = 0x06
595} CV_methodprop_e;
596
597*/
598#[derive(Debug, Copy, Clone, PartialEq, Eq)]
599pub struct FieldAttributes(u16);
600impl FieldAttributes {
601    #[inline]
602    pub fn access(self) -> u8 {
603        (self.0 & 0x0003) as u8
604    }
605    #[inline]
606    fn method_properties(self) -> u8 {
607        ((self.0 & 0x001c) >> 2) as u8
608    }
609
610    #[inline]
611    pub fn is_static(self) -> bool {
612        self.method_properties() == 0x02
613    }
614
615    #[inline]
616    pub fn is_virtual(self) -> bool {
617        self.method_properties() == 0x01
618    }
619
620    #[inline]
621    pub fn is_pure_virtual(self) -> bool {
622        self.method_properties() == 0x05
623    }
624
625    #[inline]
626    pub fn is_intro_virtual(self) -> bool {
627        matches!(self.method_properties(), 0x04 | 0x06)
628    }
629
630    // TODO
631}
632
633#[allow(unused)]
634#[repr(u8)]
635enum Access {
636    None = 0x00,
637    Private = 0x01,
638    Protected = 0x02,
639    Public = 0x03,
640}
641
642// CV_call_t and CV_funcattr_t are always found back to back
643// Treat them as a combined u16
644/*
645typedef struct CV_funcattr_t {
646    unsigned char  cxxreturnudt :1;  // true if C++ style ReturnUDT
647    unsigned char  ctor         :1;  // true if func is an instance constructor
648    unsigned char  ctorvbase    :1;  // true if func is an instance constructor of a class with virtual bases
649    unsigned char  unused       :5;  // unused
650} CV_funcattr_t;
651*/
652#[derive(Debug, Copy, Clone, PartialEq, Eq)]
653pub struct FunctionAttributes(u16);
654impl FunctionAttributes {
655    pub fn calling_convention(self) -> u8 {
656        (self.0 & 0xff) as u8
657    }
658    pub fn cxx_return_udt(self) -> bool {
659        (self.0 & 0x0100) > 0
660    }
661    pub fn is_constructor(self) -> bool {
662        (self.0 & 0x0200) > 0
663    }
664    pub fn is_constructor_with_virtual_bases(self) -> bool {
665        (self.0 & 0x0400) > 0
666    }
667}
668
669/// The kind of a `PointerType`.
670#[derive(Clone, Copy, Debug, PartialEq, Eq)]
671pub enum PointerKind {
672    /// 16 bit pointer.
673    Near16,
674    /// 16:16 far pointer.
675    Far16,
676    /// 16:16 huge pointer.
677    Huge16,
678    /// Based on segment.
679    BaseSeg,
680    /// Based on value of base.
681    BaseVal,
682    /// Based on segment value of base.
683    BaseSegVal,
684    /// Based on address of base.
685    BaseAddr,
686    /// Based on segment address of base.
687    BaseSegAddr,
688    /// Based on type.
689    BaseType,
690    /// Based on self.
691    BaseSelf,
692    /// 32-bit pointer.
693    Near32,
694    /// 48-bit 16:32 pointer.
695    Far32,
696    /// 64-bit pointer.
697    Ptr64,
698}
699
700/// The mode of a `PointerType`.
701#[derive(Clone, Copy, Debug, PartialEq, Eq)]
702pub enum PointerMode {
703    /// A regular pointer.
704    Pointer,
705    /// L-Value reference.
706    LValueReference,
707    /// Pointer to data member.
708    Member,
709    /// Pointer to member function.
710    MemberFunction,
711    /// R-Value reference.
712    RValueReference,
713}
714
715/*
716struct lfPointerAttr {
717    unsigned long   ptrtype     :5; // ordinal specifying pointer type (CV_ptrtype_e)
718    unsigned long   ptrmode     :3; // ordinal specifying pointer mode (CV_ptrmode_e)
719    unsigned long   isflat32    :1; // true if 0:32 pointer
720    unsigned long   isvolatile  :1; // TRUE if volatile pointer
721    unsigned long   isconst     :1; // TRUE if const pointer
722    unsigned long   isunaligned :1; // TRUE if unaligned pointer
723    unsigned long   isrestrict  :1; // TRUE if restricted pointer (allow agressive opts)
724    unsigned long   size        :6; // size of pointer (in bytes)
725    unsigned long   ismocom     :1; // TRUE if it is a MoCOM pointer (^ or %)
726    unsigned long   islref      :1; // TRUE if it is this pointer of member function with & ref-qualifier
727    unsigned long   isrref      :1; // TRUE if it is this pointer of member function with && ref-qualifier
728    unsigned long   unused      :10;// pad out to 32-bits for following cv_typ_t's
729} attr;
730*/
731
732#[derive(Clone, Copy, Debug, PartialEq, Eq)]
733pub struct PointerAttributes(u32);
734
735impl PointerAttributes {
736    /// Indicates the type of pointer.
737    pub fn pointer_kind(self) -> PointerKind {
738        match self.0 & 0x1f {
739            0x00 => PointerKind::Near16,
740            0x01 => PointerKind::Far16,
741            0x02 => PointerKind::Huge16,
742            0x03 => PointerKind::BaseSeg,
743            0x04 => PointerKind::BaseVal,
744            0x05 => PointerKind::BaseSegVal,
745            0x06 => PointerKind::BaseAddr,
746            0x07 => PointerKind::BaseSegAddr,
747            0x08 => PointerKind::BaseType,
748            0x09 => PointerKind::BaseSelf,
749            0x0a => PointerKind::Near32,
750            0x0b => PointerKind::Far32,
751            0x0c => PointerKind::Ptr64,
752            _ => unreachable!(),
753        }
754    }
755
756    /// Returns the mode of this pointer.
757    pub fn pointer_mode(self) -> PointerMode {
758        match (self.0 >> 5) & 0x7 {
759            0x00 => PointerMode::Pointer,
760            0x01 => PointerMode::LValueReference,
761            0x02 => PointerMode::Member,
762            0x03 => PointerMode::MemberFunction,
763            0x04 => PointerMode::RValueReference,
764            _ => unreachable!(),
765        }
766    }
767
768    /// Returns `true` if this points to a member (either data or function).
769    pub fn pointer_to_member(self) -> bool {
770        matches!(
771            self.pointer_mode(),
772            PointerMode::Member | PointerMode::MemberFunction
773        )
774    }
775
776    /// Returns `true` if this is a flat `0:32` pointer.
777    pub fn is_flat_32(self) -> bool {
778        (self.0 & 0x100) != 0
779    }
780
781    /// Returns `true` if this pointer is `volatile`.
782    pub fn is_volatile(self) -> bool {
783        (self.0 & 0x200) != 0
784    }
785
786    /// Returns `true` if this pointer is `const`.
787    pub fn is_const(self) -> bool {
788        (self.0 & 0x400) != 0
789    }
790
791    /// Returns `true` if this pointer is unaligned.
792    pub fn is_unaligned(self) -> bool {
793        (self.0 & 0x800) != 0
794    }
795
796    /// Returns `true` if this pointer is restricted (allow aggressive opts).
797    pub fn is_restrict(self) -> bool {
798        (self.0 & 0x1000) != 0
799    }
800
801    /// Is this a C++ reference, as opposed to a C pointer?
802    pub fn is_reference(self) -> bool {
803        matches!(
804            self.pointer_mode(),
805            PointerMode::LValueReference | PointerMode::RValueReference
806        )
807    }
808
809    /// The size of the pointer in bytes.
810    pub fn size(self) -> u8 {
811        let size = ((self.0 >> 13) & 0x3f) as u8;
812        if size != 0 {
813            return size;
814        }
815
816        match self.pointer_kind() {
817            PointerKind::Near32 | PointerKind::Far32 => 4,
818            PointerKind::Ptr64 => 8,
819            _ => 0,
820        }
821    }
822
823    /// Returns `true` if this is a MoCOM pointer (`^` or `%`).
824    pub fn is_mocom(self) -> bool {
825        (self.0 & 0x40000) != 0
826    }
827}
828
829/// The information parsed from a type record with kind
830/// `LF_CLASS`, `LF_CLASS_ST`, `LF_STRUCTURE`, `LF_STRUCTURE_ST` or `LF_INTERFACE`.
831// https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L1631
832#[derive(Debug, Clone, PartialEq, Eq)]
833pub struct ClassType<'t> {
834    pub kind: ClassKind,
835
836    /// Count of number of elements in this class
837    pub count: u16,
838    pub properties: TypeProperties,
839
840    /// Type index which describes the fields of this class
841    pub fields: Option<TypeIndex>,
842
843    /// Type index which describes the class from which this class is derived, if any
844    pub derived_from: Option<TypeIndex>,
845
846    /// Type index which describes the shape of the vtable for this class, if any
847    pub vtable_shape: Option<TypeIndex>,
848
849    pub size: u64,
850
851    /// Display name of the class including type parameters.
852    pub name: RawString<'t>,
853
854    /// Mangled name, if present.
855    pub unique_name: Option<RawString<'t>>,
856}
857
858/// Used by `ClassType` to distinguish class-like concepts.
859#[derive(Debug, Copy, Clone, PartialEq, Eq)]
860pub enum ClassKind {
861    Class,
862    Struct,
863    Interface,
864}
865
866/// The information parsed from a type record with kind `LF_MEMBER` or `LF_MEMBER_ST`.
867#[derive(Debug, Clone, PartialEq, Eq)]
868pub struct MemberType<'t> {
869    pub attributes: FieldAttributes,
870    pub field_type: TypeIndex,
871    pub offset: u64,
872    pub name: RawString<'t>,
873}
874
875/// The information parsed from a type record with kind `LF_MFUNCTION`.
876#[derive(Debug, Copy, Clone, PartialEq, Eq)]
877pub struct MemberFunctionType {
878    pub return_type: TypeIndex,
879    pub class_type: TypeIndex,
880    pub this_pointer_type: Option<TypeIndex>,
881    pub attributes: FunctionAttributes,
882    pub parameter_count: u16,
883    pub argument_list: TypeIndex,
884    pub this_adjustment: u32,
885}
886
887/// The information parsed from a type record with kind `LF_METHOD` or `LF_METHOD_ST`.
888#[derive(Debug, Clone, PartialEq, Eq)]
889pub struct OverloadedMethodType<'t> {
890    pub count: u16,
891    pub method_list: TypeIndex,
892    pub name: RawString<'t>,
893}
894
895/// The information parsed from a type record with kind `LF_ONEMETHOD` or `LF_ONEMETHOD_ST`.
896#[derive(Debug, Clone, PartialEq, Eq)]
897pub struct MethodType<'t> {
898    pub attributes: FieldAttributes,
899    pub method_type: TypeIndex,
900    pub vtable_offset: Option<u32>,
901    pub name: RawString<'t>,
902}
903
904/// The information parsed from a type record with kind `LF_STMEMBER` or `LF_STMEMBER_ST`.
905#[derive(Debug, Clone, PartialEq, Eq)]
906pub struct StaticMemberType<'t> {
907    pub attributes: FieldAttributes,
908    pub field_type: TypeIndex,
909    pub name: RawString<'t>,
910}
911
912/// The information parsed from a type record with kind
913/// `LF_NESTTYPE`, `LF_NESTTYPE_ST`, `LF_NESTTYPEEX`, or `LF_NESTTYPEEX_ST`.
914#[derive(Debug, Clone, PartialEq, Eq)]
915pub struct NestedType<'t> {
916    pub attributes: FieldAttributes,
917    pub nested_type: TypeIndex,
918    pub name: RawString<'t>,
919}
920
921/// The information parsed from a type record with kind `LF_BCLASS` or `LF_BINTERFACE`.
922#[derive(Debug, Copy, Clone, PartialEq, Eq)]
923pub struct BaseClassType {
924    pub kind: ClassKind,
925    pub attributes: FieldAttributes,
926    pub base_class: TypeIndex,
927
928    /// Describes the offset of the base class within the class
929    pub offset: u32,
930}
931
932/// The information parsed from a type record with kind `LF_VBCLASS` or `LF_IVBCLASS`.
933#[derive(Debug, Copy, Clone, PartialEq, Eq)]
934pub struct VirtualBaseClassType {
935    pub direct: bool,
936    pub attributes: FieldAttributes,
937    pub base_class: TypeIndex,
938    pub base_pointer: TypeIndex,
939
940    pub base_pointer_offset: u32,
941    pub virtual_base_offset: u32,
942}
943
944/// The information parsed from a type record with kind `LF_VFUNCTAB`.
945#[derive(Debug, Copy, Clone, PartialEq, Eq)]
946pub struct VirtualFunctionTablePointerType {
947    pub table: TypeIndex,
948}
949
950/// The information parsed from a type record with kind `LF_PROCEDURE`.
951#[derive(Debug, Copy, Clone, PartialEq, Eq)]
952pub struct ProcedureType {
953    pub return_type: Option<TypeIndex>,
954    pub attributes: FunctionAttributes,
955    pub parameter_count: u16,
956    pub argument_list: TypeIndex,
957}
958
959/// The information parsed from a type record with kind `LF_POINTER`.
960#[derive(Debug, Copy, Clone, PartialEq, Eq)]
961pub struct PointerType {
962    pub underlying_type: TypeIndex,
963    pub attributes: PointerAttributes,
964    pub containing_class: Option<TypeIndex>,
965}
966
967/// The information parsed from a type record with kind `LF_MODIFIER`.
968#[derive(Debug, Copy, Clone, PartialEq, Eq)]
969pub struct ModifierType {
970    pub underlying_type: TypeIndex,
971    pub constant: bool,
972    pub volatile: bool,
973    pub unaligned: bool,
974}
975
976/// The information parsed from a type record with kind `LF_ENUM` or `LF_ENUM_ST`.
977#[derive(Debug, Clone, PartialEq, Eq)]
978pub struct EnumerationType<'t> {
979    pub count: u16,
980    pub properties: TypeProperties,
981    pub underlying_type: TypeIndex,
982    pub fields: TypeIndex,
983    pub name: RawString<'t>,
984    pub unique_name: Option<RawString<'t>>,
985}
986
987/// The information parsed from a type record with kind `LF_ENUMERATE` or `LF_ENUMERATE_ST`.
988#[derive(Debug, Clone, PartialEq, Eq)]
989pub struct EnumerateType<'t> {
990    pub attributes: FieldAttributes,
991    pub value: Variant,
992    pub name: RawString<'t>,
993}
994
995/// The information parsed from a type record with kind
996/// `LF_ARRAY`, `LF_ARRAY_ST` or `LF_STRIDED_ARRAY`.
997#[derive(Debug, Clone, PartialEq, Eq)]
998pub struct ArrayType {
999    pub element_type: TypeIndex,
1000    pub indexing_type: TypeIndex,
1001    pub stride: Option<u32>,
1002
1003    /// Contains array dimensions as specified in the PDB. This is not what you expect:
1004    ///
1005    /// * Dimensions are specified in terms of byte sizes, not element counts.
1006    /// * Multidimensional arrays aggregate the lower dimensions into the sizes of the higher
1007    ///   dimensions.
1008    ///
1009    /// Thus a `float[4][4]` has `dimensions: [16, 64]`. Determining array dimensions in terms
1010    /// of element counts requires determining the size of the `element_type` and iteratively
1011    /// dividing.
1012    pub dimensions: Vec<u32>,
1013}
1014
1015/// The information parsed from a type record with kind `LF_UNION` or `LF_UNION_ST`.
1016#[derive(Debug, Clone, PartialEq, Eq)]
1017pub struct UnionType<'t> {
1018    pub count: u16,
1019    pub properties: TypeProperties,
1020    pub fields: TypeIndex,
1021    pub size: u64,
1022    pub name: RawString<'t>,
1023    pub unique_name: Option<RawString<'t>>,
1024}
1025
1026/// The information parsed from a type record with kind `LF_BITFIELD`.
1027#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1028pub struct BitfieldType {
1029    pub underlying_type: TypeIndex,
1030    pub length: u8,
1031    pub position: u8,
1032}
1033
1034/// The information parsed from a type record with kind `LF_FIELDLIST`.
1035#[derive(Debug, Clone, PartialEq, Eq)]
1036pub struct FieldList<'t> {
1037    pub fields: Vec<TypeData<'t>>,
1038
1039    /// Sometimes fields can't all fit in a single FieldList, in which case the FieldList
1040    /// refers to another FieldList in a chain.
1041    pub continuation: Option<TypeIndex>,
1042}
1043
1044/// The information parsed from a type record with kind `LF_ARGLIST`.
1045#[derive(Debug, Clone, PartialEq, Eq)]
1046pub struct ArgumentList {
1047    pub arguments: Vec<TypeIndex>,
1048}
1049
1050/// The information parsed from a type record with kind `LF_METHODLIST`.
1051#[derive(Debug, Clone, PartialEq, Eq)]
1052pub struct MethodList {
1053    pub methods: Vec<MethodListEntry>,
1054}
1055
1056/// An entry in a `MethodList`.
1057#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1058pub struct MethodListEntry {
1059    pub attributes: FieldAttributes,
1060    pub method_type: TypeIndex,
1061    pub vtable_offset: Option<u32>,
1062}
1063
1064/*
1065// arrays:
1066ParseBuf::from("\x03\x15\xa0\xdc\x0b\x00\x23\x00\x00\x00\x40\x00\x00\xf1").as_bytes(),
1067ParseBuf::from("\x03\x15\xa0\xdc\x0b\x00\x23\x00\x00\x00\x50\x00\x00\xf1").as_bytes(),
1068ParseBuf::from("\x03\x15\xa9\x12\x00\x00\x23\x00\x00\x00\x50\x02\x00\xf1").as_bytes(),
1069ParseBuf::from("\x03\x15\xac\x12\x00\x00\x23\x00\x00\x00\x6c\x00\x00\xf1").as_bytes(),
1070ParseBuf::from("\x03\x15\x14\x10\x00\x00\x23\x00\x00\x00\x80\x00\x00\xf1").as_bytes(),
1071ParseBuf::from("\x03\x15\x75\x00\x00\x00\x23\x00\x00\x00\x28\x00\x00\xf1").as_bytes(),
1072ParseBuf::from("\x03\x15\x14\x10\x00\x00\x23\x00\x00\x00\x70\x0e\x00\xf1").as_bytes(),
1073ParseBuf::from("\x03\x15\x31\x14\x00\x00\x23\x00\x00\x00\x04\x02\x00\xf1").as_bytes(),
1074ParseBuf::from("\x03\x15\x31\x14\x00\x00\x23\x00\x00\x00\x0e\x03\x00\xf1").as_bytes(),
1075ParseBuf::from("\x03\x15\x77\x13\x00\x00\x23\x00\x00\x00\x02\x80\xbd\xda\x00\xf3\xf2\xf1").as_bytes(),
1076ParseBuf::from("\x03\x15\xb7\x16\x00\x00\x23\x00\x00\x00\x28\x00\x00\xf1").as_bytes(),
1077ParseBuf::from("\x03\x15\x14\x10\x00\x00\x23\x00\x00\x00\x55\x00\x00\xf1").as_bytes(),
1078*/
1079
1080#[test]
1081fn kind_1609() {
1082    let data = &[
1083        9, 22, 0, 2, 0, 0, 22, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 6, 0, 72, 95, 115, 105, 122,
1084        101, 0, 46, 63, 65, 85, 72, 95, 115, 105, 122, 101, 64, 64, 0,
1085    ][..];
1086
1087    assert_eq!(
1088        parse_type_data(&mut ParseBuffer::from(data)).expect("parse"),
1089        TypeData::Class(ClassType {
1090            kind: ClassKind::Struct,
1091            count: 2,
1092            properties: TypeProperties(512),
1093            fields: Some(TypeIndex(0x1016)),
1094            derived_from: None,
1095            vtable_shape: None,
1096            size: 6,
1097            name: RawString::from("H_size"),
1098            unique_name: Some(RawString::from(".?AUH_size@@")),
1099        })
1100    );
1101}