use std::borrow::Cow;
use std::error::Error;
use std::fmt;
use std::marker::PhantomData;
use std::ops::{Deref, RangeBounds};
use fallible_iterator::FallibleIterator;
use gimli::read::{AttributeValue, Error as GimliError, Range};
use gimli::{constants, DwarfFileType, UnitSectionOffset};
use lazycell::LazyCell;
use thiserror::Error;
use symbolic_common::{AsSelf, Language, Name, NameMangling, SelfCell};
use crate::base::*;
use crate::private::FunctionStack;
#[doc(hidden)]
pub use gimli;
pub use gimli::RunTimeEndian as Endian;
type Slice<'a> = gimli::read::EndianSlice<'a, Endian>;
type RangeLists<'a> = gimli::read::RangeLists<Slice<'a>>;
type Unit<'a> = gimli::read::Unit<Slice<'a>>;
type DwarfInner<'a> = gimli::read::Dwarf<Slice<'a>>;
type Die<'d, 'u> = gimli::read::DebuggingInformationEntry<'u, 'u, Slice<'d>, usize>;
type Attribute<'a> = gimli::read::Attribute<Slice<'a>>;
type UnitOffset = gimli::read::UnitOffset<usize>;
type DebugInfoOffset = gimli::DebugInfoOffset<usize>;
type UnitHeader<'a> = gimli::read::UnitHeader<Slice<'a>>;
type IncompleteLineNumberProgram<'a> = gimli::read::IncompleteLineProgram<Slice<'a>>;
type LineNumberProgramHeader<'a> = gimli::read::LineProgramHeader<Slice<'a>>;
type LineProgramFileEntry<'a> = gimli::read::FileEntry<Slice<'a>>;
fn offset(addr: u64, offset: i64) -> u64 {
    (addr as i64).wrapping_sub(offset as i64) as u64
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DwarfErrorKind {
    
    InvalidUnitRef(usize),
    
    InvalidFileRef(u64),
    
    UnexpectedInline,
    
    InvertedFunctionRange,
    
    CorruptedData,
}
impl fmt::Display for DwarfErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::InvalidUnitRef(offset) => {
                write!(f, "compilation unit for offset {} does not exist", offset)
            }
            Self::InvalidFileRef(id) => write!(f, "referenced file {} does not exist", id),
            Self::UnexpectedInline => write!(f, "unexpected inline function without parent"),
            Self::InvertedFunctionRange => write!(f, "function with inverted address range"),
            Self::CorruptedData => write!(f, "corrupted dwarf debug data"),
        }
    }
}
#[derive(Debug, Error)]
#[error("{kind}")]
pub struct DwarfError {
    kind: DwarfErrorKind,
    #[source]
    source: Option<Box<dyn Error + Send + Sync + 'static>>,
}
impl DwarfError {
    
    
    fn new<E>(kind: DwarfErrorKind, source: E) -> Self
    where
        E: Into<Box<dyn Error + Send + Sync>>,
    {
        let source = Some(source.into());
        Self { kind, source }
    }
    
    pub fn kind(&self) -> DwarfErrorKind {
        self.kind
    }
}
impl From<DwarfErrorKind> for DwarfError {
    fn from(kind: DwarfErrorKind) -> Self {
        Self { kind, source: None }
    }
}
impl From<GimliError> for DwarfError {
    fn from(e: GimliError) -> Self {
        Self::new(DwarfErrorKind::CorruptedData, e)
    }
}
#[derive(Clone)]
pub struct DwarfSection<'data> {
    
    pub address: u64,
    
    pub offset: u64,
    
    pub align: u64,
    
    pub data: Cow<'data, [u8]>,
}
impl fmt::Debug for DwarfSection<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("DwarfSection")
            .field("address", &format_args!("{:#x}", self.address))
            .field("offset", &format_args!("{:#x}", self.offset))
            .field("align", &format_args!("{:#x}", self.align))
            .field("len()", &self.data.len())
            .finish()
    }
}
pub trait Dwarf<'data> {
    
    
    
    
    fn endianity(&self) -> Endian;
    
    
    
    
    
    
    
    
    fn raw_section(&self, name: &str) -> Option<DwarfSection<'data>>;
    
    
    
    
    
    
    
    
    fn section(&self, name: &str) -> Option<DwarfSection<'data>> {
        self.raw_section(name)
    }
    
    
    
    
    
    fn has_section(&self, name: &str) -> bool {
        self.raw_section(name).is_some()
    }
}
#[derive(Debug)]
struct DwarfRow {
    address: u64,
    file_index: u64,
    line: Option<u64>,
    size: Option<u64>,
}
#[derive(Debug)]
struct DwarfSequence {
    start: u64,
    end: u64,
    rows: Vec<DwarfRow>,
}
#[derive(Debug)]
struct DwarfLineProgram<'d> {
    header: LineNumberProgramHeader<'d>,
    sequences: Vec<DwarfSequence>,
}
impl<'d, 'a> DwarfLineProgram<'d> {
    fn prepare(program: IncompleteLineNumberProgram<'d>) -> Self {
        let mut sequences = Vec::new();
        let mut sequence_rows = Vec::<DwarfRow>::new();
        let mut prev_address = 0;
        let mut state_machine = program.rows();
        while let Ok(Some((_, &program_row))) = state_machine.next_row() {
            let address = program_row.address();
            
            
            
            if address == 0 {
                continue;
            }
            if let Some(last_row) = sequence_rows.last_mut() {
                if address >= last_row.address {
                    last_row.size = Some(address - last_row.address);
                }
            }
            if program_row.end_sequence() {
                
                
                if !sequence_rows.is_empty() {
                    sequences.push(DwarfSequence {
                        start: sequence_rows[0].address,
                        
                        
                        end: if address < prev_address {
                            prev_address + 1
                        } else {
                            address
                        },
                        rows: sequence_rows.drain(..).collect(),
                    });
                }
                prev_address = 0;
            } else if address < prev_address {
                
                
                
                
                
                
            } else {
                let file_index = program_row.file_index();
                let line = program_row.line();
                let mut duplicate = false;
                if let Some(last_row) = sequence_rows.last_mut() {
                    if last_row.address == address {
                        last_row.file_index = file_index;
                        last_row.line = line;
                        duplicate = true;
                    }
                }
                if !duplicate {
                    sequence_rows.push(DwarfRow {
                        address,
                        file_index,
                        line,
                        size: None,
                    });
                }
                prev_address = address;
            }
        }
        if !sequence_rows.is_empty() {
            
            
            let start = sequence_rows[0].address;
            let end = prev_address + 1;
            sequences.push(DwarfSequence {
                start,
                end,
                rows: sequence_rows,
            });
        }
        
        dmsort::sort_by_key(&mut sequences, |x| x.start);
        DwarfLineProgram {
            header: state_machine.header().clone(),
            sequences,
        }
    }
    pub fn get_rows(&self, range: &Range) -> &[DwarfRow] {
        for seq in &self.sequences {
            if seq.end <= range.begin || seq.start > range.end {
                continue;
            }
            let from = match seq.rows.binary_search_by_key(&range.begin, |x| x.address) {
                Ok(idx) => idx,
                Err(0) => continue,
                Err(next_idx) => next_idx - 1,
            };
            let len = seq.rows[from..]
                .binary_search_by_key(&range.end, |x| x.address)
                .unwrap_or_else(|e| e);
            return &seq.rows[from..from + len];
        }
        &[]
    }
}
#[derive(Clone, Copy, Debug)]
struct UnitRef<'d, 'a> {
    info: &'a DwarfInfo<'d>,
    unit: &'a Unit<'d>,
}
impl<'d, 'a> UnitRef<'d, 'a> {
    
    #[inline(always)]
    fn slice_value(&self, value: AttributeValue<Slice<'d>>) -> Option<&'d [u8]> {
        self.info
            .attr_string(self.unit, value)
            .map(|reader| reader.slice())
            .ok()
    }
    
    #[inline(always)]
    fn string_value(&self, value: AttributeValue<Slice<'d>>) -> Option<Cow<'d, str>> {
        let slice = self.slice_value(value)?;
        Some(String::from_utf8_lossy(slice))
    }
    
    
    
    
    fn resolve_reference<T, F>(&self, attr: Attribute<'d>, f: F) -> Result<Option<T>, DwarfError>
    where
        F: FnOnce(Self, &Die<'d, '_>) -> Result<Option<T>, DwarfError>,
    {
        let (unit, offset) = match attr.value() {
            AttributeValue::UnitRef(offset) => (*self, offset),
            AttributeValue::DebugInfoRef(offset) => self.info.find_unit_offset(offset)?,
            
            _ => return Ok(None),
        };
        let mut entries = unit.unit.entries_at_offset(offset)?;
        entries.next_entry()?;
        if let Some(entry) = entries.current() {
            f(unit, entry)
        } else {
            Ok(None)
        }
    }
    
    fn offset(&self) -> UnitSectionOffset {
        self.unit.header.offset()
    }
    
    fn resolve_function_name(
        &self,
        entry: &Die<'d, '_>,
        language: Language,
    ) -> Result<Option<Name<'d>>, DwarfError> {
        let mut attrs = entry.attrs();
        let mut fallback_name = None;
        let mut reference_target = None;
        while let Some(attr) = attrs.next()? {
            match attr.name() {
                
                constants::DW_AT_linkage_name | constants::DW_AT_MIPS_linkage_name => {
                    return Ok(self
                        .string_value(attr.value())
                        .map(|n| Name::new(n, NameMangling::Mangled, language)));
                }
                constants::DW_AT_name => {
                    fallback_name = Some(attr);
                }
                constants::DW_AT_abstract_origin | constants::DW_AT_specification => {
                    reference_target = Some(attr);
                }
                _ => {}
            }
        }
        if let Some(attr) = fallback_name {
            return Ok(self
                .string_value(attr.value())
                .map(|n| Name::new(n, NameMangling::Unmangled, language)));
        }
        if let Some(attr) = reference_target {
            return self.resolve_reference(attr, |ref_unit, ref_entry| {
                if self.offset() != ref_unit.offset() || entry.offset() != ref_entry.offset() {
                    ref_unit.resolve_function_name(ref_entry, language)
                } else {
                    Ok(None)
                }
            });
        }
        Ok(None)
    }
}
#[derive(Debug)]
struct DwarfUnit<'d, 'a> {
    inner: UnitRef<'d, 'a>,
    language: Language,
    line_program: Option<DwarfLineProgram<'d>>,
    prefer_dwarf_names: bool,
}
impl<'d, 'a> DwarfUnit<'d, 'a> {
    
    fn from_unit(unit: &'a Unit<'d>, info: &'a DwarfInfo<'d>) -> Result<Option<Self>, DwarfError> {
        let mut entries = unit.entries();
        let entry = match entries.next_dfs()? {
            Some((_, entry)) => entry,
            None => return Err(gimli::read::Error::MissingUnitDie.into()),
        };
        
        
        
        
        if info.kind != ObjectKind::Relocatable
            && unit.low_pc == 0
            && entry.attr(constants::DW_AT_ranges)?.is_none()
        {
            return Ok(None);
        }
        let language = match entry.attr_value(constants::DW_AT_language)? {
            Some(AttributeValue::Language(lang)) => language_from_dwarf(lang),
            _ => Language::Unknown,
        };
        let line_program = match unit.line_program {
            Some(ref program) => Some(DwarfLineProgram::prepare(program.clone())),
            None => None,
        };
        let producer = match entry.attr_value(constants::DW_AT_producer)? {
            Some(AttributeValue::String(string)) => Some(string),
            _ => None,
        };
        
        
        let prefer_dwarf_names = producer.as_deref() == Some(b"Dart VM");
        Ok(Some(DwarfUnit {
            inner: UnitRef { info, unit },
            language,
            line_program,
            prefer_dwarf_names,
        }))
    }
    
    fn compilation_dir(&self) -> &'d [u8] {
        match self.inner.unit.comp_dir {
            Some(ref dir) => dir.slice(),
            None => &[],
        }
    }
    
    fn parse_ranges(
        &self,
        entry: &Die<'d, '_>,
        range_buf: &mut Vec<Range>,
    ) -> Result<(Option<u64>, Option<u64>), DwarfError> {
        let mut tuple = (None, None);
        let mut low_pc = None;
        let mut high_pc = None;
        let mut high_pc_rel = None;
        let mut attrs = entry.attrs();
        while let Some(attr) = attrs.next()? {
            match attr.name() {
                constants::DW_AT_low_pc => match attr.value() {
                    AttributeValue::Addr(addr) => low_pc = Some(addr),
                    AttributeValue::DebugAddrIndex(index) => {
                        low_pc = Some(self.inner.info.address(self.inner.unit, index)?)
                    }
                    _ => return Err(GimliError::UnsupportedAttributeForm.into()),
                },
                constants::DW_AT_high_pc => match attr.value() {
                    AttributeValue::Addr(addr) => high_pc = Some(addr),
                    AttributeValue::DebugAddrIndex(index) => {
                        low_pc = Some(self.inner.info.address(self.inner.unit, index)?)
                    }
                    AttributeValue::Udata(size) => high_pc_rel = Some(size),
                    _ => return Err(GimliError::UnsupportedAttributeForm.into()),
                },
                constants::DW_AT_call_line => match attr.value() {
                    AttributeValue::Udata(line) => tuple.0 = Some(line),
                    _ => return Err(GimliError::UnsupportedAttributeForm.into()),
                },
                constants::DW_AT_call_file => match attr.value() {
                    AttributeValue::FileIndex(file) => tuple.1 = Some(file),
                    _ => return Err(GimliError::UnsupportedAttributeForm.into()),
                },
                constants::DW_AT_ranges
                | constants::DW_AT_rnglists_base
                | constants::DW_AT_start_scope => {
                    match self.inner.info.attr_ranges(self.inner.unit, attr.value())? {
                        Some(mut ranges) => {
                            while let Some(range) = ranges.next()? {
                                range_buf.push(range);
                            }
                        }
                        None => continue,
                    }
                }
                _ => continue,
            }
        }
        
        if !range_buf.is_empty() {
            return Ok(tuple);
        }
        
        
        
        
        let kind = self.inner.info.kind;
        let low_pc = match low_pc {
            Some(low_pc) if low_pc != 0 || kind == ObjectKind::Relocatable => low_pc,
            _ => return Ok(tuple),
        };
        let high_pc = match (high_pc, high_pc_rel) {
            (Some(high_pc), _) => high_pc,
            (_, Some(high_pc_rel)) => low_pc.wrapping_add(high_pc_rel),
            _ => return Ok(tuple),
        };
        if low_pc == high_pc {
            
            
            return Ok(tuple);
        }
        if low_pc > high_pc {
            
            return Err(DwarfErrorKind::InvertedFunctionRange.into());
        }
        range_buf.push(Range {
            begin: low_pc,
            end: high_pc,
        });
        Ok(tuple)
    }
    
    fn resolve_lines(&self, ranges: &[Range]) -> Vec<LineInfo<'d>> {
        
        let line_program = match self.line_program {
            Some(ref program) => program,
            None => return Vec::new(),
        };
        let mut lines = Vec::new();
        for range in ranges {
            
            
            
            let rows = line_program.get_rows(range);
            lines.reserve(rows.len());
            
            
            
            
            
            
            
            
            
            
            
            if let Some((first, rows)) = rows.split_first() {
                let mut last_file = first.file_index;
                let mut last_info = LineInfo {
                    address: offset(range.begin, self.inner.info.address_offset),
                    size: first.size.map(|s| s + first.address - range.begin),
                    file: self.resolve_file(first.file_index).unwrap_or_default(),
                    line: first.line.unwrap_or(0),
                };
                for row in rows {
                    let line = row.line.unwrap_or(0);
                    
                    if (last_file, last_info.line) == (row.file_index, line) {
                        
                        if let Some(size) = last_info.size.as_mut() {
                            *size += row.size.unwrap_or(0);
                        }
                        continue;
                    }
                    
                    lines.push(last_info);
                    last_file = row.file_index;
                    last_info = LineInfo {
                        address: offset(row.address, self.inner.info.address_offset),
                        size: row.size,
                        file: self.resolve_file(row.file_index).unwrap_or_default(),
                        line,
                    };
                }
                
                if let Some(size) = last_info.size.as_mut() {
                    *size = offset(range.end, self.inner.info.address_offset) - last_info.address;
                }
                lines.push(last_info);
            }
        }
        lines
    }
    
    fn file_info(
        &self,
        line_program: &LineNumberProgramHeader<'d>,
        file: &LineProgramFileEntry<'d>,
    ) -> FileInfo<'d> {
        FileInfo {
            dir: file
                .directory(line_program)
                .and_then(|attr| self.inner.slice_value(attr))
                .unwrap_or_default(),
            name: self.inner.slice_value(file.path_name()).unwrap_or_default(),
        }
    }
    
    fn resolve_file(&self, file_id: u64) -> Option<FileInfo<'d>> {
        let line_program = match self.line_program {
            Some(ref program) => &program.header,
            None => return None,
        };
        line_program
            .file(file_id)
            .map(|file| self.file_info(line_program, file))
    }
    
    fn resolve_symbol_name<R>(&self, range: R) -> Option<Name<'d>>
    where
        R: RangeBounds<u64>,
    {
        let symbol = self.inner.info.symbol_map.lookup_range(range)?;
        let name = symbol.name.clone()?;
        Some(Name::new(name, NameMangling::Mangled, self.language))
    }
    
    fn resolve_dwarf_name(&self, entry: &Die<'d, '_>) -> Option<Name<'d>> {
        self.inner
            .resolve_function_name(entry, self.language)
            .ok()
            .flatten()
    }
    
    fn functions(&self, range_buf: &mut Vec<Range>) -> Result<Vec<Function<'d>>, DwarfError> {
        let mut depth = 0;
        let mut skipped_depth = None;
        let mut functions = Vec::new();
        let mut stack = FunctionStack::new();
        let mut entries = self.inner.unit.entries();
        while let Some((movement, entry)) = entries.next_dfs()? {
            depth += movement;
            
            
            
            match skipped_depth {
                Some(skipped) if depth > skipped => continue,
                _ => skipped_depth = None,
            }
            
            
            
            stack.flush(depth, &mut functions);
            
            let inline = match entry.tag() {
                constants::DW_TAG_subprogram => false,
                constants::DW_TAG_inlined_subroutine => true,
                _ => continue,
            };
            range_buf.clear();
            let (call_line, call_file) = self.parse_ranges(entry, range_buf)?;
            
            
            
            
            
            if range_buf.is_empty() {
                skipped_depth = Some(depth);
                continue;
            }
            
            
            if !inline && range_buf.len() != 1 {
                
                
                skipped_depth = Some(depth);
                continue;
            }
            let function_address = offset(range_buf[0].begin, self.inner.info.address_offset);
            let function_size = range_buf[range_buf.len() - 1].end - range_buf[0].begin;
            let function_end = function_address + function_size;
            
            
            
            
            
            
            let symbol_name = if self.prefer_dwarf_names || inline {
                None
            } else {
                self.resolve_symbol_name(function_address..function_end)
            };
            let name = symbol_name
                .or_else(|| self.resolve_dwarf_name(entry))
                .unwrap_or_else(|| Name::new("", NameMangling::Unmangled, self.language));
            
            
            
            let lines = self.resolve_lines(&range_buf);
            if inline {
                
                
                let parent = match stack.peek_mut() {
                    Some(parent) => parent,
                    None => return Err(DwarfErrorKind::UnexpectedInline.into()),
                };
                
                
                
                
                
                
                
                if let (Some(line), Some(file_id)) = (call_line, call_file) {
                    let file = self.resolve_file(file_id).unwrap_or_default();
                    let lines = &mut parent.lines;
                    let mut index = 0;
                    for range in range_buf.iter() {
                        let range_begin = offset(range.begin, self.inner.info.address_offset);
                        let range_end = offset(range.end, self.inner.info.address_offset);
                        
                        
                        if let Some(next) = lines.get(index) {
                            if next.address > range_begin {
                                let line_info = LineInfo {
                                    address: range_begin,
                                    size: Some(range_end.min(next.address) - range_begin),
                                    file: file.clone(),
                                    line,
                                };
                                lines.insert(index, line_info);
                                index += 1;
                            }
                        }
                        while index < lines.len() {
                            let record = &mut lines[index];
                            
                            if record.address >= range_end {
                                break;
                            }
                            index += 1;
                            
                            
                            let record_end = record.address + record.size.unwrap_or(0);
                            if record_end <= range_begin {
                                continue;
                            }
                            
                            
                            
                            let split = if record_end > range_end {
                                record.size = Some(range_end - record.address);
                                Some(LineInfo {
                                    address: range_end,
                                    size: Some(record_end - range_end),
                                    file: record.file.clone(),
                                    line: record.line,
                                })
                            } else {
                                None
                            };
                            if record.address < range_begin {
                                
                                
                                
                                let max_size = range_begin - record.address;
                                if record.size.map_or(true, |prev_size| prev_size > max_size) {
                                    record.size = Some(max_size);
                                }
                                
                                
                                let size = record_end.min(range_end) - range_begin;
                                
                                
                                let line_info = LineInfo {
                                    address: range_begin,
                                    size: Some(size),
                                    file: file.clone(),
                                    line,
                                };
                                lines.insert(index, line_info);
                                index += 1;
                            } else {
                                record.file = file.clone();
                                record.line = line;
                            };
                            
                            
                            
                            if let Some(split) = split {
                                lines.insert(index, split);
                            }
                        }
                        
                        
                        if let Some(prev) = index.checked_sub(1).and_then(|i| lines.get(i)) {
                            let record_end = prev.address + prev.size.unwrap_or(0);
                            if record_end < range_end {
                                let line_info = LineInfo {
                                    address: record_end,
                                    size: Some(range_end - record_end),
                                    file: file.clone(),
                                    line,
                                };
                                lines.insert(index, line_info);
                                index += 1;
                            }
                        }
                    }
                }
            }
            let function = Function {
                address: function_address,
                size: function_size,
                name,
                compilation_dir: self.compilation_dir(),
                lines,
                inlinees: Vec::new(),
                inline,
            };
            stack.push(depth, function)
        }
        
        stack.flush(0, &mut functions);
        Ok(functions)
    }
}
fn language_from_dwarf(language: gimli::DwLang) -> Language {
    match language {
        constants::DW_LANG_C => Language::C,
        constants::DW_LANG_C11 => Language::C,
        constants::DW_LANG_C89 => Language::C,
        constants::DW_LANG_C99 => Language::C,
        constants::DW_LANG_C_plus_plus => Language::Cpp,
        constants::DW_LANG_C_plus_plus_03 => Language::Cpp,
        constants::DW_LANG_C_plus_plus_11 => Language::Cpp,
        constants::DW_LANG_C_plus_plus_14 => Language::Cpp,
        constants::DW_LANG_D => Language::D,
        constants::DW_LANG_Go => Language::Go,
        constants::DW_LANG_ObjC => Language::ObjC,
        constants::DW_LANG_ObjC_plus_plus => Language::ObjCpp,
        constants::DW_LANG_Rust => Language::Rust,
        constants::DW_LANG_Swift => Language::Swift,
        _ => Language::Unknown,
    }
}
struct DwarfSectionData<'data, S> {
    data: Cow<'data, [u8]>,
    endianity: Endian,
    _ph: PhantomData<S>,
}
impl<'data, S> DwarfSectionData<'data, S>
where
    S: gimli::read::Section<Slice<'data>>,
{
    
    fn load<D>(dwarf: &D) -> Self
    where
        D: Dwarf<'data>,
    {
        DwarfSectionData {
            data: dwarf
                .section(&S::section_name()[1..])
                .map(|section| section.data)
                .unwrap_or_default(),
            endianity: dwarf.endianity(),
            _ph: PhantomData,
        }
    }
    
    fn to_gimli(&'data self) -> S {
        S::from(Slice::new(&self.data, self.endianity))
    }
}
impl<'d, S> fmt::Debug for DwarfSectionData<'d, S>
where
    S: gimli::read::Section<Slice<'d>>,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let owned = match self.data {
            Cow::Owned(_) => true,
            Cow::Borrowed(_) => false,
        };
        f.debug_struct("DwarfSectionData")
            .field("type", &S::section_name())
            .field("endianity", &self.endianity)
            .field("len()", &self.data.len())
            .field("owned()", &owned)
            .finish()
    }
}
struct DwarfSections<'data> {
    debug_abbrev: DwarfSectionData<'data, gimli::read::DebugAbbrev<Slice<'data>>>,
    debug_info: DwarfSectionData<'data, gimli::read::DebugInfo<Slice<'data>>>,
    debug_line: DwarfSectionData<'data, gimli::read::DebugLine<Slice<'data>>>,
    debug_line_str: DwarfSectionData<'data, gimli::read::DebugLineStr<Slice<'data>>>,
    debug_str: DwarfSectionData<'data, gimli::read::DebugStr<Slice<'data>>>,
    debug_str_offsets: DwarfSectionData<'data, gimli::read::DebugStrOffsets<Slice<'data>>>,
    debug_ranges: DwarfSectionData<'data, gimli::read::DebugRanges<Slice<'data>>>,
    debug_rnglists: DwarfSectionData<'data, gimli::read::DebugRngLists<Slice<'data>>>,
}
impl<'data> DwarfSections<'data> {
    
    fn from_dwarf<D>(dwarf: &D) -> Self
    where
        D: Dwarf<'data>,
    {
        DwarfSections {
            debug_abbrev: DwarfSectionData::load(dwarf),
            debug_info: DwarfSectionData::load(dwarf),
            debug_line: DwarfSectionData::load(dwarf),
            debug_line_str: DwarfSectionData::load(dwarf),
            debug_str: DwarfSectionData::load(dwarf),
            debug_str_offsets: DwarfSectionData::load(dwarf),
            debug_ranges: DwarfSectionData::load(dwarf),
            debug_rnglists: DwarfSectionData::load(dwarf),
        }
    }
}
struct DwarfInfo<'data> {
    inner: DwarfInner<'data>,
    headers: Vec<UnitHeader<'data>>,
    units: Vec<LazyCell<Option<Unit<'data>>>>,
    symbol_map: SymbolMap<'data>,
    address_offset: i64,
    kind: ObjectKind,
}
impl<'d> Deref for DwarfInfo<'d> {
    type Target = DwarfInner<'d>;
    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}
impl<'d> DwarfInfo<'d> {
    
    pub fn parse(
        sections: &'d DwarfSections<'d>,
        symbol_map: SymbolMap<'d>,
        address_offset: i64,
        kind: ObjectKind,
    ) -> Result<Self, DwarfError> {
        let inner = gimli::read::Dwarf {
            debug_abbrev: sections.debug_abbrev.to_gimli(),
            debug_addr: Default::default(),
            debug_info: sections.debug_info.to_gimli(),
            debug_line: sections.debug_line.to_gimli(),
            debug_line_str: sections.debug_line_str.to_gimli(),
            debug_str: sections.debug_str.to_gimli(),
            debug_str_offsets: sections.debug_str_offsets.to_gimli(),
            debug_str_sup: Default::default(),
            debug_types: Default::default(),
            locations: Default::default(),
            ranges: RangeLists::new(
                sections.debug_ranges.to_gimli(),
                sections.debug_rnglists.to_gimli(),
            ),
            file_type: DwarfFileType::Main,
        };
        
        let headers = inner.units().collect::<Vec<_>>()?;
        let units = headers.iter().map(|_| LazyCell::new()).collect();
        Ok(DwarfInfo {
            inner,
            headers,
            units,
            symbol_map,
            address_offset,
            kind,
        })
    }
    
    fn get_unit(&self, index: usize) -> Result<Option<&Unit<'d>>, DwarfError> {
        
        let cell = match self.units.get(index) {
            Some(cell) => cell,
            None => return Ok(None),
        };
        let unit_opt = cell.try_borrow_with(|| {
            
            
            
            
            let header = self.headers[index];
            match self.inner.unit(header) {
                Ok(unit) => Ok(Some(unit)),
                Err(gimli::read::Error::MissingUnitDie) => Ok(None),
                Err(error) => Err(DwarfError::from(error)),
            }
        })?;
        Ok(unit_opt.as_ref())
    }
    
    fn find_unit_offset(
        &self,
        offset: DebugInfoOffset,
    ) -> Result<(UnitRef<'d, '_>, UnitOffset), DwarfError> {
        let section_offset = UnitSectionOffset::DebugInfoOffset(offset);
        let search_result = self
            .headers
            .binary_search_by_key(§ion_offset, UnitHeader::offset);
        let index = match search_result {
            Ok(index) => index,
            Err(0) => return Err(DwarfErrorKind::InvalidUnitRef(offset.0).into()),
            Err(next_index) => next_index - 1,
        };
        if let Some(unit) = self.get_unit(index)? {
            if let Some(unit_offset) = section_offset.to_unit_offset(unit) {
                return Ok((UnitRef { unit, info: self }, unit_offset));
            }
        }
        Err(DwarfErrorKind::InvalidUnitRef(offset.0).into())
    }
    
    fn units(&'d self) -> DwarfUnitIterator<'_> {
        DwarfUnitIterator {
            info: self,
            index: 0,
        }
    }
}
impl<'slf, 'd: 'slf> AsSelf<'slf> for DwarfInfo<'d> {
    type Ref = DwarfInfo<'slf>;
    fn as_self(&'slf self) -> &Self::Ref {
        unsafe { std::mem::transmute(self) }
    }
}
impl fmt::Debug for DwarfInfo<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("DwarfInfo")
            .field("headers", &self.headers)
            .field("symbol_map", &self.symbol_map)
            .field("address_offset", &self.address_offset)
            .finish()
    }
}
struct DwarfUnitIterator<'s> {
    info: &'s DwarfInfo<'s>,
    index: usize,
}
impl<'s> Iterator for DwarfUnitIterator<'s> {
    type Item = Result<DwarfUnit<'s, 's>, DwarfError>;
    fn next(&mut self) -> Option<Self::Item> {
        while self.index < self.info.headers.len() {
            let result = self.info.get_unit(self.index);
            self.index += 1;
            let unit = match result {
                Ok(Some(unit)) => unit,
                Ok(None) => continue,
                Err(error) => return Some(Err(error)),
            };
            match DwarfUnit::from_unit(unit, self.info) {
                Ok(Some(unit)) => return Some(Ok(unit)),
                Ok(None) => continue,
                Err(error) => return Some(Err(error)),
            }
        }
        None
    }
}
impl std::iter::FusedIterator for DwarfUnitIterator<'_> {}
pub struct DwarfDebugSession<'data> {
    cell: SelfCell<Box<DwarfSections<'data>>, DwarfInfo<'data>>,
}
impl<'data> DwarfDebugSession<'data> {
    
    pub fn parse<D>(
        dwarf: &D,
        symbol_map: SymbolMap<'data>,
        address_offset: i64,
        kind: ObjectKind,
    ) -> Result<Self, DwarfError>
    where
        D: Dwarf<'data>,
    {
        let sections = DwarfSections::from_dwarf(dwarf);
        let cell = SelfCell::try_new(Box::new(sections), |sections| {
            DwarfInfo::parse(unsafe { &*sections }, symbol_map, address_offset, kind)
        })?;
        Ok(DwarfDebugSession { cell })
    }
    
    pub fn files(&self) -> DwarfFileIterator<'_> {
        DwarfFileIterator {
            units: self.cell.get().units(),
            files: DwarfUnitFileIterator::default(),
            finished: false,
        }
    }
    
    pub fn functions(&self) -> DwarfFunctionIterator<'_> {
        DwarfFunctionIterator {
            units: self.cell.get().units(),
            functions: Vec::new().into_iter(),
            range_buf: Vec::new(),
            finished: false,
        }
    }
    
    
    
    pub fn source_by_path(&self, _path: &str) -> Result<Option<Cow<'_, str>>, DwarfError> {
        Ok(None)
    }
}
impl<'data, 'session> DebugSession<'session> for DwarfDebugSession<'data> {
    type Error = DwarfError;
    type FunctionIterator = DwarfFunctionIterator<'session>;
    type FileIterator = DwarfFileIterator<'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<Cow<'_, str>>, Self::Error> {
        self.source_by_path(path)
    }
}
#[derive(Debug, Default)]
struct DwarfUnitFileIterator<'s> {
    unit: Option<DwarfUnit<'s, 's>>,
    index: usize,
}
impl<'s> Iterator for DwarfUnitFileIterator<'s> {
    type Item = FileEntry<'s>;
    fn next(&mut self) -> Option<Self::Item> {
        let unit = self.unit.as_ref()?;
        let line_program = unit.line_program.as_ref().map(|p| &p.header)?;
        let file = line_program.file_names().get(self.index)?;
        self.index += 1;
        Some(FileEntry {
            compilation_dir: unit.compilation_dir(),
            info: unit.file_info(line_program, file),
        })
    }
}
pub struct DwarfFileIterator<'s> {
    units: DwarfUnitIterator<'s>,
    files: DwarfUnitFileIterator<'s>,
    finished: bool,
}
impl<'s> Iterator for DwarfFileIterator<'s> {
    type Item = Result<FileEntry<'s>, DwarfError>;
    fn next(&mut self) -> Option<Self::Item> {
        if self.finished {
            return None;
        }
        loop {
            if let Some(file_entry) = self.files.next() {
                return Some(Ok(file_entry));
            }
            let unit = match self.units.next() {
                Some(Ok(unit)) => unit,
                Some(Err(error)) => return Some(Err(error)),
                None => break,
            };
            self.files = DwarfUnitFileIterator {
                unit: Some(unit),
                index: 0,
            };
        }
        self.finished = true;
        None
    }
}
pub struct DwarfFunctionIterator<'s> {
    units: DwarfUnitIterator<'s>,
    functions: std::vec::IntoIter<Function<'s>>,
    range_buf: Vec<Range>,
    finished: bool,
}
impl<'s> Iterator for DwarfFunctionIterator<'s> {
    type Item = Result<Function<'s>, DwarfError>;
    fn next(&mut self) -> Option<Self::Item> {
        if self.finished {
            return None;
        }
        loop {
            if let Some(func) = self.functions.next() {
                return Some(Ok(func));
            }
            let unit = match self.units.next() {
                Some(Ok(unit)) => unit,
                Some(Err(error)) => return Some(Err(error)),
                None => break,
            };
            self.functions = match unit.functions(&mut self.range_buf) {
                Ok(functions) => functions.into_iter(),
                Err(error) => return Some(Err(error)),
            };
        }
        self.finished = true;
        None
    }
}
impl std::iter::FusedIterator for DwarfFunctionIterator<'_> {}