1use std::sync::Arc;
2
3use bytes::{BufMut, BytesMut};
4use wasmrs::{Frame, OperationList, WasmSocket};
5use wasmrs_host::{CallbackProvider, GuestExports, ProviderCallContext, WasiParams};
6use wasmtime::{Engine, Instance, Linker, Memory, Module, Store, TypedFunc};
7
8use super::Result;
9use crate::errors::Error;
10use crate::memory::write_bytes_to_memory;
11use crate::store::{new_store, ProviderStore};
12use crate::wasmrs_wasmtime::{self};
13
14#[allow(missing_debug_implementations)]
16pub struct WasmtimeEngineProvider {
17 module: Module,
18 engine: Arc<Engine>,
19 linker: Linker<ProviderStore>,
20 wasi_params: Option<WasiParams>,
21 pub(crate) epoch_deadlines: Option<EpochDeadlines>,
22}
23
24#[derive(Clone, Copy, Debug)]
25pub(crate) struct EpochDeadlines {
26 #[allow(dead_code)]
28 pub(crate) wasmrs_init: u64,
29
30 #[allow(dead_code)]
32 pub(crate) wasmrs_func: u64,
33}
34
35impl WasmtimeEngineProvider {
36 pub(crate) fn new_with_engine(buf: &[u8], engine: Engine, wasi_params: Option<WasiParams>) -> Result<Self> {
38 let module = Module::new(&engine, buf).map_err(Error::Module)?;
39
40 let mut linker: Linker<ProviderStore> = Linker::new(&engine);
41 wasmtime_wasi::add_to_linker(&mut linker, |s| s.wasi_ctx.as_mut().unwrap()).unwrap();
42
43 Ok(WasmtimeEngineProvider {
44 module,
45 engine: Arc::new(engine),
46 wasi_params,
47 linker,
48 epoch_deadlines: None,
49 })
50 }
51
52 pub fn new_context(
54 &self,
55 socket: Arc<WasmSocket>,
56 ) -> std::result::Result<WasmtimeCallContext, wasmrs_host::errors::Error> {
57 let store = new_store(&self.wasi_params, socket, &self.engine)
58 .map_err(|e| wasmrs_host::errors::Error::NewContext(e.to_string()))?;
59
60 WasmtimeCallContext::new(self.linker.clone(), &self.module, store)
61 .map_err(|e| wasmrs_host::errors::Error::InitFailed(e.into()))
62 }
63}
64
65#[allow(missing_debug_implementations)]
67pub struct WasmtimeCallContext {
68 guest_send: TypedFunc<i32, ()>,
69 memory: Memory,
70 store: Store<ProviderStore>,
71 instance: Instance,
72}
73
74impl WasmtimeCallContext {
75 pub(crate) fn new(
76 mut linker: Linker<ProviderStore>,
77 module: &Module,
78 mut store: Store<ProviderStore>,
79 ) -> Result<Self> {
80 wasmrs_wasmtime::add_to_linker(&mut linker)?;
81 let instance = linker.instantiate(&mut store, module).map_err(Error::Linker)?;
82
83 let guest_send = instance
84 .get_typed_func::<i32, ()>(&mut store, GuestExports::Send.as_ref())
85 .map_err(|_| crate::errors::Error::GuestSend)?;
86 let memory = instance.get_memory(&mut store, "memory").unwrap();
87
88 Ok(Self {
89 guest_send,
90 memory,
91 instance,
92 store,
93 })
94 }
95
96 pub fn send_frame(&mut self, req: Frame) -> std::result::Result<(), wasmrs::Error> {
98 let bytes = req.encode();
99 trace!(?bytes, "writing frame");
100
101 let buffer_len_bytes = wasmrs::util::to_u24_bytes(bytes.len() as u32);
102 let mut buffer = BytesMut::with_capacity(buffer_len_bytes.len() + bytes.len());
103 buffer.put(buffer_len_bytes);
104 buffer.put(bytes);
105
106 let start = self.store.data().guest_buffer.get_start();
107 let len = self.store.data().guest_buffer.get_size();
108
109 let written = write_bytes_to_memory(&mut self.store, self.memory, &buffer, start, len);
110
111 let _call = self.guest_send.call(&mut self.store, written as i32);
112
113 Ok(())
114 }
115
116 pub fn run_init(
118 &mut self,
119 host_buffer_size: u32,
120 guest_buffer_size: u32,
121 ) -> std::result::Result<(), wasmrs_host::errors::Error> {
122 if let Ok(start) = self
123 .instance
124 .get_typed_func(&mut self.store, GuestExports::Start.as_ref())
125 {
126 trace!("Calling tinygo _start method");
127 start
128 .call(&mut self.store, ())
129 .map_err(|e| wasmrs_host::errors::Error::InitFailed(e.into()))?;
130 }
131
132 let init: TypedFunc<(u32, u32, u32), ()> = self
133 .instance
134 .get_typed_func(&mut self.store, GuestExports::Init.as_ref())
135 .map_err(|_e| wasmrs_host::errors::Error::InitFailed(Box::new(Error::GuestInit)))?;
136 init
137 .call(&mut self.store, (host_buffer_size, guest_buffer_size, 128))
138 .map_err(|e| wasmrs_host::errors::Error::InitFailed(e.into()))?;
139
140 if let Ok(oplist) = self
141 .instance
142 .get_typed_func::<(), ()>(&mut self.store, GuestExports::OpListRequest.as_ref())
143 {
144 trace!("Calling operation list");
145 oplist.call(&mut self.store, ()).unwrap();
146 }
147 Ok(())
148 }
149}
150
151impl wasmrs::ModuleHost for WasmtimeCallContext {
152 fn write_frame(&mut self, req: Frame) -> std::result::Result<(), wasmrs::Error> {
154 self.send_frame(req)
155 }
156
157 fn get_export(&self, namespace: &str, operation: &str) -> std::result::Result<u32, wasmrs::Error> {
158 self
159 .store
160 .data()
161 .get_export(namespace, operation)
162 .map_err(|e| wasmrs::Error::OpList(e.to_string()))
163 }
164
165 fn get_import(&self, namespace: &str, operation: &str) -> std::result::Result<u32, wasmrs::Error> {
166 self
167 .store
168 .data()
169 .get_import(namespace, operation)
170 .map_err(|e| wasmrs::Error::OpList(e.to_string()))
171 }
172
173 fn get_operation_list(&self) -> OperationList {
174 self.store.data().op_list.lock().clone()
175 }
176}
177
178impl ProviderCallContext for WasmtimeCallContext {
179 fn init(
180 &mut self,
181 host_buffer_size: u32,
182 guest_buffer_size: u32,
183 ) -> std::result::Result<(), wasmrs_host::errors::Error> {
184 self.run_init(host_buffer_size, guest_buffer_size)
185 }
186}