ghostscope_dwarf/parser/
detailed_parser.rs

1//! Detailed DWARF parser for on-demand traversal and variable resolution
2//!
3//! This module handles detailed parsing of DWARF tree structures, including:
4//! - Tree traversal for variable collection
5//! - Variable and parameter DIE parsing
6//! - Scope-aware variable resolution
7
8use crate::{
9    core::{EvaluationResult, Result},
10    parser::ExpressionEvaluator,
11    TypeInfo,
12};
13use gimli::{EndianArcSlice, LittleEndian, Reader};
14// Alias gimli constants to upper-case identifiers to satisfy naming lints without allow attributes
15use gimli::constants::{
16    DW_AT_byte_size as DW_AT_BYTE_SIZE, DW_AT_encoding as DW_AT_ENCODING, DW_AT_name as DW_AT_NAME,
17    DW_AT_type as DW_AT_TYPE, DW_TAG_array_type as DW_TAG_ARRAY_TYPE,
18    DW_TAG_base_type as DW_TAG_BASE_TYPE, DW_TAG_class_type as DW_TAG_CLASS_TYPE,
19    DW_TAG_const_type as DW_TAG_CONST_TYPE, DW_TAG_enumeration_type as DW_TAG_ENUMERATION_TYPE,
20    DW_TAG_pointer_type as DW_TAG_POINTER_TYPE, DW_TAG_restrict_type as DW_TAG_RESTRICT_TYPE,
21    DW_TAG_structure_type as DW_TAG_STRUCTURE_TYPE,
22    DW_TAG_subroutine_type as DW_TAG_SUBROUTINE_TYPE, DW_TAG_typedef as DW_TAG_TYPEDEF,
23    DW_TAG_union_type as DW_TAG_UNION_TYPE, DW_TAG_volatile_type as DW_TAG_VOLATILE_TYPE,
24};
25use std::collections::HashSet;
26// no tracing imports needed here
27
28/// Variable with complete information including EvaluationResult
29#[derive(Debug, Clone)]
30pub struct VariableWithEvaluation {
31    pub name: String,
32    pub type_name: String,
33    pub dwarf_type: Option<TypeInfo>,
34    pub evaluation_result: EvaluationResult,
35    pub scope_depth: usize,
36    pub is_parameter: bool,
37    pub is_artificial: bool,
38}
39
40// Removed full traversal request/context types in shallow mode
41
42/// Detailed DWARF parser for tree traversal and variable collection
43#[derive(Debug)]
44pub struct DetailedParser {}
45
46impl DetailedParser {
47    /// Create new detailed parser
48    pub fn new() -> Self {
49        Self {}
50    }
51
52    /// Attach a cross-CU type name index for faster completion
53    pub fn set_type_name_index(&mut self, _index: std::sync::Arc<crate::data::TypeNameIndex>) {}
54
55    // Full type resolution intentionally removed; only shallow type resolution is supported.
56
57    /// Shallow type resolution (no recursive member expansion)
58    /// Returns minimal TypeInfo with name/size where possible.
59    pub fn resolve_type_shallow_at_offset(
60        dwarf: &gimli::Dwarf<EndianArcSlice<LittleEndian>>,
61        unit: &gimli::Unit<EndianArcSlice<LittleEndian>>,
62        mut type_offset: gimli::UnitOffset,
63    ) -> Option<TypeInfo> {
64        let mut visited = std::collections::HashSet::new();
65        // Strip typedef/qualifiers chain but keep last typedef name if it's the canonical alias
66        let mut alias_name: Option<String> = None;
67
68        let mut step = 0usize;
69        const MAX_STEPS: usize = 64;
70        loop {
71            if step >= MAX_STEPS || !visited.insert(type_offset) {
72                return Some(TypeInfo::UnknownType {
73                    name: "<depth_limit>".to_string(),
74                });
75            }
76            step += 1;
77            let entry = unit.entry(type_offset).ok()?;
78            let tag = entry.tag();
79            // Utility to read attr string name
80            let mut entry_name: Option<String> = None;
81            if let Ok(Some(a)) = entry.attr(DW_AT_NAME) {
82                if let Ok(s) = dwarf.attr_string(unit, a.value()) {
83                    if let Ok(s_str) = s.to_string_lossy() {
84                        entry_name = Some(s_str.into_owned());
85                    }
86                }
87            }
88            match tag {
89                DW_TAG_TYPEDEF => {
90                    if alias_name.is_none() {
91                        alias_name = entry_name.clone();
92                    }
93                    if let Ok(Some(gimli::AttributeValue::UnitRef(off))) =
94                        entry.attr_value(DW_AT_TYPE)
95                    {
96                        type_offset = off;
97                        continue;
98                    }
99                    return Some(TypeInfo::TypedefType {
100                        name: alias_name.unwrap_or_else(|| {
101                            entry_name.unwrap_or_else(|| "<anon_typedef>".to_string())
102                        }),
103                        underlying_type: Box::new(TypeInfo::UnknownType {
104                            name: "<unknown>".to_string(),
105                        }),
106                    });
107                }
108                DW_TAG_CONST_TYPE | DW_TAG_VOLATILE_TYPE | DW_TAG_RESTRICT_TYPE => {
109                    if let Ok(Some(gimli::AttributeValue::UnitRef(off))) =
110                        entry.attr_value(DW_AT_TYPE)
111                    {
112                        type_offset = off;
113                        continue;
114                    }
115                    return Some(TypeInfo::QualifiedType {
116                        qualifier: crate::TypeQualifier::Const,
117                        underlying_type: Box::new(TypeInfo::UnknownType {
118                            name: "<unknown>".to_string(),
119                        }),
120                    });
121                }
122                DW_TAG_POINTER_TYPE => {
123                    let mut byte_size: u64 = 8;
124                    // Default to unknown pointee until we find a concrete base/aggregate
125                    let mut target: TypeInfo = TypeInfo::UnknownType {
126                        name: "void".to_string(),
127                    };
128                    if let Ok(Some(a)) = entry.attr(DW_AT_BYTE_SIZE) {
129                        if let gimli::AttributeValue::Udata(sz) = a.value() {
130                            byte_size = sz;
131                        }
132                    }
133                    // Very shallow pointee name resolution: unwrap typedef/qualifiers only, without recursion
134                    let mut pointee_name: Option<String> = None;
135                    if let Ok(Some(gimli::AttributeValue::UnitRef(mut toff))) =
136                        entry.attr_value(DW_AT_TYPE)
137                    {
138                        // Unwrap up to a small bound to avoid deep recursion/stack blowups on real-world code
139                        for _ in 0..8 {
140                            if let Ok(tentry) = unit.entry(toff) {
141                                match tentry.tag() {
142                                    DW_TAG_TYPEDEF | DW_TAG_CONST_TYPE | DW_TAG_VOLATILE_TYPE
143                                    | DW_TAG_RESTRICT_TYPE => {
144                                        // Capture typedef name as a fallback pointee name
145                                        if tentry.tag() == DW_TAG_TYPEDEF {
146                                            if let Ok(Some(na)) = tentry.attr(DW_AT_NAME) {
147                                                if let Ok(s) = dwarf.attr_string(unit, na.value()) {
148                                                    if pointee_name.is_none() {
149                                                        if let Ok(s_str) = s.to_string_lossy() {
150                                                            pointee_name = Some(s_str.into_owned());
151                                                        }
152                                                    }
153                                                }
154                                            }
155                                        }
156                                        if let Ok(Some(gimli::AttributeValue::UnitRef(next))) =
157                                            tentry.attr_value(DW_AT_TYPE)
158                                        {
159                                            toff = next;
160                                            continue;
161                                        }
162                                        // No further type; bail
163                                    }
164                                    DW_TAG_BASE_TYPE => {
165                                        // Construct BaseType with size+encoding
166                                        let mut byte_size = 0u64;
167                                        let mut encoding = gimli::constants::DW_ATE_unsigned;
168                                        if let Ok(Some(a)) = tentry.attr(DW_AT_BYTE_SIZE) {
169                                            if let gimli::AttributeValue::Udata(sz) = a.value() {
170                                                byte_size = sz;
171                                            }
172                                        }
173                                        if let Ok(Some(a)) = tentry.attr(DW_AT_ENCODING) {
174                                            if let gimli::AttributeValue::Encoding(enc) = a.value()
175                                            {
176                                                encoding = enc;
177                                            }
178                                        }
179                                        let name = if let Ok(Some(na)) = tentry.attr(DW_AT_NAME) {
180                                            if let Ok(s) = dwarf.attr_string(unit, na.value()) {
181                                                s.to_string_lossy()
182                                                    .ok()
183                                                    .map(|c| c.into_owned())
184                                                    .unwrap_or_else(|| "<base>".into())
185                                            } else {
186                                                "<base>".into()
187                                            }
188                                        } else {
189                                            "<base>".into()
190                                        };
191                                        pointee_name = Some(name.clone());
192                                        target = TypeInfo::BaseType {
193                                            name,
194                                            size: byte_size,
195                                            encoding: encoding.0 as u16,
196                                        };
197                                    }
198                                    DW_TAG_STRUCTURE_TYPE
199                                    | DW_TAG_CLASS_TYPE
200                                    | DW_TAG_UNION_TYPE
201                                    | DW_TAG_ENUMERATION_TYPE => {
202                                        // Do NOT recursively resolve aggregates here to avoid cycles on self-referential types.
203                                        // Only record the name; deref-time upgrade will use analyzer's shallow index safely.
204                                        if let Ok(Some(na)) = tentry.attr(DW_AT_NAME) {
205                                            if let Ok(s) = dwarf.attr_string(unit, na.value()) {
206                                                if let Ok(s_str) = s.to_string_lossy() {
207                                                    let n = s_str.into_owned();
208                                                    pointee_name = Some(n.clone());
209                                                    // keep target as UnknownType{name} to avoid deep recursion here
210                                                    if matches!(
211                                                        target,
212                                                        TypeInfo::UnknownType { .. }
213                                                    ) {
214                                                        target = TypeInfo::UnknownType { name: n };
215                                                    }
216                                                }
217                                            }
218                                        }
219                                    }
220                                    _ => {}
221                                }
222                                break;
223                            } else {
224                                break;
225                            }
226                        }
227                    }
228                    // If only a name was found without concrete target, carry it as UnknownType
229                    if let Some(n) = pointee_name {
230                        if matches!(target, TypeInfo::UnknownType { .. }) {
231                            target = TypeInfo::UnknownType { name: n };
232                        }
233                    }
234                    return Some(TypeInfo::PointerType {
235                        target_type: Box::new(target),
236                        size: byte_size,
237                    });
238                }
239                DW_TAG_BASE_TYPE => {
240                    let name = entry_name.unwrap_or_else(|| "<base>".to_string());
241                    let mut byte_size = 0u64;
242                    let mut encoding = gimli::constants::DW_ATE_unsigned;
243                    let mut attrs = entry.attrs();
244                    while let Ok(Some(a)) = attrs.next() {
245                        match a.name() {
246                            DW_AT_BYTE_SIZE => {
247                                if let gimli::AttributeValue::Udata(sz) = a.value() {
248                                    byte_size = sz;
249                                }
250                            }
251                            DW_AT_ENCODING => {
252                                if let gimli::AttributeValue::Encoding(enc) = a.value() {
253                                    encoding = enc;
254                                }
255                            }
256                            _ => {}
257                        }
258                    }
259                    return Some(TypeInfo::BaseType {
260                        name,
261                        size: byte_size,
262                        encoding: encoding.0 as u16,
263                    });
264                }
265                DW_TAG_STRUCTURE_TYPE | DW_TAG_CLASS_TYPE => {
266                    let name = alias_name.clone().unwrap_or_else(|| {
267                        entry_name.unwrap_or_else(|| "<anon_struct>".to_string())
268                    });
269                    let mut byte_size = 0u64;
270                    if let Ok(Some(a)) = entry.attr(DW_AT_BYTE_SIZE) {
271                        if let gimli::AttributeValue::Udata(sz) = a.value() {
272                            byte_size = sz;
273                        }
274                    }
275                    // Collect only direct member DIEs
276                    let mut members: Vec<crate::StructMember> = Vec::new();
277                    if let Ok(mut tree) = unit.entries_tree(Some(entry.offset())) {
278                        if let Ok(root) = tree.root() {
279                            let mut children = root.children();
280                            while let Ok(Some(child)) = children.next() {
281                                let ce = child.entry();
282                                if ce.tag() == gimli::DW_TAG_member {
283                                    // member name
284                                    let mut m_name = String::new();
285                                    if let Ok(Some(na)) = ce.attr(DW_AT_NAME) {
286                                        if let Ok(s) = dwarf.attr_string(unit, na.value()) {
287                                            if let Ok(s_str) = s.to_string_lossy() {
288                                                m_name = s_str.into_owned();
289                                            }
290                                        }
291                                    }
292                                    // member type (shallow)
293                                    let mut m_type = TypeInfo::UnknownType {
294                                        name: "unknown".to_string(),
295                                    };
296                                    if let Ok(Some(gimli::AttributeValue::UnitRef(toff))) =
297                                        ce.attr_value(DW_AT_TYPE)
298                                    {
299                                        if let Some(ti) =
300                                            Self::resolve_type_shallow_at_offset(dwarf, unit, toff)
301                                        {
302                                            m_type = ti;
303                                        }
304                                    }
305                                    // member offset (simple evaluation)
306                                    let mut m_offset: u64 = 0;
307                                    if let Ok(Some(ml)) = ce.attr(gimli::DW_AT_data_member_location)
308                                    {
309                                        match ml.value() {
310                                            gimli::AttributeValue::Udata(v) => m_offset = v,
311                                            gimli::AttributeValue::Exprloc(expr) => {
312                                                // Try to eval simple DW_OP_constu / plus_uconst
313                                                if let Some(v) =
314                                                    Self::eval_member_offset_expr_local(&expr)
315                                                {
316                                                    m_offset = v;
317                                                }
318                                            }
319                                            _ => {}
320                                        }
321                                    }
322                                    // bit offsets/sizes (optional)
323                                    let mut bit_offset: Option<u8> = None;
324                                    let mut bit_size: Option<u8> = None;
325                                    if let Ok(Some(bo)) = ce.attr(gimli::DW_AT_bit_offset) {
326                                        if let gimli::AttributeValue::Udata(v) = bo.value() {
327                                            bit_offset = u8::try_from(v).ok();
328                                        }
329                                    }
330                                    if let Ok(Some(bs)) = ce.attr(gimli::DW_AT_data_bit_offset) {
331                                        if let gimli::AttributeValue::Udata(v) = bs.value() {
332                                            bit_offset = u8::try_from(v % 8).ok();
333                                            m_offset = v / 8;
334                                        }
335                                    }
336                                    if let Ok(Some(bsz)) = ce.attr(gimli::DW_AT_bit_size) {
337                                        if let gimli::AttributeValue::Udata(v) = bsz.value() {
338                                            bit_size = u8::try_from(v).ok();
339                                        }
340                                    }
341                                    if m_name.is_empty() {
342                                        m_name = format!("member_{}", members.len());
343                                    }
344                                    // Wrap bitfield member type into BitfieldType for standalone printing
345                                    let member_type = if let Some(bs) = bit_size {
346                                        let bo = bit_offset.unwrap_or(0);
347                                        TypeInfo::BitfieldType {
348                                            underlying_type: Box::new(m_type),
349                                            bit_offset: bo,
350                                            bit_size: bs,
351                                        }
352                                    } else {
353                                        m_type
354                                    };
355                                    members.push(crate::StructMember {
356                                        name: m_name,
357                                        member_type,
358                                        offset: m_offset,
359                                        bit_offset,
360                                        bit_size,
361                                    });
362                                }
363                            }
364                        }
365                    }
366                    // Post-process: infer array total_size/element_count when missing (from next member offset or struct size)
367                    if !members.is_empty() {
368                        // Pre-build sorted offsets
369                        let mut offsets: Vec<u64> = members.iter().map(|m| m.offset).collect();
370                        offsets.sort_unstable();
371                        offsets.dedup();
372
373                        for m in &mut members {
374                            // Only for array members with missing size info
375                            if let TypeInfo::ArrayType {
376                                element_type,
377                                element_count,
378                                total_size,
379                            } = &m.member_type
380                            {
381                                if element_count.is_none() && total_size.is_none() {
382                                    let cur_off = m.offset;
383                                    let next_off = offsets
384                                        .iter()
385                                        .cloned()
386                                        .filter(|&o| o > cur_off)
387                                        .min()
388                                        .unwrap_or(byte_size);
389                                    let avail = next_off.saturating_sub(cur_off);
390                                    if avail > 0 {
391                                        let elem_sz = element_type.size();
392                                        let mut new_count: Option<u64> = None;
393                                        if elem_sz > 0 && avail % elem_sz == 0 {
394                                            new_count = Some(avail / elem_sz);
395                                        }
396                                        m.member_type = TypeInfo::ArrayType {
397                                            element_type: element_type.clone(),
398                                            element_count: new_count,
399                                            total_size: Some(avail),
400                                        };
401                                    }
402                                }
403                            }
404                        }
405                    }
406                    return Some(TypeInfo::StructType {
407                        name,
408                        size: byte_size,
409                        members,
410                    });
411                }
412                DW_TAG_UNION_TYPE => {
413                    let name = alias_name.clone().unwrap_or_else(|| {
414                        entry_name.unwrap_or_else(|| "<anon_union>".to_string())
415                    });
416                    let mut byte_size = 0u64;
417                    if let Ok(Some(a)) = entry.attr(DW_AT_BYTE_SIZE) {
418                        if let gimli::AttributeValue::Udata(sz) = a.value() {
419                            byte_size = sz;
420                        }
421                    }
422                    let mut members: Vec<crate::StructMember> = Vec::new();
423                    if let Ok(mut tree) = unit.entries_tree(Some(entry.offset())) {
424                        if let Ok(root) = tree.root() {
425                            let mut children = root.children();
426                            while let Ok(Some(child)) = children.next() {
427                                let ce = child.entry();
428                                if ce.tag() == gimli::DW_TAG_member {
429                                    let mut m_name = String::new();
430                                    if let Ok(Some(na)) = ce.attr(gimli::DW_AT_name) {
431                                        if let Ok(s) = dwarf.attr_string(unit, na.value()) {
432                                            if let Ok(s_str) = s.to_string_lossy() {
433                                                m_name = s_str.into_owned();
434                                            }
435                                        }
436                                    }
437                                    let mut m_type = TypeInfo::UnknownType {
438                                        name: "unknown".to_string(),
439                                    };
440                                    if let Ok(Some(gimli::AttributeValue::UnitRef(toff))) =
441                                        ce.attr_value(DW_AT_TYPE)
442                                    {
443                                        if let Some(ti) =
444                                            Self::resolve_type_shallow_at_offset(dwarf, unit, toff)
445                                        {
446                                            m_type = ti;
447                                        }
448                                    }
449                                    if m_name.is_empty() {
450                                        m_name = format!("member_{}", members.len());
451                                    }
452                                    members.push(crate::StructMember {
453                                        name: m_name,
454                                        member_type: m_type,
455                                        offset: 0,
456                                        bit_offset: None,
457                                        bit_size: None,
458                                    });
459                                }
460                            }
461                        }
462                    }
463                    return Some(TypeInfo::UnionType {
464                        name,
465                        size: byte_size,
466                        members,
467                    });
468                }
469                DW_TAG_ENUMERATION_TYPE => {
470                    let name = alias_name
471                        .clone()
472                        .unwrap_or_else(|| entry_name.unwrap_or_else(|| "<anon_enum>".to_string()));
473                    // Parse base type and size
474                    let mut byte_size = 0u64;
475                    if let Ok(Some(a)) = entry.attr(DW_AT_BYTE_SIZE) {
476                        if let gimli::AttributeValue::Udata(sz) = a.value() {
477                            byte_size = sz;
478                        }
479                    }
480                    // Default base type as signed int; size from byte_size or 4
481                    let mut base_type: TypeInfo = TypeInfo::BaseType {
482                        name: "int".to_string(),
483                        size: if byte_size > 0 { byte_size } else { 4 },
484                        encoding: gimli::constants::DW_ATE_signed.0 as u16,
485                    };
486                    // If DW_AT_type refers to a base type, resolve it shallowly
487                    if let Ok(Some(gimli::AttributeValue::UnitRef(toff))) =
488                        entry.attr_value(DW_AT_TYPE)
489                    {
490                        if let Some(ti) = Self::resolve_type_shallow_at_offset(dwarf, unit, toff) {
491                            // Accept only base/qualified/typedef chain base type as enum underlying type
492                            base_type = ti;
493                            // If enum size missing, use underlying base type size
494                            let bs = base_type.size();
495                            if byte_size == 0 && bs > 0 {
496                                byte_size = bs;
497                            }
498                        }
499                    }
500                    // Collect enum variants (one level)
501                    let mut variants: Vec<crate::EnumVariant> = Vec::new();
502                    if let Ok(mut tree) = unit.entries_tree(Some(entry.offset())) {
503                        if let Ok(root) = tree.root() {
504                            let mut children = root.children();
505                            while let Ok(Some(child)) = children.next() {
506                                let ce = child.entry();
507                                if ce.tag() == gimli::DW_TAG_enumerator {
508                                    let mut v_name = String::new();
509                                    if let Ok(Some(na)) = ce.attr(gimli::DW_AT_name) {
510                                        if let Ok(s) = dwarf.attr_string(unit, na.value()) {
511                                            if let Ok(s_str) = s.to_string_lossy() {
512                                                v_name = s_str.into_owned();
513                                            }
514                                        }
515                                    }
516                                    let mut v_val: i64 = 0;
517                                    if let Ok(Some(cv)) = ce.attr(gimli::DW_AT_const_value) {
518                                        let signed = match &base_type {
519                                            TypeInfo::BaseType { encoding, .. } => {
520                                                *encoding
521                                                    == gimli::constants::DW_ATE_signed.0 as u16
522                                                    || *encoding
523                                                        == gimli::constants::DW_ATE_signed_char.0
524                                                            as u16
525                                            }
526                                            TypeInfo::TypedefType {
527                                                underlying_type, ..
528                                            }
529                                            | TypeInfo::QualifiedType {
530                                                underlying_type, ..
531                                            } => {
532                                                matches!(
533                                                    &**underlying_type,
534                                                    TypeInfo::BaseType { encoding, .. }
535                                                        if *encoding == gimli::constants::DW_ATE_signed.0 as u16
536                                                            || *encoding
537                                                                == gimli::constants::DW_ATE_signed_char.0 as u16
538                                                )
539                                            }
540                                            _ => true,
541                                        };
542                                        v_val = match cv.value() {
543                                            gimli::AttributeValue::Udata(u) => u as i64,
544                                            gimli::AttributeValue::Sdata(s) => s,
545                                            gimli::AttributeValue::Data1(b) => {
546                                                let u = b as u64;
547                                                if signed && (u & 0x80) != 0 {
548                                                    (u as i8) as i64
549                                                } else {
550                                                    u as i64
551                                                }
552                                            }
553                                            gimli::AttributeValue::Data2(u) => {
554                                                let u = u as u64;
555                                                if signed && (u & 0x8000) != 0 {
556                                                    (u as i16) as i64
557                                                } else {
558                                                    u as i64
559                                                }
560                                            }
561                                            gimli::AttributeValue::Data4(u) => {
562                                                let u = u as u64;
563                                                if signed && (u & 0x8000_0000) != 0 {
564                                                    (u as i32) as i64
565                                                } else {
566                                                    u as i64
567                                                }
568                                            }
569                                            gimli::AttributeValue::Data8(u) => u as i64,
570                                            _ => v_val,
571                                        };
572                                    }
573                                    if v_name.is_empty() {
574                                        v_name = format!("variant_{}", variants.len());
575                                    }
576                                    variants.push(crate::EnumVariant {
577                                        name: v_name,
578                                        value: v_val,
579                                    });
580                                }
581                            }
582                        }
583                    }
584                    return Some(TypeInfo::EnumType {
585                        name,
586                        size: byte_size,
587                        base_type: Box::new(base_type),
588                        variants,
589                    });
590                }
591                DW_TAG_ARRAY_TYPE => {
592                    // element_type shallow + total_size if available + subrange element_count (one step deeper)
593                    let mut elem_type: Option<TypeInfo> = None;
594                    if let Ok(Some(gimli::AttributeValue::UnitRef(eoff))) =
595                        entry.attr_value(DW_AT_TYPE)
596                    {
597                        elem_type = Self::resolve_type_shallow_at_offset(dwarf, unit, eoff);
598                    }
599                    let element_type = Box::new(elem_type.unwrap_or(TypeInfo::UnknownType {
600                        name: "<elem>".to_string(),
601                    }));
602                    let mut total_size: Option<u64> = None;
603                    if let Ok(Some(a)) = entry.attr(DW_AT_BYTE_SIZE) {
604                        if let gimli::AttributeValue::Udata(sz) = a.value() {
605                            total_size = Some(sz);
606                        }
607                    }
608                    // Optional: subrange child yields count/upper_bound
609                    let mut element_count: Option<u64> = None;
610                    if let Ok(mut tree) = unit.entries_tree(Some(entry.offset())) {
611                        if let Ok(root) = tree.root() {
612                            let mut children = root.children();
613                            while let Ok(Some(child)) = children.next() {
614                                let ce = child.entry();
615                                if ce.tag() == gimli::DW_TAG_subrange_type {
616                                    // Prefer DW_AT_count; fallback to upper_bound (+1)
617                                    if let Ok(Some(cv)) = ce.attr(gimli::DW_AT_count) {
618                                        match cv.value() {
619                                            gimli::AttributeValue::Udata(u) => {
620                                                element_count = Some(u);
621                                            }
622                                            gimli::AttributeValue::Sdata(s) => {
623                                                if s >= 0 {
624                                                    element_count = Some(s as u64);
625                                                }
626                                            }
627                                            gimli::AttributeValue::Data1(b) => {
628                                                element_count = Some(b as u64);
629                                            }
630                                            gimli::AttributeValue::Data2(u) => {
631                                                element_count = Some(u as u64);
632                                            }
633                                            gimli::AttributeValue::Data4(u) => {
634                                                element_count = Some(u as u64);
635                                            }
636                                            gimli::AttributeValue::Data8(u) => {
637                                                element_count = Some(u);
638                                            }
639                                            _ => {}
640                                        }
641                                    }
642                                    if element_count.is_none() {
643                                        if let Ok(Some(ub)) = ce.attr(gimli::DW_AT_upper_bound) {
644                                            let ub_v: Option<i64> = match ub.value() {
645                                                gimli::AttributeValue::Udata(u) => Some(u as i64),
646                                                gimli::AttributeValue::Sdata(s) => Some(s),
647                                                gimli::AttributeValue::Data1(b) => Some(b as i64),
648                                                gimli::AttributeValue::Data2(u) => Some(u as i64),
649                                                gimli::AttributeValue::Data4(u) => Some(u as i64),
650                                                gimli::AttributeValue::Data8(u) => Some(u as i64),
651                                                _ => None,
652                                            };
653                                            if let Some(ub_i) = ub_v {
654                                                if ub_i >= 0 {
655                                                    element_count = Some((ub_i as u64) + 1);
656                                                }
657                                            }
658                                        }
659                                    }
660                                    // Stop at first subrange
661                                    if element_count.is_some() {
662                                        break;
663                                    }
664                                }
665                            }
666                        }
667                    }
668                    // If total_size absent but count + elem size known, compute it
669                    if total_size.is_none() {
670                        let es = element_type.size();
671                        if let Some(cnt) = element_count {
672                            if es > 0 {
673                                total_size = Some(es * cnt);
674                            }
675                        }
676                    }
677                    return Some(TypeInfo::ArrayType {
678                        element_type,
679                        element_count,
680                        total_size,
681                    });
682                }
683                DW_TAG_SUBROUTINE_TYPE => {
684                    return Some(TypeInfo::FunctionType {
685                        return_type: None,
686                        parameters: Vec::new(),
687                    });
688                }
689                _ => {
690                    // Fallback: return alias name or entry name
691                    let nm = alias_name
692                        .or(entry_name)
693                        .unwrap_or_else(|| "<unknown>".to_string());
694                    return Some(TypeInfo::UnknownType { name: nm });
695                }
696            }
697        }
698    }
699
700    /// Local simple evaluator for DW_AT_data_member_location exprloc when it's a constant offset.
701    fn eval_member_offset_expr_local(
702        expr: &gimli::Expression<EndianArcSlice<LittleEndian>>,
703    ) -> Option<u64> {
704        let temp = expr.0.to_slice().ok();
705        let bytes = temp.as_deref().unwrap_or(&[]);
706        if bytes.is_empty() {
707            return None;
708        }
709        let mut rdr = gimli::EndianSlice::new(bytes, LittleEndian);
710        if let Ok(op) = rdr.read_u8() {
711            match op {
712                0x10 => rdr.read_uleb128().ok(),                   // DW_OP_constu
713                0x11 => rdr.read_sleb128().ok().map(|v| v as u64), // DW_OP_consts
714                0x23 => rdr.read_uleb128().ok(),                   // DW_OP_plus_uconst
715                _ => None,
716            }
717        } else {
718            None
719        }
720    }
721
722    // Full variable collection and traversal helpers removed in shallow-only mode
723
724    // parse_variable_entry wrapper removed; use parse_variable_entry_with_mode
725
726    /// Parse a variable and optionally skip full DWARF type resolution
727    pub fn parse_variable_entry_with_mode(
728        &mut self,
729        entry: &gimli::DebuggingInformationEntry<EndianArcSlice<LittleEndian>>,
730        unit: &gimli::Unit<EndianArcSlice<LittleEndian>>,
731        dwarf: &gimli::Dwarf<EndianArcSlice<LittleEndian>>,
732        address: u64,
733        get_cfa: Option<&dyn Fn(u64) -> Result<Option<crate::core::CfaResult>>>,
734        scope_depth: usize,
735    ) -> Result<Option<VariableWithEvaluation>> {
736        // No traversal context retained in shallow mode
737        // Resolve basic
738        let mut visited = std::collections::HashSet::new();
739        let Some(name) = Self::resolve_name_with_origins(entry, unit, dwarf, &mut visited)? else {
740            return Ok(None);
741        };
742        let is_parameter = entry.tag() == gimli::constants::DW_TAG_formal_parameter;
743        let type_name = Self::resolve_type_name(entry, unit, dwarf)?;
744        let evaluation_result = self.parse_location(entry, unit, dwarf, address, get_cfa)?;
745        // Full type resolution disabled in shallow mode
746        let dwarf_type = None;
747        Ok(Some(VariableWithEvaluation {
748            name,
749            type_name,
750            dwarf_type,
751            evaluation_result,
752            scope_depth,
753            is_parameter,
754            is_artificial: false,
755        }))
756    }
757
758    /// Resolve type name for a variable or type DIE.
759    ///
760    /// This function follows DW_AT_type chains (pointer/const/array/typedef) and
761    /// includes a recursion guard to break true cycles (e.g., typedef A->B->A).
762    fn resolve_type_name(
763        entry: &gimli::DebuggingInformationEntry<EndianArcSlice<LittleEndian>>,
764        unit: &gimli::Unit<EndianArcSlice<LittleEndian>>,
765        dwarf: &gimli::Dwarf<EndianArcSlice<LittleEndian>>,
766    ) -> Result<String> {
767        let mut visited_types: HashSet<gimli::UnitOffset> = HashSet::new();
768        Self::resolve_type_name_rec(entry, unit, dwarf, &mut visited_types)
769    }
770
771    fn resolve_type_name_rec(
772        entry: &gimli::DebuggingInformationEntry<EndianArcSlice<LittleEndian>>,
773        unit: &gimli::Unit<EndianArcSlice<LittleEndian>>,
774        dwarf: &gimli::Dwarf<EndianArcSlice<LittleEndian>>,
775        visited: &mut HashSet<gimli::UnitOffset>,
776    ) -> Result<String> {
777        // Follow DW_AT_type if present
778        let Some(type_off) = Self::resolve_type_ref(entry, unit)? else {
779            // As a fallback, try to use the entry's own name if any
780            let mut name_visited = HashSet::new();
781            if let Some(n) = Self::resolve_name_with_origins(entry, unit, dwarf, &mut name_visited)?
782            {
783                return Ok(n);
784            }
785            return Ok("unknown".to_string());
786        };
787
788        // Recursion guard: if we've seen this type offset already, break the cycle
789        if !visited.insert(type_off) {
790            return Ok("<recursive>".to_string());
791        }
792
793        let mut tree = unit.entries_tree(Some(type_off))?;
794        let type_node = tree.root()?;
795        let type_entry = type_node.entry();
796
797        // If this DIE has a name, prefer it directly
798        let mut name_visited = HashSet::new();
799        if let Some(name) =
800            Self::resolve_name_with_origins(type_entry, unit, dwarf, &mut name_visited)?
801        {
802            return Ok(name);
803        }
804
805        // Handle wrapper/indirection DIEs by following their DW_AT_type
806        match type_entry.tag() {
807            gimli::constants::DW_TAG_pointer_type => {
808                let pointee = Self::resolve_type_name_rec(type_entry, unit, dwarf, visited)?;
809                Ok(format!("{pointee}*"))
810            }
811            gimli::constants::DW_TAG_const_type => {
812                let base = Self::resolve_type_name_rec(type_entry, unit, dwarf, visited)?;
813                Ok(format!("const {base}"))
814            }
815            gimli::constants::DW_TAG_array_type => {
816                let elem = Self::resolve_type_name_rec(type_entry, unit, dwarf, visited)?;
817                Ok(format!("{elem}[]"))
818            }
819            gimli::constants::DW_TAG_typedef => {
820                // Use typedef's own name if present; otherwise follow underlying type
821                let mut tvisited = HashSet::new();
822                if let Some(tname) =
823                    Self::resolve_name_with_origins(type_entry, unit, dwarf, &mut tvisited)?
824                {
825                    Ok(tname)
826                } else {
827                    Self::resolve_type_name_rec(type_entry, unit, dwarf, visited)
828                }
829            }
830            // Fallback: stringify the DWARF tag
831            other => Ok(format!("{other:?}")),
832        }
833    }
834
835    /// Parse location attribute
836    pub fn parse_location(
837        &self,
838        entry: &gimli::DebuggingInformationEntry<EndianArcSlice<LittleEndian>>,
839        unit: &gimli::Unit<EndianArcSlice<LittleEndian>>,
840        dwarf: &gimli::Dwarf<EndianArcSlice<LittleEndian>>,
841        address: u64,
842        get_cfa: Option<&dyn Fn(u64) -> Result<Option<crate::core::CfaResult>>>,
843    ) -> Result<EvaluationResult> {
844        // Use ExpressionEvaluator for unified logic
845        ExpressionEvaluator::evaluate_location(entry, unit, dwarf, address, get_cfa)
846    }
847
848    // extract_name removed; call resolve_name_with_origins directly when needed
849
850    /// Get cache statistics from type resolver
851    pub fn get_cache_stats(&self) -> usize {
852        0
853    }
854
855    fn resolve_attr_with_origins(
856        entry: &gimli::DebuggingInformationEntry<EndianArcSlice<LittleEndian>>,
857        unit: &gimli::Unit<EndianArcSlice<LittleEndian>>,
858        attr: gimli::DwAt,
859        visited: &mut HashSet<gimli::UnitOffset>,
860    ) -> Result<Option<gimli::AttributeValue<EndianArcSlice<LittleEndian>>>> {
861        if let Some(value) = entry.attr_value(attr)? {
862            return Ok(Some(value));
863        }
864
865        for origin_attr in [
866            gimli::constants::DW_AT_abstract_origin,
867            gimli::constants::DW_AT_specification,
868        ] {
869            if let Some(gimli::AttributeValue::UnitRef(offset)) = entry.attr_value(origin_attr)? {
870                if visited.insert(offset) {
871                    let origin_entry = unit.entry(offset)?;
872                    if let Some(value) =
873                        Self::resolve_attr_with_origins(&origin_entry, unit, attr, visited)?
874                    {
875                        return Ok(Some(value));
876                    }
877                }
878            }
879        }
880
881        Ok(None)
882    }
883
884    fn resolve_name_with_origins(
885        entry: &gimli::DebuggingInformationEntry<EndianArcSlice<LittleEndian>>,
886        unit: &gimli::Unit<EndianArcSlice<LittleEndian>>,
887        dwarf: &gimli::Dwarf<EndianArcSlice<LittleEndian>>,
888        visited: &mut HashSet<gimli::UnitOffset>,
889    ) -> Result<Option<String>> {
890        if let Some(attr) =
891            Self::resolve_attr_with_origins(entry, unit, gimli::constants::DW_AT_name, visited)?
892        {
893            return Self::attr_to_string(attr, unit, dwarf);
894        }
895        Ok(None)
896    }
897
898    fn resolve_type_ref(
899        entry: &gimli::DebuggingInformationEntry<EndianArcSlice<LittleEndian>>,
900        unit: &gimli::Unit<EndianArcSlice<LittleEndian>>,
901    ) -> Result<Option<gimli::UnitOffset>> {
902        let mut visited = HashSet::new();
903        Ok(Self::resolve_attr_with_origins(
904            entry,
905            unit,
906            gimli::constants::DW_AT_type,
907            &mut visited,
908        )?
909        .and_then(|value| match value {
910            gimli::AttributeValue::UnitRef(offset) => Some(offset),
911            _ => None,
912        }))
913    }
914
915    fn attr_to_string(
916        attr: gimli::AttributeValue<EndianArcSlice<LittleEndian>>,
917        unit: &gimli::Unit<EndianArcSlice<LittleEndian>>,
918        dwarf: &gimli::Dwarf<EndianArcSlice<LittleEndian>>,
919    ) -> Result<Option<String>> {
920        if let Ok(attr_string) = dwarf.attr_string(unit, attr.clone()) {
921            if let Ok(s_str) = attr_string.to_string_lossy() {
922                return Ok(Some(s_str.into_owned()));
923            }
924        }
925
926        if let gimli::AttributeValue::String(s) = attr {
927            return Ok(s.to_string().ok().map(|cow| cow.into_owned()));
928        }
929
930        Ok(None)
931    }
932
933    // resolve_flag_with_origins and entry_pc_matches removed with variable traversal helpers
934}
935
936impl Default for DetailedParser {
937    fn default() -> Self {
938        Self::new()
939    }
940}