Skip to main content

auto_wasi/
lib.rs

1#![warn(missing_docs)]
2//! Wrapper around [`wasmtime-wasi`](https://docs.rs/wasmtime-wasi) that automatically detects the WASI version
3//! used by the module.
4//!
5//! # Example
6//! 
7//! ```rust
8//! # use auto_wasi::*;
9//! # use wasmtime::*;
10//! # use wasmtime_wasi::*;
11//! # fn test() -> anyhow::Result<()> {
12//! let wat = r#"
13//! (module
14//!     (type $empty (func))
15//!     ;; In the real world this would be an actual wasi import,
16//!     ;; but this crate only checks the module name.
17//!     (import "wasi_snapshot_preview1" "" (func (type $empty)))
18//!  )
19//! "#;
20//! let store = Store::default();
21//! let ctx = WasiCtx::new(std::env::args())?;
22//!
23//! let wasm = wat::parse_str(wat)?;
24//! let wasi = AutoWasi::detect(&store, ctx, wasm)?;
25//! # Ok(()) }
26//! ```
27use anyhow::Result;
28use wasi_common::WasiCtx;
29use wasmparser::{Parser, Payload};
30use wasmtime::{Func, Linker, Store};
31
32/// An instantiated instance of the wasi exports.
33///
34/// This represents a wasi module which can be used to instantiate other wasm modules.
35/// This structure exports all that various fields of the wasi instance as fields which can be used to implement your own instantiation logic, if necessary.
36/// Additionally [`AutoWasi::get_export`](crate::AutoWasi::get_export) can be used to do name-based resolution.
37pub enum AutoWasi {
38    /// WASI imports for the old `wasi_unstable` import module.
39    Snapshot0(wasmtime_wasi::old::snapshot_0::Wasi),
40    /// WASI imports for the current `wasi_snapshot_preview1` import module.
41    Snapshot1(wasmtime_wasi::Wasi),
42}
43
44impl AutoWasi {
45    /// Creates a new [`AutoWasi`](crate::AutoWasi) that allows for linking from the detected
46    /// wasi version.
47    pub fn detect<T: AsRef<[u8]>>(store: &Store, ctx: WasiCtx, binary: T) -> Result<Self> {
48        let version = WasiVersion::detect(binary)?;
49        Ok(Self::new(store, ctx, version))
50    }
51
52    /// Creates a new [`AutoWasi`](crate::AutoWasi) that allows for linking from the provided
53    /// [`WasiVersion`](crate::WasiVersion).
54    pub fn new(store: &Store, ctx: WasiCtx, version: WasiVersion) -> Self {
55        match version {
56            WasiVersion::Snapshot0 => {
57                let wasi = wasmtime_wasi::old::snapshot_0::Wasi::new(&store, ctx);
58                Self::Snapshot0(wasi)
59            }
60            WasiVersion::Snapshot1 => {
61                let wasi = wasmtime_wasi::Wasi::new(&store, ctx);
62                Self::Snapshot1(wasi)
63            }
64        }
65    }
66
67    /// Looks up a field called name in this structure, returning it if found.
68    /// This is often useful when instantiating a wasmtime instance where name resolution often happens with strings.
69    pub fn get_export(&self, name: &str) -> Option<&Func> {
70        match self {
71            Self::Snapshot0(wasi) => wasi.get_export(name),
72            Self::Snapshot1(wasi) => wasi.get_export(name),
73        }
74    }
75
76    /// Adds all instance items to the specified Linker.
77    pub fn add_to_linker(&self, linker: &mut Linker) -> Result<()> {
78        match self {
79            Self::Snapshot0(wasi) => wasi.add_to_linker(linker),
80            Self::Snapshot1(wasi) => wasi.add_to_linker(linker),
81        }
82    }
83}
84
85/// The version of WASI that a binary relies on.
86#[derive(Debug, Clone, PartialEq, Eq)]
87pub enum WasiVersion {
88    /// Called `wasi_unstable` in binaries.
89    Snapshot0,
90    /// Called `wasi_snapshot_preview1` in binaries.
91    Snapshot1,
92}
93
94impl WasiVersion {
95    /// Detects the WASI version used by the binary, defaults to the latest.
96    pub fn detect<T: AsRef<[u8]>>(binary: T) -> Result<Self> {
97        for payload in Parser::new(0).parse_all(binary.as_ref()) {
98            match payload? {
99                Payload::ImportSection(reader) => {
100                    for import in reader {
101                        if import?.module == "wasi_unstable" {
102                            return Ok(Self::Snapshot0);
103                        }
104                    }
105                }
106                _ => {}
107            }
108        }
109
110        Ok(Self::default())
111    }
112}
113
114impl Default for WasiVersion {
115    fn default() -> Self {
116        Self::Snapshot1
117    }
118}