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
13fn 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
53struct 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 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
141const 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
211impl 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
305struct 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#[derive(Debug)]
326#[allow(dead_code)]
327struct TypeLayoutPointer {
328 key_in_map: Option<usize>,
329 type_: FmtFullType,
330}
331
332struct 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}