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
//! Hosting the CLR from unix with Rust

extern crate libloading as libl;

use std::collections::HashMap;
use std::ffi::CString;
use std::io;
use std::os::raw::{c_char, c_int, c_uint, c_void};


// -- Private utility functions -- //
fn to_c_str<T: Into<Vec<u8>>>(t: T) -> *const c_char {
    CString::new(t).unwrap().as_ptr() as *const c_char
}


/// A function pointer type for the init function
type CoreClrInitializeFn = unsafe extern fn(
    *const c_char,
    *const c_char,
    c_int,
    *const *const c_char,
    *const *const c_char,
    *const *const c_void,
    *const c_uint) -> c_int;

/// A function pointer type for the shutdown function
type CoreClrShutdownFn = unsafe extern fn(*const c_void, c_uint) -> c_int;

/// A function pointer type for the second shutdown function
type CoreClrShutdown2Fn = unsafe extern fn(*const c_void, c_uint, *const c_int) -> c_int;

/// A function pointer type for the create delegate function
type CoreClrCreateDelegateFn = unsafe extern fn(
    *const c_void,
    c_uint,
    *const c_char,
    *const c_char,
    *const c_char,
    *const *const c_void) -> c_int;

/// The CoreClr object represents a binding to the CLR maintained by a private handle and domain id
pub struct UnixCoreClr {
    host_handle: *const c_void,
    domain_id: c_uint
}

impl UnixCoreClr {
    /// Private helper function to grab a reference to the library in the current context
    fn library() -> libl::Result<libl::Library> {
        libl::Library::new("/usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.0.0/libcoreclr.dylib")
    }

    /// Creates a new CLR object
    pub fn init(
        exe_path: &str,
        app_domain_friendly_name: &str,
        properties_option: Option<HashMap<&str, &str>>) -> libl::Result<UnixCoreClr>
    {
        // Create the host handle and its ref
        let host_handle = 0 as *const c_void;
        let host_handle_ref = &host_handle as *const *const c_void;
        
        // Create the domain id and its ref
        let domain_id = 0 as c_uint;
        let domain_id_ref = &domain_id as *const c_uint;

        // Raw C string refs from exe_path and app_domain_friendly_name slices
        let exe_path_raw = to_c_str(exe_path);
        let app_domain_friendly_name_raw = to_c_str(app_domain_friendly_name);

        // Either use the provided option or a blank hashmap
        let properties = properties_option.unwrap_or_else(HashMap::new);

        // Count for upcoming vecs
        let properties_count = properties.len() as c_int;

        // Collect property keys into a vec
        let properties_keys: Vec<*const c_char> = properties.keys()
                .map(|&k| to_c_str(k))
                .collect();
        
        // Collect equivalent property values into another vec
        let properties_values: Vec<*const c_char> = properties.values()
                .map(|&v| to_c_str(v))
                .collect();

        // Grab refs of the two to pass into the actual function call
        let properties_keys_ref = properties_keys.as_ptr();
        let properties_values_ref = properties_values.as_ptr();

        // Open up an unsafe block for actually loading functions from the CLR libs
        unsafe {
            let coreclr_library = UnixCoreClr::library()?;
            let coreclr_initialize: libl::Symbol<CoreClrInitializeFn> = coreclr_library.get(b"coreclr_initialize")?;

            // Initialize the CLR
            match coreclr_initialize(
                exe_path_raw,
                app_domain_friendly_name_raw,
                properties_count,
                properties_keys_ref,
                properties_values_ref,
                host_handle_ref,
                domain_id_ref)
            {
                // If healthy exit code, return a model of the CLR
                0 => Ok(UnixCoreClr {
                    host_handle: host_handle,
                    domain_id: domain_id
                }),
                // Else panic
                code => panic!("Failed to initialize ({:X}).  Host handle: {:?}, domain id: {:?}.", code, host_handle, domain_id)
            }
        }
    }

    /// Shuts down the CLR
    pub fn shutdown(self: Self) -> io::Result<()> {
        unsafe {
            let coreclr_library = UnixCoreClr::library()?;
            let coreclr_shutdown: libl::Symbol<CoreClrShutdownFn> = coreclr_library.get(b"coreclr_shutdown")?;

            // Shutdown the CLR
            match coreclr_shutdown(self.host_handle, self.domain_id) {
                // If healthy exit code, return unit
                0 => Ok(()),
                // Else panic
                _ => panic!("Failed to shutdown")
            }
        }
    }

    /// Shuts down the CLR, returning a latched exit code?  Should follow up on what that is.
    pub fn shutdown_2(self: Self) -> io::Result<c_int> {
        let latched_exit_code = -1 as c_int;
        let latched_exit_code_ref = &latched_exit_code as *const c_int;

        unsafe {
            let coreclr_library = UnixCoreClr::library()?;
            let coreclr_shutdown_2: libl::Symbol<CoreClrShutdown2Fn> = coreclr_library.get(b"coreclr_shutdown_2")?;

            // Shutdown the CLR
            match coreclr_shutdown_2(self.host_handle, self.domain_id, latched_exit_code_ref) {
                // If healthy exit code, return the resulting exit code
                0 => Ok(latched_exit_code),
                // Else panic
                _ => panic!("Failed to shutdown")
            }
        }
    }

    /// Spins a raw delegate pointer out of the CLR
    pub fn create_raw_delegate(
        self: &Self,
        entry_point_assembly_name: &str,
        entry_point_type_name: &str,
        entry_point_method_name: &str) -> io::Result<*const c_void>
    {
        let coreclr_delegate = 0 as *const c_void;
        let coreclr_delegate_ref = &coreclr_delegate as *const *const c_void;
        
        unsafe {
            let coreclr_library = UnixCoreClr::library()?;
            let coreclr_create_delegate: libl::Symbol<CoreClrCreateDelegateFn> = coreclr_library.get(b"coreclr_create_delegate")?;

            // Create the delegate
            match coreclr_create_delegate(
                self.host_handle,
                self.domain_id,
                to_c_str(entry_point_assembly_name),
                to_c_str(entry_point_type_name),
                to_c_str(entry_point_method_name),
                coreclr_delegate_ref)
            {
                // If healthy exit code, return the resulting exit code
                0 => Ok(coreclr_delegate),
                // Else panic
                _ => panic!("Failed to shutdown")
            }
        }
    }
}