use crate::binds::{MonoException, MonoMethod, MonoObject};
use crate::tupleutilis::{CompareClasses, TupleToFFIPtrs};
use crate::{Class, Exception, InteropSend, Object, ObjectTrait};
use core::{ffi::c_void, marker::PhantomData};
use std::ffi::CString;
use std::ptr::null_mut;
pub struct Method<Args: TupleToFFIPtrs + CompareClasses> {
method: *mut MonoMethod,
args_type: PhantomData<Args>,
}
impl<Args: TupleToFFIPtrs + CompareClasses> Method<Args> {
#[must_use]
pub fn get_ptr(&self) -> *mut MonoMethod {
self.method
}
#[must_use]
pub fn can_acces_method<T: TupleToFFIPtrs + CompareClasses>(&self, called: &Method<T>) -> bool {
(unsafe { crate::binds::mono_method_can_access_method(self.method, called.method) } != 0)
}
#[doc(hidden)]
#[must_use]
pub fn get_token(&self) -> u32 {
unsafe { crate::binds::mono_method_get_token(self.method) }
}
#[doc(hidden)]
#[must_use]
pub fn get_index(&self) -> u32 {
unsafe { crate::binds::mono_method_get_index(self.method) }
}
#[must_use]
pub fn get_param_count(&self) -> u32 {
let sig = unsafe { crate::binds::mono_method_signature(self.method) };
unsafe { crate::binds::mono_signature_get_param_count(sig) }
}
#[must_use]
pub fn get_from_name(
class: &crate::class::Class,
name: &str,
param_count: i32,
) -> Option<Self> {
let cstr = CString::new(name).expect(crate::STR2CSTR_ERR);
let res = unsafe {
Self::from_ptr_checked(crate::binds::mono_class_get_method_from_name(
class.get_ptr(),
cstr.as_ptr(),
param_count,
))
};
drop(cstr);
res
}
#[must_use]
pub fn get_param_names(&self) -> Vec<String> {
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>());
unsafe {
crate::binds::mono_method_get_param_names(self.method, 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);
res
}
#[must_use]
pub fn get_params(&self) -> Vec<Class> {
let sig = unsafe { crate::binds::mono_method_signature(self.method) };
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,
std::ptr::addr_of_mut!(iter).cast::<*mut c_void>(),
);
if ptr.is_null() {
null_mut()
} else {
crate::binds::mono_class_from_mono_type(ptr)
}
})
} {
res.push(class);
}
res
}
#[must_use]
pub fn get_return(&self) -> Class {
let sig = unsafe { crate::binds::mono_method_signature(self.method) };
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!")
}
}
}
impl<Args: CompareClasses + TupleToFFIPtrs> Method<Args> {
pub fn invoke(
&self,
object: Option<Object>,
mut args: Args,
) -> Result<Option<Object>, Exception> {
let obj_ptr = object.map_or(core::ptr::null_mut(), |obj| obj.get_ptr());
let mut expect: *mut MonoException = null_mut();
let mut params = args.get_ptrs();
let res_ptr = unsafe {
crate::binds::mono_runtime_invoke(
self.get_ptr(),
obj_ptr.cast::<std::os::raw::c_void>(),
std::ptr::addr_of_mut!(params).cast::<*mut c_void>(),
std::ptr::addr_of_mut!(expect).cast::<*mut MonoObject>(),
)
};
let _ = &args;
let res = unsafe { Object::from_ptr(res_ptr) };
if expect.is_null() {
Ok(res)
} else {
let except = unsafe {
Exception::from_ptr(expect.cast())
.expect("Imposible: pointer is null and not null at the same time.")
};
Err(except)
}
}
pub unsafe fn from_ptr(met_ptr: *mut MonoMethod) -> Option<Self> {
if met_ptr.is_null() {
return None;
}
let res = Self {
method: met_ptr,
args_type: PhantomData,
};
let params = res.get_params();
if <Args as CompareClasses>::compare(¶ms) {
Some(res)
} else {
None
}
}
pub unsafe fn from_ptr_checked(met_ptr: *mut MonoMethod) -> Option<Self> {
if met_ptr.is_null() {
return None;
}
let res = Self {
method: met_ptr,
args_type: PhantomData,
};
let params = res.get_params();
if !<Args as CompareClasses>::compare(¶ms) {
return None;
}
Some(res)
}
}