use crate::abi::{decode, encode, PackType, Value};
use crate::interface_impl::InterfaceImpl;
use crate::metadata::TypeHash;
use crate::runtime::interceptor::CallInterceptor;
use crate::runtime::RuntimeError;
use std::future::Future;
use std::marker::PhantomData;
use std::sync::Arc;
use thiserror::Error;
use wasmtime::{Caller, Engine, Linker};
fn block_on_interceptor<F: Future>(fut: F) -> F::Output {
let handle = tokio::runtime::Handle::current();
tokio::task::block_in_place(|| handle.block_on(fut))
}
pub const INPUT_BUFFER_OFFSET: usize = 0;
pub const RESULT_PTR_OFFSET: usize = 16 * 1024;
pub const RESULT_LEN_OFFSET: usize = 16 * 1024 + 4;
pub const OUTPUT_BUFFER_OFFSET: usize = 16 * 1024;
pub const OUTPUT_BUFFER_CAPACITY: usize = 32 * 1024;
#[derive(Debug, Clone)]
pub struct HostFunctionError {
pub interface: String,
pub function: String,
pub kind: HostFunctionErrorKind,
}
impl std::fmt::Display for HostFunctionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"host function error in {}::{}: {}",
self.interface, self.function, self.kind
)
}
}
impl std::error::Error for HostFunctionError {}
#[derive(Debug, Clone)]
pub enum HostFunctionErrorKind {
MemoryRead(String),
Decode(String),
TypeConversion(String),
MemoryWrite(String),
Encode(String),
}
impl std::fmt::Display for HostFunctionErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MemoryRead(e) => write!(f, "memory read failed: {}", e),
Self::Decode(e) => write!(f, "decode failed: {}", e),
Self::TypeConversion(e) => write!(f, "type conversion failed: {}", e),
Self::MemoryWrite(e) => write!(f, "memory write failed: {}", e),
Self::Encode(e) => write!(f, "encode failed: {}", e),
}
}
}
pub type ErrorHandler = Arc<dyn Fn(&HostFunctionError) + Send + Sync>;
fn default_error_handler(err: &HostFunctionError) {
eprintln!("[composite] {}", err);
}
#[derive(Error, Debug)]
pub enum LinkerError {
#[error("Function registration failed: {0}")]
FunctionRegistration(String),
#[error("Memory error: {0}")]
MemoryError(String),
#[error("Encoding error: {0}")]
EncodingError(String),
#[error("Decoding error: {0}")]
DecodingError(String),
#[error("Type conversion error: {0}")]
ConversionError(String),
}
impl From<RuntimeError> for LinkerError {
fn from(e: RuntimeError) -> Self {
LinkerError::MemoryError(e.to_string())
}
}
pub struct Ctx<'a, T> {
caller: Caller<'a, T>,
}
impl<'a, T> Ctx<'a, T> {
pub fn new(caller: Caller<'a, T>) -> Self {
Self { caller }
}
pub fn data(&self) -> &T {
self.caller.data()
}
pub fn data_mut(&mut self) -> &mut T {
self.caller.data_mut()
}
pub fn caller(&self) -> &Caller<'a, T> {
&self.caller
}
pub fn caller_mut(&mut self) -> &mut Caller<'a, T> {
&mut self.caller
}
pub fn read_value(&mut self, ptr: i32, len: i32) -> Result<Value, LinkerError> {
let memory = self
.caller
.get_export("memory")
.and_then(|e| e.into_memory())
.ok_or_else(|| LinkerError::MemoryError("no memory export".into()))?;
let ptr = ptr as usize;
let len = len as usize;
let mut buffer = vec![0u8; len];
memory
.read(&self.caller, ptr, &mut buffer)
.map_err(|e| LinkerError::MemoryError(e.to_string()))?;
decode(&buffer).map_err(|e| LinkerError::DecodingError(e.to_string()))
}
pub fn write_value_at(
&mut self,
out_ptr: i32,
out_cap: i32,
value: &Value,
) -> Result<i32, LinkerError> {
let bytes = encode(value).map_err(|e| LinkerError::EncodingError(e.to_string()))?;
if bytes.len() > out_cap as usize {
return Err(LinkerError::MemoryError(format!(
"output buffer too small: need {} bytes, have {} capacity",
bytes.len(),
out_cap
)));
}
let memory = self
.caller
.get_export("memory")
.and_then(|e| e.into_memory())
.ok_or_else(|| LinkerError::MemoryError("no memory export".into()))?;
memory
.write(&mut self.caller, out_ptr as usize, &bytes)
.map_err(|e| LinkerError::MemoryError(e.to_string()))?;
Ok(bytes.len() as i32)
}
pub fn write_value(&mut self, value: &Value) -> Result<(i32, i32), LinkerError> {
let out_ptr = OUTPUT_BUFFER_OFFSET as i32;
let out_cap = OUTPUT_BUFFER_CAPACITY as i32;
let len = self.write_value_at(out_ptr, out_cap, value)?;
Ok((out_ptr, len))
}
pub fn read_string(&mut self, ptr: i32, len: i32) -> Result<String, LinkerError> {
let memory = self
.caller
.get_export("memory")
.and_then(|e| e.into_memory())
.ok_or_else(|| LinkerError::MemoryError("no memory export".into()))?;
let ptr = ptr as usize;
let len = len as usize;
let mut buffer = vec![0u8; len];
memory
.read(&self.caller, ptr, &mut buffer)
.map_err(|e| LinkerError::MemoryError(e.to_string()))?;
String::from_utf8(buffer).map_err(|e| LinkerError::DecodingError(e.to_string()))
}
}
pub struct HostLinkerBuilder<'a, T> {
linker: &'a mut Linker<T>,
engine: &'a Engine,
error_handler: Option<ErrorHandler>,
interceptor: Option<Arc<dyn CallInterceptor>>,
_marker: PhantomData<T>,
}
impl<'a, T> HostLinkerBuilder<'a, T> {
pub fn new(engine: &'a Engine, linker: &'a mut Linker<T>) -> Self {
Self {
linker,
engine,
error_handler: None,
interceptor: None,
_marker: PhantomData,
}
}
pub fn set_interceptor(&mut self, interceptor: Arc<dyn CallInterceptor>) -> &mut Self {
self.interceptor = Some(interceptor);
self
}
pub fn interceptor(&self) -> Option<&Arc<dyn CallInterceptor>> {
self.interceptor.as_ref()
}
pub fn on_error<F>(&mut self, handler: F) -> &mut Self
where
F: Fn(&HostFunctionError) + Send + Sync + 'static,
{
self.error_handler = Some(Arc::new(handler));
self
}
pub fn interface(&mut self, name: &str) -> Result<InterfaceBuilder<'_, 'a, T>, LinkerError> {
let error_handler = self.error_handler.clone();
let interceptor = self.interceptor.clone();
Ok(InterfaceBuilder {
linker: self,
module_name: name.to_string(),
error_handler,
interceptor,
})
}
pub fn interface_from_impl(
&mut self,
interface: &InterfaceImpl,
) -> Result<(InterfaceBuilder<'_, 'a, T>, TypeHash), LinkerError> {
let hash = interface.hash();
let builder = self.interface(interface.name())?;
Ok((builder, hash))
}
pub fn register_provider<P: HostFunctionProvider<T>>(
&mut self,
provider: &P,
) -> Result<&mut Self, LinkerError> {
provider.register(self)?;
Ok(self)
}
pub fn inner(&mut self) -> &mut Linker<T> {
self.linker
}
pub fn engine(&self) -> &Engine {
self.engine
}
}
pub struct InterfaceBuilder<'a, 'b, T> {
linker: &'a mut HostLinkerBuilder<'b, T>,
module_name: String,
error_handler: Option<ErrorHandler>,
interceptor: Option<Arc<dyn CallInterceptor>>,
}
impl<T: 'static> InterfaceBuilder<'_, '_, T> {
pub fn func_raw<Params, Results>(
&mut self,
name: &str,
func: impl wasmtime::IntoFunc<T, Params, Results>,
) -> Result<&mut Self, LinkerError> {
self.linker
.linker
.func_wrap(&self.module_name, name, func)
.map_err(|e| LinkerError::FunctionRegistration(e.to_string()))?;
Ok(self)
}
pub fn func_typed<P, R, F>(&mut self, name: &str, func: F) -> Result<&mut Self, LinkerError>
where
P: TryFrom<Value> + 'static,
<P as TryFrom<Value>>::Error: std::fmt::Debug,
R: Into<Value> + 'static,
F: Fn(&mut Ctx<'_, T>, P) -> R + Send + Sync + 'static,
{
let func = Arc::new(func);
let error_handler = self.error_handler.clone();
let interceptor = self.interceptor.clone();
let interface_name = self.module_name.clone();
let func_name = name.to_string();
self.linker
.linker
.func_wrap(
&self.module_name,
name,
move |caller: Caller<'_, T>,
in_ptr: i32,
in_len: i32,
out_ptr_ptr: i32,
out_len_ptr: i32|
-> i32 {
let func = func.clone();
let error_handler = error_handler.clone();
let interceptor = interceptor.clone();
let interface_name = interface_name.clone();
let func_name = func_name.clone();
let report = |kind: HostFunctionErrorKind| {
let error = HostFunctionError {
interface: interface_name.clone(),
function: func_name.clone(),
kind,
};
if let Some(handler) = &error_handler {
handler(&error);
} else {
default_error_handler(&error);
}
};
let write_output = |ctx: &mut Ctx<'_, T>, value: &Value| -> i32 {
let bytes = match encode(value) {
Ok(b) => b,
Err(e) => {
report(HostFunctionErrorKind::Encode(e.to_string()));
return -1;
}
};
let memory = match ctx
.caller
.get_export("memory")
.and_then(|e| e.into_memory())
{
Some(m) => m,
None => {
report(HostFunctionErrorKind::MemoryWrite(
"no memory export".to_string(),
));
return -1;
}
};
let data_offset = OUTPUT_BUFFER_OFFSET + 8;
if let Err(e) = memory.write(&mut ctx.caller, data_offset, &bytes) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
if let Err(e) = memory.write(
&mut ctx.caller,
out_ptr_ptr as usize,
&(data_offset as i32).to_le_bytes(),
) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
if let Err(e) = memory.write(
&mut ctx.caller,
out_len_ptr as usize,
&(bytes.len() as i32).to_le_bytes(),
) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
0
};
let mut ctx = Ctx::new(caller);
let input_value = match ctx.read_value(in_ptr, in_len) {
Ok(v) => v,
Err(e) => {
report(HostFunctionErrorKind::Decode(e.to_string()));
return -1;
}
};
if let Some(ref interceptor) = interceptor {
if let Some(recorded_output) = block_on_interceptor(
interceptor.before_import(&interface_name, &func_name, &input_value),
) {
block_on_interceptor(interceptor.after_import(
&interface_name,
&func_name,
&input_value,
&recorded_output,
));
return write_output(&mut ctx, &recorded_output);
}
}
let input_value_for_interceptor =
interceptor.as_ref().map(|_| input_value.clone());
let input: P = match P::try_from(input_value) {
Ok(p) => p,
Err(e) => {
report(HostFunctionErrorKind::TypeConversion(format!("{:?}", e)));
return -1;
}
};
let output: R = func(&mut ctx, input);
let output_value: Value = output.into();
if let Some(ref interceptor) = interceptor {
if let Some(ref iv) = input_value_for_interceptor {
block_on_interceptor(interceptor.after_import(
&interface_name,
&func_name,
iv,
&output_value,
));
}
}
write_output(&mut ctx, &output_value)
},
)
.map_err(|e| LinkerError::FunctionRegistration(e.to_string()))?;
Ok(self)
}
pub fn func_typed_result<P, R, E, F>(
&mut self,
name: &str,
func: F,
) -> Result<&mut Self, LinkerError>
where
P: TryFrom<Value> + 'static,
<P as TryFrom<Value>>::Error: std::fmt::Debug,
R: PackType + 'static,
E: PackType + 'static,
F: Fn(&mut Ctx<'_, T>, P) -> Result<R, E> + Send + Sync + 'static,
{
let func = Arc::new(func);
let error_handler = self.error_handler.clone();
let interceptor = self.interceptor.clone();
let interface_name = self.module_name.clone();
let func_name = name.to_string();
self.linker
.linker
.func_wrap(
&self.module_name,
name,
move |caller: Caller<'_, T>,
in_ptr: i32,
in_len: i32,
out_ptr_ptr: i32,
out_len_ptr: i32|
-> i32 {
let func = func.clone();
let error_handler = error_handler.clone();
let interceptor = interceptor.clone();
let interface_name = interface_name.clone();
let func_name = func_name.clone();
let report = |kind: HostFunctionErrorKind| {
let error = HostFunctionError {
interface: interface_name.clone(),
function: func_name.clone(),
kind,
};
if let Some(handler) = &error_handler {
handler(&error);
} else {
default_error_handler(&error);
}
};
let write_output = |ctx: &mut Ctx<'_, T>, value: &Value| -> i32 {
let bytes = match encode(value) {
Ok(b) => b,
Err(e) => {
report(HostFunctionErrorKind::Encode(e.to_string()));
return -1;
}
};
let memory = match ctx
.caller
.get_export("memory")
.and_then(|e| e.into_memory())
{
Some(m) => m,
None => {
report(HostFunctionErrorKind::MemoryWrite(
"no memory export".to_string(),
));
return -1;
}
};
let data_offset = OUTPUT_BUFFER_OFFSET + 8;
if let Err(e) = memory.write(&mut ctx.caller, data_offset, &bytes) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
if let Err(e) = memory.write(
&mut ctx.caller,
out_ptr_ptr as usize,
&(data_offset as i32).to_le_bytes(),
) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
if let Err(e) = memory.write(
&mut ctx.caller,
out_len_ptr as usize,
&(bytes.len() as i32).to_le_bytes(),
) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
0
};
let mut ctx = Ctx::new(caller);
let input_value = match ctx.read_value(in_ptr, in_len) {
Ok(v) => v,
Err(e) => {
report(HostFunctionErrorKind::Decode(e.to_string()));
return -1;
}
};
if let Some(ref interceptor) = interceptor {
if let Some(recorded_output) = block_on_interceptor(
interceptor.before_import(&interface_name, &func_name, &input_value),
) {
block_on_interceptor(interceptor.after_import(
&interface_name,
&func_name,
&input_value,
&recorded_output,
));
return write_output(&mut ctx, &recorded_output);
}
}
let input_value_for_interceptor =
interceptor.as_ref().map(|_| input_value.clone());
let input: P = match P::try_from(input_value) {
Ok(p) => p,
Err(e) => {
report(HostFunctionErrorKind::TypeConversion(format!("{:?}", e)));
return -1;
}
};
let result = func(&mut ctx, input);
let output_value: Value = match result {
Ok(value) => Value::Result {
ok_type: R::value_type(),
err_type: E::value_type(),
value: Ok(Box::new(value.into())),
},
Err(error) => Value::Result {
ok_type: R::value_type(),
err_type: E::value_type(),
value: Err(Box::new(error.into())),
},
};
if let Some(ref interceptor) = interceptor {
if let Some(ref iv) = input_value_for_interceptor {
block_on_interceptor(interceptor.after_import(
&interface_name,
&func_name,
iv,
&output_value,
));
}
}
write_output(&mut ctx, &output_value)
},
)
.map_err(|e| LinkerError::FunctionRegistration(e.to_string()))?;
Ok(self)
}
pub fn name(&self) -> &str {
&self.module_name
}
}
impl<T: Send + Clone + 'static> InterfaceBuilder<'_, '_, T> {
pub fn func_async<P, R, F, Fut>(
&mut self,
name: &str,
func: F,
) -> Result<&mut Self, LinkerError>
where
P: TryFrom<Value> + Send + 'static,
<P as TryFrom<Value>>::Error: std::fmt::Debug,
R: Into<Value> + Send + 'static,
F: Fn(AsyncCtx<T>, P) -> Fut + Send + Sync + 'static,
Fut: Future<Output = R> + Send + 'static,
{
let func = Arc::new(func);
let error_handler = self.error_handler.clone();
let interceptor = self.interceptor.clone();
let interface_name = self.module_name.clone();
let func_name = name.to_string();
self.linker
.linker
.func_wrap_async(
&self.module_name,
name,
move |mut caller: Caller<'_, T>,
(in_ptr, in_len, out_ptr_ptr, out_len_ptr): (i32, i32, i32, i32)| {
let func = func.clone();
let error_handler = error_handler.clone();
let interceptor = interceptor.clone();
let interface_name = interface_name.clone();
let func_name = func_name.clone();
let state = caller.data().clone();
Box::new(async move {
let report = |kind: HostFunctionErrorKind| {
let error = HostFunctionError {
interface: interface_name.clone(),
function: func_name.clone(),
kind,
};
if let Some(handler) = &error_handler {
handler(&error);
} else {
default_error_handler(&error);
}
};
let memory = match caller
.get_export("memory")
.and_then(|e| e.into_memory())
{
Some(m) => m,
None => {
report(HostFunctionErrorKind::MemoryRead(
"no memory export".to_string(),
));
return -1;
}
};
let mut buffer = vec![0u8; in_len as usize];
if let Err(e) = memory.read(&caller, in_ptr as usize, &mut buffer) {
report(HostFunctionErrorKind::MemoryRead(e.to_string()));
return -1;
}
let input_value = match decode(&buffer) {
Ok(v) => v,
Err(e) => {
report(HostFunctionErrorKind::Decode(e.to_string()));
return -1;
}
};
if let Some(ref interceptor) = interceptor {
if let Some(recorded_output) = interceptor.before_import(&interface_name, &func_name, &input_value).await {
interceptor.after_import(&interface_name, &func_name, &input_value, &recorded_output).await;
let bytes = match encode(&recorded_output) {
Ok(b) => b,
Err(e) => {
report(HostFunctionErrorKind::Encode(e.to_string()));
return -1;
}
};
let data_offset = OUTPUT_BUFFER_OFFSET + 8;
if let Err(e) = memory.write(&mut caller, data_offset, &bytes) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
if let Err(e) = memory.write(&mut caller, out_ptr_ptr as usize, &(data_offset as i32).to_le_bytes()) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
if let Err(e) = memory.write(&mut caller, out_len_ptr as usize, &(bytes.len() as i32).to_le_bytes()) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
return 0;
}
}
let input_value_for_interceptor = interceptor.as_ref().map(|_| input_value.clone());
let input: P = match P::try_from(input_value) {
Ok(p) => p,
Err(e) => {
report(HostFunctionErrorKind::TypeConversion(format!("{:?}", e)));
return -1;
}
};
let ctx = AsyncCtx::new(state);
let output: R = func(ctx, input).await;
let output_value: Value = output.into();
if let Some(ref interceptor) = interceptor {
if let Some(ref iv) = input_value_for_interceptor {
interceptor.after_import(&interface_name, &func_name, iv, &output_value).await;
}
}
let bytes = match encode(&output_value) {
Ok(b) => b,
Err(e) => {
report(HostFunctionErrorKind::Encode(e.to_string()));
return -1;
}
};
let data_offset = OUTPUT_BUFFER_OFFSET + 8;
if let Err(e) = memory.write(&mut caller, data_offset, &bytes) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
if let Err(e) = memory.write(&mut caller, out_ptr_ptr as usize, &(data_offset as i32).to_le_bytes()) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
if let Err(e) = memory.write(&mut caller, out_len_ptr as usize, &(bytes.len() as i32).to_le_bytes()) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
0 })
},
)
.map_err(|e| LinkerError::FunctionRegistration(e.to_string()))?;
Ok(self)
}
pub fn func_async_result<P, R, E, F, Fut>(
&mut self,
name: &str,
func: F,
) -> Result<&mut Self, LinkerError>
where
P: TryFrom<Value> + Send + 'static,
<P as TryFrom<Value>>::Error: std::fmt::Debug,
R: PackType + Send + 'static,
E: PackType + Send + 'static,
F: Fn(AsyncCtx<T>, P) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<R, E>> + Send + 'static,
{
let func = Arc::new(func);
let error_handler = self.error_handler.clone();
let interceptor = self.interceptor.clone();
let interface_name = self.module_name.clone();
let func_name = name.to_string();
self.linker
.linker
.func_wrap_async(
&self.module_name,
name,
move |mut caller: Caller<'_, T>,
(in_ptr, in_len, out_ptr_ptr, out_len_ptr): (i32, i32, i32, i32)| {
let func = func.clone();
let error_handler = error_handler.clone();
let interceptor = interceptor.clone();
let interface_name = interface_name.clone();
let func_name = func_name.clone();
let state = caller.data().clone();
Box::new(async move {
let report = |kind: HostFunctionErrorKind| {
let error = HostFunctionError {
interface: interface_name.clone(),
function: func_name.clone(),
kind,
};
if let Some(handler) = &error_handler {
handler(&error);
} else {
default_error_handler(&error);
}
};
let memory = match caller
.get_export("memory")
.and_then(|e| e.into_memory())
{
Some(m) => m,
None => {
report(HostFunctionErrorKind::MemoryRead(
"no memory export".to_string(),
));
return -1;
}
};
let mut buffer = vec![0u8; in_len as usize];
if let Err(e) = memory.read(&caller, in_ptr as usize, &mut buffer) {
report(HostFunctionErrorKind::MemoryRead(e.to_string()));
return -1;
}
let input_value = match decode(&buffer) {
Ok(v) => v,
Err(e) => {
report(HostFunctionErrorKind::Decode(e.to_string()));
return -1;
}
};
if let Some(ref interceptor) = interceptor {
if let Some(recorded_output) = interceptor.before_import(&interface_name, &func_name, &input_value).await {
interceptor.after_import(&interface_name, &func_name, &input_value, &recorded_output).await;
let bytes = match encode(&recorded_output) {
Ok(b) => b,
Err(e) => {
report(HostFunctionErrorKind::Encode(e.to_string()));
return -1;
}
};
let data_offset = OUTPUT_BUFFER_OFFSET + 8;
if let Err(e) = memory.write(&mut caller, data_offset, &bytes) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
if let Err(e) = memory.write(&mut caller, out_ptr_ptr as usize, &(data_offset as i32).to_le_bytes()) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
if let Err(e) = memory.write(&mut caller, out_len_ptr as usize, &(bytes.len() as i32).to_le_bytes()) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
return 0;
}
}
let input_value_for_interceptor = interceptor.as_ref().map(|_| input_value.clone());
let input: P = match P::try_from(input_value) {
Ok(p) => p,
Err(e) => {
report(HostFunctionErrorKind::TypeConversion(format!("{:?}", e)));
return -1;
}
};
let ctx = AsyncCtx::new(state);
let result = func(ctx, input).await;
let output_value: Value = match result {
Ok(value) => Value::Result {
ok_type: R::value_type(),
err_type: E::value_type(),
value: Ok(Box::new(value.into())),
},
Err(error) => Value::Result {
ok_type: R::value_type(),
err_type: E::value_type(),
value: Err(Box::new(error.into())),
},
};
if let Some(ref interceptor) = interceptor {
if let Some(ref iv) = input_value_for_interceptor {
interceptor.after_import(&interface_name, &func_name, iv, &output_value).await;
}
}
let bytes = match encode(&output_value) {
Ok(b) => b,
Err(e) => {
report(HostFunctionErrorKind::Encode(e.to_string()));
return -1;
}
};
let data_offset = OUTPUT_BUFFER_OFFSET + 8;
if let Err(e) = memory.write(&mut caller, data_offset, &bytes) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
if let Err(e) = memory.write(&mut caller, out_ptr_ptr as usize, &(data_offset as i32).to_le_bytes()) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
if let Err(e) = memory.write(&mut caller, out_len_ptr as usize, &(bytes.len() as i32).to_le_bytes()) {
report(HostFunctionErrorKind::MemoryWrite(e.to_string()));
return -1;
}
0 })
},
)
.map_err(|e| LinkerError::FunctionRegistration(e.to_string()))?;
Ok(self)
}
}
pub struct AsyncCtx<T> {
state: T,
}
impl<T> AsyncCtx<T> {
pub fn new(state: T) -> Self {
Self { state }
}
pub fn data(&self) -> &T {
&self.state
}
pub fn data_mut(&mut self) -> &mut T {
&mut self.state
}
pub fn into_inner(self) -> T {
self.state
}
}
pub trait HostFunctionProvider<T> {
fn register(&self, builder: &mut HostLinkerBuilder<'_, T>) -> Result<(), LinkerError>;
}
use crate::runtime::HostState;
pub struct DefaultHostProvider;
impl HostFunctionProvider<HostState> for DefaultHostProvider {
fn register(&self, builder: &mut HostLinkerBuilder<'_, HostState>) -> Result<(), LinkerError> {
builder
.interface("host")?
.func_raw(
"log",
|mut caller: Caller<'_, HostState>, ptr: i32, len: i32| {
let memory = caller
.get_export("memory")
.and_then(|e| e.into_memory())
.expect("memory export");
let ptr = ptr as usize;
let len = len as usize;
let mut buffer = vec![0u8; len];
memory.read(&caller, ptr, &mut buffer).expect("read memory");
if let Ok(msg) = String::from_utf8(buffer) {
caller.data().log_messages.lock().unwrap().push(msg);
}
},
)?
.func_raw("alloc", |caller: Caller<'_, HostState>, size: i32| -> i32 {
let mut offset = caller.data().alloc_offset.lock().unwrap();
let ptr = *offset;
*offset += size as usize;
*offset = (*offset + 7) & !7;
ptr as i32
})?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_interface_builder_creation() {
let engine = Engine::default();
let mut linker = Linker::<()>::new(&engine);
let mut builder = HostLinkerBuilder::new(&engine, &mut linker);
assert!(builder.interface("host").is_ok());
assert!(builder.interface("theater:simple/runtime").is_ok());
assert!(builder.interface("wasi:cli/args").is_ok());
}
#[test]
fn test_func_raw_registration() -> Result<(), LinkerError> {
let engine = Engine::default();
let mut linker = Linker::<()>::new(&engine);
let mut builder = HostLinkerBuilder::new(&engine, &mut linker);
builder
.interface("test")?
.func_raw("add", |_caller: Caller<'_, ()>, a: i32, b: i32| a + b)?;
Ok(())
}
struct TestProvider;
impl HostFunctionProvider<()> for TestProvider {
fn register(&self, builder: &mut HostLinkerBuilder<'_, ()>) -> Result<(), LinkerError> {
builder
.interface("test")?
.func_raw("noop", |_: Caller<'_, ()>| {})?;
Ok(())
}
}
#[test]
fn test_provider_registration() {
let engine = Engine::default();
let mut linker = Linker::<()>::new(&engine);
let mut builder = HostLinkerBuilder::new(&engine, &mut linker);
let result = builder.register_provider(&TestProvider);
assert!(result.is_ok());
}
}