wasmy_vm/
handler.rs

1use std::{
2    any::Any as _,
3    collections::HashMap,
4    sync::{Once, RwLock},
5};
6
7pub use inventory::submit as submit_handler;
8use lazy_static::lazy_static;
9pub use wasmy_abi::{abi::*, types::*};
10pub use wasmy_macros::vm_handle;
11
12pub type VmHandler = fn(usize, &Any) -> Result<Any>;
13
14pub struct VmHandlerApi {
15    method: Method,
16    handler: VmHandler,
17}
18
19static COLLECT_AND_REGISTER_ONCE: Once = Once::new();
20
21impl VmHandlerApi {
22    pub const fn new(method: Method, handler: VmHandler) -> Self {
23        VmHandlerApi { method, handler }
24    }
25    pub fn register(&self) {
26        set_handler(self.method, self.handler)
27    }
28    pub fn pack_any<R: Message>(data: R) -> Result<Any> {
29        pack_any(data)
30    }
31    pub fn unpack_any<R: Message>(data: &Any) -> Result<R> {
32        unpack_any(data)
33    }
34    pub unsafe fn try_as<T: Message>(ptr: usize) -> Option<&'static T> {
35        if ptr > 0 {
36            let ptr = ptr as *const T;
37            if !ptr.is_null() {
38                return Some(&*ptr);
39            }
40        }
41        None
42    }
43    pub(crate) fn collect_and_register_once() {
44        COLLECT_AND_REGISTER_ONCE.call_once(|| collect_and_register_handlers());
45    }
46}
47
48lazy_static! {
49    static ref MUX: RwLock<HashMap<Method, VmHandler>> =
50        RwLock::new(HashMap::<Method, VmHandler>::new());
51}
52
53fn collect_and_register_handlers() {
54    inventory::collect!(VmHandlerApi);
55    for info in inventory::iter::<VmHandlerApi> {
56        info.register();
57    }
58    for (method, hdl) in MUX.read().unwrap().iter() {
59        println!(
60            "collect_and_register_handlers: method={}, hdl_type_id={:?}",
61            method,
62            hdl.type_id()
63        );
64    }
65}
66
67pub fn set_handler(method: Method, hdl: VmHandler) {
68    let ty = hdl.type_id();
69    if let Some(old) = MUX.write().unwrap().insert(method, hdl) {
70        if old.type_id() != ty {
71            panic!(
72                "duplicate register handler: method={}, old_type_id={:?}, new_type_id={:?}",
73                method,
74                old.type_id(),
75                ty
76            );
77        }
78    }
79}
80
81#[allow(dead_code)]
82pub(crate) fn vm_invoke(ctx_ptr: usize, args_pb: &Vec<u8>) -> OutRets {
83    match InArgs::parse_from_bytes(&args_pb) {
84        Ok(vm_args) => handle(ctx_ptr, vm_args),
85        Err(err) => CodeMsg::new(CODE_PROTO, err).into(),
86    }
87}
88
89fn handle(ctx_ptr: usize, args: InArgs) -> OutRets {
90    let res: Result<Any> = MUX.read().unwrap().get(&args.get_method()).ok_or_else(|| {
91        CodeMsg::new(CODE_NONE, format!("undefined virtual machine method({})", args.get_method()))
92    })?(ctx_ptr, args.get_data());
93    match res {
94        Ok(a) => a.into(),
95        Err(e) => e.into(),
96    }
97}
98
99pub(crate) struct WasmHandlerApi();
100
101impl WasmHandlerApi {
102    pub(crate) const fn onload_symbol() -> &'static str {
103        "_wasmy_wasm_onload"
104    }
105    pub(crate) fn method_to_symbol(method: WasmMethod) -> String {
106        format!("_wasmy_wasm_handle_{}", method)
107    }
108    pub(crate) fn symbol_to_method(symbol: &str) -> Option<WasmMethod> {
109        if let Some(s) = symbol.strip_prefix("_wasmy_wasm_handle_") { s.parse().ok() } else { None }
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use crate::WasmHandlerApi;
116
117    #[test]
118    fn method_to_symbol() {
119        let method = WasmHandlerApi::method_to_symbol(10);
120        assert_eq!(method, "_wasmy_wasm_handle_10");
121    }
122
123    #[test]
124    fn symbol_to_method() {
125        let method = WasmHandlerApi::symbol_to_method("_wasmy_wasm_handle_10");
126        assert_eq!(method, Some(10));
127    }
128}