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}