diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3635277501..a2fd551571 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -201,7 +201,7 @@ jobs: - run: $CENTOS cargo build --release --bin wasmtime --bin wasm2obj shell: bash # Build `libwasmtime_api.so` - - run: $CENTOS cargo build --release --features wasm-c-api --manifest-path wasmtime-api/Cargo.toml + - run: $CENTOS cargo build --release --manifest-path wasmtime-api/Cargo.toml shell: bash # Test what we just built - run: $CENTOS cargo test --release --all --exclude lightbeam --exclude wasmtime-wasi-c --exclude wasmtime-py --exclude wasmtime-api diff --git a/README.md b/README.md index 2ca99c5493..8e82aa6db8 100644 --- a/README.md +++ b/README.md @@ -35,34 +35,24 @@ of ongoing research. Additional goals for Wasmtime include: - Support a variety of host APIs (not just WASI), with fast calling sequences, and develop proposals for additional API modules to be part of WASI. - - Implement the [proposed WebAssembly C API]. - - Facilitate testing, experimentation, and development around the [Cranelift] and - [Lightbeam] JITs. + - Facilitate development and testing around the [Cranelift] and [Lightbeam] JITs, + and other WebAssembly execution strategies. - Develop a native ABI used for compiling WebAssembly suitable for use in both JIT and AOT to native object files. -[proposed WebAssembly C API]: https://github.com/rossberg/wasm-c-api [Cranelift]: https://github.com/CraneStation/cranelift -[Lightbeam]: https://github.com/CraneStation/lightbeam +[Lightbeam]: https://github.com/CraneStation/wasmtime/tree/master/lightbeam #### Including Wasmtime in your project -Wasmtime exposes an API for JIT compilation through the `wasmtime-jit` subcrate, which depends on `wasmtime-environ` and `wasmtime-runtime` for the ABI and runtime support respectively. However, this API is not documented and subject to change. Please use at your own risk! -Build the individual crates as such: +Wasmtime exposes an API for embedding as a library through the `wasmtime-api` subcrate, +which contains both a [high-level and safe Rust API], as well as a C-compatible API +compatible with the [proposed WebAssembly C API]. -``` -cargo build --package wasmtime-jit -``` +For more information, see the [Rust API embedding chapter] of the Wasmtime documentation. -Wasmtime does not currently publish these crates on crates.io. They may be included as a git dependency, like this: - -```toml -[dependencies] -wasmtime-environ = { git = "https://github.com/CraneStation/wasmtime", rev = "somecommithash" } -wasmtime-runtime = { git = "https://github.com/CraneStation/wasmtime", rev = "somecommithash" } -wasmtime-jit = { git = "https://github.com/CraneStation/wasmtime", rev = "somecommithash" } -``` - -All three crates must be specified as dependencies for `wasmtime-jit` to build correctly, at the moment. +[high-level and safe Rust API]: https://docs.rs/wasmtime-api/ +[proposed WebAssembly C API]: https://github.com/WebAssembly/wasm-c-api +[Rust API embedding chapter]: https://cranestation.github.io/wasmtime/embed-rust.html It's Wasmtime. diff --git a/docs/embed-rust.md b/docs/embed-rust.md index 34d837a1f5..3701871d0b 100644 --- a/docs/embed-rust.md +++ b/docs/embed-rust.md @@ -1,3 +1,99 @@ # Embedding Wasmtime in Rust -... more coming soon +This document shows how to embed Wasmtime using the Rust API, and run a simple +wasm program. + +# Create some wasm + +Let's create a simple WebAssembly file with a single exported function that returns an integer: + +```wat +(;; wat2wasm hello.wat -o $WASM_FILES/hello.wasm ;;) +(module + (func (export "answer") (result i32) + i32.const 42 + ) +) +``` + +# Create rust project + +``` +$ cargo new --bin wasmtime_hello +$ cd wasmtime_hello +$ cp $WASM_FILES/hello.wasm . +``` + +We will be using the wasmtime engine/API to run the wasm file, so we will add the dependency to `Cargo.toml`: + +``` +[dependencies] +wasmtime-api = { git = "https://github.com/CraneStation/wasmtime" } +``` + +It is time to add code to the `src/main.rs`. First, the engine and storage need to be activated: + +```rust +use wasmtime_api::*; + +let engine = HostRef::new(Engine::default()); +let store = HostRef::new(Store::new(&engine)); +``` + +The `HostRef` will be used a lot -- it is a "convenience" object to store and refer an object between the host and +the embedded environments. + +The `hello.wasm` can be read from the file system and provided to the `Module` object constructor as `&[u8]`: + +```rust +use std::fs::read; + +let hello_wasm = read("hello.wasm").expect("wasm file"); + +let module = HostRef::new(Module::new(&store, &hello_wasm).expect("wasm module")); +``` + +The module instance can now be created. Normally, you would provide exports, but in this case, there is none required: + +```rust +let instance = Instance::new(&store, &module, &[]).expect("wasm instance"); +``` + +Everything is set. If a WebAssembly module has a start function -- it was run. +The instance's exports can be used at this point. This wasm file has only one export, so we can index it directly: + +```rust +let answer_fn = instance.exports()[0].func().expect("answer function"); +``` + +The exported function can be called using the `call` method. Remember that in most of the cases, +a `HostRef<_>` object will be returned, so `borrow()` or `borrow_mut()` method has to be used to refer the +specific object. The exported "answer" function accepts no parameters and returns a single `i32` value. + +```rust +let result = answer_fn.borrow().call(&[]).expect("success"); +println!("Answer: {}", result[0].i32()); +``` + +The names of the WebAssembly module's imports and exports can be discovered by means of module's corresponding methods. + +# src/main.rs + +```rust +use std::fs::read; +use wasmtime_api::*; + +fn main() { + let engine = HostRef::new(Engine::default()); + let store = HostRef::new(Store::new(&engine)); + + let wasm = read("hello.wasm").expect("wasm file"); + + let module = HostRef::new(Module::new(&store, &wasm).expect("wasm module")); + let instance = Instance::new(&store, &module, &[]).expect("wasm instance"); + + let answer_fn = instance.exports()[0].func().expect("answer function"); + let result = answer_fn.borrow().call(&[]).expect("success"); + println!("Answer: {}", result[0].i32()); +} +``` diff --git a/wasmtime-api/Cargo.toml b/wasmtime-api/Cargo.toml index 8975cb2ba7..84131196c6 100644 --- a/wasmtime-api/Cargo.toml +++ b/wasmtime-api/Cargo.toml @@ -30,7 +30,6 @@ hashbrown = { version = "0.6.0", optional = true } [features] default = ["std"] std = ["cranelift-codegen/std", "cranelift-wasm/std", "wasmtime-environ/std", "wasmparser/std"] -wasm-c-api = [] core = ["hashbrown/nightly", "cranelift-codegen/core", "cranelift-wasm/core", "wasmtime-environ/core", "wasmparser/core"] [dev-dependencies] diff --git a/wasmtime-api/c-examples/Makefile b/wasmtime-api/c-examples/Makefile index 9fa9351753..d3ef772c46 100644 --- a/wasmtime-api/c-examples/Makefile +++ b/wasmtime-api/c-examples/Makefile @@ -46,9 +46,9 @@ WASM_CC_LIBS = $(error unsupported C++) # Compiler config ifeq (${WASMTIME_API_MODE},release) - CARGO_BUILD_FLAGS = --features "wasm-c-api" --release + CARGO_BUILD_FLAGS = --release else - CARGO_BUILD_FLAGS = --features "wasm-c-api" + CARGO_BUILD_FLAGS = endif ifeq (${C_COMP},clang) diff --git a/wasmtime-api/src/lib.rs b/wasmtime-api/src/lib.rs index 46866ea65d..866c17c8f8 100644 --- a/wasmtime-api/src/lib.rs +++ b/wasmtime-api/src/lib.rs @@ -14,7 +14,6 @@ mod trap; mod types; mod values; -#[cfg(feature = "wasm-c-api")] pub mod wasm; #[macro_use]