use crate::error::Error;
use bitflags::bitflags;
use pdb::{
ArgumentList, ArrayType, ClassKind, ClassType, CrossModuleExports, CrossModuleImports,
CrossModuleRef, DebugInformation, FallibleIterator, FunctionAttributes, IdData, IdIndex,
IdInformation, Item, ItemFinder, ItemIndex, ItemIter, MachineType, MemberFunctionType,
ModifierType, Module, ModuleInfo, PointerMode, PointerType, PrimitiveKind, PrimitiveType,
ProcedureType, RawString, StringTable, TypeData, TypeIndex, TypeInformation, UnionType,
Variant,
};
use range_collections::range_set::RangeSetRange;
use range_collections::{RangeSet, RangeSet2};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt::Write;
use std::mem;
use std::sync::Mutex;
type Result<V> = std::result::Result<V, Error>;
bitflags! {
#[derive(Clone, Copy)]
pub struct TypeFormatterFlags: u32 {
const NO_FUNCTION_RETURN = 0b1;
const NO_MEMBER_FUNCTION_STATIC = 0b10;
const SPACE_AFTER_COMMA = 0b100;
const SPACE_BEFORE_POINTER = 0b1000;
const NAME_ONLY = 0b10000;
const NO_ARGUMENTS = 0b100000;
}
}
impl Default for TypeFormatterFlags {
fn default() -> Self {
Self::NO_FUNCTION_RETURN
| Self::NO_MEMBER_FUNCTION_STATIC
| Self::SPACE_AFTER_COMMA
| Self::NAME_ONLY
}
}
pub trait ModuleProvider<'s> {
fn get_module_info(
&self,
module_index: usize,
module: &Module,
) -> std::result::Result<Option<&ModuleInfo<'s>>, pdb::Error>;
}
pub struct TypeFormatter<'a, 's> {
module_provider: &'a (dyn ModuleProvider<'s> + Sync),
modules: Vec<Module<'a>>,
string_table: Option<&'a StringTable<'s>>,
cache: Mutex<TypeFormatterCache<'a>>,
ptr_size: u64,
flags: TypeFormatterFlags,
}
struct TypeFormatterCache<'a> {
type_map: TypeMap<'a>,
type_size_cache: TypeSizeCache<'a>,
id_map: IdMap<'a>,
module_name_map: Option<HashMap<String, usize>>,
module_imports: HashMap<usize, Result<CrossModuleImports<'a>>>,
module_exports: HashMap<usize, Result<CrossModuleExports>>,
}
struct TypeFormatterForModule<'cache, 'a, 's> {
module_index: usize,
module_provider: &'a (dyn ModuleProvider<'s> + Sync),
modules: &'cache [Module<'a>],
string_table: Option<&'a StringTable<'s>>,
cache: &'cache mut TypeFormatterCache<'a>,
ptr_size: u64,
flags: TypeFormatterFlags,
}
impl<'a, 's> TypeFormatter<'a, 's> {
pub fn new_from_parts(
module_provider: &'a (dyn ModuleProvider<'s> + Sync),
modules: Vec<Module<'a>>,
debug_info: &DebugInformation<'s>,
type_info: &'a TypeInformation<'s>,
id_info: &'a IdInformation<'s>,
string_table: Option<&'a StringTable<'s>>,
flags: TypeFormatterFlags,
) -> std::result::Result<Self, pdb::Error> {
let type_map = TypeMap {
iter: type_info.iter(),
finder: type_info.finder(),
};
let type_size_cache = TypeSizeCache {
forward_ref_sizes: HashMap::new(),
cached_ranges: RangeSet::empty(),
};
let id_map = IdMap {
iter: id_info.iter(),
finder: id_info.finder(),
};
let ptr_size = match debug_info.machine_type()? {
MachineType::Amd64 | MachineType::Arm64 | MachineType::Ia64 | MachineType::RiscV64 => 8,
MachineType::RiscV128 => 16,
_ => 4,
};
Ok(Self {
module_provider,
modules,
string_table,
cache: Mutex::new(TypeFormatterCache {
type_map,
type_size_cache,
id_map,
module_name_map: None,
module_imports: HashMap::new(),
module_exports: HashMap::new(),
}),
ptr_size,
flags,
})
}
pub fn modules(&self) -> &[Module<'a>] {
&self.modules
}
fn for_module<F, R>(&self, module_index: usize, f: F) -> R
where
F: FnOnce(&mut TypeFormatterForModule<'_, 'a, 's>) -> R,
{
let mut cache = self.cache.lock().unwrap();
let mut for_module = TypeFormatterForModule {
module_index,
module_provider: self.module_provider,
modules: &self.modules,
string_table: self.string_table,
cache: &mut cache,
ptr_size: self.ptr_size,
flags: self.flags,
};
f(&mut for_module)
}
pub fn get_type_size(&self, module_index: usize, index: TypeIndex) -> u64 {
self.for_module(module_index, |tf| tf.get_type_size(index))
}
pub fn format_function(
&self,
name: &str,
module_index: usize,
function_type_index: TypeIndex,
) -> Result<String> {
let mut s = String::new();
self.emit_function(&mut s, name, module_index, function_type_index)?;
Ok(s)
}
pub fn emit_function(
&self,
w: &mut impl Write,
name: &str,
module_index: usize,
function_type_index: TypeIndex,
) -> Result<()> {
self.for_module(module_index, |tf| {
tf.emit_function(w, name, function_type_index)
})
}
pub fn format_id(&self, module_index: usize, id_index: IdIndex) -> Result<String> {
let mut s = String::new();
self.emit_id(&mut s, module_index, id_index)?;
Ok(s)
}
pub fn emit_id(
&self,
w: &mut impl Write,
module_index: usize,
id_index: IdIndex,
) -> Result<()> {
self.for_module(module_index, |tf| tf.emit_id(w, id_index))
}
}
impl<'cache, 'a, 's> TypeFormatterForModule<'cache, 'a, 's> {
pub fn get_type_size(&mut self, index: TypeIndex) -> u64 {
if let Ok(type_data) = self.parse_type_index(index) {
self.get_data_size(index, &type_data)
} else {
0
}
}
pub fn emit_function(
&mut self,
w: &mut impl Write,
name: &str,
function_type_index: TypeIndex,
) -> Result<()> {
if function_type_index == TypeIndex(0) {
return self.emit_name_str(w, name);
}
match self.parse_type_index(function_type_index)? {
TypeData::MemberFunction(t) => {
if t.this_pointer_type.is_none() {
self.maybe_emit_static(w)?;
}
self.maybe_emit_return_type(w, Some(t.return_type), t.attributes)?;
self.emit_name_str(w, name)?;
self.emit_method_args(w, t, true)?;
}
TypeData::Procedure(t) => {
self.maybe_emit_return_type(w, t.return_type, t.attributes)?;
self.emit_name_str(w, name)?;
if !self.has_flags(TypeFormatterFlags::NO_ARGUMENTS) {
write!(w, "(")?;
self.emit_type_index(w, t.argument_list)?;
write!(w, ")")?;
}
}
_ => {
write!(w, "{}", name)?;
}
}
Ok(())
}
pub fn emit_id(&mut self, w: &mut impl Write, id_index: IdIndex) -> Result<()> {
let id_data = match self.parse_id_index(id_index) {
Ok(id_data) => id_data,
Err(Error::PdbError(pdb::Error::UnimplementedTypeKind(t))) => {
write!(w, "<unimplemented type kind 0x{:x}>", t)?;
return Ok(());
}
Err(Error::PdbError(pdb::Error::TypeNotFound(type_index))) => {
write!(w, "<missing type 0x{:x}>", type_index)?;
return Ok(());
}
Err(e) => return Err(e),
};
match id_data {
IdData::MemberFunction(m) => {
let t = match self.parse_type_index(m.function_type)? {
TypeData::MemberFunction(t) => t,
_ => return Err(Error::MemberFunctionIdIsNotMemberFunctionType),
};
if t.this_pointer_type.is_none() {
self.maybe_emit_static(w)?;
}
self.maybe_emit_return_type(w, Some(t.return_type), t.attributes)?;
self.emit_type_index(w, m.parent)?;
write!(w, "::")?;
self.emit_name_str(w, &m.name.to_string())?;
self.emit_method_args(w, t, true)?;
}
IdData::Function(f) => {
let t = match self.parse_type_index(f.function_type)? {
TypeData::Procedure(t) => t,
_ => return Err(Error::FunctionIdIsNotProcedureType),
};
self.maybe_emit_return_type(w, t.return_type, t.attributes)?;
if let Some(scope) = f.scope {
self.emit_id(w, scope)?;
write!(w, "::")?;
}
self.emit_name_str(w, &f.name.to_string())?;
if !self.has_flags(TypeFormatterFlags::NO_ARGUMENTS) {
write!(w, "(")?;
self.emit_type_index(w, t.argument_list)?;
write!(w, ")")?;
}
}
IdData::String(s) => {
let name = s.name.to_string();
if Self::is_anonymous_namespace(&name) {
write!(w, "`anonymous namespace'")?;
} else {
write!(w, "{}", name)?;
}
}
IdData::StringList(s) => {
write!(w, "\"")?;
for (i, type_index) in s.substrings.iter().enumerate() {
if i > 0 {
write!(w, "\" \"")?;
}
self.emit_type_index(w, *type_index)?;
}
write!(w, "\"")?;
}
other => write!(w, "<unhandled id scope {:?}>::", other)?,
}
Ok(())
}
fn is_anonymous_namespace(name: &str) -> bool {
name.strip_prefix("?A0x")
.map_or(false, |rest| u32::from_str_radix(rest, 16).is_ok())
}
fn resolve_index<I>(&mut self, index: I) -> Result<I>
where
I: ItemIndex,
{
if !index.is_cross_module() {
return Ok(index);
}
let string_table = self
.string_table
.ok_or(Error::CantResolveCrossModuleRefWithoutStringTable)?;
let TypeFormatterCache {
module_name_map,
module_imports,
module_exports,
..
} = self.cache;
let modules = self.modules;
let module_provider = self.module_provider;
let self_module_index = self.module_index;
let get_module = |module_index: usize| -> Result<&'a ModuleInfo<'s>> {
let module = modules
.get(module_index)
.ok_or(Error::OutOfRangeModuleIndex(module_index))?;
let module_info = module_provider
.get_module_info(module_index, module)?
.ok_or(Error::ModuleInfoNotFound(module_index))?;
Ok(module_info)
};
let module_name_map = module_name_map.get_or_insert_with(|| {
modules
.iter()
.enumerate()
.map(|(module_index, module)| {
let name = module.module_name().to_ascii_lowercase();
(name, module_index)
})
.collect()
});
let imports = module_imports
.entry(self_module_index)
.or_insert_with(|| Ok(get_module(self_module_index)?.imports()?))
.as_mut()
.map_err(|err| mem::replace(err, Error::ModuleImportsUnsuccessful))?;
let CrossModuleRef(module_ref, local_index) = imports.resolve_import(index)?;
let ref_module_name = module_ref
.0
.to_string_lossy(string_table)?
.to_ascii_lowercase();
let ref_module_index = *module_name_map
.get(&ref_module_name)
.ok_or(Error::ModuleNameNotFound(ref_module_name))?;
let module_exports = module_exports
.entry(ref_module_index)
.or_insert_with(|| Ok(get_module(ref_module_index)?.exports()?))
.as_mut()
.map_err(|err| mem::replace(err, Error::ModuleExportsUnsuccessful))?;
let index = module_exports
.resolve_import(local_index)?
.ok_or_else(|| Error::LocalIndexNotInExports(local_index.0.into()))?;
Ok(index)
}
fn parse_type_index(&mut self, index: TypeIndex) -> Result<TypeData<'a>> {
let index = self.resolve_index(index)?;
let item = self.cache.type_map.try_get(index)?;
Ok(item.parse()?)
}
fn parse_id_index(&mut self, index: IdIndex) -> Result<IdData<'a>> {
let index = self.resolve_index(index)?;
let item = self.cache.id_map.try_get(index)?;
Ok(item.parse()?)
}
fn get_class_size(&mut self, index: TypeIndex, class_type: &ClassType<'a>) -> u64 {
if class_type.properties.forward_reference() {
let name = class_type.unique_name.unwrap_or(class_type.name);
let size = self.cache.type_size_cache.get_size_for_forward_reference(
index,
name,
&mut self.cache.type_map,
);
size.unwrap_or(class_type.size)
} else {
class_type.size
}
}
fn get_union_size(&mut self, index: TypeIndex, union_type: &UnionType<'a>) -> u64 {
if union_type.properties.forward_reference() {
let name = union_type.unique_name.unwrap_or(union_type.name);
let size = self.cache.type_size_cache.get_size_for_forward_reference(
index,
name,
&mut self.cache.type_map,
);
size.unwrap_or(union_type.size)
} else {
union_type.size
}
}
fn get_data_size(&mut self, type_index: TypeIndex, type_data: &TypeData<'a>) -> u64 {
match type_data {
TypeData::Primitive(t) => {
if t.indirection.is_some() {
return self.ptr_size;
}
match t.kind {
PrimitiveKind::NoType | PrimitiveKind::Void => 0,
PrimitiveKind::Char
| PrimitiveKind::UChar
| PrimitiveKind::RChar
| PrimitiveKind::I8
| PrimitiveKind::U8
| PrimitiveKind::Bool8 => 1,
PrimitiveKind::WChar
| PrimitiveKind::RChar16
| PrimitiveKind::Short
| PrimitiveKind::UShort
| PrimitiveKind::I16
| PrimitiveKind::U16
| PrimitiveKind::F16
| PrimitiveKind::Bool16 => 2,
PrimitiveKind::RChar32
| PrimitiveKind::Long
| PrimitiveKind::ULong
| PrimitiveKind::I32
| PrimitiveKind::U32
| PrimitiveKind::F32
| PrimitiveKind::F32PP
| PrimitiveKind::Bool32
| PrimitiveKind::HRESULT => 4,
PrimitiveKind::I64
| PrimitiveKind::U64
| PrimitiveKind::Quad
| PrimitiveKind::UQuad
| PrimitiveKind::F64
| PrimitiveKind::Complex32
| PrimitiveKind::Bool64 => 8,
PrimitiveKind::I128
| PrimitiveKind::U128
| PrimitiveKind::Octa
| PrimitiveKind::UOcta
| PrimitiveKind::F128
| PrimitiveKind::Complex64 => 16,
PrimitiveKind::F48 => 6,
PrimitiveKind::F80 => 10,
PrimitiveKind::Complex80 => 20,
PrimitiveKind::Complex128 => 32,
_ => panic!("Unknown PrimitiveKind {:?} in get_data_size", t.kind),
}
}
TypeData::Class(t) => self.get_class_size(type_index, t),
TypeData::MemberFunction(_) => self.ptr_size,
TypeData::Procedure(_) => self.ptr_size,
TypeData::Pointer(t) => t.attributes.size().into(),
TypeData::Array(t) => (*t.dimensions.last().unwrap()).into(),
TypeData::Union(t) => self.get_union_size(type_index, t),
TypeData::Enumeration(t) => self.get_type_size(t.underlying_type),
TypeData::Enumerate(t) => match t.value {
Variant::I8(_) | Variant::U8(_) => 1,
Variant::I16(_) | Variant::U16(_) => 2,
Variant::I32(_) | Variant::U32(_) => 4,
Variant::I64(_) | Variant::U64(_) => 8,
},
TypeData::Modifier(t) => self.get_type_size(t.underlying_type),
_ => 0,
}
}
fn has_flags(&self, flags: TypeFormatterFlags) -> bool {
self.flags.intersects(flags)
}
fn maybe_emit_static(&self, w: &mut impl Write) -> Result<()> {
if self.has_flags(TypeFormatterFlags::NO_MEMBER_FUNCTION_STATIC) {
return Ok(());
}
w.write_str("static ")?;
Ok(())
}
fn maybe_emit_return_type(
&mut self,
w: &mut impl Write,
type_index: Option<TypeIndex>,
attrs: FunctionAttributes,
) -> Result<()> {
if self.has_flags(TypeFormatterFlags::NO_FUNCTION_RETURN) {
return Ok(());
}
self.emit_return_type(w, type_index, attrs)?;
Ok(())
}
fn emit_name_str(&mut self, w: &mut impl Write, name: &str) -> Result<()> {
if name.is_empty() {
write!(w, "<name omitted>")?;
} else {
write!(w, "{}", name)?;
}
Ok(())
}
fn emit_return_type(
&mut self,
w: &mut impl Write,
type_index: Option<TypeIndex>,
attrs: FunctionAttributes,
) -> Result<()> {
if !attrs.is_constructor() {
if let Some(index) = type_index {
self.emit_type_index(w, index)?;
write!(w, " ")?;
}
}
Ok(())
}
fn check_ptr_class(&mut self, ptr: TypeIndex, class: TypeIndex) -> Result<PtrToClassKind> {
if let TypeData::Pointer(ptr_type) = self.parse_type_index(ptr)? {
let underlying_type = ptr_type.underlying_type;
if underlying_type == class {
return Ok(PtrToClassKind::PtrToGivenClass { constant: false });
}
let underlying_type_data = self.parse_type_index(underlying_type)?;
if let TypeData::Modifier(modifier) = underlying_type_data {
if modifier.underlying_type == class {
return Ok(PtrToClassKind::PtrToGivenClass {
constant: modifier.constant,
});
}
}
};
Ok(PtrToClassKind::OtherType)
}
fn get_class_constness_and_extra_arguments(
&mut self,
this: TypeIndex,
class: TypeIndex,
) -> Result<(bool, Option<TypeIndex>)> {
match self.check_ptr_class(this, class)? {
PtrToClassKind::PtrToGivenClass { constant } => {
Ok((constant, None))
}
PtrToClassKind::OtherType => {
Ok((false, Some(this)))
}
}
}
fn emit_method_args(
&mut self,
w: &mut impl Write,
method_type: MemberFunctionType,
allow_emit_const: bool,
) -> Result<()> {
if self.has_flags(TypeFormatterFlags::NO_ARGUMENTS) {
return Ok(());
}
let args_list = match self.parse_type_index(method_type.argument_list)? {
TypeData::ArgumentList(t) => t,
_ => {
return Err(Error::ArgumentTypeNotArgumentList);
}
};
let (is_const_method, extra_first_arg) = match method_type.this_pointer_type {
None => {
(false, None)
}
Some(this_type) => {
self.get_class_constness_and_extra_arguments(this_type, method_type.class_type)?
}
};
write!(w, "(")?;
if let Some(first_arg) = extra_first_arg {
self.emit_type_index(w, first_arg)?;
self.emit_arg_list(w, args_list, true)?;
} else {
self.emit_arg_list(w, args_list, false)?;
}
write!(w, ")")?;
if is_const_method && allow_emit_const {
write!(w, " const")?;
}
Ok(())
}
fn emit_attributes(
&mut self,
w: &mut impl Write,
attrs: Vec<PtrAttributes>,
allow_space_at_beginning: bool,
mut previous_byte_was_pointer_sigil: bool,
) -> Result<()> {
let mut is_at_beginning = true;
for attr in attrs.iter().rev() {
if attr.is_pointee_const {
if !is_at_beginning || allow_space_at_beginning {
write!(w, " ")?;
}
write!(w, "const")?;
is_at_beginning = false;
previous_byte_was_pointer_sigil = false;
}
if self.has_flags(TypeFormatterFlags::SPACE_BEFORE_POINTER)
&& !previous_byte_was_pointer_sigil
&& (!is_at_beginning || allow_space_at_beginning)
{
write!(w, " ")?;
}
is_at_beginning = false;
match attr.mode {
PointerMode::Pointer => write!(w, "*")?,
PointerMode::LValueReference => write!(w, "&")?,
PointerMode::Member => write!(w, "::*")?,
PointerMode::MemberFunction => write!(w, "::*")?,
PointerMode::RValueReference => write!(w, "&&")?,
}
previous_byte_was_pointer_sigil = true;
if attr.is_pointer_const {
write!(w, " const")?;
previous_byte_was_pointer_sigil = false;
}
}
Ok(())
}
fn emit_member_ptr(
&mut self,
w: &mut impl Write,
fun: MemberFunctionType,
attributes: Vec<PtrAttributes>,
) -> Result<()> {
self.emit_return_type(w, Some(fun.return_type), fun.attributes)?;
write!(w, "(")?;
self.emit_type_index(w, fun.class_type)?;
self.emit_attributes(w, attributes, false, false)?;
write!(w, ")")?;
self.emit_method_args(w, fun, false)?;
Ok(())
}
fn emit_proc_ptr(
&mut self,
w: &mut impl Write,
fun: ProcedureType,
attributes: Vec<PtrAttributes>,
) -> Result<()> {
self.emit_return_type(w, fun.return_type, fun.attributes)?;
write!(w, "(")?;
self.emit_attributes(w, attributes, false, false)?;
write!(w, ")")?;
write!(w, "(")?;
self.emit_type_index(w, fun.argument_list)?;
write!(w, ")")?;
Ok(())
}
fn emit_other_ptr(
&mut self,
w: &mut impl Write,
type_data: TypeData,
attributes: Vec<PtrAttributes>,
) -> Result<()> {
let mut buf = String::new();
self.emit_type(&mut buf, type_data)?;
let previous_byte_was_pointer_sigil = buf
.as_bytes()
.last()
.map(|&b| b == b'*' || b == b'&')
.unwrap_or(false);
w.write_str(&buf)?;
self.emit_attributes(w, attributes, true, previous_byte_was_pointer_sigil)?;
Ok(())
}
fn emit_ptr_helper(
&mut self,
w: &mut impl Write,
attributes: Vec<PtrAttributes>,
type_data: TypeData,
) -> Result<()> {
match type_data {
TypeData::MemberFunction(t) => self.emit_member_ptr(w, t, attributes)?,
TypeData::Procedure(t) => self.emit_proc_ptr(w, t, attributes)?,
_ => self.emit_other_ptr(w, type_data, attributes)?,
};
Ok(())
}
fn emit_ptr(&mut self, w: &mut impl Write, ptr: PointerType, is_const: bool) -> Result<()> {
let mut attributes = vec![PtrAttributes {
is_pointer_const: ptr.attributes.is_const() || is_const,
is_pointee_const: false,
mode: ptr.attributes.pointer_mode(),
}];
let mut ptr = ptr;
loop {
let type_data = self.parse_type_index(ptr.underlying_type)?;
match type_data {
TypeData::Pointer(t) => {
attributes.push(PtrAttributes {
is_pointer_const: t.attributes.is_const(),
is_pointee_const: false,
mode: t.attributes.pointer_mode(),
});
ptr = t;
}
TypeData::Modifier(t) => {
attributes.last_mut().unwrap().is_pointee_const = t.constant;
let underlying_type_data = self.parse_type_index(t.underlying_type)?;
if let TypeData::Pointer(t) = underlying_type_data {
attributes.push(PtrAttributes {
is_pointer_const: t.attributes.is_const(),
is_pointee_const: false,
mode: t.attributes.pointer_mode(),
});
ptr = t;
} else {
self.emit_ptr_helper(w, attributes, underlying_type_data)?;
return Ok(());
}
}
_ => {
self.emit_ptr_helper(w, attributes, type_data)?;
return Ok(());
}
}
}
}
fn get_array_info(&mut self, array: ArrayType) -> Result<(Vec<u64>, TypeIndex, TypeData<'a>)> {
let mut base = array;
let mut dims = Vec::new();
dims.push(base.dimensions[0].into());
loop {
let type_index = base.element_type;
let type_data = self.parse_type_index(type_index)?;
match type_data {
TypeData::Array(a) => {
dims.push(a.dimensions[0].into());
base = a;
}
_ => {
return Ok((dims, type_index, type_data));
}
}
}
}
fn emit_array(&mut self, w: &mut impl Write, array: ArrayType) -> Result<()> {
let (dimensions_as_bytes, base_index, base) = self.get_array_info(array)?;
let base_size = self.get_data_size(base_index, &base);
self.emit_type(w, base)?;
let mut iter = dimensions_as_bytes.into_iter().peekable();
while let Some(current_level_byte_size) = iter.next() {
let next_level_byte_size = *iter.peek().unwrap_or(&base_size);
if next_level_byte_size != 0 {
let element_count = current_level_byte_size / next_level_byte_size;
write!(w, "[{}]", element_count)?;
} else {
write!(w, "[]")?;
};
}
Ok(())
}
fn emit_modifier(&mut self, w: &mut impl Write, modifier: ModifierType) -> Result<()> {
let type_data = self.parse_type_index(modifier.underlying_type)?;
match type_data {
TypeData::Pointer(ptr) => self.emit_ptr(w, ptr, modifier.constant)?,
TypeData::Primitive(prim) => self.emit_primitive(w, prim, modifier.constant)?,
_ => {
if modifier.constant {
write!(w, "const ")?
}
self.emit_type(w, type_data)?;
}
}
Ok(())
}
fn emit_class(&mut self, w: &mut impl Write, class: ClassType) -> Result<()> {
if self.has_flags(TypeFormatterFlags::NAME_ONLY) {
write!(w, "{}", class.name)?;
} else {
let name = match class.kind {
ClassKind::Class => "class",
ClassKind::Interface => "interface",
ClassKind::Struct => "struct",
};
write!(w, "{} {}", name, class.name)?
}
Ok(())
}
fn emit_arg_list(
&mut self,
w: &mut impl Write,
list: ArgumentList,
comma_before_first: bool,
) -> Result<()> {
if let Some((first, args)) = list.arguments.split_first() {
if comma_before_first {
write!(w, ",")?;
if self.has_flags(TypeFormatterFlags::SPACE_AFTER_COMMA) {
write!(w, " ")?;
}
}
self.emit_type_index(w, *first)?;
for index in args.iter() {
write!(w, ",")?;
if self.has_flags(TypeFormatterFlags::SPACE_AFTER_COMMA) {
write!(w, " ")?;
}
self.emit_type_index(w, *index)?;
}
}
Ok(())
}
fn emit_primitive(
&mut self,
w: &mut impl Write,
prim: PrimitiveType,
is_const: bool,
) -> Result<()> {
let name = match prim.kind {
PrimitiveKind::NoType => "<NoType>",
PrimitiveKind::Void => "void",
PrimitiveKind::Char => "signed char",
PrimitiveKind::UChar => "unsigned char",
PrimitiveKind::RChar => "char",
PrimitiveKind::WChar => "wchar_t",
PrimitiveKind::RChar16 => "char16_t",
PrimitiveKind::RChar32 => "char32_t",
PrimitiveKind::I8 => "int8_t",
PrimitiveKind::U8 => "uint8_t",
PrimitiveKind::Short => "short",
PrimitiveKind::UShort => "unsigned short",
PrimitiveKind::I16 => "int16_t",
PrimitiveKind::U16 => "uint16_t",
PrimitiveKind::Long => "long",
PrimitiveKind::ULong => "unsigned long",
PrimitiveKind::I32 => "int",
PrimitiveKind::U32 => "unsigned int",
PrimitiveKind::Quad => "long long",
PrimitiveKind::UQuad => "unsigned long long",
PrimitiveKind::I64 => "int64_t",
PrimitiveKind::U64 => "uint64_t",
PrimitiveKind::I128 | PrimitiveKind::Octa => "int128_t",
PrimitiveKind::U128 | PrimitiveKind::UOcta => "uint128_t",
PrimitiveKind::F16 => "float16_t",
PrimitiveKind::F32 => "float",
PrimitiveKind::F32PP => "float",
PrimitiveKind::F48 => "float48_t",
PrimitiveKind::F64 => "double",
PrimitiveKind::F80 => "long double",
PrimitiveKind::F128 => "long double",
PrimitiveKind::Complex32 => "complex<float>",
PrimitiveKind::Complex64 => "complex<double>",
PrimitiveKind::Complex80 => "complex<long double>",
PrimitiveKind::Complex128 => "complex<long double>",
PrimitiveKind::Bool8 => "bool",
PrimitiveKind::Bool16 => "bool16_t",
PrimitiveKind::Bool32 => "bool32_t",
PrimitiveKind::Bool64 => "bool64_t",
PrimitiveKind::HRESULT => "HRESULT",
_ => panic!("Unknown PrimitiveKind {:?} in emit_primitive", prim.kind),
};
if prim.indirection.is_some() {
if self.has_flags(TypeFormatterFlags::SPACE_BEFORE_POINTER) {
if is_const {
write!(w, "{} const *", name)?
} else {
write!(w, "{} *", name)?
}
} else if is_const {
write!(w, "{} const*", name)?
} else {
write!(w, "{}*", name)?
}
} else if is_const {
write!(w, "const {}", name)?
} else {
write!(w, "{}", name)?
}
Ok(())
}
fn emit_named(&mut self, w: &mut impl Write, base: &str, name: RawString) -> Result<()> {
if self.has_flags(TypeFormatterFlags::NAME_ONLY) {
write!(w, "{}", name)?
} else {
write!(w, "{} {}", base, name)?
}
Ok(())
}
fn emit_type_index(&mut self, w: &mut impl Write, index: TypeIndex) -> Result<()> {
match self.parse_type_index(index) {
Ok(type_data) => self.emit_type(w, type_data),
Err(Error::PdbError(pdb::Error::UnimplementedTypeKind(t))) => {
write!(w, "<unimplemented type kind 0x{:x}>", t)?;
Ok(())
}
Err(Error::PdbError(pdb::Error::TypeNotFound(type_index))) => {
write!(w, "<missing type 0x{:x}>", type_index)?;
Ok(())
}
Err(e) => Err(e),
}
}
fn emit_type(&mut self, w: &mut impl Write, type_data: TypeData) -> Result<()> {
match self.emit_type_inner(w, type_data) {
Ok(()) => Ok(()),
Err(Error::PdbError(pdb::Error::TypeNotFound(type_index))) => {
write!(w, "<missing type 0x{:x}>", type_index)?;
Ok(())
}
Err(e) => Err(e),
}
}
fn emit_type_inner(&mut self, w: &mut impl Write, type_data: TypeData) -> Result<()> {
match type_data {
TypeData::Primitive(t) => self.emit_primitive(w, t, false)?,
TypeData::Class(t) => self.emit_class(w, t)?,
TypeData::MemberFunction(t) => {
self.maybe_emit_return_type(w, Some(t.return_type), t.attributes)?;
write!(w, "()")?;
self.emit_method_args(w, t, false)?;
}
TypeData::Procedure(t) => {
self.maybe_emit_return_type(w, t.return_type, t.attributes)?;
write!(w, "()(")?;
self.emit_type_index(w, t.argument_list)?;
write!(w, "")?;
}
TypeData::ArgumentList(t) => self.emit_arg_list(w, t, false)?,
TypeData::Pointer(t) => self.emit_ptr(w, t, false)?,
TypeData::Array(t) => self.emit_array(w, t)?,
TypeData::Union(t) => self.emit_named(w, "union", t.name)?,
TypeData::Enumeration(t) => self.emit_named(w, "enum", t.name)?,
TypeData::Enumerate(t) => self.emit_named(w, "enum class", t.name)?,
TypeData::Modifier(t) => self.emit_modifier(w, t)?,
_ => write!(w, "unhandled type /* {:?} */", type_data)?,
}
Ok(())
}
}
#[derive(Eq, PartialEq)]
enum PtrToClassKind {
PtrToGivenClass {
constant: bool,
},
OtherType,
}
#[derive(Debug)]
struct PtrAttributes {
is_pointer_const: bool,
is_pointee_const: bool,
mode: PointerMode,
}
struct ItemMap<'a, I: ItemIndex> {
iter: ItemIter<'a, I>,
finder: ItemFinder<'a, I>,
}
impl<'a, I> ItemMap<'a, I>
where
I: ItemIndex,
{
pub fn try_get(&mut self, index: I) -> std::result::Result<Item<'a, I>, pdb::Error> {
if index <= self.finder.max_index() {
return self.finder.find(index);
}
while let Some(item) = self.iter.next()? {
self.finder.update(&self.iter);
match item.index().partial_cmp(&index) {
Some(Ordering::Equal) => return Ok(item),
Some(Ordering::Greater) => break,
_ => continue,
}
}
Err(pdb::Error::TypeNotFound(index.into()))
}
}
type IdMap<'a> = ItemMap<'a, IdIndex>;
type TypeMap<'a> = ItemMap<'a, TypeIndex>;
struct TypeSizeCache<'a> {
forward_ref_sizes: HashMap<RawString<'a>, u64>,
cached_ranges: RangeSet2<u32>,
}
impl<'a> TypeSizeCache<'a> {
pub fn get_size_for_forward_reference(
&mut self,
index: TypeIndex,
name: RawString<'a>,
type_map: &mut TypeMap<'a>,
) -> Option<u64> {
if let Some(size) = self.forward_ref_sizes.get(&name) {
return Some(*size);
}
let start_index = index.0;
let candidate_range = RangeSet::from((start_index + 1)..);
let uncached_ranges = &candidate_range - &self.cached_ranges;
for uncached_range in uncached_ranges.iter() {
let (range_start, range_end) = match uncached_range {
RangeSetRange::Range(r) => (*r.start, Some(*r.end)),
RangeSetRange::RangeFrom(r) => (*r.start, None),
};
for index in range_start.. {
if let Some(range_end) = range_end {
if index >= range_end {
break;
}
}
if let Ok(item) = type_map.try_get(TypeIndex(index)) {
let s = self.update_forward_ref_size_map(&item);
if let Some((found_name, found_size)) = s {
if found_name == name {
self.cached_ranges |= RangeSet::from(start_index..(index + 1));
return Some(found_size);
}
}
} else {
break;
}
}
}
self.cached_ranges |= RangeSet::from(start_index..);
None
}
pub fn update_forward_ref_size_map(
&mut self,
item: &Item<'a, TypeIndex>,
) -> Option<(RawString<'a>, u64)> {
if let Ok(type_data) = item.parse() {
match type_data {
TypeData::Class(t) => {
if !t.properties.forward_reference() {
let name = t.unique_name.unwrap_or(t.name);
self.forward_ref_sizes.insert(name, t.size);
return Some((name, t.size));
}
}
TypeData::Union(t) => {
if !t.properties.forward_reference() {
let name = t.unique_name.unwrap_or(t.name);
self.forward_ref_sizes.insert(name, t.size);
return Some((name, t.size));
}
}
_ => {}
}
}
None
}
}