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}