Skip to main content

mun_abi/
struct_info.rs

1use std::{ffi::CStr, os::raw::c_char, slice, str};
2
3use crate::type_id::TypeId;
4use crate::Guid;
5
6/// Represents a struct declaration.
7#[repr(C)]
8#[derive(Debug)]
9pub struct StructDefinition<'a> {
10    /// The unique identifier of this struct
11    pub guid: Guid,
12    /// Struct fields' names
13    pub field_names: *const *const c_char,
14    /// Struct fields' information
15    pub(crate) field_types: *const TypeId<'a>,
16    /// Struct fields' offsets
17    pub(crate) field_offsets: *const u16,
18    // TODO: Field accessibility levels
19    // const MunPrivacy_t *field_privacies,
20    /// Number of fields
21    pub(crate) num_fields: u16,
22    // TODO: Add struct accessibility level
23    /// Struct memory kind
24    pub memory_kind: StructMemoryKind,
25}
26
27/// Represents the kind of memory management a struct uses.
28#[repr(u8)]
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize))]
31pub enum StructMemoryKind {
32    /// A garbage collected struct is allocated on the heap and uses reference semantics when passed
33    /// around.
34    Gc,
35
36    /// A value struct is allocated on the stack and uses value semantics when passed around.
37    ///
38    /// NOTE: When a value struct is used in an external API, a wrapper is created that _pins_ the
39    /// value on the heap. The heap-allocated value needs to be *manually deallocated*!
40    Value,
41}
42
43impl<'a> StructDefinition<'a> {
44    /// Returns the struct's field names.
45    pub fn field_names(&self) -> impl Iterator<Item = &str> {
46        let field_names = if self.num_fields == 0 {
47            &[]
48        } else {
49            unsafe { slice::from_raw_parts(self.field_names, self.num_fields as usize) }
50        };
51
52        field_names
53            .iter()
54            .map(|n| unsafe { str::from_utf8_unchecked(CStr::from_ptr(*n).to_bytes()) })
55    }
56
57    /// Returns the struct's field types.
58    pub fn field_types(&self) -> &[TypeId<'a>] {
59        if self.num_fields == 0 {
60            &[]
61        } else {
62            unsafe { slice::from_raw_parts(self.field_types, self.num_fields as usize) }
63        }
64    }
65
66    /// Returns the struct's field offsets.
67    pub fn field_offsets(&self) -> &[u16] {
68        if self.num_fields == 0 {
69            &[]
70        } else {
71            unsafe { slice::from_raw_parts(self.field_offsets, self.num_fields as usize) }
72        }
73    }
74
75    /// Returns the number of struct fields.
76    pub fn num_fields(&self) -> usize {
77        self.num_fields.into()
78    }
79}
80
81impl Default for StructMemoryKind {
82    fn default() -> Self {
83        StructMemoryKind::Gc
84    }
85}
86
87impl<'a> PartialEq for StructDefinition<'a> {
88    fn eq(&self, other: &Self) -> bool {
89        self.guid == other.guid
90    }
91}
92
93impl<'a> Eq for StructDefinition<'a> {}
94
95#[cfg(feature = "serde")]
96impl<'a> serde::Serialize for StructDefinition<'a> {
97    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
98    where
99        S: serde::Serializer,
100    {
101        use itertools::Itertools;
102        use serde::ser::SerializeStruct;
103
104        let mut s = serializer.serialize_struct("StructInfo", 3)?;
105
106        #[derive(serde::Serialize)]
107        struct Field<'a> {
108            name: &'a str,
109            r#type: &'a TypeId<'a>,
110            offset: &'a u16,
111        }
112
113        s.serialize_field("guid", &self.guid)?;
114        s.serialize_field(
115            "fields",
116            &self
117                .field_names()
118                .zip(self.field_types())
119                .zip(self.field_offsets())
120                .map(|((name, ty), offset)| Field {
121                    name,
122                    r#type: ty,
123                    offset,
124                })
125                .collect_vec(),
126        )?;
127        s.serialize_field("memory_kind", &self.memory_kind)?;
128        s.end()
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use crate::type_id::HasStaticTypeId;
135    use std::ffi::CString;
136
137    use crate::test_utils::{fake_struct_definition, FAKE_FIELD_NAME, FAKE_STRUCT_NAME};
138
139    use super::StructMemoryKind;
140
141    #[test]
142    fn test_struct_info_fields_none() {
143        let field_names = &[];
144        let field_types = &[];
145        let field_offsets = &[];
146        let struct_info = fake_struct_definition(
147            &CString::new(FAKE_STRUCT_NAME).unwrap(),
148            field_names,
149            field_types,
150            field_offsets,
151            Default::default(),
152        );
153
154        assert_eq!(struct_info.field_names().count(), 0);
155        assert_eq!(struct_info.field_types(), field_types);
156        assert_eq!(struct_info.field_offsets(), field_offsets);
157    }
158
159    #[test]
160    fn test_struct_info_fields_some() {
161        let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name.");
162        let field_name = CString::new(FAKE_FIELD_NAME).expect("Invalid fake field name.");
163        let type_id = i32::type_id();
164
165        let field_names = &[field_name.as_ptr()];
166        let field_types = &[type_id.clone()];
167        let field_offsets = &[1];
168        let struct_info = fake_struct_definition(
169            &struct_name,
170            field_names,
171            field_types,
172            field_offsets,
173            Default::default(),
174        );
175
176        assert_eq!(struct_info.num_fields(), 1);
177        for (lhs, rhs) in struct_info.field_names().zip([FAKE_FIELD_NAME].iter()) {
178            assert_eq!(lhs, *rhs)
179        }
180        assert_eq!(struct_info.field_types(), field_types);
181        assert_eq!(struct_info.field_offsets(), field_offsets);
182    }
183
184    #[test]
185    fn test_struct_info_memory_kind_gc() {
186        let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name.");
187        let struct_memory_kind = StructMemoryKind::Gc;
188        let struct_info = fake_struct_definition(&struct_name, &[], &[], &[], struct_memory_kind);
189
190        assert_eq!(struct_info.memory_kind, struct_memory_kind);
191    }
192
193    #[test]
194    fn test_struct_info_memory_kind_value() {
195        let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name.");
196        let struct_memory_kind = StructMemoryKind::Value;
197        let struct_info = fake_struct_definition(&struct_name, &[], &[], &[], struct_memory_kind);
198
199        assert_eq!(struct_info.memory_kind, struct_memory_kind);
200    }
201}