dwat/
format.rs

1//! Formatting methods for type information.
2use crate::dwarf::borrowable_dwarf::BorrowableDwarf;
3use crate::unit_has_members::UnitHasMembers;
4use crate::unit_inner_type::UnitInnerType;
5use crate::unit_name_type::UnitNamedType;
6use crate::{Member, Error, Type};
7use crate::dwarf::{DwarfContext, GimliCU};
8
9pub fn format_type<D>(dwarf: &D, unit: &GimliCU, member_name: String, typ: Type,
10                      level: usize, tablevel: usize, verbosity: u8,
11                      base_offset: usize)
12-> Result<String, Error>
13where D: DwarfContext + BorrowableDwarf {
14    let mut out = String::new();
15    match typ {
16        Type::Array(a) => {
17            let inner = a.u_get_type(unit)?;
18            let inner_fmt = format_type(dwarf, unit, "".to_string(), inner,
19                                        level+1, tablevel, verbosity,
20                                        base_offset)?;
21            out.push_str(&inner_fmt);
22            if !out.ends_with('*') {
23                out.push(' ');
24            }
25            if level == 0 {
26                out.push_str(&member_name);
27            }
28
29            let bound = a.u_get_bound(unit)?;
30            let bound_str = {
31                if bound == 0 {
32                    String::from("[]")
33                } else {
34                    format!("[{bound}]")
35                }
36            };
37            out.push_str(&bound_str);
38            return Ok(out);
39        }
40        Type::Typedef(t) => {
41            let name = t.u_name(dwarf, unit)?;
42            if level == 0 {
43                out.push_str(
44                    &format!("{name} {member_name}")
45                );
46                return Ok(out);
47            }
48            out.push_str(&name);
49        },
50        Type::Struct(t) => {
51            let name = t.u_name(dwarf, unit);
52            match name {
53                Ok(name) => {
54                    if level == 0 {
55                        out.push_str(
56                            &format!("struct {name} {member_name}")
57                        );
58                        return Ok(out);
59                    }
60                    out.push_str(&format!("struct {name}"));
61                    return Ok(out);
62                }
63                Err(Error::NameAttributeNotFound) => {
64                    // reaching here means we hit a nested struct type
65                    out.push_str("struct {\n");
66                    for memb in t.u_members(unit)?.into_iter() {
67                        out.push_str(
68                            &format_member(dwarf, unit, memb, tablevel+1,
69                                           verbosity, base_offset)?
70                        );
71                    }
72
73                    for _ in 0..=tablevel {
74                        out.push_str("    ");
75                    }
76                    out.push('}');
77                    return Ok(out);
78                }
79                Err(e) => return Err(e)
80            }
81        },
82        Type::Enum(t) => {
83            match t.u_name(dwarf, unit) {
84                Ok(name) => {
85                    if level == 0 {
86                        out.push_str(
87                            &format!("enum {name} {member_name}")
88                        );
89                        return Ok(out)
90                    }
91                    // TODO: print enum members
92                    out.push_str(&format!("enum {name}"));
93                }
94                Err(Error::NameAttributeNotFound) => {
95                    if level == 0 {
96                        out.push_str(&format!("enum {member_name}"));
97                        return Ok(out)
98                    }
99                    // TODO: print enum members
100                    out.push_str("enum");
101                }
102                Err(e) => return Err(e)
103            }
104        },
105        Type::Union(u) => {
106            let name = u.u_name(dwarf, unit);
107            match name {
108                Ok(name) => {
109                    if level == 0 {
110                        out.push_str(
111                            &format!("union {name} {member_name}")
112                        );
113                        return Ok(out);
114                    }
115                    out.push_str(&format!("union {name}"));
116                    return Ok(out);
117                }
118                Err(Error::NameAttributeNotFound) => {
119                    out.push_str("union {\n");
120                    for memb in u.u_members(unit)?.into_iter() {
121                        out.push_str(
122                            &format_member(dwarf, unit, memb, tablevel+1,
123                                           verbosity, base_offset)?);
124                    }
125
126                    for _ in 0..=tablevel {
127                        out.push_str("    ");
128                    }
129                    out.push('}');
130
131                    return Ok(out);
132                }
133                Err(e) => return Err(e)
134            }
135        },
136        Type::Base(t) => {
137            let name = t.u_name(dwarf, unit)?;
138            if level == 0 {
139                out.push_str(&format!("{name} {member_name}"));
140                return Ok(out);
141            }
142            out.push_str(&name);
143            return Ok(out);
144        },
145        Type::Subroutine(t) => {
146            // just return comma separated arg string
147            let params = t.u_get_params(unit)?;
148            for pidx in 0..params.len() {
149                let param = params[pidx].u_get_type(unit)?;
150                // recursively convert type to string
151                out.push_str(&format_type(dwarf, unit, "".to_string(),
152                                          param, level+1, tablevel, verbosity,
153                                          base_offset)?);
154                if pidx != params.len()-1 {
155                    out.push_str(", ");
156                }
157            };
158        },
159        Type::Pointer(p) => {
160            let inner = p.u_get_type(unit);
161
162            // pointers to subroutines must be handled differently
163            if let Ok(Type::Subroutine(subp)) = inner {
164
165                let return_type = match subp.u_get_type(unit) {
166                    Ok(rtype) => format_type(dwarf, unit, "".to_string(), rtype,
167                                             level+1, tablevel, verbosity,
168                                             base_offset)?,
169                    Err(Error::TypeAttributeNotFound) => "void".to_string(),
170                    Err(e) => return Err(e)
171                };
172
173                let argstr = {
174                    format_type(dwarf, unit, "".to_string(),
175                                Type::Subroutine(subp),
176                                level+1, tablevel, verbosity,
177                                base_offset)?
178                };
179
180                out.push_str(
181                    &format!("{return_type} (*{member_name})({argstr})")
182                );
183                return Ok(out);
184            }
185
186            // FORMAT: {type} *{member_name}
187
188            let ptr_type = match inner {
189                Ok(inner) => {
190                    format_type(dwarf, unit, "".to_string(), inner,
191                                level+1, tablevel, verbosity,
192                                base_offset)?
193                },
194                Err(Error::TypeAttributeNotFound) => {
195                    "void".to_string()
196                },
197                Err(e) => return Err(e)
198            };
199            out.push_str(&ptr_type);
200
201            if ptr_type.ends_with('*'){
202                out.push('*');
203            } else {
204                out.push_str(" *");
205            }
206
207            if level == 0 {
208                out.push_str(&member_name);
209                return Ok(out);
210            }
211            return Ok(out);
212        },
213        Type::Const(c) => {
214            let inner = c.u_get_type(unit);
215            match inner {
216                Ok(inner) => {
217                    let inner_fmt = format_type(dwarf, unit, "".to_string(),
218                                                inner, level+1, tablevel,
219                                                verbosity, base_offset)?;
220                    out.push_str(&format!("const {inner_fmt}"));
221                }
222                Err(Error::TypeAttributeNotFound) => {
223                    out.push_str("const void");
224                }
225                Err(e) => return Err(e)
226            }
227        },
228        Type::Volatile(c) => {
229            let inner = c.u_get_type(unit)?;
230            let inner_fmt = format_type(dwarf, unit, "".to_string(), inner,
231                                        level+1, tablevel, verbosity,
232                                        base_offset)?;
233            out.push_str(&format!("volatile {inner_fmt}"));
234            return Ok(out);
235        },
236        Type::Restrict(c) => {
237            let inner = c.u_get_type(unit)?;
238            let inner_fmt = format_type(dwarf, unit, "".to_string(), inner,
239                                        level+1, tablevel, verbosity,
240                                        base_offset)?;
241            out.push_str(&format!("{inner_fmt} restrict"));
242            return Ok(out);
243        }
244        Type::Variable(c) => {
245            let name = c.u_name(dwarf, unit)?;
246            out.push_str(&format!("{name}"));
247            return Ok(out);
248        }
249    }
250    Ok(out)
251}
252
253pub fn format_member<D>(
254    dwarf: &D,
255    unit: &GimliCU,
256    member: Member,
257    tablevel: usize,
258    verbosity: u8,
259    base_offset: usize
260)
261-> Result<String, Error>
262where D: DwarfContext + BorrowableDwarf {
263    let mtype = member.u_get_type(unit)?;
264    let name = match member.u_name(dwarf, unit) {
265        Ok(name) => name,
266        Err(Error::NameAttributeNotFound) => {
267            // members can be anon structs or unions
268            // it would be nice to check for those cases and propogate the error
269            // otherwise, but type modifiers would also need to be stripped...
270            // just excluding the name on error is probably fine tbh
271            "".to_string()
272        },
273        Err(e) => return Err(e)
274    };
275
276    let mut formatted = String::new();
277    for _ in 0..=tablevel {
278        formatted.push_str("    ");
279    }
280
281    let memb_offset = match member.u_offset(unit) {
282        Ok(memb_offset) => memb_offset,
283        Err(Error::MemberLocationAttributeNotFound) => 0,
284        Err(e) => return Err(e)
285
286    };
287    let offset = base_offset + memb_offset;
288
289    formatted.push_str(
290        &format_type(dwarf, unit, name, mtype, 0, tablevel, verbosity, offset)?
291    );
292
293    match member.u_bit_size(unit) {
294        Ok(bitsz) => {
295            formatted.push_str(&format!(":{bitsz}"));
296        }
297        Err(Error::BitSizeAttributeNotFound) => {},
298        Err(e) => return Err(e)
299    }
300
301    formatted.push(';');
302
303    if verbosity > 0 {
304        // generic padding based on last newline in formatted string
305        let last_newline = formatted.rfind('\n').map(|idx| idx+1).unwrap_or(0);
306
307        // cast to signed to prevent underflow
308        let last_line_len: isize = (formatted.len()-last_newline) as isize;
309        for _ in 0..(48-last_line_len) {
310            formatted.push(' ');
311        }
312
313        let bytesz = member.u_byte_size(unit)?;
314        formatted.push_str(&format!("\t/* {bytesz: >4} | \
315                                          {offset: >4} */"));
316    }
317
318    formatted.push('\n');
319
320    Ok(formatted)
321}