abi_stable/type_layout/
printing.rs

1use super::*;
2
3use std::{
4    cell::{Cell, RefCell},
5    collections::{HashMap, HashSet},
6};
7
8use core_extensions::SelfOps;
9
10#[cfg(test)]
11mod tests;
12
13/// A function which recursively traverses a type layout,
14/// calling `callback` for every `TypeLayout` it goes over.
15///
16fn traverse_type_layouts<'a, F>(layout: &'a TypeLayout, mut callback: F)
17where
18    F: FnMut(&'a TypeLayout),
19{
20    let mut state = RecursionState {
21        visited: HashSet::new(),
22    };
23
24    traverse_type_layouts_inner(layout, &mut state, &mut callback);
25}
26
27fn traverse_type_layouts_inner<'a, F>(
28    layout: &'a TypeLayout,
29    state: &mut RecursionState,
30    callback: &mut F,
31) where
32    F: FnMut(&'a TypeLayout),
33{
34    if state.visited.replace(layout.get_utypeid()).is_none() {
35        callback(layout);
36
37        for nested_layout in layout.shared_vars.type_layouts() {
38            traverse_type_layouts_inner(nested_layout(), state, callback);
39        }
40
41        if let Some(extra_checks) = layout.extra_checks() {
42            for nested_layout in &*extra_checks.nested_type_layouts() {
43                traverse_type_layouts_inner(nested_layout, state, callback);
44            }
45        }
46    }
47}
48
49struct RecursionState {
50    visited: HashSet<UTypeId>,
51}
52
53////////////////////////////////////////////////////////////////////////////////
54
55struct DebugState {
56    counter: Cell<usize>,
57    map: RefCell<HashMap<UTypeId, Option<usize>>>,
58    display_stack: RefCell<Vec<UTypeId>>,
59    full_type_stack: RefCell<Vec<UTypeId>>,
60}
61
62thread_local! {
63    static DEBUG_STATE:DebugState=DebugState{
64        counter:Cell::new(0),
65        map:RefCell::new(HashMap::new()),
66        display_stack:RefCell::new(Vec::new()),
67        full_type_stack:RefCell::new(Vec::new()),
68    };
69}
70
71const GET_ERR: &str =
72    "Expected DEBUG_STATE.map to contain the UTypeId of all recursive `TypeLayout`s";
73
74impl Debug for TypeLayout {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        let mut current_level = !0;
77        DEBUG_STATE.with(|state| {
78            current_level = state.counter.get();
79            if current_level < 1 {
80                state.counter.set(current_level + 1);
81            }
82        });
83
84        if current_level >= 1 {
85            let mut index = None;
86            DEBUG_STATE.with(|state| {
87                index = *state.map.borrow().get(&self.get_utypeid()).expect(GET_ERR);
88            });
89            let ptr = TypeLayoutPointer {
90                key_in_map: index,
91                type_: self.full_type(),
92            };
93            return Debug::fmt(&ptr, f);
94        }
95
96        let mut type_infos = Vec::<&'static TypeLayout>::new();
97
98        if current_level == 0 {
99            DEBUG_STATE.with(|state| {
100                let mut i = 0usize;
101
102                let mut map = state.map.borrow_mut();
103
104                traverse_type_layouts(self, |this| {
105                    map.entry(this.get_utypeid())
106                        .or_insert(None)
107                        .get_or_insert_with(|| {
108                            type_infos.push(this);
109                            let index = i;
110                            i += 1;
111                            index
112                        });
113                });
114            })
115        }
116
117        // This guard is used to uninitialize the map when returning from this function,
118        // even on panics.
119        let _guard = DecrementLevel;
120
121        f.debug_struct("TypeLayout")
122            .field("name", &self.name())
123            .field("full_type", &self.full_type())
124            .field("is_nonzero", &self.is_nonzero())
125            .field("alignment", &self.alignment())
126            .field("size", &self.size())
127            .field("data", &self.data())
128            .field("extra_checks", &self.extra_checks())
129            .field("type_id", &self.get_utypeid())
130            .field("item_info", &self.item_info())
131            .field("phantom_fields", &self.phantom_fields())
132            .field("tag", &self.tag())
133            .field("repr_attr", &self.repr_attr())
134            .field("mod_refl_mode", &self.mod_refl_mode())
135            .observe(|_| drop(_guard))
136            .field("nested_type_layouts", &WithIndices(&type_infos))
137            .finish()
138    }
139}
140
141////////////////
142
143const RECURSIVE_INDICATOR: &str = "<{recursive}>";
144
145impl Display for TypeLayout {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        let mut is_cyclic = false;
148
149        DEBUG_STATE.with(|state| {
150            let mut stack = state.display_stack.borrow_mut();
151            let tid = self.get_utypeid();
152            is_cyclic = stack.contains(&tid);
153            if !is_cyclic {
154                stack.push(tid);
155            }
156        });
157
158        if is_cyclic {
159            write!(f, "{}{}", self.name(), RECURSIVE_INDICATOR)?;
160        } else {
161            let _guard = DisplayGuard;
162
163            let (package, version) = self.item_info().package_and_version();
164            writeln!(
165                f,
166                "--- Type Layout ---\n\
167                 type:{ty}\n\
168                 size:{size} align:{align}\n\
169                 package:'{package}' version:'{version}'\n\
170                 line:{line} mod:{mod_path}",
171                ty = self.full_type(),
172                size = self.size(),
173                align = self.alignment(),
174                package = package,
175                version = version,
176                line = self.item_info().line,
177                mod_path = self.item_info().mod_path,
178            )?;
179            writeln!(f, "data:\n{}", self.data().to_string().left_padder(4))?;
180            let phantom_fields = self.phantom_fields();
181            if !phantom_fields.is_empty() {
182                writeln!(f, "Phantom fields:\n")?;
183                for field in phantom_fields {
184                    write!(f, "{}", field.to_string().left_padder(4))?;
185                }
186            }
187            writeln!(f, "Tag:\n{}", self.tag().to_string().left_padder(4))?;
188            let extra_checks = match self.extra_checks() {
189                Some(x) => x.to_string(),
190                None => "<nothing>".to_string(),
191            };
192            writeln!(f, "Extra checks:\n{}", extra_checks.left_padder(4))?;
193            writeln!(f, "Repr attribute:{:?}", self.repr_attr())?;
194            writeln!(f, "Module reflection mode:{:?}", self.mod_refl_mode())?;
195        }
196
197        Ok(())
198    }
199}
200
201struct DisplayGuard;
202
203impl Drop for DisplayGuard {
204    fn drop(&mut self) {
205        DEBUG_STATE.with(|state| {
206            state.display_stack.borrow_mut().pop();
207        });
208    }
209}
210
211////////////////
212
213impl Display for FmtFullType {
214    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215        Debug::fmt(self, f)
216    }
217}
218impl Debug for FmtFullType {
219    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220        let mut is_cyclic = false;
221
222        DEBUG_STATE.with(|state| {
223            let mut stack = state.full_type_stack.borrow_mut();
224            let tid = self.utypeid;
225            is_cyclic = stack.contains(&tid);
226            if !is_cyclic {
227                stack.push(tid);
228            }
229        });
230
231        if is_cyclic {
232            write!(f, "{}{}", self.name, RECURSIVE_INDICATOR)?;
233        } else {
234            use self::TLPrimitive as TLP;
235
236            let _guard = FmtFullTypeGuard;
237
238            let (typename, start_gen, before_ty, ty_sep, end_gen) = match self.primitive {
239                Some(TLP::SharedRef) => ("&", "", " ", " ", " "),
240                Some(TLP::MutRef) => ("&", "", " mut ", " ", " "),
241                Some(TLP::ConstPtr) => ("*const", " ", "", " ", " "),
242                Some(TLP::MutPtr) => ("*mut", " ", "", " ", " "),
243                Some(TLP::Array { .. }) => ("", "[", "", ";", "]"),
244                Some(TLP::U8) | Some(TLP::I8) | Some(TLP::U16) | Some(TLP::I16)
245                | Some(TLP::U32) | Some(TLP::I32) | Some(TLP::U64) | Some(TLP::I64)
246                | Some(TLP::Usize) | Some(TLP::Isize) | Some(TLP::Bool) | Some(TLP::F32)
247                | Some(TLP::F64) | None => (self.name, "<", "", ", ", ">"),
248            };
249
250            fmt::Display::fmt(typename, f)?;
251            let mut is_before_ty = true;
252            let generics = self.generics;
253            if !generics.is_empty() {
254                fmt::Display::fmt(start_gen, f)?;
255
256                let post_iter = |i: usize, len: usize, f: &mut Formatter<'_>| -> fmt::Result {
257                    if i + 1 < len {
258                        fmt::Display::fmt(ty_sep, f)?;
259                    }
260                    Ok(())
261                };
262
263                let mut i = 0;
264
265                let total_generics_len =
266                    generics.lifetime_count() + generics.types.len() + generics.consts.len();
267
268                for param in self.generics.lifetimes() {
269                    fmt::Display::fmt(param, &mut *f)?;
270                    post_iter(i, total_generics_len, &mut *f)?;
271                    i += 1;
272                }
273                for param in generics.types.iter().cloned() {
274                    let layout = param.get();
275                    if is_before_ty {
276                        fmt::Display::fmt(before_ty, &mut *f)?;
277                        is_before_ty = false;
278                    }
279                    fmt::Debug::fmt(&layout.full_type(), &mut *f)?;
280                    post_iter(i, total_generics_len, &mut *f)?;
281                    i += 1;
282                }
283                for param in generics.consts.iter() {
284                    fmt::Debug::fmt(param, &mut *f)?;
285                    post_iter(i, total_generics_len, &mut *f)?;
286                    i += 1;
287                }
288                fmt::Display::fmt(end_gen, f)?;
289            }
290        }
291        Ok(())
292    }
293}
294
295struct FmtFullTypeGuard;
296
297impl Drop for FmtFullTypeGuard {
298    fn drop(&mut self) {
299        DEBUG_STATE.with(|state| {
300            state.full_type_stack.borrow_mut().pop();
301        });
302    }
303}
304
305////////////////
306
307struct DecrementLevel;
308
309impl Drop for DecrementLevel {
310    fn drop(&mut self) {
311        DEBUG_STATE.with(|state| {
312            let current = state.counter.get();
313            if current == 0 {
314                let mut map = state.map.borrow_mut();
315                map.clear();
316                map.shrink_to_fit();
317            }
318            state.counter.set(current.saturating_sub(1));
319        })
320    }
321}
322
323////////////////
324
325#[derive(Debug)]
326#[allow(dead_code)]
327struct TypeLayoutPointer {
328    key_in_map: Option<usize>,
329    type_: FmtFullType,
330}
331
332////////////////
333
334struct WithIndices<'a, T>(&'a [T]);
335
336impl<'a, T> Debug for WithIndices<'a, T>
337where
338    T: Debug,
339{
340    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341        f.debug_map().entries(self.0.iter().enumerate()).finish()
342    }
343}