wasm_bridge_js/no_bindgen/
linker.rs1use 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 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}