Skip to main content

elytra_cli/
wasm.rs

1use std::{cell::Cell, error::Error, fs::File, io::{Read, Write}, path::Path};
2use log::debug;
3
4use color_eyre::eyre::eyre;
5
6#[cfg(feature = "wasmtime")]
7use wasmtime::*;
8
9#[cfg(feature = "wasmi")]
10use wasmi::*;
11
12use crate::ElytraDevice;
13
14pub struct WasmDevice {
15    instance: Instance,
16    store: Store<HostState>,
17    log: Cell<Vec<([u8; 64], [u8; 64])>>
18}
19
20type HostState = u32;
21
22type Message = (u64, u64, u64, u64, u64, u64, u64, u64);
23
24fn pack64(value: [u8; 64]) -> Message {
25    let mut buf = [0u64; 8];
26    for (i, bytes) in value.chunks(8).enumerate() {
27        buf[i] = u64::from_be_bytes(bytes.try_into().unwrap());
28    }
29    buf.into()
30}
31
32impl WasmDevice {
33    pub fn new(file_path: &Path) -> Result<Self, Box<dyn Error>> {
34
35        let engine = Engine::default();
36        // Now we can compile the above Wasm module with the given Wasm source.
37
38        let mut file = File::open(file_path)?;
39        let mut bytes = Vec::new();
40        file.read_to_end(&mut bytes)?;
41        let module = Module::new(&engine, bytes)?;
42
43        for e in module.exports() {
44            debug!("WASM Export {}: {:#?}", e.name(), e.ty());
45        }
46
47        let mut store = Store::new(&engine, 0);
48        let linker = <Linker<HostState>>::new(&engine);
49
50
51        #[cfg(feature = "wasmtime")]
52        let instance = linker.instantiate(&mut store, &module)?;
53
54        #[cfg(feature = "wasmi")]
55        let instance = linker.instantiate_and_start(&mut store, &module)?;
56
57        Ok(Self {
58            instance, store, log: Cell::new(vec![])
59        })
60    }
61}
62
63impl ElytraDevice for WasmDevice {
64    fn send_command_raw(&mut self, bytes: [u8; 64]) -> Result<[u8; 64], Box<dyn std::error::Error>> {
65        let msg_in = pack64(bytes);
66        let send_fn = self.instance.get_typed_func::<Message, u32>(&mut self.store, "send")
67            .unwrap();
68        let recieve_fn = self.instance.get_typed_func::<u32, u64>(&mut self.store, "recieve")
69            .unwrap();
70        
71
72        let res_pack_count: u32 = send_fn.call(&mut self.store, msg_in)?;
73
74        if res_pack_count == 0 {
75            return Err(eyre!("Error response from WASM device"))?
76        }
77
78        let mut out_data = [0u8; 64];
79        let mut cursor = out_data.as_mut_slice();
80        for i in 0..res_pack_count {
81            let packed = recieve_fn.call(&mut self.store, i)?;
82            let _ = cursor.write(&packed.to_be_bytes())?;
83        }
84        
85        Ok(out_data)
86    }
87
88    fn log_chat(&mut self, bytes_out: [u8; 64], bytes_in: [u8; 64]) {
89        self.log.get_mut().push((bytes_out, bytes_in));
90    }
91    
92    fn get_log(&mut self) -> Vec<([u8; 64], [u8; 64])> {
93        self.log.replace(vec![])
94    }
95}