use std::io::{self, Write};
use crate::{
formatting::{
generics,
helpers::{assembly_scoped_name, format_typespec_from_blob, quote_identifier},
},
metadata::{tables::TypeAttributes, typesystem::CilType},
CilObject,
};
pub(super) fn format_type_begin(
w: &mut dyn Write,
cil_type: &CilType,
asm: &CilObject,
) -> io::Result<()> {
let flags = cil_type.flags;
write!(w, ".class")?;
match flags & TypeAttributes::VISIBILITY_MASK {
TypeAttributes::NOT_PUBLIC => write!(w, " private")?,
TypeAttributes::PUBLIC => write!(w, " public")?,
TypeAttributes::NESTED_PUBLIC => write!(w, " nested public")?,
TypeAttributes::NESTED_PRIVATE => write!(w, " nested private")?,
TypeAttributes::NESTED_FAMILY => write!(w, " nested family")?,
TypeAttributes::NESTED_ASSEMBLY => write!(w, " nested assembly")?,
TypeAttributes::NESTED_FAM_AND_ASSEM => write!(w, " nested famandassem")?,
TypeAttributes::NESTED_FAM_OR_ASSEM => write!(w, " nested famorassem")?,
_ => {}
}
match flags & TypeAttributes::LAYOUT_MASK {
TypeAttributes::SEQUENTIAL_LAYOUT => write!(w, " sequential")?,
TypeAttributes::EXPLICIT_LAYOUT => write!(w, " explicit")?,
_ => write!(w, " auto")?,
}
match flags & TypeAttributes::STRING_FORMAT_MASK {
TypeAttributes::UNICODE_CLASS => write!(w, " unicode")?,
TypeAttributes::AUTO_CLASS => write!(w, " autochar")?,
_ => write!(w, " ansi")?,
}
if flags.contains(TypeAttributes::INTERFACE) {
write!(w, " interface")?;
}
if flags.contains(TypeAttributes::ABSTRACT) {
write!(w, " abstract")?;
}
if flags.contains(TypeAttributes::SEALED) {
write!(w, " sealed")?;
}
if flags.contains(TypeAttributes::BEFORE_FIELD_INIT) {
write!(w, " beforefieldinit")?;
}
if flags.contains(TypeAttributes::SERIALIZABLE) {
write!(w, " serializable")?;
}
if flags.contains(TypeAttributes::SPECIAL_NAME) {
write!(w, " specialname")?;
}
if flags.contains(TypeAttributes::RT_SPECIAL_NAME) {
write!(w, " rtspecialname")?;
}
if flags.contains(TypeAttributes::IMPORT) {
write!(w, " import")?;
}
let type_name = if cil_type.enclosing_type().is_some() {
quote_identifier(&cil_type.name)
} else {
quote_identifier(&cil_type.fullname())
};
write!(w, " {type_name}")?;
generics::write_generic_params(w, &cil_type.generic_params, asm)?;
if let Some(base) = cil_type.base() {
let base_name = format_type_ref(&base, asm);
write!(w, "\n extends {base_name}")?;
}
let interfaces: Vec<_> = cil_type.interfaces.iter().collect();
if !interfaces.is_empty() {
write!(w, "\n implements ")?;
for (i, (_, entry)) in interfaces.iter().enumerate() {
if i > 0 {
write!(w, ", ")?;
}
if let Some(iface_type) = entry.interface.upgrade() {
write!(w, "{}", format_type_ref(&iface_type, asm))?;
}
}
}
writeln!(w)?;
writeln!(w, "{{")?;
if let Some(&packing) = cil_type.packing_size.get() {
if packing > 0 {
writeln!(w, " .pack {packing}")?;
}
}
if let Some(&size) = cil_type.class_size.get() {
if size > 0 {
writeln!(w, " .size {size}")?;
}
}
Ok(())
}
fn format_type_ref(cil_type: &CilType, asm: &CilObject) -> String {
if cil_type.token.table() == 0x1B {
if let Some(formatted) = format_typespec_from_blob(asm, &cil_type.token) {
return formatted;
}
}
assembly_scoped_name(cil_type, asm)
}
pub(super) fn format_type_end(w: &mut dyn Write, cil_type: &CilType) -> io::Result<()> {
let display_name = if cil_type.enclosing_type().is_some() {
cil_type.name.clone()
} else {
cil_type.fullname()
};
writeln!(w, "}} // end of class {display_name}")?;
writeln!(w)?;
Ok(())
}