mun_abi/
type_info.rs

1use std::{
2    convert::TryInto,
3    ffi::CStr,
4    fmt::Debug,
5    fmt::{self, Formatter},
6    os::raw::c_char,
7    str,
8};
9
10use crate::{type_id::TypeId, Guid, StructDefinition};
11
12/// Represents the type declaration for a type that is exported by an assembly.
13///
14/// When multiple Mun modules reference the same type, only one module exports the type; the module
15/// that contains the type definition. All the other Mun modules reference the type through a
16/// [`TypeId`].
17///
18/// The modules that defines the type exports the data to reduce the filesize of the assemblies and
19/// to ensure only one definition exists. When linking all assemblies together the type definitions
20/// from all assemblies are loaded and the information is shared to modules that reference the type.
21///
22/// TODO: add support for polymorphism, enumerations, type parameters, generic type definitions, and
23///   constructed generic types.
24#[repr(C)]
25pub struct TypeDefinition<'a> {
26    /// Type name
27    pub name: *const c_char,
28    /// The exact size of the type in bits without any padding
29    pub(crate) size_in_bits: u32,
30    /// The alignment of the type
31    pub(crate) alignment: u8,
32    /// Type group
33    pub data: TypeDefinitionData<'a>,
34}
35
36impl<'a> Debug for TypeDefinition<'a> {
37    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
38        f.debug_struct("TypeDefinition")
39            .field("name", &self.name())
40            .field("size_in_bits", &self.size_in_bits)
41            .field("alignment", &self.alignment)
42            .field("data", &self.data)
43            .finish()
44    }
45}
46
47#[cfg(feature = "serde")]
48impl<'a> serde::Serialize for TypeDefinition<'a> {
49    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
50    where
51        S: serde::Serializer,
52    {
53        use serde::ser::SerializeStruct;
54
55        let mut s = serializer.serialize_struct("TypeDefinition", 4)?;
56        s.serialize_field("name", self.name())?;
57        s.serialize_field("size_in_bits", &self.size_in_bits)?;
58        s.serialize_field("alignment", &self.alignment)?;
59        s.serialize_field("data", &self.data)?;
60        s.end()
61    }
62}
63
64/// Contains data specific to a group of types that illicit the same characteristics.
65#[repr(u8)]
66#[derive(Debug, PartialEq, Eq)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize))]
68pub enum TypeDefinitionData<'a> {
69    /// Struct types (i.e. record, tuple, or unit structs)
70    Struct(StructDefinition<'a>),
71}
72
73impl<'a> TypeDefinition<'a> {
74    /// Returns true if this instance is an instance of the given `TypeId`.
75    pub fn is_instance_of(&self, type_id: &TypeId<'a>) -> bool {
76        match (&self.data, type_id) {
77            (TypeDefinitionData::Struct(s), TypeId::Concrete(guid)) => &s.guid == guid,
78            _ => false,
79        }
80    }
81
82    /// Returns the type's name.
83    pub fn name(&self) -> &str {
84        unsafe { str::from_utf8_unchecked(CStr::from_ptr(self.name).to_bytes()) }
85    }
86
87    /// Returns the GUID if this type represents a concrete type.
88    pub fn as_concrete(&self) -> &Guid {
89        match &self.data {
90            TypeDefinitionData::Struct(s) => &s.guid,
91        }
92    }
93
94    /// Retrieves the type's struct information, if available.
95    pub fn as_struct(&self) -> Option<&StructDefinition> {
96        let TypeDefinitionData::Struct(s) = &self.data;
97        Some(s)
98    }
99
100    /// Returns the size of the type in bits
101    pub fn size_in_bits(&self) -> usize {
102        self.size_in_bits
103            .try_into()
104            .expect("cannot convert size in bits to platform size")
105    }
106
107    /// Returns the size of the type in bytes
108    pub fn size_in_bytes(&self) -> usize {
109        ((self.size_in_bits + 7) / 8)
110            .try_into()
111            .expect("cannot covert size in bytes to platform size")
112    }
113
114    /// Returns the alignment of the type in bytes
115    pub fn alignment(&self) -> usize {
116        self.alignment
117            .try_into()
118            .expect("cannot convert alignment to platform size")
119    }
120}
121
122impl<'a> fmt::Display for TypeDefinition<'a> {
123    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
124        write!(f, "{}", self.name())
125    }
126}
127
128impl<'a> PartialEq for TypeDefinition<'a> {
129    fn eq(&self, other: &Self) -> bool {
130        self.size_in_bits == other.size_in_bits
131            && self.alignment == other.alignment
132            && self.data == other.data
133    }
134}
135
136impl<'a> Eq for TypeDefinition<'a> {}
137
138unsafe impl<'a> Send for TypeDefinition<'a> {}
139unsafe impl<'a> Sync for TypeDefinition<'a> {}
140
141impl<'a> TypeDefinitionData<'a> {
142    /// Returns whether this is a struct type.
143    pub fn is_struct(&self) -> bool {
144        matches!(self, TypeDefinitionData::Struct(_))
145    }
146}
147
148/// A trait that defines that for a type we can statically return a type name.
149pub trait HasStaticTypeName {
150    /// Returns a reference to the TypeInfo for the type
151    fn type_name() -> &'static CStr;
152}
153
154#[cfg(test)]
155mod tests {
156    use std::ffi::CString;
157
158    use crate::test_utils::{fake_struct_definition, fake_type_definition, FAKE_TYPE_NAME};
159
160    use super::TypeDefinitionData;
161
162    #[test]
163    fn test_type_definition_name() {
164        let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
165        let field_names = &[];
166        let field_types = &[];
167        let field_offsets = &[];
168        let struct_info = fake_struct_definition(
169            &type_name,
170            field_names,
171            field_types,
172            field_offsets,
173            Default::default(),
174        );
175
176        let type_definition =
177            fake_type_definition(&type_name, 1, 1, TypeDefinitionData::Struct(struct_info));
178        assert_eq!(type_definition.name(), FAKE_TYPE_NAME);
179    }
180
181    #[test]
182    fn test_type_definition_size_alignment() {
183        let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
184        let field_names = &[];
185        let field_types = &[];
186        let field_offsets = &[];
187        let struct_info = fake_struct_definition(
188            &type_name,
189            field_names,
190            field_types,
191            field_offsets,
192            Default::default(),
193        );
194
195        let type_definition =
196            fake_type_definition(&type_name, 24, 8, TypeDefinitionData::Struct(struct_info));
197
198        assert_eq!(type_definition.size_in_bits(), 24);
199        assert_eq!(type_definition.size_in_bytes(), 3);
200        assert_eq!(type_definition.alignment(), 8);
201    }
202
203    #[test]
204    fn test_type_definition_group_struct() {
205        let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
206        let field_names = &[];
207        let field_types = &[];
208        let field_offsets = &[];
209        let struct_info = fake_struct_definition(
210            &type_name,
211            field_names,
212            field_types,
213            field_offsets,
214            Default::default(),
215        );
216
217        let type_definition =
218            fake_type_definition(&type_name, 1, 1, TypeDefinitionData::Struct(struct_info));
219        assert!(type_definition.data.is_struct());
220    }
221
222    #[test]
223    fn test_type_definition_eq() {
224        let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
225        let field_names = &[];
226        let field_types = &[];
227        let field_offsets = &[];
228        let struct_info = fake_struct_definition(
229            &type_name,
230            field_names,
231            field_types,
232            field_offsets,
233            Default::default(),
234        );
235
236        let type_definition =
237            fake_type_definition(&type_name, 1, 1, TypeDefinitionData::Struct(struct_info));
238        assert_eq!(type_definition, type_definition);
239    }
240}