Skip to main content

wasmtime_wizer/component/
wasmtime.rs

1use crate::Wizer;
2use crate::component::ComponentInstanceState;
3use wasmtime::component::{
4    Component, ComponentExportIndex, Instance, Lift, Val, WasmList, types::ComponentItem,
5};
6use wasmtime::{Result, Store, error::Context as _, format_err};
7
8#[cfg(feature = "wasmprinter")]
9use wasmtime::ToWasmtimeResult as _;
10
11impl Wizer {
12    /// Same as [`Wizer::run`], except for components.
13    pub async fn run_component<T: Send>(
14        &self,
15        store: &mut Store<T>,
16        wasm: &[u8],
17        instantiate: impl AsyncFnOnce(&mut Store<T>, &Component) -> Result<Instance>,
18    ) -> wasmtime::Result<(Vec<u8>, Vec<Val>)> {
19        let (cx, instrumented_wasm) = self.instrument_component(wasm)?;
20
21        #[cfg(feature = "wasmprinter")]
22        log::debug!(
23            "instrumented wasm: {}",
24            wasmprinter::print_bytes(&instrumented_wasm).to_wasmtime_result()?,
25        );
26
27        let engine = store.engine();
28        let component = Component::new(engine, &instrumented_wasm)
29            .context("failed to compile the Wasm component")?;
30        let (index, args, mut rets) = self.validate_component_init_func(&component)?;
31
32        let instance = instantiate(store, &component).await?;
33        self.initialize_component(store, &instance, index, args, &mut rets)
34            .await?;
35        let snap = self
36            .snapshot_component(cx, &mut WasmtimeWizerComponent { store, instance })
37            .await?;
38        Ok((snap, rets))
39    }
40
41    fn validate_component_init_func(
42        &self,
43        component: &Component,
44    ) -> wasmtime::Result<(ComponentExportIndex, Vec<Val>, Vec<Val>)> {
45        let init_func = self.get_init_func();
46
47        use wasmtime::component::wasm_wave::{untyped::UntypedFuncCall, wasm::WasmFunc};
48        use wasmtime::component::wit_parser::ItemName;
49        let (func_name, func_call) = if init_func.contains('(') {
50            let call = UntypedFuncCall::parse(init_func)
51                .with_context(|| format!("parsing `{init_func}` as wave function call"))?;
52            let item_name = call
53                .item_name()
54                .map_err(wasmtime::Error::from_anyhow)
55                .with_context(|| format!("parsing `{init_func}` as wave function call"))?;
56            (item_name, Some(call))
57        } else {
58            (
59                init_func
60                    .parse::<ItemName>()
61                    .map_err(wasmtime::Error::from_anyhow)
62                    .with_context(|| format!("parsing `{init_func}` as wit item name"))?,
63                None,
64            )
65        };
66
67        let (ty, index) = component
68            .get_export(None, func_name)
69            .ok_or_else(|| format_err!("the component does export the function `{init_func}`"))?;
70
71        let ty = match ty {
72            ComponentItem::ComponentFunc(ty) => ty,
73            _ => wasmtime::bail!("the component's `{init_func}` export is not a function",),
74        };
75
76        if let Some(func_call) = func_call {
77            let param_types = WasmFunc::params(&ty).collect::<Vec<_>>();
78            let param_vals = func_call.to_wasm_params(&param_types).with_context(|| {
79                format!("parsing `{init_func}` params as types {param_types:?}")
80            })?;
81            Ok((
82                index,
83                param_vals,
84                vec![Val::Bool(false); ty.results().len()],
85            ))
86        } else {
87            if ty.params().len() != 0 || ty.results().len() != 0 {
88                wasmtime::bail!(
89                    "the component's `{init_func}` function export does not have type `[] -> []`",
90                );
91            }
92            Ok((index, vec![], vec![]))
93        }
94    }
95
96    async fn initialize_component<T: Send>(
97        &self,
98        store: &mut Store<T>,
99        instance: &Instance,
100        index: ComponentExportIndex,
101        args: Vec<Val>,
102        rets: &mut Vec<Val>,
103    ) -> wasmtime::Result<()> {
104        let init_func = instance
105            .get_func(&mut *store, index)
106            .expect("checked by `validate_init_func`");
107        init_func
108            .call_async(&mut *store, &args, rets)
109            .await
110            .with_context(|| format!("the initialization function trapped"))?;
111        Ok(())
112    }
113}
114
115/// Implementation of [`ComponentInstanceState`] backed by Wasmtime.
116pub struct WasmtimeWizerComponent<'a, T: 'static> {
117    /// The Wasmtime-based store that owns the `instance` field.
118    pub store: &'a mut Store<T>,
119    /// The instance that this will load state from.
120    pub instance: Instance,
121}
122
123impl<T: Send> WasmtimeWizerComponent<'_, T> {
124    async fn call_func<R, R2>(
125        &mut self,
126        instance: &str,
127        func: &str,
128        use_ret: impl FnOnce(&mut Store<T>, R) -> R2,
129    ) -> R2
130    where
131        R: Lift + 'static,
132    {
133        log::debug!("invoking {instance}#{func}");
134        let (_, instance_export) = self
135            .instance
136            .get_export(&mut *self.store, None, instance)
137            .unwrap();
138        let (_, func_export) = self
139            .instance
140            .get_export(&mut *self.store, Some(&instance_export), func)
141            .unwrap();
142        let func = self
143            .instance
144            .get_typed_func::<(), (R,)>(&mut *self.store, func_export)
145            .unwrap();
146        let ret = func.call_async(&mut *self.store, ()).await.unwrap().0;
147        use_ret(&mut *self.store, ret)
148    }
149}
150
151impl<T: Send> ComponentInstanceState for WasmtimeWizerComponent<'_, T> {
152    async fn call_func_ret_list_u8(
153        &mut self,
154        instance: &str,
155        func: &str,
156        contents: impl FnOnce(&[u8]) + Send,
157    ) {
158        self.call_func(instance, func, |store, list: WasmList<u8>| {
159            contents(list.as_le_slice(&store));
160        })
161        .await
162    }
163
164    async fn call_func_ret_s32(&mut self, instance: &str, func: &str) -> i32 {
165        self.call_func(instance, func, |_, r| r).await
166    }
167
168    async fn call_func_ret_s64(&mut self, instance: &str, func: &str) -> i64 {
169        self.call_func(instance, func, |_, r| r).await
170    }
171
172    async fn call_func_ret_f32(&mut self, instance: &str, func: &str) -> u32 {
173        self.call_func(instance, func, |_, r: f32| r.to_bits())
174            .await
175    }
176
177    async fn call_func_ret_f64(&mut self, instance: &str, func: &str) -> u64 {
178        self.call_func(instance, func, |_, r: f64| r.to_bits())
179            .await
180    }
181}