wasm_bridge_js/no_bindgen/
linker.rs

1use std::rc::Rc;
2
3use js_sys::{Array, Function, Object, Reflect};
4use wasm_bindgen::{prelude::*, JsValue};
5
6use crate::*;
7
8pub struct Linker<T> {
9    fns: Vec<PreparedFn<T>>,
10}
11
12impl<T> Linker<T> {
13    pub fn new(_engine: &Engine) -> Self {
14        Self { fns: vec![] }
15    }
16
17    pub fn instantiate(
18        &self,
19        store: impl AsContextMut<Data = T>,
20        module: &Module,
21    ) -> Result<Instance, Error> {
22        let (imports, drop_handles) = self.collect_imports(store);
23        Instance::new_with_imports(module, &imports, drop_handles)
24    }
25
26    pub async fn instantiate_async(
27        &self,
28        store: impl AsContextMut<Data = T>,
29        module: &Module,
30    ) -> Result<Instance> {
31        let (imports, drop_handles) = self.collect_imports(store);
32        Instance::new_with_imports_async(module, &imports, drop_handles).await
33    }
34
35    fn collect_imports(&self, store: impl AsContextMut<Data = T>) -> (Object, Vec<DropHandle>) {
36        let store = store.as_context();
37
38        let imports = Object::new();
39        let mut drop_handles = vec![];
40
41        for func in self.fns.iter() {
42            let drop_handle = func.add_to_imports(&imports, store.data_handle());
43            drop_handles.push(drop_handle);
44        }
45
46        (imports, drop_handles)
47    }
48
49    pub fn func_new<F>(
50        &mut self,
51        module: &str,
52        name: &str,
53        _type: FuncType,
54        func: F,
55    ) -> Result<&mut Self>
56    where
57        F: Fn(Caller<T>, &[Val], &mut [Val]) -> Result<()> + 'static,
58        T: 'static,
59    {
60        let func_rc = Rc::new(func);
61        let creator = move |handle: DataHandle<T>| {
62            let caller = Caller::new(handle);
63            let func_clone = func_rc.clone();
64
65            let closure =
66                Closure::<dyn Fn(Array) -> Result<JsValue, JsValue>>::new(move |js_args: Array| {
67                    let mut args = Vec::with_capacity(js_args.length() as _);
68                    for index in 0..args.capacity() {
69                        let js_val = Reflect::get_u32(&js_args, index as _)?;
70                        args.push(Val::from_js_value(&js_val).map_err::<JsValue, _>(|e| {
71                            format!("Cannot convert JsValue to Val: {e:}").into()
72                        })?);
73                    }
74
75                    // TODO: support different amounts of return values? HOW????
76                    let mut rets = vec![Val::I32(0)];
77
78                    func_clone(caller.clone(), &args, &mut rets).map_err::<JsValue, _>(|e| {
79                        format!("Error in imported function: {e:?}").into()
80                    })?;
81
82                    Ok(rets[0].to_js_value())
83                });
84
85            let (js_func, handler) = DropHandle::from_closure(closure);
86            let js_func = transform_dynamic_closure_arguments(js_func);
87
88            (js_func, handler)
89        };
90
91        self.fns
92            .push(PreparedFn::new(module, name, Box::new(creator)));
93
94        Ok(self)
95    }
96
97    pub fn func_wrap<Params, Results, F>(
98        &mut self,
99        module: &str,
100        name: &str,
101        func: F,
102    ) -> Result<&mut Self>
103    where
104        F: IntoMakeClosure<T, Params, Results> + 'static,
105    {
106        let creator = func.into_make_closure();
107
108        self.fns.push(PreparedFn::new(module, name, creator));
109
110        Ok(self)
111    }
112}
113
114#[derive(Debug)]
115pub struct DropHandle(Box<dyn std::fmt::Debug>);
116pub type DropHandles = Rc<Vec<DropHandle>>;
117
118impl DropHandle {
119    pub(crate) fn new<T: std::fmt::Debug + 'static>(value: T) -> Self {
120        Self(Box::new(value))
121    }
122
123    pub(crate) fn from_closure(
124        closure: impl AsRef<JsValue> + std::fmt::Debug + 'static,
125    ) -> (JsValue, Self) {
126        let js_value = closure.as_ref().clone();
127
128        (js_value, Self::new(closure))
129    }
130}
131
132struct PreparedFn<T> {
133    module: String,
134    name: String,
135    creator: MakeClosure<T>,
136}
137
138impl<T> PreparedFn<T> {
139    fn new(module: &str, name: &str, creator: MakeClosure<T>) -> Self {
140        Self {
141            module: module.into(),
142            name: name.into(),
143            creator,
144        }
145    }
146
147    #[must_use]
148    fn add_to_imports(&self, imports: &JsValue, handle: &DataHandle<T>) -> DropHandle {
149        let module = Self::module(imports, &self.module);
150
151        let (js_val, handler) = (self.creator)(handle.clone());
152
153        Reflect::set(&module, &self.name.as_str().into(), &js_val).expect("module is object");
154
155        handler
156    }
157
158    fn module(imports: &JsValue, module: &str) -> JsValue {
159        let module_str: JsValue = module.into();
160        let existing = Reflect::get(imports, &module_str).expect("imports is object");
161
162        if existing.is_object() {
163            existing
164        } else {
165            let new_module: JsValue = Object::new().into();
166            Reflect::set(imports, &module_str, &new_module).expect("imports is object");
167            new_module
168        }
169    }
170}
171
172fn transform_dynamic_closure_arguments(closure: JsValue) -> JsValue {
173    let transformer: Function = js_sys::eval(r#"(func) => (...args) => func(args)"#)
174        .unwrap()
175        .into();
176    debug_assert!(transformer.is_function(), "transformer is a function");
177
178    transformer.call1(&JsValue::UNDEFINED, &closure).unwrap()
179}
180
181pub async fn instantiate_async<T>(
182    store: impl AsContextMut<Data = T>,
183    linker: &Linker<T>,
184    module: &Module,
185) -> Result<Instance> {
186    linker.instantiate_async(store, module).await
187}