#![allow(missing_docs)]
use crate::common::*;
use crate::tpi::constants::*;
use crate::tpi::primitive::*;
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TypeData<'t> {
Primitive(PrimitiveType),
Class(ClassType<'t>),
Member(MemberType<'t>),
MemberFunction(MemberFunctionType),
OverloadedMethod(OverloadedMethodType<'t>),
Method(MethodType<'t>),
StaticMember(StaticMemberType<'t>),
Nested(NestedType<'t>),
BaseClass(BaseClassType),
VirtualBaseClass(VirtualBaseClassType),
VirtualFunctionTablePointer(VirtualFunctionTablePointerType),
Procedure(ProcedureType),
Pointer(PointerType),
Modifier(ModifierType),
Enumeration(EnumerationType<'t>),
Enumerate(EnumerateType<'t>),
Array(ArrayType),
Union(UnionType<'t>),
Bitfield(BitfieldType),
FieldList(FieldList<'t>),
ArgumentList(ArgumentList),
MethodList(MethodList),
}
impl<'t> TypeData<'t> {
pub fn name(&self) -> Option<RawString<'t>> {
let name = match *self {
TypeData::Class(ClassType { ref name, .. })
| TypeData::Member(MemberType { ref name, .. })
| TypeData::OverloadedMethod(OverloadedMethodType { ref name, .. })
| TypeData::StaticMember(StaticMemberType { ref name, .. })
| TypeData::Nested(NestedType { ref name, .. })
| TypeData::Enumeration(EnumerationType { ref name, .. })
| TypeData::Enumerate(EnumerateType { ref name, .. })
| TypeData::Union(UnionType { ref name, .. }) => name,
_ => return None,
};
Some(*name)
}
}
pub(crate) fn parse_type_data<'t>(mut buf: &mut ParseBuffer<'t>) -> Result<TypeData<'t>> {
let leaf = buf.parse_u16()?;
match leaf {
LF_CLASS | LF_CLASS_ST | LF_STRUCTURE | LF_STRUCTURE_ST | LF_INTERFACE => {
let mut class = ClassType {
kind: match leaf {
LF_CLASS | LF_CLASS_ST => ClassKind::Class,
LF_STRUCTURE | LF_STRUCTURE_ST => ClassKind::Struct,
LF_INTERFACE => ClassKind::Interface,
_ => unreachable!(),
},
count: buf.parse_u16()?,
properties: TypeProperties(buf.parse_u16()?),
fields: parse_optional_type_index(&mut buf)?,
derived_from: parse_optional_type_index(&mut buf)?,
vtable_shape: parse_optional_type_index(&mut buf)?,
size: parse_unsigned(&mut buf)? as u16,
name: parse_string(leaf, buf)?,
unique_name: None,
};
if class.properties.has_unique_name() {
class.unique_name = Some(parse_string(leaf, buf)?);
}
Ok(TypeData::Class(class))
}
LF_STRUCTURE19 => {
let mut class = ClassType {
kind: ClassKind::Struct,
properties: TypeProperties(buf.parse_u32()? as u16),
fields: parse_optional_type_index(&mut buf)?,
derived_from: parse_optional_type_index(&mut buf)?,
vtable_shape: parse_optional_type_index(&mut buf)?,
count: buf.parse_u16()?,
size: parse_unsigned(&mut buf)? as u16,
name: parse_string(leaf, buf)?,
unique_name: None,
};
if class.properties.has_unique_name() {
class.unique_name = Some(parse_string(leaf, buf)?);
}
Ok(TypeData::Class(class))
}
LF_MEMBER | LF_MEMBER_ST => Ok(TypeData::Member(MemberType {
attributes: FieldAttributes(buf.parse_u16()?),
field_type: buf.parse()?,
offset: parse_unsigned(&mut buf)? as u16,
name: parse_string(leaf, &mut buf)?,
})),
LF_NESTTYPE | LF_NESTTYPE_ST | LF_NESTTYPEEX | LF_NESTTYPEEX_ST => {
let raw_attr = match leaf {
LF_NESTTYPEEX | LF_NESTTYPEEX_ST => buf.parse_u16()?,
_ => {
buf.parse_u16()?;
0
}
};
Ok(TypeData::Nested(NestedType {
attributes: FieldAttributes(raw_attr),
nested_type: buf.parse()?,
name: parse_string(leaf, &mut buf)?,
}))
}
LF_MFUNCTION => Ok(TypeData::MemberFunction(MemberFunctionType {
return_type: buf.parse()?,
class_type: buf.parse()?,
this_pointer_type: parse_optional_type_index(&mut buf)?,
attributes: FunctionAttributes(buf.parse_u16()?),
parameter_count: buf.parse_u16()?,
argument_list: buf.parse()?,
this_adjustment: buf.parse_u32()?,
})),
LF_METHOD | LF_METHOD_ST => Ok(TypeData::OverloadedMethod(OverloadedMethodType {
count: buf.parse_u16()?,
method_list: buf.parse()?,
name: parse_string(leaf, &mut buf)?,
})),
LF_ONEMETHOD | LF_ONEMETHOD_ST => {
let attr = FieldAttributes(buf.parse_u16()?);
Ok(TypeData::Method(MethodType {
attributes: attr,
method_type: buf.parse()?,
vtable_offset: if attr.is_intro_virtual() {
Some(buf.parse_u32()? as u32)
} else {
None
},
name: parse_string(leaf, &mut buf)?,
}))
}
LF_BCLASS | LF_BINTERFACE => Ok(TypeData::BaseClass(BaseClassType {
kind: match leaf {
LF_BCLASS => ClassKind::Class,
LF_BINTERFACE => ClassKind::Interface,
_ => unreachable!(),
},
attributes: FieldAttributes(buf.parse_u16()?),
base_class: buf.parse()?,
offset: parse_unsigned(&mut buf)? as u32,
})),
LF_VFUNCTAB => {
buf.parse_u16()?;
Ok(TypeData::VirtualFunctionTablePointer(
VirtualFunctionTablePointerType {
table: buf.parse()?,
},
))
}
LF_STMEMBER | LF_STMEMBER_ST => Ok(TypeData::StaticMember(StaticMemberType {
attributes: FieldAttributes(buf.parse_u16()?),
field_type: buf.parse()?,
name: parse_string(leaf, &mut buf)?,
})),
LF_POINTER => {
let underlying_type = buf.parse()?;
let attributes = PointerAttributes(buf.parse()?);
let containing_class = if attributes.pointer_to_member() {
Some(buf.parse()?)
} else {
None
};
Ok(TypeData::Pointer(PointerType {
underlying_type,
attributes,
containing_class,
}))
}
LF_PROCEDURE => Ok(TypeData::Procedure(ProcedureType {
return_type: parse_optional_type_index(&mut buf)?,
attributes: FunctionAttributes(buf.parse_u16()?),
parameter_count: buf.parse_u16()?,
argument_list: buf.parse()?,
})),
LF_MODIFIER => {
let type_index = buf.parse()?;
let flags = buf.parse_u16()?;
Ok(TypeData::Modifier(ModifierType {
underlying_type: type_index,
constant: (flags & 0x01) != 0,
volatile: (flags & 0x02) != 0,
unaligned: (flags & 0x04) != 0,
}))
}
LF_ENUM | LF_ENUM_ST => {
let mut enumeration = EnumerationType {
count: buf.parse_u16()?,
properties: TypeProperties(buf.parse_u16()?),
underlying_type: buf.parse()?,
fields: buf.parse()?,
name: parse_string(leaf, &mut buf)?,
unique_name: None,
};
if enumeration.properties.has_unique_name() {
enumeration.unique_name = Some(parse_string(leaf, &mut buf)?);
}
Ok(TypeData::Enumeration(enumeration))
}
LF_ENUMERATE | LF_ENUMERATE_ST => Ok(TypeData::Enumerate(EnumerateType {
attributes: FieldAttributes(buf.parse_u16()?),
value: buf.parse()?,
name: parse_string(leaf, &mut buf)?,
})),
LF_ARRAY | LF_ARRAY_ST | LF_STRIDED_ARRAY => {
let element_type = buf.parse()?;
let indexing_type = buf.parse()?;
let stride: Option<u32> = if leaf == LF_STRIDED_ARRAY {
Some(buf.parse_u32()?)
} else {
None
};
let mut dimensions: Vec<u32> = Vec::new();
loop {
let dim = parse_unsigned(&mut buf)?;
if dim > u64::from(u32::max_value()) {
return Err(Error::UnimplementedFeature("u64 array sizes"));
}
dimensions.push(dim as u32);
if buf.is_empty() {
return Err(Error::UnexpectedEof);
}
if buf.peek_u8()? == 0x00 {
buf.parse_u8()?;
break;
}
}
parse_padding(&mut buf)?;
assert!(buf.is_empty());
Ok(TypeData::Array(ArrayType {
element_type,
indexing_type,
stride,
dimensions,
}))
}
LF_UNION | LF_UNION_ST => {
let mut union = UnionType {
count: buf.parse_u16()?,
properties: TypeProperties(buf.parse_u16()?),
fields: buf.parse()?,
size: parse_unsigned(&mut buf)? as u32,
name: parse_string(leaf, &mut buf)?,
unique_name: None,
};
if union.properties.has_unique_name() {
union.unique_name = Some(parse_string(leaf, &mut buf)?);
}
Ok(TypeData::Union(union))
}
LF_BITFIELD => Ok(TypeData::Bitfield(BitfieldType {
underlying_type: buf.parse()?,
length: buf.parse_u8()?,
position: buf.parse_u8()?,
})),
LF_VTSHAPE => {
Err(Error::UnimplementedTypeKind(leaf))
}
LF_VFTABLE => {
Err(Error::UnimplementedTypeKind(leaf))
}
LF_VBCLASS | LF_IVBCLASS => Ok(TypeData::VirtualBaseClass(VirtualBaseClassType {
direct: leaf == LF_VBCLASS,
attributes: FieldAttributes(buf.parse_u16()?),
base_class: buf.parse()?,
base_pointer: buf.parse()?,
base_pointer_offset: parse_unsigned(&mut buf)? as u32,
virtual_base_offset: parse_unsigned(&mut buf)? as u32,
})),
LF_FIELDLIST => {
let mut fields: Vec<TypeData<'t>> = Vec::new();
let mut continuation: Option<TypeIndex> = None;
while !buf.is_empty() {
match buf.peek_u16()? {
LF_INDEX => {
buf.parse_u16()?;
continuation = Some(buf.parse()?);
}
_ => {
fields.push(parse_type_data(&mut buf)?);
}
}
parse_padding(&mut buf)?;
}
Ok(TypeData::FieldList(FieldList {
fields,
continuation,
}))
}
LF_ARGLIST => {
let count = buf.parse_u32()?;
let mut arglist: Vec<TypeIndex> = Vec::with_capacity(count as usize);
for _ in 0..count {
arglist.push(buf.parse()?);
}
Ok(TypeData::ArgumentList(ArgumentList { arguments: arglist }))
}
LF_METHODLIST => {
let mut methods: Vec<MethodListEntry> = Vec::new();
while !buf.is_empty() {
let attr = FieldAttributes(buf.parse_u16()?);
buf.parse_u16()?;
methods.push(MethodListEntry {
attributes: attr,
method_type: buf.parse()?,
vtable_offset: if attr.is_intro_virtual() {
Some(buf.parse_u32()?)
} else {
None
},
});
}
Ok(TypeData::MethodList(MethodList { methods }))
}
_ => Err(Error::UnimplementedTypeKind(leaf)),
}
}
#[inline]
fn parse_optional_type_index(buf: &mut ParseBuffer<'_>) -> Result<Option<TypeIndex>> {
let index = buf.parse()?;
if index == TypeIndex(0) || index == TypeIndex(0xffff) {
Ok(None)
} else {
Ok(Some(index))
}
}
#[inline]
fn parse_string<'t>(leaf: u16, buf: &mut ParseBuffer<'t>) -> Result<RawString<'t>> {
if leaf > LF_ST_MAX {
buf.parse_cstring()
} else {
buf.parse_u8_pascal_string()
}
}
#[inline]
fn parse_padding(buf: &mut ParseBuffer<'_>) -> Result<()> {
while !buf.is_empty() && buf.peek_u8()? >= 0xf0 {
let padding = buf.parse_u8()?;
if padding > 0xf0 {
buf.take((padding & 0x0f) as usize - 1)?;
}
}
Ok(())
}
fn parse_unsigned(buf: &mut ParseBuffer<'_>) -> Result<u64> {
let leaf = buf.parse_u16()?;
if leaf < LF_NUMERIC {
return Ok(u64::from(leaf));
}
match leaf {
LF_CHAR => Ok(u64::from(buf.parse_u8()?)),
LF_USHORT => Ok(u64::from(buf.parse_u16()?)),
LF_ULONG => Ok(u64::from(buf.parse_u32()?)),
LF_UQUADWORD => Ok(buf.parse_u64()?),
_ => {
if cfg!(debug_assertions) {
unreachable!();
} else {
Err(Error::UnexpectedNumericPrefix(leaf))
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct TypeProperties(u16);
impl TypeProperties {
pub fn packed(self) -> bool {
self.0 & 0x0001 != 0
}
pub fn constructors(self) -> bool {
self.0 & 0x0002 != 0
}
pub fn overloaded_operators(self) -> bool {
self.0 & 0x0004 != 0
}
pub fn is_nested_type(self) -> bool {
self.0 & 0x0008 != 0
}
pub fn contains_nested_types(self) -> bool {
self.0 & 0x0010 != 0
}
pub fn overloaded_assignment(self) -> bool {
self.0 & 0x0020 != 0
}
pub fn overloaded_casting(self) -> bool {
self.0 & 0x0040 != 0
}
pub fn forward_reference(self) -> bool {
self.0 & 0x0080 != 0
}
pub fn scoped_definition(self) -> bool {
self.0 & 0x0100 != 0
}
pub fn has_unique_name(self) -> bool {
self.0 & 0x0200 != 0
}
pub fn sealed(self) -> bool {
self.0 & 0x0400 != 0
}
pub fn hfa(self) -> u8 {
((self.0 & 0x1800) >> 11) as u8
}
pub fn intrinsic_type(self) -> bool {
self.0 & 0x1000 != 0
}
pub fn mocom(self) -> u8 {
((self.0 & 0x6000) >> 14) as u8
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct FieldAttributes(u16);
impl FieldAttributes {
#[inline]
pub fn access(self) -> u8 {
(self.0 & 0x0003) as u8
}
#[inline]
fn method_properties(self) -> u8 {
((self.0 & 0x001c) >> 2) as u8
}
#[inline]
pub fn is_static(self) -> bool {
self.method_properties() == 0x02
}
#[inline]
pub fn is_virtual(self) -> bool {
self.method_properties() == 0x01
}
#[inline]
pub fn is_pure_virtual(self) -> bool {
self.method_properties() == 0x05
}
#[inline]
pub fn is_intro_virtual(self) -> bool {
matches!(self.method_properties(), 0x04 | 0x06)
}
}
#[allow(unused)]
#[repr(u8)]
enum Access {
None = 0x00,
Private = 0x01,
Protected = 0x02,
Public = 0x03,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct FunctionAttributes(u16);
impl FunctionAttributes {
pub fn calling_convention(self) -> u8 {
(self.0 & 0xff) as u8
}
pub fn cxx_return_udt(self) -> bool {
(self.0 & 0x0100) > 0
}
pub fn is_constructor(self) -> bool {
(self.0 & 0x0200) > 0
}
pub fn is_constructor_with_virtual_bases(self) -> bool {
(self.0 & 0x0400) > 0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PointerKind {
Near16,
Far16,
Huge16,
BaseSeg,
BaseVal,
BaseSegVal,
BaseAddr,
BaseSegAddr,
BaseType,
BaseSelf,
Near32,
Far32,
Ptr64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PointerMode {
Pointer,
LValueReference,
Member,
MemberFunction,
RValueReference,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PointerAttributes(u32);
impl PointerAttributes {
pub fn pointer_kind(self) -> PointerKind {
match self.0 & 0x1f {
0x00 => PointerKind::Near16,
0x01 => PointerKind::Far16,
0x02 => PointerKind::Huge16,
0x03 => PointerKind::BaseSeg,
0x04 => PointerKind::BaseVal,
0x05 => PointerKind::BaseSegVal,
0x06 => PointerKind::BaseAddr,
0x07 => PointerKind::BaseSegAddr,
0x08 => PointerKind::BaseType,
0x09 => PointerKind::BaseSelf,
0x0a => PointerKind::Near32,
0x0b => PointerKind::Far32,
0x0c => PointerKind::Ptr64,
_ => unreachable!(),
}
}
pub fn pointer_mode(self) -> PointerMode {
match (self.0 >> 5) & 0x7 {
0x00 => PointerMode::Pointer,
0x01 => PointerMode::LValueReference,
0x02 => PointerMode::Member,
0x03 => PointerMode::MemberFunction,
0x04 => PointerMode::RValueReference,
_ => unreachable!(),
}
}
pub fn pointer_to_member(self) -> bool {
matches!(
self.pointer_mode(),
PointerMode::Member | PointerMode::MemberFunction
)
}
pub fn is_flat_32(self) -> bool {
(self.0 & 0x100) != 0
}
pub fn is_volatile(self) -> bool {
(self.0 & 0x200) != 0
}
pub fn is_const(self) -> bool {
(self.0 & 0x400) != 0
}
pub fn is_unaligned(self) -> bool {
(self.0 & 0x800) != 0
}
pub fn is_restrict(self) -> bool {
(self.0 & 0x1000) != 0
}
pub fn is_reference(self) -> bool {
matches!(
self.pointer_mode(),
PointerMode::LValueReference | PointerMode::RValueReference
)
}
pub fn size(self) -> u8 {
let size = ((self.0 >> 13) & 0x3f) as u8;
if size != 0 {
return size;
}
match self.pointer_kind() {
PointerKind::Near32 | PointerKind::Far32 => 4,
PointerKind::Ptr64 => 8,
_ => 0,
}
}
pub fn is_mocom(self) -> bool {
(self.0 & 0x40000) != 0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ClassType<'t> {
pub kind: ClassKind,
pub count: u16,
pub properties: TypeProperties,
pub fields: Option<TypeIndex>,
pub derived_from: Option<TypeIndex>,
pub vtable_shape: Option<TypeIndex>,
pub size: u16,
pub name: RawString<'t>,
pub unique_name: Option<RawString<'t>>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ClassKind {
Class,
Struct,
Interface,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MemberType<'t> {
pub attributes: FieldAttributes,
pub field_type: TypeIndex,
pub offset: u16,
pub name: RawString<'t>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct MemberFunctionType {
pub return_type: TypeIndex,
pub class_type: TypeIndex,
pub this_pointer_type: Option<TypeIndex>,
pub attributes: FunctionAttributes,
pub parameter_count: u16,
pub argument_list: TypeIndex,
pub this_adjustment: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OverloadedMethodType<'t> {
pub count: u16,
pub method_list: TypeIndex,
pub name: RawString<'t>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MethodType<'t> {
pub attributes: FieldAttributes,
pub method_type: TypeIndex,
pub vtable_offset: Option<u32>,
pub name: RawString<'t>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StaticMemberType<'t> {
pub attributes: FieldAttributes,
pub field_type: TypeIndex,
pub name: RawString<'t>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NestedType<'t> {
pub attributes: FieldAttributes,
pub nested_type: TypeIndex,
pub name: RawString<'t>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BaseClassType {
pub kind: ClassKind,
pub attributes: FieldAttributes,
pub base_class: TypeIndex,
pub offset: u32,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct VirtualBaseClassType {
pub direct: bool,
pub attributes: FieldAttributes,
pub base_class: TypeIndex,
pub base_pointer: TypeIndex,
pub base_pointer_offset: u32,
pub virtual_base_offset: u32,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct VirtualFunctionTablePointerType {
pub table: TypeIndex,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ProcedureType {
pub return_type: Option<TypeIndex>,
pub attributes: FunctionAttributes,
pub parameter_count: u16,
pub argument_list: TypeIndex,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct PointerType {
pub underlying_type: TypeIndex,
pub attributes: PointerAttributes,
pub containing_class: Option<TypeIndex>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ModifierType {
pub underlying_type: TypeIndex,
pub constant: bool,
pub volatile: bool,
pub unaligned: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumerationType<'t> {
pub count: u16,
pub properties: TypeProperties,
pub underlying_type: TypeIndex,
pub fields: TypeIndex,
pub name: RawString<'t>,
pub unique_name: Option<RawString<'t>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumerateType<'t> {
pub attributes: FieldAttributes,
pub value: Variant,
pub name: RawString<'t>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArrayType {
pub element_type: TypeIndex,
pub indexing_type: TypeIndex,
pub stride: Option<u32>,
pub dimensions: Vec<u32>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnionType<'t> {
pub count: u16,
pub properties: TypeProperties,
pub fields: TypeIndex,
pub size: u32,
pub name: RawString<'t>,
pub unique_name: Option<RawString<'t>>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BitfieldType {
pub underlying_type: TypeIndex,
pub length: u8,
pub position: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FieldList<'t> {
pub fields: Vec<TypeData<'t>>,
pub continuation: Option<TypeIndex>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArgumentList {
pub arguments: Vec<TypeIndex>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MethodList {
pub methods: Vec<MethodListEntry>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct MethodListEntry {
pub attributes: FieldAttributes,
pub method_type: TypeIndex,
pub vtable_offset: Option<u32>,
}
#[test]
fn kind_1609() {
let data = &[
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,
101, 0, 46, 63, 65, 85, 72, 95, 115, 105, 122, 101, 64, 64, 0,
][..];
assert_eq!(
parse_type_data(&mut ParseBuffer::from(data)).expect("parse"),
TypeData::Class(ClassType {
kind: ClassKind::Struct,
count: 2,
properties: TypeProperties(512),
fields: Some(TypeIndex(0x1016)),
derived_from: None,
vtable_shape: None,
size: 6,
name: RawString::from("H_size"),
unique_name: Some(RawString::from(".?AUH_size@@")),
})
);
}