use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt;
use std::iter;
use once_cell::sync::OnceCell;
use symbolic_common::{Arch, CodeId, DebugId};
use symbolic_ppdb::EmbeddedSource;
use symbolic_ppdb::{Document, FormatError, PortablePdb};
use crate::base::*;
use crate::sourcebundle::SourceFileDescriptor;
pub type PortablePdbSymbolIterator<'data> = iter::Empty<Symbol<'data>>;
pub type PortablePdbFunctionIterator<'session> =
iter::Empty<Result<Function<'session>, FormatError>>;
pub struct PortablePdbObject<'data> {
data: &'data [u8],
ppdb: PortablePdb<'data>,
}
impl<'data> PortablePdbObject<'data> {
pub fn portable_pdb(&self) -> &PortablePdb {
&self.ppdb
}
pub fn data(&self) -> &'data [u8] {
self.data
}
}
impl<'data: 'object, 'object> ObjectLike<'data, 'object> for PortablePdbObject<'data> {
type Error = FormatError;
type Session = PortablePdbDebugSession<'data>;
type SymbolIterator = PortablePdbSymbolIterator<'data>;
fn debug_id(&self) -> DebugId {
self.ppdb.pdb_id().unwrap_or_default()
}
fn code_id(&self) -> Option<CodeId> {
None
}
fn arch(&self) -> Arch {
Arch::Unknown
}
fn kind(&self) -> ObjectKind {
ObjectKind::Debug
}
fn load_address(&self) -> u64 {
0
}
fn has_symbols(&self) -> bool {
false
}
fn symbols(&self) -> PortablePdbSymbolIterator<'data> {
iter::empty()
}
fn symbol_map(&self) -> SymbolMap<'data> {
SymbolMap::new()
}
fn has_debug_info(&self) -> bool {
self.ppdb.has_debug_info()
}
fn debug_session(&self) -> Result<PortablePdbDebugSession<'data>, FormatError> {
PortablePdbDebugSession::new(&self.ppdb)
}
fn has_unwind_info(&self) -> bool {
false
}
fn has_sources(&self) -> bool {
self.ppdb.has_source_links().unwrap_or(false)
|| match self.ppdb.get_embedded_sources() {
Ok(mut iter) => iter.any(|v| v.is_ok()),
Err(_) => false,
}
}
fn is_malformed(&self) -> bool {
false
}
fn file_format(&self) -> FileFormat {
FileFormat::PortablePdb
}
}
impl<'data> Parse<'data> for PortablePdbObject<'data> {
type Error = FormatError;
fn test(data: &[u8]) -> bool {
PortablePdb::peek(data)
}
fn parse(data: &'data [u8]) -> Result<Self, Self::Error> {
let ppdb = PortablePdb::parse(data)?;
Ok(Self { data, ppdb })
}
}
impl fmt::Debug for PortablePdbObject<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PortablePdbObject")
.field("portable_pdb", &self.portable_pdb())
.finish()
}
}
pub struct PortablePdbDebugSession<'data> {
ppdb: PortablePdb<'data>,
sources: OnceCell<HashMap<String, PPDBSource<'data>>>,
}
#[derive(Debug, Clone)]
enum PPDBSource<'data> {
Embedded(EmbeddedSource<'data>),
Link(Document),
}
impl<'data> PortablePdbDebugSession<'data> {
fn new(ppdb: &'_ PortablePdb<'data>) -> Result<Self, FormatError> {
Ok(PortablePdbDebugSession {
ppdb: ppdb.clone(),
sources: OnceCell::new(),
})
}
fn init_sources(&self) -> HashMap<String, PPDBSource<'data>> {
let count = self.ppdb.get_documents_count().unwrap_or(0);
let mut result = HashMap::with_capacity(count);
if let Ok(iter) = self.ppdb.get_embedded_sources() {
for source in iter.flatten() {
result.insert(source.get_path().to_string(), PPDBSource::Embedded(source));
}
};
for i in 1..count + 1 {
if let Ok(doc) = self.ppdb.get_document(i) {
if !result.contains_key(&doc.name) {
result.insert(doc.name.clone(), PPDBSource::Link(doc));
}
}
}
result
}
pub fn functions(&self) -> PortablePdbFunctionIterator<'_> {
iter::empty()
}
pub fn files(&self) -> PortablePdbFileIterator<'_> {
PortablePdbFileIterator::new(&self.ppdb)
}
pub fn source_by_path(
&self,
path: &str,
) -> Result<Option<SourceFileDescriptor<'_>>, FormatError> {
let sources = self.sources.get_or_init(|| self.init_sources());
match sources.get(path) {
None => Ok(None),
Some(PPDBSource::Embedded(source)) => source.get_contents().map(|bytes| {
Some(SourceFileDescriptor::new_embedded(
from_utf8_cow_lossy(&bytes),
None,
))
}),
Some(PPDBSource::Link(document)) => Ok(self
.ppdb
.get_source_link(document)
.map(SourceFileDescriptor::new_remote)),
}
}
}
impl<'data, 'session> DebugSession<'session> for PortablePdbDebugSession<'data> {
type Error = FormatError;
type FunctionIterator = PortablePdbFunctionIterator<'session>;
type FileIterator = PortablePdbFileIterator<'session>;
fn functions(&'session self) -> Self::FunctionIterator {
self.functions()
}
fn files(&'session self) -> Self::FileIterator {
self.files()
}
fn source_by_path(&self, path: &str) -> Result<Option<SourceFileDescriptor<'_>>, Self::Error> {
self.source_by_path(path)
}
}
pub struct PortablePdbFileIterator<'s> {
ppdb: &'s PortablePdb<'s>,
row: usize,
size: usize,
}
impl<'s> PortablePdbFileIterator<'s> {
fn new(ppdb: &'s PortablePdb<'s>) -> Self {
PortablePdbFileIterator {
ppdb,
row: 1,
size: 0,
}
}
}
impl<'s> Iterator for PortablePdbFileIterator<'s> {
type Item = Result<FileEntry<'s>, FormatError>;
fn next(&mut self) -> Option<Self::Item> {
if self.size == 0 {
match self.ppdb.get_documents_count() {
Ok(size) => {
debug_assert!(size != usize::MAX);
self.size = size;
}
Err(e) => {
return Some(Err(e));
}
}
}
if self.row > self.size {
return None;
}
let index = self.row;
self.row += 1;
let document = match self.ppdb.get_document(index) {
Ok(doc) => doc,
Err(e) => {
return Some(Err(e));
}
};
Some(Ok(FileEntry::new(
Cow::default(),
FileInfo::from_path_owned(document.name.as_bytes()),
)))
}
}