wrapped_mono 0.4.0

`wrapped_mono` is a safe, lightweight wrapper around the mono library. It allows embedding of the mono runtime inside a rust project. Inside this embedded runtime code written in languages supporting the .NET framework, such as C# and F#, can be run. This allows usage of libraries written in those languages, and using them as a scripting language. The mono runtime is used by many game engines, and this wrapper allows using it with projects written in Rust too.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
use crate::binds::{MonoDelegate, MonoMethod};
use crate::binds::{MonoException, MonoObject};
use crate::gc::{gc_unsafe_enter, gc_unsafe_exit, GCHandle};
use crate::tupleutilis::{CompareClasses, TupleToPtrs};
#[allow(unused_imports)] // for docs
use crate::Method;
use crate::ObjectTrait;
use crate::{Class, Domain, Exception, InteropClass, InteropRecive, InteropSend, MString, Object};
use core::ptr::null_mut;
use std::ffi::c_void;
use std::marker::PhantomData;
/// A safe representation of a delegate.
/// Args - a Tuple type made from types of all arguments accepted by this particular delegate
/// # Safety
/// ## Type Mismatch
/// When a delegate is received from mono runtime it's argument types are checked, but those checks are not yet made for a delegate with either 1 or no arguments.
/// This is not a bug, it only means that safety features will not catch some of your errors(wrong types provided by the user of this crate). As long as the signature on the Rust side matches the signature on the C#/F# side, you will never encounter this problem.
/// ## All arguments **must** implement InteropClass!
/// While this is not enforced jet because of limitations of the API(no support for C# tuples), **IT IS STILL NECESSARY**. Ignoring this warning and using Delegates with arguments not implementing InteropClass **will lead to crashes and undefined behaviour**. Before filing bug reports, check that all arguments of your function implement InteropClass.
pub struct Delegate<Args: InteropSend> {
    #[cfg(not(feature = "referneced_objects"))]
    dptr: *mut MonoDelegate,
    #[cfg(feature = "referneced_objects")]
    handle: GCHandle,
    args_type: PhantomData<Args>,
}
impl<Args: InteropSend> Delegate<Args> {
    fn get_ptr(&self) -> *mut MonoDelegate {
        #[cfg(not(feature = "referneced_objects"))]
        {
            self.dptr
        }
        #[cfg(feature = "referneced_objects")]
        {
            self.handle.get_target() as *mut MonoDelegate
        }
    }
    /// Counts number of parameters(arguments) this function accepts.
    /// # Arguments
    /// |Name   |Type   |Description|
    /// |-------|-------|------|
    /// |self|&[Delegate]|Rust representation of a delegate to get argument count of|
    pub fn get_param_count(&self) -> u32 {
        #[cfg(feature = "referneced_objects")]
        let marker = gc_unsafe_enter();
        let sig = unsafe { crate::binds::mono_method_signature(self.get_method_ptr()) };
        let pcount = unsafe { crate::binds::mono_signature_get_param_count(sig) };
        #[cfg(feature = "referneced_objects")]
        gc_unsafe_exit(marker);
        pcount
    }
    /// Returns list of classes of parameters of delegate *self*.
    /// # Arguments
    /// |Name   |Type   |Description|
    /// |-------|-------|------|
    /// |self|&[`Delegate`]|Rust representation of a delegate to get argument types off|
    pub fn get_params(&self) -> Vec<Class> {
        #[cfg(feature = "referneced_objects")]
        let marker = gc_unsafe_enter();
        let sig = unsafe { crate::binds::mono_method_signature(self.get_method_ptr()) };
        let mut iter: usize = 0;
        let mut res = Vec::with_capacity(self.get_param_count() as usize);
        while let Some(class) = unsafe {
            Class::from_ptr({
                let ptr = crate::binds::mono_signature_get_params(
                    sig,
                    &mut iter as *mut usize as *mut *mut c_void,
                );
                if ptr.is_null() {
                    null_mut()
                } else {
                    crate::binds::mono_class_from_mono_type(ptr)
                }
            })
        } {
            res.push(class);
        }
        #[cfg(feature = "referneced_objects")]
        gc_unsafe_exit(marker);
        res
    }
    // Gets names of all parameters delegate *self* accepts.
    /// # Arguments
    /// |Name   |Type   |Description|
    /// |-------|-------|------|
    /// |self|&[`Delegate`]|Rust representation of a delegate to get names of arguments off|
    pub fn get_param_names(&self) -> Vec<String> {
        use std::ffi::CString;
        let pcount = self.get_param_count() as usize;
        let mut ptrs: Vec<*const i8> = Vec::with_capacity(pcount);
        ptrs.resize(pcount, std::ptr::null::<i8>());
        #[cfg(feature = "referneced_objects")]
        let marker = gc_unsafe_enter();
        unsafe {
            crate::binds::mono_method_get_param_names(
                self.get_method_ptr(),
                ptrs.as_ptr() as *mut *const i8,
            )
        };
        let mut res: Vec<String> = Vec::with_capacity(pcount);
        for ptr in &ptrs {
            let cstr = unsafe { CString::from_raw(*ptr as *mut i8) };
            res.push(
                cstr.to_str()
                    .expect("Could not create String from ptr")
                    .to_owned(),
            );
            let _ = cstr.into_raw();
        }
        drop(ptrs);
        #[cfg(feature = "referneced_objects")]
        gc_unsafe_exit(marker);
        res
    }
    /// Returns the return type of delegate *self*, if no return type returns *System.Void*
    /// # Arguments
    /// |Name   |Type   |Description|
    /// |-------|-------|------|
    /// |self|&[`Delegate`]|Rust representation of a delegate to get return type off|
    pub fn get_return(&self) -> Class {
        let sig = unsafe { crate::binds::mono_method_signature(self.get_method_ptr()) };
        let ptr = unsafe { crate::binds::mono_signature_get_return_type(sig) };
        unsafe {
            Class::from_ptr(crate::binds::mono_class_from_mono_type(ptr)).expect("Got no method return type, but no return type should be signaled by System.Void type!")
        }
    }
    fn get_method_ptr(&self) -> *mut MonoMethod {
        unsafe { crate::binds::mono_get_delegate_invoke(self.get_class().get_ptr()) }
    }
}
/// Trait implemented only for [`Delegate`] type. Splits some functions up from from main [`Method`] type, allowing for different amount of delegate arguments.
pub trait DelegateTrait<Args: InteropSend> {
    /// Creates new Delegate type from a *mut MonoDelegate. Checks if arguments of [`MonoDelegate`] and rust representation of a [`Delegate`] match and if not panic.
    /// Returns [`None`] if pointer is null.
    /// # Arguments
    /// |Name   |Type   |Description|
    /// |-------|-------|------|
    /// |met_ptr|*mut [`MonoDelegate`]|Pointer to delegate to create a representation for.|
    /// # Safety
    /// Pointer must be either a valid pointer to [`MonoDelegate`] recived from mono runtime, or a null pointer.
    /// **WARNING** argument types not yet checked for delegates with 1 or 0 arguments. This results from limitations of Rust type system and this version of the API, and can't be solved without some realy nasty hacks,
    /// but will be fixed in the future.
    unsafe fn from_ptr(ptr: *mut MonoDelegate) -> Option<Self>
    where
        Self: Sized;
    /// Creates new Delegate type from a *mut MonoDelegate. Checks if arguments of [`MonoDelegate`] and rust representation of a [`Delegate`] match and if not returns None.
    /// Returns [`None`] if pointer is null.
    /// # Arguments
    /// |Name   |Type   |Description|
    /// |-------|-------|------|
    /// |met_ptr|*mut [`MonoDelegate`]|Pointer to delegate to create a representation for.|
    /// # Safety
    /// Pointer must be either a valid pointer to [`MonoDelegate`] recived from mono runtime, or a null pointer.
    /// **WARNING** argument types not yet checked for delegates with 1 or 0 arguments. This results from limitations of Rust type system and this version of the API, and can't be solved without some realy nasty hacks,
    /// but will be fixed in the future.
    unsafe fn from_ptr_checked(ptr: *mut MonoDelegate) -> Option<Self>
    where
        Self: Sized;
    /// Invokes this delegate.
    /// # Arguments
    /// | Name   | Type   | Description|
    /// |--------|--------|-------|
    /// | self   | &`Self`|Reference to delegate to invoke. |
    /// | args   | `Args`|Arguments to pass to delegate |
    fn invoke(&self, params: Args) -> Result<Option<Object>, Exception>;
}
impl<Args: InteropSend> DelegateTrait<Args> for Delegate<Args> {
    default unsafe fn from_ptr(ptr: *mut MonoDelegate) -> Option<Self> {
        if ptr.is_null() {
            None
        } else {
            #[cfg(not(feature = "referneced_objects"))]
            {
                Some(Self {
                    dptr: ptr,
                    args_type: PhantomData,
                })
            }
            #[cfg(feature = "referneced_objects")]
            {
                Some(Self {
                    handle: GCHandle::create_default(ptr as *mut crate::binds::MonoObject),
                    args_type: PhantomData,
                })
            }
        }
    }
    default unsafe fn from_ptr_checked(ptr: *mut MonoDelegate) -> Option<Self> {
        if ptr.is_null() {
            None
        } else {
            #[cfg(not(feature = "referneced_objects"))]
            {
                Some(Self {
                    dptr: ptr,
                    args_type: PhantomData,
                })
            }
            #[cfg(feature = "referneced_objects")]
            {
                Some(Self {
                    handle: GCHandle::create_default(ptr as *mut crate::binds::MonoObject),
                    args_type: PhantomData,
                })
            }
        }
    }
    default fn invoke(&self, params: Args) -> Result<Option<Object>, Exception> {
        let mut expect: *mut MonoException = null_mut();
        //convert argument types
        let mut args = <Args as InteropSend>::get_mono_rep(params);
        //convert arguments to pointers
        let mut params = &mut args as *mut _ as *mut c_void;
        #[cfg(feature = "referneced_objects")]
        let marker = gc_unsafe_enter();
        //invoke the delegate itself
        let res_ptr = unsafe {
            crate::binds::mono_runtime_delegate_invoke(
                self.get_ptr() as *mut _,
                &mut params as *mut *mut c_void,
                &mut expect as *mut *mut MonoException as *mut *mut MonoObject,
            )
        };
        //hold args as long as params lives.
        crate::hold(&args);
        //get result
        let res = unsafe { Object::from_ptr(res_ptr) };
        println!("expect:{}", expect as usize);
        if expect.is_null() {
            #[cfg(feature = "referneced_objects")]
            gc_unsafe_exit(marker);
            Ok(res)
        } else {
            let except = unsafe {
                Exception::from_ptr(expect)
                    .expect("Imposible: pointer is null and not null at the same time.")
            };
            #[cfg(feature = "referneced_objects")]
            gc_unsafe_exit(marker);
            Err(except)
        }
    }
}
impl<Args: InteropSend> DelegateTrait<Args> for Delegate<Args>
where
    <Args as InteropSend>::TargetType: TupleToPtrs + CompareClasses,
{
    default unsafe fn from_ptr(ptr: *mut MonoDelegate) -> Option<Self> {
        let res = {
            if ptr.is_null() {
                return None;
            } else {
                #[cfg(not(feature = "referneced_objects"))]
                {
                    Self {
                        dptr: ptr,
                        args_type: PhantomData,
                    }
                }
                #[cfg(feature = "referneced_objects")]
                {
                    Self {
                        handle: GCHandle::create_default(ptr as *mut crate::binds::MonoObject),
                        args_type: PhantomData,
                    }
                }
            }
        };
        // Do type checks
        let params = res.get_params();
        if !<<Args as InteropSend>::TargetType as CompareClasses>::compare(&params) {
            use std::fmt::Write;
            let mut msg = format!(
                "Delegate Type Mismatch! Got a deleagte accepting {} arguments of types:",
                params.len()
            );
            for param in params {
                write!(msg, ",\"{}\"", param.get_name_sig())
                    .expect("Could not print inproper function argument types!");
            }
            panic!("{}", msg);
        }
        Some(res)
    }
    default unsafe fn from_ptr_checked(ptr: *mut MonoDelegate) -> Option<Self> {
        let res = {
            if ptr.is_null() {
                return None;
            } else {
                #[cfg(not(feature = "referneced_objects"))]
                {
                    Self {
                        dptr: ptr,
                        args_type: PhantomData,
                    }
                }
                #[cfg(feature = "referneced_objects")]
                {
                    Self {
                        handle: GCHandle::create_default(ptr as *mut crate::binds::MonoObject),
                        args_type: PhantomData,
                    }
                }
            }
        };
        // Do type checks
        let params = res.get_params();
        if !<<Args as InteropSend>::TargetType as CompareClasses>::compare(&params) {
            None
        } else {
            Some(res)
        }
    }
    default fn invoke(&self, params: Args) -> Result<Option<Object>, Exception> {
        let mut expect: *mut MonoException = null_mut();
        //convert argument types
        let mut args = <Args as InteropSend>::get_mono_rep(params);
        let mut params =
            <<Args as InteropSend>::TargetType as TupleToPtrs>::get_ptrs(&mut args as *mut _);
        #[cfg(feature = "referneced_objects")]
        let marker = gc_unsafe_enter();
        //invoke the delegate itself
        let res_ptr = unsafe {
            crate::binds::mono_runtime_delegate_invoke(
                self.get_ptr() as *mut _,
                &mut params as *mut _ as *mut *mut c_void,
                &mut expect as *mut *mut MonoException as *mut *mut MonoObject,
            )
        };
        //hold args as long as params lives.
        crate::hold(&args);
        //get result
        let res = unsafe { Object::from_ptr(res_ptr) };
        if expect.is_null() {
            #[cfg(feature = "referneced_objects")]
            gc_unsafe_exit(marker);
            Ok(res)
        } else {
            let except = unsafe {
                Exception::from_ptr(expect)
                    .expect("Imposible: pointer is null and not null at the same time.")
            };
            #[cfg(feature = "referneced_objects")]
            gc_unsafe_exit(marker);
            Err(except)
        }
    }
}

impl<Args: InteropSend> InteropRecive for Delegate<Args> {
    type SourceType = *mut MonoDelegate;
    // unless this function is abused, this argument should come from the mono runtime, so it should be always valid.
    #[allow(clippy::not_unsafe_ptr_arg_deref)]
    fn get_rust_rep(ptr: *mut MonoDelegate) -> Delegate<Args> {
        unsafe { Self::from_ptr(ptr).expect("Expected non-null value but got null") }
    }
}
impl<Args: InteropSend> InteropRecive for Option<Delegate<Args>> {
    type SourceType = *mut MonoDelegate;
    // unless this function is abused, this argument should come from the mono runtime, so it should be always valid.
    #[allow(clippy::not_unsafe_ptr_arg_deref)]
    fn get_rust_rep(ptr: *mut MonoDelegate) -> Option<Delegate<Args>> {
        unsafe { Delegate::from_ptr(ptr) }
    }
}
impl<Args: InteropSend> InteropClass for Delegate<Args> {
    fn get_mono_class() -> Class {
        Class::get_delegate_class()
    }
}
impl<Args: InteropSend> InteropSend for Delegate<Args> {
    type TargetType = *mut MonoDelegate;
    fn get_mono_rep(input: Delegate<Args>) -> Self::TargetType {
        input.get_ptr()
    }
}
impl<Args: InteropSend> ObjectTrait for Delegate<Args> {
    fn hash(&self) -> i32 {
        unsafe { crate::binds::mono_object_hash(self.get_ptr() as *mut _) }
    }
    fn get_domain(&self) -> Domain {
        unsafe {
            Domain::from_ptr(crate::binds::mono_object_get_domain(
                self.get_ptr() as *mut _
            ))
        }
    }
    fn get_size(&self) -> u32 {
        unsafe { crate::binds::mono_object_get_size(self.get_ptr() as *mut _) }
    }
    fn reflection_get_token(&self) -> u32 {
        unsafe { crate::binds::mono_reflection_get_token(self.get_ptr() as *mut _) }
    }
    fn get_class(&self) -> Class {
        unsafe {
            Class::from_ptr(crate::binds::mono_object_get_class(self.get_ptr() as *mut _))
                .expect("Could not get class of an object")
        }
    }
    fn to_mstring(&self) -> Result<Option<MString>, Exception> {
        let mut exc: *mut crate::binds::MonoException = core::ptr::null_mut();
        let res = unsafe {
            MString::from_ptr(crate::binds::mono_object_to_string(
                self.get_ptr() as *mut _,
                &mut exc as *mut *mut crate::binds::MonoException
                    as *mut *mut crate::binds::MonoObject,
            ))
        };
        let exc = unsafe { Exception::from_ptr(exc) };
        match exc {
            Some(e) => Err(e),
            None => Ok(res),
        }
    }
    fn cast_to_object(&self) -> Object {
        unsafe { Object::from_ptr(self.get_ptr() as *mut _) }.unwrap() /*Faliure impossible, object is always an object.*/
    }
    fn cast_from_object(obj: &Object) -> Option<Self> {
        if !Self::get_mono_class().is_assignable_from(&obj.get_class()) {
            None
        } else {
            unsafe { Self::from_ptr_checked(obj.get_ptr() as *mut _) }
        }
    }
}
impl<O: ObjectTrait, Args: InteropSend> PartialEq<O> for Delegate<Args> {
    fn eq(&self, other: &O) -> bool {
        self.get_ptr() as *mut _ == other.cast_to_object().get_ptr()
    }
}