use super::*;
use std::{
collections::{HashSet,HashMap},
cell::{Cell,RefCell},
};
use core_extensions::SelfOps;
#[cfg(test)]
mod tests;
fn traverse_type_layouts<'a,F>(layout:&'a TypeLayout,mut callback:F)
where
F:FnMut(&'a TypeLayout)
{
let mut state=RecursionState{
visited:HashSet::new(),
};
traverse_type_layouts_inner(
layout,
&mut state,
&mut callback,
);
}
fn traverse_type_layouts_inner<'a,F>(
layout:&'a TypeLayout,
state:&mut RecursionState,
callback:&mut F,
)where
F:FnMut(&'a TypeLayout)
{
if state.visited.replace(layout.get_utypeid()).is_none() {
callback(layout);
for layout in layout.shared_vars.type_layouts() {
traverse_type_layouts_inner(layout.get(), state, callback);
}
if let Some(extra_checks)=layout.extra_checks() {
for layout in &*extra_checks.nested_type_layouts() {
traverse_type_layouts_inner(layout, state, callback);
}
}
}
}
struct RecursionState{
visited:HashSet<UTypeId>,
}
struct DebugState{
counter:Cell<usize>,
map:RefCell<HashMap<UTypeId,Option<usize>>>,
display_stack:RefCell<Vec<UTypeId>>,
full_type_stack:RefCell<Vec<UTypeId>>,
}
thread_local!{
static DEBUG_STATE:DebugState=DebugState{
counter:Cell::new(0),
map:RefCell::new(HashMap::new()),
display_stack:RefCell::new(Vec::new()),
full_type_stack:RefCell::new(Vec::new()),
};
}
const GET_ERR:&'static str=
"Expected DEBUG_STATE.map to contain the UTypeId of all recursive `TypeLayout`s";
impl Debug for TypeLayout{
fn fmt(&self,f:&mut fmt::Formatter<'_>)->fmt::Result{
let mut current_level=!0;
DEBUG_STATE.with(|state|{
current_level=state.counter.get();
if current_level < 1 {
state.counter.set(current_level+1);
}
});
if current_level>=1 {
let mut index=None;
DEBUG_STATE.with(|state|{
index=*state.map.borrow().get(&self.get_utypeid()).expect(GET_ERR) ;
});
let ptr=TypeLayoutPointer{
key_in_map:index,
type_:self.full_type(),
};
return Debug::fmt(&ptr,f);
}
let mut type_infos=Vec::<&'static TypeLayout>::new();
if current_level==0 {
DEBUG_STATE.with(|state|{
let mut i=0usize;
let mut map=state.map.borrow_mut();
traverse_type_layouts(self,|this|{
map.entry(this.get_utypeid())
.or_insert(None)
.get_or_insert_with(||{
type_infos.push(this);
let index=i;
i+=1;
index
});
});
})
}
let _guard=DecrementLevel;
f.debug_struct("TypeLayout")
.field("name",&self.name())
.field("full_type",&self.full_type())
.field("is_nonzero",&self.is_nonzero())
.field("alignment",&self.alignment())
.field("size",&self.size())
.field("data",&self.data())
.field("extra_checks",&self.extra_checks())
.field("type_id",&self.get_utypeid())
.field("item_info",&self.item_info())
.field("phantom_fields",&self.phantom_fields())
.field("tag",&self.tag())
.field("repr_attr",&self.repr_attr())
.field("mod_refl_mode",&self.mod_refl_mode())
.observe(|_| drop(_guard) )
.field("nested_type_layouts",&WithIndices(&type_infos))
.finish()
}
}
const RECURSIVE_INDICATOR:&'static str="<{recursive}>";
impl Display for TypeLayout {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut is_cyclic=false;
DEBUG_STATE.with(|state|{
let mut stack=state.display_stack.borrow_mut();
let tid=self.get_utypeid();
is_cyclic=stack.contains(&tid);
if !is_cyclic {
stack.push(tid);
}
});
if is_cyclic {
write!(f,"{}{}",self.name(),RECURSIVE_INDICATOR)?;
}else{
let _guard=DisplayGuard;
let (package,version)=self.item_info().package_and_version();
writeln!(
f,
"--- Type Layout ---\n\
type:{ty}\n\
size:{size} align:{align}\n\
package:'{package}' version:'{version}'\n\
line:{line} mod:{mod_path}",
ty =self.full_type(),
size =self.size(),
align=self.alignment(),
package=package,
version=version,
line=self.item_info().line,
mod_path=self.item_info().mod_path,
)?;
writeln!(f,"data:\n{}",self.data().to_string().left_padder(4))?;
let phantom_fields=self.phantom_fields();
if !phantom_fields.is_empty() {
writeln!(f,"Phantom fields:\n")?;
for field in phantom_fields {
write!(f,"{}",field.to_string().left_padder(4))?;
}
}
writeln!(f,"Tag:\n{}",self.tag().to_string().left_padder(4))?;
let extra_checks=
match self.extra_checks() {
Some(x)=>x.to_string(),
None=>"<nothing>".to_string(),
};
writeln!(f,"Extra checks:\n{}",extra_checks.left_padder(4))?;
writeln!(f,"Repr attribute:{:?}",self.repr_attr())?;
writeln!(f,"Module reflection mode:{:?}",self.mod_refl_mode())?;
}
Ok(())
}
}
struct DisplayGuard;
impl Drop for DisplayGuard{
fn drop(&mut self){
DEBUG_STATE.with(|state|{
state.display_stack.borrow_mut().pop();
});
}
}
impl Display for FmtFullType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(self, f)
}
}
impl Debug for FmtFullType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut is_cyclic=false;
DEBUG_STATE.with(|state|{
let mut stack=state.full_type_stack.borrow_mut();
let tid=self.utypeid;
is_cyclic=stack.contains(&tid);
if !is_cyclic {
stack.push(tid);
}
});
if is_cyclic {
write!(f,"{}{}",self.name,RECURSIVE_INDICATOR)?;
}else{
use self::TLPrimitive as TLP;
let _guard=FmtFullTypeGuard;
let (typename, start_gen, before_ty, ty_sep, end_gen) = match self.primitive {
Some(TLP::SharedRef) => ("&", "", " ", " ", " "),
Some(TLP::MutRef) => ("&", "", " mut ", " ", " "),
Some(TLP::ConstPtr) => ("*const", " ", "", " ", " "),
Some(TLP::MutPtr) => ("*mut", " ", "", " ", " "),
Some(TLP::Array{..}) => ("", "[", "", ";", "]"),
Some(TLP::U8)|Some(TLP::I8)
|Some(TLP::U16)|Some(TLP::I16)
|Some(TLP::U32)|Some(TLP::I32)
|Some(TLP::U64)|Some(TLP::I64)
|Some(TLP::Usize)|Some(TLP::Isize)
|Some(TLP::Bool)
|None => (self.name, "<", "", ", ", ">"),
};
fmt::Display::fmt(typename, f)?;
let mut is_before_ty = true;
let generics = self.generics;
if !generics.is_empty() {
fmt::Display::fmt(start_gen, f)?;
let post_iter = |i: usize, len: usize, f: &mut Formatter<'_>| -> fmt::Result {
if i+1 < len {
fmt::Display::fmt(ty_sep, f)?;
}
Ok(())
};
let mut i=0;
let total_generics_len=
generics.lifetime_count()+generics.types.len()+generics.consts.len();
for param in self.generics.lifetimes() {
fmt::Display::fmt(param, &mut *f)?;
post_iter(i,total_generics_len, &mut *f)?;
i+=1;
}
for param in generics.types.iter().cloned() {
let layout=param.get();
if is_before_ty {
fmt::Display::fmt(before_ty, &mut *f)?;
is_before_ty = false;
}
fmt::Debug::fmt(&layout.full_type(), &mut *f)?;
post_iter(i,total_generics_len, &mut *f)?;
i+=1;
}
for param in generics.consts.iter() {
fmt::Debug::fmt(param, &mut *f)?;
post_iter(i,total_generics_len, &mut *f)?;
i+=1;
}
fmt::Display::fmt(end_gen, f)?;
}
}
Ok(())
}
}
struct FmtFullTypeGuard;
impl Drop for FmtFullTypeGuard{
fn drop(&mut self){
DEBUG_STATE.with(|state|{
state.full_type_stack.borrow_mut().pop();
});
}
}
struct DecrementLevel;
impl Drop for DecrementLevel{
fn drop(&mut self){
DEBUG_STATE.with(|state|{
let current=state.counter.get();
if current==0 {
let mut map=state.map.borrow_mut();
map.clear();
map.shrink_to_fit();
}
state.counter.set(current.saturating_sub(1));
})
}
}
#[derive(Debug)]
struct TypeLayoutPointer{
key_in_map:Option<usize>,
type_:FmtFullType,
}
struct WithIndices<'a,T>(&'a [T]);
impl<'a,T> Debug for WithIndices<'a,T>
where
T:Debug
{
fn fmt(&self,f:&mut fmt::Formatter<'_>)->fmt::Result{
f.debug_map()
.entries(self.0.iter().enumerate())
.finish()
}
}