From 950dc2fbc6e66314c8c780eb59d07984548fc65b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 Mar 2020 16:41:17 -0500 Subject: [PATCH] Fill out API docs on `wasmtime::Module` (#1305) * Fill out API docs on `wasmtime::Module` Part of #1272 * Apply suggestions from code review Co-Authored-By: Nick Fitzgerald Co-authored-by: Nick Fitzgerald --- crates/api/src/module.rs | 243 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) diff --git a/crates/api/src/module.rs b/crates/api/src/module.rs index 80c29d9d69..627559b4cb 100644 --- a/crates/api/src/module.rs +++ b/crates/api/src/module.rs @@ -73,11 +73,63 @@ fn into_table_type(tt: wasmparser::TableType) -> TableType { /// representation. Instead you'll need to create an /// [`Instance`](crate::Instance) to interact with the wasm module. /// +/// Creating a `Module` currently involves compiling code, meaning that it can +/// be an expensive operation. All `Module` instances are compiled according to +/// the configuration in [`Config`], but typically they're JIT-compiled. If +/// you'd like to instantiate a module multiple times you can do so with +/// compiling the original wasm module only once with a single [`Module`] +/// instance. +/// /// ## Modules and `Clone` /// /// Using `clone` on a `Module` is a cheap operation. It will not create an /// entirely new module, but rather just a new reference to the existing module. /// In other words it's a shallow copy, not a deep copy. +/// +/// ## Examples +/// +/// There are a number of ways you can create a `Module`, for example pulling +/// the bytes from a number of locations. One example is loading a module from +/// the filesystem: +/// +/// ```no_run +/// # use wasmtime::*; +/// # fn main() -> anyhow::Result<()> { +/// let store = Store::default(); +/// let module = Module::from_file(&store, "path/to/foo.wasm")?; +/// # Ok(()) +/// # } +/// ``` +/// +/// You can also load the wasm text format if more convenient too: +/// +/// ```no_run +/// # use wasmtime::*; +/// # fn main() -> anyhow::Result<()> { +/// let store = Store::default(); +/// // Now we're using the WebAssembly text extension: `.wat`! +/// let module = Module::from_file(&store, "path/to/foo.wat")?; +/// # Ok(()) +/// # } +/// ``` +/// +/// And if you've already got the bytes in-memory you can use the +/// [`Module::new`] constructor: +/// +/// ```no_run +/// # use wasmtime::*; +/// # fn main() -> anyhow::Result<()> { +/// let store = Store::default(); +/// # let wasm_bytes: Vec = Vec::new(); +/// let module = Module::new(&store, &wasm_bytes)?; +/// +/// // It also works with the text format! +/// let module = Module::new(&store, "(module (func))")?; +/// # Ok(()) +/// # } +/// ``` +/// +/// [`Config`]: crate::Config #[derive(Clone)] pub struct Module { inner: Arc, @@ -143,6 +195,32 @@ impl Module { /// /// [binary]: https://webassembly.github.io/spec/core/binary/index.html /// [text]: https://webassembly.github.io/spec/core/text/index.html + /// + /// # Examples + /// + /// The `new` function can be invoked with a in-memory array of bytes: + /// + /// ```no_run + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// # let store = Store::default(); + /// # let wasm_bytes: Vec = Vec::new(); + /// let module = Module::new(&store, &wasm_bytes)?; + /// # Ok(()) + /// # } + /// ``` + /// + /// Or you can also pass in a string to be parsed as the wasm text + /// format: + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// # let store = Store::default(); + /// let module = Module::new(&store, "(module (func))")?; + /// # Ok(()) + /// # } + /// ``` pub fn new(store: &Store, bytes: impl AsRef<[u8]>) -> Result { #[cfg(feature = "wat")] let bytes = wat::parse_bytes(bytes.as_ref())?; @@ -166,6 +244,28 @@ impl Module { /// This is a convenience function that will read the `file` provided and /// pass the bytes to the [`Module::new`] function. For more information /// see [`Module::new`] + /// + /// # Examples + /// + /// ```no_run + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// let store = Store::default(); + /// let module = Module::from_file(&store, "./path/to/foo.wasm")?; + /// # Ok(()) + /// # } + /// ``` + /// + /// The `.wat` text format is also supported: + /// + /// ```no_run + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// # let store = Store::default(); + /// let module = Module::from_file(&store, "./path/to/foo.wat")?; + /// # Ok(()) + /// # } + /// ``` pub fn from_file(store: &Store, file: impl AsRef) -> Result { #[cfg(feature = "wat")] let wasm = wat::parse_file(file)?; @@ -182,6 +282,29 @@ impl Module { /// by this function. It's generally recommended to use [`Module::new`], /// but if it's required to not support the text format this function can be /// used instead. + /// + /// # Examples + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// # let store = Store::default(); + /// let wasm = b"\0asm\x01\0\0\0"; + /// let module = Module::from_binary(&store, wasm)?; + /// # Ok(()) + /// # } + /// ``` + /// + /// Note that the text format is **not** accepted by this function: + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// # let store = Store::default(); + /// assert!(Module::from_binary(&store, b"(module)").is_err()); + /// # Ok(()) + /// # } + /// ``` pub fn from_binary(store: &Store, binary: &[u8]) -> Result { Module::validate(store, binary)?; // Note that the call to `from_binary_unchecked` here should be ok @@ -278,18 +401,138 @@ impl Module { /// Returns identifier/name that this [`Module`] has. This name /// is used in traps/backtrace details. + /// + /// Note that most LLVM/clang/Rust-produced modules do not have a name + /// associated with them, but other wasm tooling can be used to inject or + /// add a name. + /// + /// # Examples + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// # let store = Store::default(); + /// let module = Module::new(&store, "(module $foo)")?; + /// assert_eq!(module.name(), Some("foo")); + /// + /// let module = Module::new(&store, "(module)")?; + /// assert_eq!(module.name(), None); + /// + /// let module = Module::new_with_name(&store, "(module)", "bar")?; + /// assert_eq!(module.name(), Some("bar")); + /// # Ok(()) + /// # } + /// ``` pub fn name(&self) -> Option<&str> { self.inner.names.module_name.as_deref() } /// Returns the list of imports that this [`Module`] has and must be /// satisfied. + /// + /// This function returns the list of imports that the wasm module has, but + /// only the types of each import. The type of each import is used to + /// typecheck the [`Instance::new`](crate::Instance::new) method's `imports` + /// argument. The arguments to that function must match up 1-to-1 with the + /// entries in the array returned here. + /// + /// The imports returned reflect the order of the imports in the wasm module + /// itself, and note that no form of deduplication happens. + /// + /// # Examples + /// + /// Modules with no imports return an empty list here: + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// # let store = Store::default(); + /// let module = Module::new(&store, "(module)")?; + /// assert_eq!(module.imports().len(), 0); + /// # Ok(()) + /// # } + /// ``` + /// + /// and modules with imports will have a non-empty list: + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// # let store = Store::default(); + /// let wat = r#" + /// (module + /// (import "host" "foo" (func)) + /// ) + /// "#; + /// let module = Module::new(&store, wat)?; + /// assert_eq!(module.imports().len(), 1); + /// let import = &module.imports()[0]; + /// assert_eq!(import.module(), "host"); + /// assert_eq!(import.name(), "foo"); + /// match import.ty() { + /// ExternType::Func(_) => { /* ... */ } + /// _ => panic!("unexpected import type!"), + /// } + /// # Ok(()) + /// # } + /// ``` pub fn imports(&self) -> &[ImportType] { &self.inner.imports } /// Returns the list of exports that this [`Module`] has and will be /// available after instantiation. + /// + /// This function will return the type of each item that will be returned + /// from [`Instance::exports`](crate::Instance::exports). Each entry in this + /// list corresponds 1-to-1 with that list, and the entries here will + /// indicate the name of the export along with the type of the export. + /// + /// # Examples + /// + /// Modules might not have any exports: + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// # let store = Store::default(); + /// let module = Module::new(&store, "(module)")?; + /// assert!(module.exports().is_empty()); + /// # Ok(()) + /// # } + /// ``` + /// + /// When the exports are not empty, you can inspect each export: + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// # let store = Store::default(); + /// let wat = r#" + /// (module + /// (func (export "foo")) + /// (memory (export "memory") 1) + /// ) + /// "#; + /// let module = Module::new(&store, wat)?; + /// assert_eq!(module.exports().len(), 2); + /// + /// let foo = &module.exports()[0]; + /// assert_eq!(foo.name(), "foo"); + /// match foo.ty() { + /// ExternType::Func(_) => { /* ... */ } + /// _ => panic!("unexpected export type!"), + /// } + /// + /// let memory = &module.exports()[1]; + /// assert_eq!(memory.name(), "memory"); + /// match memory.ty() { + /// ExternType::Memory(_) => { /* ... */ } + /// _ => panic!("unexpected export type!"), + /// } + /// # Ok(()) + /// # } + /// ``` pub fn exports(&self) -> &[ExportType] { &self.inner.exports }