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