virtual_machine/
manager.rs

1//! Virtual Machine Manager - Global singleton for WASM runtime management.
2
3//!
4//! The Manager provides a centralized interface for initializing the WASM runtime,
5//! registering host functions, and executing WASM modules. It maintains a global
6//! singleton instance that can be accessed throughout the system.
7
8use core::{ffi::CStr, mem::forget};
9
10use abi_context::FileIdentifier;
11use alloc::{string::ToString, vec, vec::Vec};
12use synchronization::once_lock::OnceLock;
13use task::TaskIdentifier;
14use virtual_file_system::File;
15use wamr_rust_sdk::{
16    sys::{wasm_runtime_is_xip_file, wasm_runtime_load, wasm_runtime_register_module},
17    value::WasmValue,
18};
19
20use crate::{Error, Instance, Module, Registrable, Result, Runtime};
21
22/// Global singleton instance of the Virtual Machine Manager
23static MANAGER_INSTANCE: OnceLock<Manager> = OnceLock::new();
24
25/// Initialize the Virtual Machine Manager with a set of registrable host functions.
26///
27/// This function must be called once before any WASM operations can be performed.
28/// It creates a global singleton Manager instance that will persist for the
29/// lifetime of the application.
30///
31/// # Arguments
32///
33/// * `Registrables` - Array of traits implementing host functions that can be called from WASM
34///
35/// # Returns
36///
37/// A static reference to the initialized Manager instance
38///
39/// # Example
40///
41/// ```rust,ignore
42/// let manager = Initialize(&[&MyHostFunctions]);
43/// ```
44pub fn initialize(registrables: &[&dyn Registrable]) -> &'static Manager {
45    MANAGER_INSTANCE
46        .get_or_init(|| Manager::new(registrables).expect("Cannot create virtual machine manager"));
47
48    get_instance()
49}
50
51/// Get a reference to the initialized Virtual Machine Manager instance.
52///
53/// # Panics
54///
55/// Panics if called before `Initialize()` has been called.
56///
57/// # Returns
58///
59/// A static reference to the Manager instance
60pub fn get_instance() -> &'static Manager {
61    MANAGER_INSTANCE
62        .try_get()
63        .expect("Cannot get virtual machine manager instance before initialization")
64}
65
66/// The Virtual Machine Manager handles WASM runtime lifecycle and module execution.
67///
68/// This struct encapsulates the WASM runtime and provides high-level operations
69/// for executing WASM modules with proper I/O redirection and resource management.
70pub struct Manager {
71    runtime: Runtime,
72}
73
74unsafe impl Send for Manager {}
75
76unsafe impl Sync for Manager {}
77
78impl Manager {
79    /// Create a new Virtual Machine Manager with the given registrable host functions.
80    ///
81    /// This function initializes the WASM runtime, registers all provided host functions,
82    /// and pre-loads any modules that the registrables provide.
83    ///
84    /// # Arguments
85    ///
86    /// * `Registrables` - Array of objects implementing host functions and optionally providing WASM modules
87    ///
88    /// # Returns
89    ///
90    /// A new Manager instance or an error if initialization fails
91    ///
92    /// # Errors
93    ///
94    /// Returns an error if:
95    /// - Runtime initialization fails
96    /// - Host function registration fails  
97    /// - Module loading fails
98    pub fn new(registrables: &[&dyn Registrable]) -> Result<Self> {
99        let mut runtime_builder = Runtime::builder();
100
101        for registrable in registrables {
102            runtime_builder = runtime_builder.register(*registrable);
103        }
104
105        let runtime = runtime_builder.build()?;
106
107        let manager = Self { runtime };
108
109        for registrable in registrables {
110            if let Some(module_binary) = registrable.get_binary() {
111                manager.load_module(module_binary, registrable.is_xip(), registrable.get_name())?;
112            }
113        }
114
115        Ok(manager)
116    }
117
118    /// Load a WASM module from a buffer for execution.
119    ///
120    /// This method loads a WASM module into the runtime, either as a regular module
121    /// or as an XIP (execute-in-place) module for AOT compiled binaries.
122    ///
123    /// # Arguments
124    ///
125    /// * `Buffer` - The WASM module bytecode
126    /// * `XIP` - Whether this is an XIP AOT compiled module
127    /// * `Name` - Name to register the module under
128    ///
129    /// # Returns
130    ///
131    /// Success or an error if loading fails
132    ///
133    /// # Errors
134    ///
135    /// Returns an error if the module is not an XIP AOT compiled module or if the module cannot be loaded from the buffer.
136    fn load_module(&self, buffer: &[u8], xip: bool, name: &str) -> Result<()> {
137        if unsafe { xip && !wasm_runtime_is_xip_file(buffer.as_ptr(), buffer.len() as u32) } {
138            return Err(Error::InvalidModule);
139        }
140
141        unsafe {
142            let mut buffer = if xip {
143                Vec::from_raw_parts(buffer.as_ptr() as *mut u8, buffer.len(), buffer.len())
144            } else {
145                buffer.to_vec()
146            };
147
148            let mut error_buffer = [0_i8; 128];
149
150            let module = wasm_runtime_load(
151                buffer.as_mut_ptr(),
152                buffer.len() as u32,
153                error_buffer.as_mut_ptr(),
154                error_buffer.len() as u32,
155            );
156
157            if module.is_null() {
158                return Err(Error::CompilationError(
159                    CStr::from_ptr(error_buffer.as_ptr())
160                        .to_string_lossy()
161                        .to_string(),
162                ));
163            }
164
165            if !wasm_runtime_register_module(
166                name.as_ptr() as *const i8,
167                module,
168                error_buffer.as_mut_ptr(),
169                error_buffer.len() as u32,
170            ) {
171                return Err(Error::InternalError);
172            }
173
174            forget(buffer);
175        }
176
177        Ok(())
178    }
179
180    /// Execute a WASM module with the specified I/O configuration.
181    ///
182    /// This is the main entry point for executing WASM modules. It creates a new
183    /// module instance, sets up the execution environment with proper I/O redirection,
184    /// and calls the module's main function.
185    ///
186    /// # Arguments
187    ///
188    /// * `Buffer` - The WASM module bytecode to execute
189    /// * `Stack_size` - Stack size in bytes for the WASM instance
190    /// * `Standard_in` - File identifier for standard input
191    /// * `Standard_out` - File identifier for standard output  
192    /// * `Standard_error` - File identifier for standard error
193    ///
194    /// # Returns
195    ///
196    /// The return values from the WASM module's main function
197    ///
198    /// # Errors
199    ///
200    /// Returns an error if module loading, instantiation, or execution fails
201    pub async fn execute(
202        &'static self,
203        buffer: Vec<u8>,
204        stack_size: usize,
205        (standard_in, standard_out, standard_error): (File, File, File),
206        function_name: Option<&str>,
207        function_arguments: Vec<WasmValue>,
208        task: TaskIdentifier,
209    ) -> Result<Vec<WasmValue>> {
210        let abi_context = abi_context::get_instance();
211
212        abi_context
213            .call_abi(async || {
214                let standard_in = abi_context
215                    .insert_file(
216                        task,
217                        standard_in.into_synchronous_file(),
218                        Some(FileIdentifier::STANDARD_IN),
219                    )
220                    .ok_or(Error::FailedToRegisterFileContext)?;
221                let standard_out = abi_context
222                    .insert_file(
223                        task,
224                        standard_out.into_synchronous_file(),
225                        Some(FileIdentifier::STANDARD_OUT),
226                    )
227                    .ok_or(Error::FailedToRegisterFileContext)?;
228                let standard_error = abi_context
229                    .insert_file(
230                        task,
231                        standard_error.into_synchronous_file(),
232                        Some(FileIdentifier::STANDARD_ERROR),
233                    )
234                    .ok_or(Error::FailedToRegisterFileContext)?;
235
236                let module = Module::from_buffer(
237                    &self.runtime,
238                    buffer,
239                    "module",
240                    standard_in,
241                    standard_out,
242                    standard_error,
243                )
244                .await?;
245
246                let instance = Instance::new(&self.runtime, &module, stack_size)?;
247
248                let result = if let Some(function_name) = function_name {
249                    instance.call_export_function(function_name, &function_arguments)?
250                } else {
251                    instance.call_main(function_arguments.as_ref())?
252                };
253
254                Ok(result)
255            })
256            .await
257    }
258}