From ddd23000106853a62a0159b00a8f7fbb509c0917 Mon Sep 17 00:00:00 2001 From: XAMPPRocky <4464295+XAMPPRocky@users.noreply.github.com> Date: Wed, 11 Dec 2019 19:46:45 +0100 Subject: [PATCH] Document Callable, Trap, HostRef, and ValType (#693) --- crates/api/src/callable.rs | 77 ++++++++++++++++++++++++++++++++++++++ crates/api/src/ref.rs | 31 +++++++++++++++ crates/api/src/trap.rs | 19 ++++++++-- crates/api/src/types.rs | 19 ++++++++-- crates/api/tests/traps.rs | 2 +- 5 files changed, 140 insertions(+), 8 deletions(-) diff --git a/crates/api/src/callable.rs b/crates/api/src/callable.rs index 84026ca3d5..c06b76d62b 100644 --- a/crates/api/src/callable.rs +++ b/crates/api/src/callable.rs @@ -9,7 +9,84 @@ use wasmtime_environ::ir; use wasmtime_jit::InstanceHandle; use wasmtime_runtime::Export; +/// A trait representing a function that can be imported and called from inside +/// WebAssembly. +/// # Example +/// ``` +/// use wasmtime::{HostRef, Val}; +/// +/// struct TimesTwo; +/// +/// impl wasmtime::Callable for TimesTwo { +/// fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef> { +/// let mut value = params[0].unwrap_i32(); +/// value *= 2; +/// results[0] = value.into(); +/// +/// Ok(()) +/// } +/// } +/// +/// # fn main () -> Result<(), Box> { +/// // Simple module that imports our host function ("times_two") and re-exports +/// // it as "run". +/// let binary = wat::parse_str(r#" +/// (module +/// (func $times_two (import "" "times_two") (param i32) (result i32)) +/// (func +/// (export "run") +/// (param i32) +/// (result i32) +/// (local.get 0) +/// (call $times_two)) +/// ) +/// "#)?; +/// +/// // Initialise environment and our module. +/// let engine = HostRef::new(wasmtime::Engine::default()); +/// let store = HostRef::new(wasmtime::Store::new(&engine)); +/// let module = HostRef::new(wasmtime::Module::new(&store, &binary)?); +/// +/// // Define the type of the function we're going to call. +/// let times_two_type = wasmtime::FuncType::new( +/// // Parameters +/// Box::new([wasmtime::ValType::I32]), +/// // Results +/// Box::new([wasmtime::ValType::I32]) +/// ); +/// +/// // Build a reference to the "times_two" function that can be used. +/// let times_two_function = HostRef::new( +/// wasmtime::Func::new(&store, times_two_type, std::rc::Rc::new(TimesTwo)) +/// ); +/// +/// // Create module instance that imports our function +/// let instance = wasmtime::Instance::new( +/// &store, +/// &module, +/// &[times_two_function.into()] +/// )?; +/// +/// // Get "run" function from the exports. +/// let run_function = instance.exports()[0].func().unwrap(); +/// +/// // Borrow and call "run". Returning any error message from Wasm as a string. +/// let original = 5i32; +/// let results = run_function +/// .borrow() +/// .call(&[original.into()]) +/// .map_err(|trap| trap.borrow().to_string())?; +/// +/// // Compare that the results returned matches what we expect. +/// assert_eq!(original * 2, results[0].unwrap_i32()); +/// # Ok(()) +/// # } +/// ``` pub trait Callable { + /// What is called when the function is invoked in WebAssembly. + /// `params` is an immutable list of parameters provided to the function. + /// `results` is mutable list of results to be potentially set by your + /// function. Produces a `Trap` if the function encounters any errors. fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef>; } diff --git a/crates/api/src/ref.rs b/crates/api/src/ref.rs index c6010c0624..52f14e6a83 100644 --- a/crates/api/src/ref.rs +++ b/crates/api/src/ref.rs @@ -46,14 +46,19 @@ impl Drop for AnyAndHostInfo { #[derive(Clone)] pub struct OtherRef(Rc>); +/// Represents an opaque reference to any data within WebAssembly. #[derive(Clone)] pub enum AnyRef { + /// A reference to no data. Null, + /// A reference to data stored internally in `wasmtime`. Ref(InternalRef), + /// A reference to data located outside of `wasmtime`. Other(OtherRef), } impl AnyRef { + /// Creates a new instance of `AnyRef` from `Box`. pub fn new(data: Box) -> Self { let info = AnyAndHostInfo { any: data, @@ -62,10 +67,14 @@ impl AnyRef { AnyRef::Other(OtherRef(Rc::new(RefCell::new(info)))) } + /// Creates a `Null` reference. pub fn null() -> Self { AnyRef::Null } + /// Returns the data stored in the reference if available. + /// # Panics + /// Panics if the variant isn't `AnyRef::Other`. pub fn data(&self) -> cell::Ref> { match self { AnyRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any), @@ -73,6 +82,8 @@ impl AnyRef { } } + /// Returns true if the two `AnyRef`'s point to the same value (not just + /// values that compare as equal). pub fn ptr_eq(&self, other: &AnyRef) -> bool { match (self, other) { (AnyRef::Null, AnyRef::Null) => true, @@ -84,6 +95,9 @@ impl AnyRef { } } + /// Returns a mutable reference to the host information if available. + /// # Panics + /// Panics if `AnyRef` is already borrowed or `AnyRef` is `Null`. pub fn host_info(&self) -> Option>> { match self { AnyRef::Null => panic!("null"), @@ -98,6 +112,9 @@ impl AnyRef { } } + /// Sets the host information for an `AnyRef`. + /// # Panics + /// Panics if `AnyRef` is already borrowed or `AnyRef` is `Null`. pub fn set_host_info(&self, info: Option>) { match self { AnyRef::Null => panic!("null"), @@ -133,9 +150,11 @@ impl Drop for ContentBox { } } +/// Represents a piece of data located in the host environment. pub struct HostRef(Rc>>); impl HostRef { + /// Creates a new `HostRef` from `T`. pub fn new(item: T) -> HostRef { let anyref_data: Weak> = Weak::new(); let content = ContentBox { @@ -146,18 +165,30 @@ impl HostRef { HostRef(Rc::new(RefCell::new(content))) } + /// Immutably borrows the wrapped data. + /// # Panics + /// Panics if the value is currently mutably borrowed. pub fn borrow(&self) -> cell::Ref { cell::Ref::map(self.0.borrow(), |b| &b.content) } + /// Mutably borrows the wrapped data. + /// # Panics + /// Panics if the `HostRef` is already borrowed. pub fn borrow_mut(&self) -> cell::RefMut { cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.content) } + /// Returns true if the two `HostRef`'s point to the same value (not just + /// values that compare as equal). pub fn ptr_eq(&self, other: &HostRef) -> bool { Rc::ptr_eq(&self.0, &other.0) } + /// Returns an opaque reference to the wrapped data in the form of + /// an `AnyRef`. + /// # Panics + /// Panics if `HostRef` is already mutably borrowed. pub fn anyref(&self) -> AnyRef { let r = self.0.borrow_mut().anyref_data.upgrade(); if let Some(r) = r { diff --git a/crates/api/src/trap.rs b/crates/api/src/trap.rs index 77628138bd..dff98354d6 100644 --- a/crates/api/src/trap.rs +++ b/crates/api/src/trap.rs @@ -1,5 +1,7 @@ use thiserror::Error; +/// A struct representing an aborted instruction execution, with a message +/// indicating the cause. #[derive(Error, Debug)] #[error("Wasm trap: {message}")] pub struct Trap { @@ -7,14 +9,25 @@ pub struct Trap { } impl Trap { - pub fn new(message: String) -> Trap { - Trap { message } + /// Creates a new `Trap` with `message`. + /// # Example + /// ``` + /// let trap = wasmtime::Trap::new("unexpected error"); + /// assert_eq!("unexpected error", trap.message()); + /// ``` + pub fn new>(message: I) -> Trap { + Self { + message: message.into(), + } } + /// Create a `Trap` without defining a message for the trap. Mostly useful + /// for prototypes and tests. pub fn fake() -> Trap { - Trap::new("TODO trap".to_string()) + Self::new("TODO trap") } + /// Returns a reference the `message` stored in `Trap`. pub fn message(&self) -> &str { &self.message } diff --git a/crates/api/src/types.rs b/crates/api/src/types.rs index 2084985f87..36fde8ff20 100644 --- a/crates/api/src/types.rs +++ b/crates/api/src/types.rs @@ -39,7 +39,7 @@ impl Limits { self.min } - /// Returs the maximum amount for these limits, if specified. + /// Returns the maximum amount for these limits, if specified. pub fn max(&self) -> Option { self.max } @@ -47,18 +47,28 @@ impl Limits { // Value Types +/// A list of all possible value types in WebAssembly. #[derive(Debug, Clone, Eq, PartialEq)] pub enum ValType { + /// Signed 32 bit integer. I32, + /// Signed 64 bit integer. I64, + /// Floating point 32 bit integer. F32, + /// Floating point 64 bit integer. F64, + /// A 128 bit number. V128, + /// A reference to opaque data in the Wasm instance. AnyRef, /* = 128 */ + /// A reference to a Wasm function. FuncRef, } impl ValType { + /// Returns true if `ValType` matches any of the numeric types. (e.g. `I32`, + /// `I64`, `F32`, `F64`). pub fn is_num(&self) -> bool { match self { ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => true, @@ -66,6 +76,7 @@ impl ValType { } } + /// Returns true if `ValType` matches either of the reference types. pub fn is_ref(&self) -> bool { match self { ValType::AnyRef | ValType::FuncRef => true, @@ -113,8 +124,8 @@ pub enum ExternType { macro_rules! accessors { ($(($variant:ident($ty:ty) $get:ident $unwrap:ident))*) => ($( - /// Attempt to return the underlying type of this external type, - /// returning `None` if it is a different type. + /// Attempt to return the underlying type of this external type, + /// returning `None` if it is a different type. pub fn $get(&self) -> Option<&$ty> { if let ExternType::$variant(e) = self { Some(e) @@ -123,7 +134,7 @@ macro_rules! accessors { } } - /// Returns the underlying descriptor of this [`ExternType`], panicking + /// Returns the underlying descriptor of this [`ExternType`], panicking /// if it is a different type. /// /// # Panics diff --git a/crates/api/tests/traps.rs b/crates/api/tests/traps.rs index 2e53d1b898..7ead7b322c 100644 --- a/crates/api/tests/traps.rs +++ b/crates/api/tests/traps.rs @@ -8,7 +8,7 @@ fn test_trap_return() -> Result<(), String> { impl Callable for HelloCallback { fn call(&self, _params: &[Val], _results: &mut [Val]) -> Result<(), HostRef> { - Err(HostRef::new(Trap::new("test 123".into()))) + Err(HostRef::new(Trap::new("test 123"))) } }