1use std::{
2 collections::BTreeMap,
3 sync::{Arc, Mutex, OnceLock},
4};
5
6use sim_kernel::{Error, Result};
7use sim_wasm_abi::{Handle, WasmFrameLimits, WasmRuntime, WasmiRuntime};
8
9#[derive(Clone)]
10pub(crate) struct WasmRegion {
11 pub(crate) runtime: Arc<dyn WasmRuntime>,
12 pub(crate) module: Handle,
13}
14
15fn wasm_regions() -> &'static Mutex<BTreeMap<String, Arc<WasmRegion>>> {
16 static REGISTRY: OnceLock<Mutex<BTreeMap<String, Arc<WasmRegion>>>> = OnceLock::new();
17 REGISTRY.get_or_init(|| Mutex::new(BTreeMap::new()))
18}
19
20pub fn register_wasm_region(region: &str, wasm_bytes: &[u8]) -> Result<()> {
25 let runtime: Arc<dyn WasmRuntime> =
26 Arc::new(WasmiRuntime::with_limits(WasmFrameLimits::default()));
27 register_wasm_region_with_runtime(region, runtime, wasm_bytes)
28}
29
30pub(crate) fn register_wasm_region_with_runtime(
31 region: &str,
32 runtime: Arc<dyn WasmRuntime>,
33 wasm_bytes: &[u8],
34) -> Result<()> {
35 let module = runtime.instantiate_bytes(wasm_bytes)?;
36 let mut registry = wasm_regions()
37 .lock()
38 .map_err(|_| Error::HostError("wasm region registry mutex poisoned".to_owned()))?;
39 registry.insert(region.to_owned(), Arc::new(WasmRegion { runtime, module }));
40 Ok(())
41}
42
43pub(crate) fn lookup_wasm_region(region: &str) -> Result<Arc<WasmRegion>> {
44 let registry = wasm_regions()
45 .lock()
46 .map_err(|_| Error::HostError("wasm region registry mutex poisoned".to_owned()))?;
47 registry
48 .get(region)
49 .cloned()
50 .ok_or_else(|| Error::Eval(format!("unknown wasm region {region}")))
51}
52
53#[cfg(test)]
54mod tests {
55 use super::{lookup_wasm_region, register_wasm_region};
56 use std::sync::atomic::{AtomicU64, Ordering};
57
58 use sim_kernel::Error;
59 use wat::parse_str as wat_parse_str;
60
61 static NEXT_REGION_ID: AtomicU64 = AtomicU64::new(1);
62
63 fn unique_region_name() -> String {
64 format!(
65 "test-r6-region-{}",
66 NEXT_REGION_ID.fetch_add(1, Ordering::Relaxed)
67 )
68 }
69
70 fn minimal_wasm_guest_bytes() -> Vec<u8> {
71 wat_parse_str(
72 r#"(module
73 (memory (export "memory") 1)
74 (func (export "sim_alloc") (param i32) (result i32) i32.const 0)
75 (func (export "sim_manifest") (result i64) i64.const 0)
76 (func (export "sim_exports") (result i64) i64.const 0)
77 (func (export "sim_call") (param i32 i32 i32 i32) (result i64) i64.const 0)
78 )"#,
79 )
80 .expect("hand-written wasm guest fixture should assemble")
81 }
82
83 #[test]
84 fn register_wasm_region_instantiates_a_wasmi_module() {
85 let region = unique_region_name();
86 let bytes = minimal_wasm_guest_bytes();
87
88 register_wasm_region(®ion, &bytes).unwrap();
89
90 let registered = lookup_wasm_region(®ion).unwrap();
91 assert_eq!(registered.module.0, 1);
92 }
93
94 #[test]
95 fn lookup_wasm_region_reports_unknown_region_clearly() {
96 let err = match lookup_wasm_region("missing-r6-region") {
97 Ok(_) => panic!("expected unknown wasm region lookup to fail"),
98 Err(err) => err,
99 };
100 assert!(
101 matches!(err, Error::Eval(message) if message == "unknown wasm region missing-r6-region")
102 );
103 }
104}