Skip to main content

mono_rt/types/
image.rs

1use std::ffi::{CStr, CString};
2use std::ptr;
3
4use super::{MonoClass, mono_handle};
5use crate::{MonoError, MonoImageOpenStatus, Result, api};
6
7struct FindContext<'a> {
8    target_name: &'a str,
9    result: Option<MonoImage>,
10    api_failed: bool,
11}
12
13mono_handle!(MonoImage);
14
15impl MonoImage {
16    /// Finds a loaded assembly image by name.
17    ///
18    /// # Errors
19    ///
20    /// Returns [`MonoError::Uninitialized`] if the Mono API has not been initialized.
21    pub fn find(name: &str) -> Result<Option<Self>> {
22        let mut ctx = FindContext {
23            target_name: name,
24            result: None,
25            api_failed: false,
26        };
27        api()?.assembly_foreach(find_image_callback, ptr::addr_of_mut!(ctx).cast());
28        if ctx.api_failed {
29            return Err(MonoError::Uninitialized);
30        }
31        Ok(ctx.result)
32    }
33
34    /// Loads raw assembly bytes into a `MonoImage` without touching disk.
35    ///
36    /// The bytes are always copied into Mono's internal heap, so `data` can be freed after
37    /// this call returns.
38    ///
39    /// # Errors
40    ///
41    /// Returns [`MonoError::ImageOpenFailed`] if Mono rejects the image data.
42    /// Returns [`MonoError::Uninitialized`] if the Mono API has not been initialized.
43    pub fn open_from_data(data: &mut [u8]) -> Result<Self> {
44        let data_len = u32::try_from(data.len())
45            .map_err(|_| MonoError::ImageOpenFailed(MonoImageOpenStatus::ImageInvalid))?;
46
47        let mut status: i32 = 0;
48        let ptr = api()?.image_open_from_data(
49            data.as_mut_ptr().cast(),
50            data_len,
51            1,
52            std::ptr::addr_of_mut!(status),
53        );
54
55        let s = MonoImageOpenStatus::from_raw(status);
56        if !s.is_ok() {
57            return Err(MonoError::ImageOpenFailed(s));
58        }
59
60        MonoImage::from_ptr(ptr).ok_or(MonoError::ImageOpenFailed(
61            MonoImageOpenStatus::ImageInvalid,
62        ))
63    }
64
65    /// Returns the Mono-supplied error message for a raw `MonoImageOpenStatus` integer.
66    ///
67    /// # Errors
68    ///
69    /// Returns [`MonoError::Uninitialized`] if the Mono API has not been initialized.
70    pub fn open_status_message(status: i32) -> Result<String> {
71        let ptr = api()?.image_strerror(status);
72        if ptr.is_null() {
73            return Ok("unknown status".to_owned());
74        }
75
76        Ok(unsafe { CStr::from_ptr(ptr) }
77            .to_string_lossy()
78            .into_owned())
79    }
80
81    /// Resolves a class in this image by namespace and name.
82    ///
83    /// # Errors
84    ///
85    /// Returns [`MonoError::NullByteInName`] if `namespace` or `name` contain an interior null byte.
86    /// Returns [`MonoError::Uninitialized`] if the Mono API has not been initialized.
87    pub fn class_from_name(self, namespace: &str, name: &str) -> Result<Option<MonoClass>> {
88        let ns = CString::new(namespace).map_err(|_| MonoError::NullByteInName)?;
89        let nm = CString::new(name).map_err(|_| MonoError::NullByteInName)?;
90        let ptr = api()?.class_from_name(self.as_ptr(), ns.as_ptr(), nm.as_ptr());
91        Ok(MonoClass::from_ptr(ptr))
92    }
93}
94
95/// Callback for `mono_assembly_foreach` to find an image by name.
96///
97/// # Safety
98///
99/// `assembly` must be a valid `MonoAssembly*` and `user_data` must be a valid pointer to a
100/// `FindContext`, both on a Mono-attached thread.
101unsafe extern "C" fn find_image_callback(assembly: *mut c_void, user_data: *mut c_void) {
102    let Ok(api) = api() else {
103        let ctx = unsafe { &mut *user_data.cast::<FindContext<'_>>() };
104        ctx.api_failed = true;
105        return;
106    };
107
108    let ctx = unsafe { &mut *user_data.cast::<FindContext<'_>>() };
109    if ctx.result.is_some() {
110        return;
111    }
112
113    let image = api.assembly_get_image(assembly);
114    if image.is_null() {
115        return;
116    }
117
118    let name_ptr = api.image_get_name(image);
119    if name_ptr.is_null() {
120        return;
121    }
122
123    let name = unsafe { CStr::from_ptr(name_ptr) }.to_str().unwrap_or("");
124    if name == ctx.target_name {
125        ctx.result = Some(unsafe { MonoImage::from_ptr_unchecked(image) });
126    }
127}