mun_runtime/
adt.rs

1use crate::{
2    garbage_collector::GcRootPtr,
3    marshal::Marshal,
4    reflection::{ArgumentReflection, ReturnTypeReflection},
5    GarbageCollector, Runtime,
6};
7use mun_memory::{
8    gc::{GcPtr, GcRuntime, HasIndirectionPtr},
9    Type,
10};
11use std::{
12    ptr::{self, NonNull},
13    sync::Arc,
14};
15
16/// Represents a Mun struct pointer.
17#[repr(transparent)]
18#[derive(Clone)]
19pub struct RawStruct(GcPtr);
20
21impl RawStruct {
22    /// Returns a pointer to the struct memory.
23    pub unsafe fn get_ptr(&self) -> *const u8 {
24        self.0.deref()
25    }
26}
27
28/// Type-agnostic wrapper for interoperability with a Mun struct. This is merely a reference to the
29/// Mun struct, that will be garbage collected unless it is rooted.
30#[derive(Clone)]
31pub struct StructRef<'s> {
32    raw: RawStruct,
33    runtime: &'s Runtime,
34}
35
36impl<'s> StructRef<'s> {
37    /// Creates a `StructRef` that wraps a raw Mun struct.
38    fn new<'r>(raw: RawStruct, runtime: &'r Runtime) -> Self
39    where
40        'r: 's,
41    {
42        Self { raw, runtime }
43    }
44
45    /// Consumes the `StructRef`, returning a raw Mun struct.
46    pub fn into_raw(self) -> RawStruct {
47        self.raw
48    }
49
50    /// Roots the `StructRef`.
51    pub fn root(self) -> RootedStruct {
52        RootedStruct::new(&self.runtime.gc, self.raw)
53    }
54
55    /// Returns the type information of the struct.
56    pub fn type_info(&self) -> Type {
57        self.runtime.gc.ptr_type(self.raw.0)
58    }
59
60    /// Returns the struct's field at the specified `offset`.
61    ///
62    /// # Safety
63    ///
64    /// The offset must be the location of a variable of type T.
65    unsafe fn get_field_ptr_unchecked<T>(&self, offset: usize) -> NonNull<T> {
66        // SAFETY: self.raw's memory pointer is never null
67        let ptr = self.raw.get_ptr();
68
69        NonNull::new_unchecked(ptr.add(offset).cast::<T>() as *mut T)
70    }
71
72    /// Retrieves the value of the field corresponding to the specified `field_name`.
73    pub fn get<T: ReturnTypeReflection + Marshal<'s>>(&self, field_name: &str) -> Result<T, String>
74    where
75        T: 's,
76    {
77        let type_info = self.type_info();
78
79        // Safety: `as_struct` is guaranteed to return `Some` for `StructRef`s.
80        let struct_info = type_info.as_struct().unwrap();
81
82        let field_info = struct_info
83            .fields()
84            .find_by_name(field_name)
85            .ok_or_else(|| {
86                format!(
87                    "Struct `{}` does not contain field `{}`.",
88                    type_info.name(),
89                    field_name
90                )
91            })?;
92
93        if !T::accepts_type(&field_info.ty()) {
94            return Err(format!(
95                "Mismatched types for `{}::{}`. Expected: `{}`. Found: `{}`.",
96                type_info.name(),
97                field_name,
98                T::type_hint(),
99                field_info.ty().name(),
100            ));
101        };
102
103        // SAFETY: The offset in the ABI is always valid.
104        let field_ptr = unsafe { self.get_field_ptr_unchecked::<T::MunType>(field_info.offset()) };
105        Ok(Marshal::marshal_from_ptr(
106            field_ptr,
107            self.runtime,
108            &field_info.ty(),
109        ))
110    }
111
112    /// Replaces the value of the field corresponding to the specified `field_name` and returns the
113    /// old value.
114    pub fn replace<T: ArgumentReflection + Marshal<'s>>(
115        &mut self,
116        field_name: &str,
117        value: T,
118    ) -> Result<T, String>
119    where
120        T: 's,
121    {
122        let type_info = self.type_info();
123
124        // Safety: `as_struct` is guaranteed to return `Some` for `StructRef`s.
125        let struct_info = type_info.as_struct().unwrap();
126
127        let field_info = struct_info
128            .fields()
129            .find_by_name(field_name)
130            .ok_or_else(|| {
131                format!(
132                    "Struct `{}` does not contain field `{}`.",
133                    type_info.name(),
134                    field_name
135                )
136            })?;
137
138        let value_type = value.type_info(self.runtime);
139        if field_info.ty() != value_type {
140            return Err(format!(
141                "Mismatched types for `{}::{}`. Expected: `{}`. Found: `{}`.",
142                type_info.name(),
143                field_name,
144                value_type.name(),
145                field_info.ty()
146            ));
147        }
148
149        // SAFETY: The offset in the ABI is always valid.
150        let field_ptr = unsafe { self.get_field_ptr_unchecked::<T::MunType>(field_info.offset()) };
151        let old = Marshal::marshal_from_ptr(field_ptr, self.runtime, &field_info.ty());
152        Marshal::marshal_to_ptr(value, field_ptr, &field_info.ty());
153        Ok(old)
154    }
155
156    /// Sets the value of the field corresponding to the specified `field_name`.
157    pub fn set<T: ArgumentReflection + Marshal<'s>>(
158        &mut self,
159        field_name: &str,
160        value: T,
161    ) -> Result<(), String> {
162        let type_info = self.type_info();
163
164        // Safety: `as_struct` is guaranteed to return `Some` for `StructRef`s.
165        let struct_info = type_info.as_struct().unwrap();
166
167        let field_info = struct_info
168            .fields()
169            .find_by_name(field_name)
170            .ok_or_else(|| {
171                format!(
172                    "Struct `{}` does not contain field `{}`.",
173                    type_info.name(),
174                    field_name
175                )
176            })?;
177
178        let value_type = value.type_info(self.runtime);
179        if field_info.ty() != value_type {
180            return Err(format!(
181                "Mismatched types for `{}::{}`. Expected: `{}`. Found: `{}`.",
182                type_info.name(),
183                field_name,
184                value_type.name(),
185                field_info.ty()
186            ));
187        }
188
189        // SAFETY: The offset in the ABI is always valid.
190        let field_ptr = unsafe { self.get_field_ptr_unchecked::<T::MunType>(field_info.offset()) };
191        Marshal::marshal_to_ptr(value, field_ptr, &field_info.ty());
192        Ok(())
193    }
194}
195
196impl<'r> ArgumentReflection for StructRef<'r> {
197    fn type_info(&self, _runtime: &Runtime) -> Type {
198        self.type_info()
199    }
200}
201
202impl<'s> Marshal<'s> for StructRef<'s> {
203    type MunType = RawStruct;
204
205    fn marshal_from<'r>(value: Self::MunType, runtime: &'r Runtime) -> Self
206    where
207        'r: 's,
208    {
209        StructRef::new(value, runtime)
210    }
211
212    fn marshal_into<'r>(self) -> Self::MunType {
213        self.into_raw()
214    }
215
216    fn marshal_from_ptr<'r>(
217        ptr: NonNull<Self::MunType>,
218        runtime: &'r Runtime,
219        type_info: &Type,
220    ) -> StructRef<'s>
221    where
222        Self: 's,
223        'r: 's,
224    {
225        let struct_info = type_info.as_struct().unwrap();
226
227        // Copy the contents of the struct based on what kind of pointer we are dealing with
228        let gc_handle = if struct_info.is_value_struct() {
229            // For a value struct, `ptr` points to a struct value.
230
231            // Create a new object using the runtime's intrinsic
232            let mut gc_handle = runtime.gc().alloc(type_info);
233
234            // Construct
235            let src = ptr.cast::<u8>().as_ptr() as *const _;
236            let dest = unsafe { gc_handle.deref_mut::<u8>() };
237            unsafe { ptr::copy_nonoverlapping(src, dest, type_info.value_layout().size()) };
238
239            gc_handle
240        } else {
241            // For a gc struct, `ptr` points to a `GcPtr`.
242            unsafe { *ptr.cast::<GcPtr>().as_ptr() }
243        };
244
245        StructRef::new(RawStruct(gc_handle), runtime)
246    }
247
248    fn marshal_to_ptr(value: Self, mut ptr: NonNull<Self::MunType>, type_info: &Type) {
249        let struct_info = type_info.as_struct().unwrap();
250        if struct_info.is_value_struct() {
251            let dest = ptr.cast::<u8>().as_ptr();
252            unsafe {
253                ptr::copy_nonoverlapping(
254                    value.into_raw().get_ptr(),
255                    dest,
256                    type_info.value_layout().size(),
257                )
258            };
259        } else {
260            unsafe { *ptr.as_mut() = value.into_raw() };
261        }
262    }
263}
264
265impl<'r> ReturnTypeReflection for StructRef<'r> {
266    /// Returns true if this specified type can be stored in an instance of this type
267    fn accepts_type(ty: &Type) -> bool {
268        ty.is_struct()
269    }
270
271    fn type_hint() -> &'static str {
272        "struct"
273    }
274}
275
276/// Type-agnostic wrapper for interoperability with a Mun struct, that has been rooted. To marshal,
277/// obtain a `StructRef` for the `RootedStruct`.
278#[derive(Clone)]
279pub struct RootedStruct {
280    handle: GcRootPtr,
281}
282
283impl RootedStruct {
284    /// Creates a `RootedStruct` that wraps a raw Mun struct.
285    fn new(gc: &Arc<GarbageCollector>, raw: RawStruct) -> Self {
286        assert!(gc.ptr_type(raw.0).is_struct());
287        Self {
288            handle: GcRootPtr::new(gc, raw.0),
289        }
290    }
291
292    /// Converts the `RootedStruct` into a `StructRef`, using an external shared reference to a
293    /// `Runtime`.
294    pub fn as_ref<'r>(&self, runtime: &'r Runtime) -> StructRef<'r> {
295        assert_eq!(Arc::as_ptr(&runtime.gc), self.handle.runtime().as_ptr());
296        StructRef::new(RawStruct(self.handle.handle()), runtime)
297    }
298}