1use mun_capi_utils::error::ErrorHandle;
4use mun_capi_utils::{mun_error_try, try_deref_mut};
5use std::mem::ManuallyDrop;
6
7use crate::runtime::Runtime;
8use mun_memory::{ffi::Type, gc::GcRuntime};
9
10pub use mun_memory::gc::GcPtr;
11
12#[no_mangle]
23pub unsafe extern "C" fn mun_gc_alloc(runtime: Runtime, ty: Type, obj: *mut GcPtr) -> ErrorHandle {
24 let runtime = mun_error_try!(runtime
25 .inner()
26 .map_err(|e| format!("invalid argument 'runtime': {e}")));
27 let ty = mun_error_try!(ty
28 .to_owned()
29 .map_err(|e| format!("invalid argument 'obj': {e}"))
30 .map(ManuallyDrop::new));
31 let obj = try_deref_mut!(obj);
32 *obj = runtime.gc().alloc(&ty);
33 ErrorHandle::default()
34}
35
36#[no_mangle]
47pub unsafe extern "C" fn mun_gc_ptr_type(
48 runtime: Runtime,
49 obj: GcPtr,
50 ty: *mut Type,
51) -> ErrorHandle {
52 let runtime = mun_error_try!(runtime
53 .inner()
54 .map_err(|e| format!("invalid argument 'runtime': {e}")));
55 let ty = try_deref_mut!(ty);
56 *ty = runtime.gc().ptr_type(obj).into();
57 ErrorHandle::default()
58}
59
60#[no_mangle]
74pub unsafe extern "C" fn mun_gc_root(runtime: Runtime, obj: GcPtr) -> ErrorHandle {
75 let runtime = mun_error_try!(runtime
76 .inner()
77 .map_err(|e| format!("invalid argument 'runtime': {e}")));
78 runtime.gc().root(obj);
79 ErrorHandle::default()
80}
81
82#[no_mangle]
95pub unsafe extern "C" fn mun_gc_unroot(runtime: Runtime, obj: GcPtr) -> ErrorHandle {
96 let runtime = mun_error_try!(runtime
97 .inner()
98 .map_err(|e| format!("invalid argument 'runtime': {e}")));
99 runtime.gc().unroot(obj);
100 ErrorHandle::default()
101}
102
103#[no_mangle]
115pub unsafe extern "C" fn mun_gc_collect(runtime: Runtime, reclaimed: *mut bool) -> ErrorHandle {
116 let runtime = mun_error_try!(runtime
117 .inner()
118 .map_err(|e| format!("invalid argument 'runtime': {e}")));
119 let reclaimed = try_deref_mut!(reclaimed);
120 *reclaimed = runtime.gc_collect();
121 ErrorHandle::default()
122}
123
124#[cfg(test)]
125mod tests {
126 use mun_memory::ffi::{mun_type_equal, Type};
127 use mun_memory::gc::{HasIndirectionPtr, RawGcPtr};
128
129 use super::*;
130 use crate::{
131 runtime::mun_runtime_get_type_info_by_name, test_invalid_runtime, test_util::TestDriver,
132 };
133 use mun_capi_utils::error::mun_error_destroy;
134 use mun_capi_utils::{assert_error_snapshot, assert_getter1, assert_getter2};
135 use std::{
136 ffi::CString,
137 mem::{self},
138 ptr,
139 };
140
141 test_invalid_runtime!(
142 gc_alloc(Type::null(), ptr::null_mut()),
143 gc_ptr_type(mem::zeroed::<GcPtr>(), ptr::null_mut()),
144 gc_root(mem::zeroed::<GcPtr>()),
145 gc_unroot(mem::zeroed::<GcPtr>()),
146 gc_collect(ptr::null_mut())
147 );
148
149 #[test]
150 fn test_gc_alloc_invalid_type_info() {
151 let driver = TestDriver::new(
152 r#"
153 pub struct Foo;
154 "#,
155 );
156
157 assert_error_snapshot!(
158 unsafe { mun_gc_alloc(driver.runtime, Type::null(), ptr::null_mut()) },
159 @r###""invalid argument \'obj\': null pointer""###
160 );
161 }
162
163 #[test]
164 fn test_gc_alloc_invalid_obj() {
165 let driver = TestDriver::new(
166 r#"
167 pub struct Foo;
168 "#,
169 );
170
171 let type_name = CString::new("Foo").expect("Invalid type name.");
172 assert_getter2!(mun_runtime_get_type_info_by_name(
173 driver.runtime,
174 type_name.as_ptr(),
175 has_type,
176 ty,
177 ));
178 assert!(has_type);
179
180 assert_error_snapshot!(
181 unsafe { mun_gc_alloc(driver.runtime, ty, ptr::null_mut()) },
182 @r###""invalid argument \'obj\': null pointer""###
183 );
184 }
185
186 #[test]
187 fn test_gc_alloc() {
188 let driver = TestDriver::new(
189 r#"
190 pub struct Foo;
191 "#,
192 );
193
194 let type_name = CString::new("Foo").expect("Invalid type name.");
195 assert_getter2!(mun_runtime_get_type_info_by_name(
196 driver.runtime,
197 type_name.as_ptr(),
198 has_type,
199 ty,
200 ));
201 assert!(has_type);
202
203 assert_getter2!(mun_gc_alloc(driver.runtime, ty, obj));
204 assert_ne!(unsafe { obj.deref::<u8>() }, ptr::null());
205
206 assert_getter1!(mun_gc_collect(driver.runtime, reclaimed));
207 assert!(reclaimed);
208 }
209
210 #[test]
211 fn test_gc_ptr_type_invalid_type_info() {
212 let driver = TestDriver::new(
213 r#"
214 pub struct Foo;
215 "#,
216 );
217
218 assert_error_snapshot!(
219 unsafe {
220 let raw_ptr: RawGcPtr = ptr::null();
221 mun_gc_ptr_type(driver.runtime, raw_ptr.into(), ptr::null_mut())
222 },
223 @r###""invalid argument \'ty\': null pointer""###
224 );
225 }
226
227 #[test]
228 fn test_gc_ptr_type() {
229 let driver = TestDriver::new(
230 r#"
231 pub struct Foo;
232 "#,
233 );
234
235 let type_name = CString::new("Foo").expect("Invalid type name.");
236 assert_getter2!(mun_runtime_get_type_info_by_name(
237 driver.runtime,
238 type_name.as_ptr(),
239 has_type,
240 ty,
241 ));
242 assert!(has_type);
243
244 assert_getter2!(mun_gc_alloc(driver.runtime, ty, obj));
245 assert_ne!(unsafe { obj.deref::<u8>() }, ptr::null());
246
247 assert_getter2!(mun_gc_ptr_type(driver.runtime, obj, ptr_ty));
248 assert!(unsafe { mun_type_equal(ty, ptr_ty) });
249
250 assert_getter1!(mun_gc_collect(driver.runtime, reclaimed));
251 assert!(reclaimed);
252 }
253
254 #[test]
255 fn test_gc_rooting() {
256 let driver = TestDriver::new(
257 r#"
258 pub struct Foo;
259 "#,
260 );
261
262 let type_name = CString::new("Foo").expect("Invalid type name.");
263 assert_getter2!(mun_runtime_get_type_info_by_name(
264 driver.runtime,
265 type_name.as_ptr(),
266 has_type,
267 ty,
268 ));
269 assert!(has_type);
270
271 assert_getter2!(mun_gc_alloc(driver.runtime, ty, obj));
272 assert_ne!(unsafe { obj.deref::<u8>() }, ptr::null());
273
274 assert!(unsafe { mun_gc_root(driver.runtime, obj) }.is_ok());
275
276 assert_getter1!(mun_gc_collect(driver.runtime, reclaimed));
277 assert!(!reclaimed);
278
279 assert!(unsafe { mun_gc_unroot(driver.runtime, obj) }.is_ok());
280
281 assert_getter1!(mun_gc_collect(driver.runtime, reclaimed));
282 assert!(reclaimed);
283 }
284
285 #[test]
286 fn test_gc_ptr_collect_invalid_reclaimed() {
287 let driver = TestDriver::new(
288 r#"
289 pub struct Foo;
290
291 pub fn main() -> Foo { Foo }
292 "#,
293 );
294
295 assert_error_snapshot!(
296 unsafe { mun_gc_collect(driver.runtime, ptr::null_mut()) },
297 @r###""invalid argument \'reclaimed\': null pointer""###
298 );
299 }
300}