wadge_sys/
lib.rs

1use core::ffi::{c_char, c_void, CStr};
2use core::ptr::NonNull;
3use core::slice;
4
5use std::sync::{Arc, LazyLock, Mutex};
6
7use anyhow::{bail, ensure, Context as _};
8use tracing::{instrument, trace_span};
9use tracing_subscriber::EnvFilter;
10use wasmtime::component::{Resource, ResourceAny, Val};
11use wasmtime_cabish::{deref_arg, lift_params, lower_results};
12use wasmtime_wasi::WasiView;
13
14mod ffi;
15
16#[repr(C)]
17#[derive(Debug)]
18pub struct List<T> {
19    pub ptr: *const T,
20    pub len: usize,
21}
22
23static ENGINE: LazyLock<wasmtime::Engine> = LazyLock::new(wasmtime::Engine::default);
24
25#[repr(C)]
26#[derive(Debug)]
27pub struct Config {
28    pub wasm: List<u8>,
29}
30
31pub struct Instance {
32    instance: Mutex<wadge::Instance>,
33    subscriber: Arc<dyn tracing::Subscriber + Send + Sync + 'static>,
34}
35
36#[instrument(level = "trace")]
37fn instantiate(config: Config) -> anyhow::Result<Instance> {
38    let Config { wasm } = config;
39    ensure!(!wasm.ptr.is_null(), "`wasm_ptr` must not be null");
40    let wasm = unsafe { slice::from_raw_parts(wasm.ptr, wasm.len) };
41    let instance = wadge::instantiate(wadge::Config {
42        engine: ENGINE.clone(),
43        wasm,
44    })
45    .context("failed to instantiate component")?;
46    let subscriber = tracing_subscriber::fmt()
47        .without_time()
48        .with_env_filter(EnvFilter::from_env("WADGE_LOG"))
49        .finish();
50    Ok(Instance {
51        instance: instance.into(),
52        subscriber: Arc::new(subscriber),
53    })
54}
55
56#[instrument(level = "debug", ret(level = "debug"))]
57fn call(
58    instance_ptr: *mut c_void,
59    instance: *const c_char,
60    name: *const c_char,
61    args: *const *mut c_void,
62) -> anyhow::Result<()> {
63    let inst =
64        NonNull::new(instance_ptr.cast::<Instance>()).context("`instance_ptr` must not be null")?;
65    ensure!(!instance.is_null(), "`instance` must not be null");
66    ensure!(!name.is_null(), "`name` must not be null");
67    let instance = unsafe { CStr::from_ptr(instance) }
68        .to_str()
69        .context("`instance` is not valid UTF-8")?;
70    let name = unsafe { CStr::from_ptr(name) }
71        .to_str()
72        .context("`name` is not valid UTF-8")?;
73    let inst = unsafe { inst.as_ref() };
74    let _log = tracing::subscriber::set_default(Arc::clone(&inst.subscriber));
75    let Ok(mut inst) = inst.instance.lock() else {
76        bail!("failed to lock instance mutex")
77    };
78    let _span = trace_span!("call", "instance" = instance, "name" = name).entered();
79    if let Some(ty) = name.strip_prefix("[resource-drop]") {
80        let (rep, _) = deref_arg::<u32>(args)?;
81        let rep = unsafe { rep.read() };
82        let store = inst.store();
83        let res = WasiView::table(store.data_mut())
84            .delete::<ResourceAny>(Resource::new_own(rep))
85            .with_context(|| format!("failed to delete `{ty}` from table"))?;
86        res.resource_drop(store)
87            .with_context(|| format!("failed to drop `{ty}`"))?;
88    } else {
89        let mut func = inst
90            .func(instance, name)
91            .context("failed to lookup function")?;
92        let tys = func.params();
93        let (params, args) =
94            lift_params(func.store(), &tys, args).context("failed to lift parameters")?;
95        let results_ty = func.results();
96        let mut results = vec![Val::Bool(false); results_ty.len()];
97        func.call(&params, &mut results)?;
98        lower_results(func.store(), results, &results_ty, args)
99            .context("failed to lower results")?;
100    }
101    Ok(())
102}