wasmrs_testhost/
engine_provider.rs

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/// A wasmRS engine provider that encapsulates the Wasmtime WebAssembly runtime
15#[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  /// Deadline for wasmRS initialization code. Expressed in number of epoch ticks
27  #[allow(dead_code)]
28  pub(crate) wasmrs_init: u64,
29
30  /// Deadline for user-defined wasmRS function computation. Expressed in number of epoch ticks
31  #[allow(dead_code)]
32  pub(crate) wasmrs_func: u64,
33}
34
35impl WasmtimeEngineProvider {
36  /// Creates a new instance of a [WasmtimeEngineProvider] from a separately created [wasmtime::Engine].
37  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  /// Create a new call context
53  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/// Raw Wasmtime call context
66#[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  /// Send a raw wasmrs frame to the WebAssembly module
97  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  /// Run wasmrs init function.
117  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  /// Request-Response interaction model of RSocket.
153  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}