diff --git a/Cargo.lock b/Cargo.lock index 8ab8b647b..886eb59e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,14 @@ version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" +[[package]] +name = "api-tests" +version = "0.16.2" +dependencies = [ + "wabt", + "wasmer", +] + [[package]] name = "arrayref" version = "0.3.6" @@ -1843,7 +1851,10 @@ dependencies = [ name = "wasmer" version = "0.16.2" dependencies = [ + "wasmer-clif-backend", + "wasmer-llvm-backend", "wasmer-runtime-core", + "wasmer-singlepass-backend", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e2d955ba1..ff1ae6889 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ wasmer-wasi-experimental-io-devices = { path = "lib/wasi-experimental-io-devices [workspace] members = [ "lib/api", + "lib/api-tests", "lib/clif-backend", "lib/singlepass-backend", "lib/runtime", diff --git a/lib/api-tests/Cargo.toml b/lib/api-tests/Cargo.toml new file mode 100644 index 000000000..03713b5c8 --- /dev/null +++ b/lib/api-tests/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "api-tests" +version = "0.16.2" +authors = ["The Wasmer Engineering Team "] +edition = "2018" +license = "MIT" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +wasmer = { version = "0.16.2", path = "../api" } + +[dev-dependencies] +wabt = "0.9.1" \ No newline at end of file diff --git a/lib/api-tests/src/lib.rs b/lib/api-tests/src/lib.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/lib/api-tests/src/lib.rs @@ -0,0 +1 @@ + diff --git a/lib/api-tests/tests/high_level_api.rs b/lib/api-tests/tests/high_level_api.rs new file mode 100644 index 000000000..db6cd46a0 --- /dev/null +++ b/lib/api-tests/tests/high_level_api.rs @@ -0,0 +1,33 @@ +static TEST_WAT: &str = r#" +(module + (table $test-table 2 anyfunc) + (export "test-table" (table $test-table)) + (export "ret_2" (func $ret_2)) + (export "ret_4" (func $ret_4)) + (elem (;0;) (i32.const 0) $ret_2) + (func $ret_2 (result i32) + i32.const 2) + (func $ret_4 (result i32) + i32.const 4) +) +"#; + +#[test] +fn it_works() { + use wasmer::{imports, CompiledModule, Func, Module, Table}; + let wasm = wabt::wat2wasm(TEST_WAT).unwrap(); + // TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be + // misleading, if so we may want to do something about it. + let module = Module::new(wasm).unwrap(); + let import_object = imports! {}; + let instance = module.instantiate(&import_object).unwrap(); + + let ret_2: Func<(), i32> = instance.exports_new().get("ret_2").unwrap(); + let ret_4: Func<(), i32> = instance.exports_new().get("ret_4").unwrap(); + + assert_eq!(ret_2.call(), Ok(2)); + assert_eq!(ret_4.call(), Ok(4)); + + let _test_table: Table = instance.exports_new().get("test-table").unwrap(); + // TODO: when table get is stablized test this +} diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml new file mode 100644 index 000000000..85ed576b9 --- /dev/null +++ b/lib/api/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "wasmer" +version = "0.16.2" +authors = ["The Wasmer Engineering Team "] +edition = "2018" +publish = true +description = "The high-level public API of the Wasmer WebAssembly runtime" +license = "MIT" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +wasmer-runtime-core = { version = "0.16.2", path = "../runtime-core" } + +[dependencies.wasmer-singlepass-backend] +path = "../singlepass-backend" +version = "0.16.2" +optional = true + +[dependencies.wasmer-llvm-backend] +path = "../llvm-backend" +optional = true + +[dependencies.wasmer-clif-backend] +path = "../clif-backend" +version = "0.16.2" +optional = true + +[features] +default = ["cranelift", "default-backend-cranelift"] + +singlepass = ["wasmer-singlepass-backend"] +llvm = ["wasmer-llvm-backend"] +cranelift = ["wasmer-clif-backend"] + +default-backend-singlepass = ["singlepass"] +default-backend-llvm = ["llvm"] +default-backend-cranelift = ["cranelift"] + +deterministic-execution = ["wasmer-singlepass-backend/deterministic-execution", "wasmer-runtime-core/deterministic-execution"] \ No newline at end of file diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs new file mode 100644 index 000000000..9ca45696c --- /dev/null +++ b/lib/api/src/lib.rs @@ -0,0 +1,185 @@ +#![deny( + dead_code, +// missing_docs, + nonstandard_style, + unused_imports, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] +// Aspirational. I hope to have no unsafe code in this crate. +#![forbid(unsafe_code)] +#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")] +#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")] + +//! TODO: Write high value, high-level API intro docs here +//! Intro/background information +//! +//! quick links to places in this document/other crates/standards etc. +//! +//! example code, link to projects using it +//! +//! more info, what to do if you run into problems + +/// Commonly used types and functions. +pub mod prelude { + pub use crate::module::*; + pub use wasmer_runtime_core::instance::{DynFunc, Instance}; + pub use wasmer_runtime_core::memory::Memory; + pub use wasmer_runtime_core::table::Table; + pub use wasmer_runtime_core::Func; + pub use wasmer_runtime_core::{func, imports}; +} + +pub mod module { + //! Types and functions for WebAssembly modules. + //! + //! # Usage + //! ## Create a Module + //! + //! ``` + //! ``` + //! + //! ## Get the exports from a Module + //! ``` + //! # use wasmer::*; + //! # fn get_exports(module: &Module) { + //! let exports: Vec = module.exports().collect(); + //! # } + //! ``` + // TODO: verify that this is the type we want to export, with extra methods on it + pub use wasmer_runtime_core::module::Module; + // should this be in here? + pub use wasmer_runtime_core::module::{ExportDescriptor, ExportKind, Import, ImportType}; + // TODO: implement abstract module API +} + +pub mod memory { + //! Types and functions for Wasm linear memory. + pub use wasmer_runtime_core::memory::{Atomically, Memory, MemoryView}; +} + +pub mod wasm { + //! Various types exposed by the Wasmer Runtime. + //! + //! TODO: Add index with links to sub sections + //! + //! # Globals + //! + //! # Tables + pub use wasmer_runtime_core::global::Global; + pub use wasmer_runtime_core::module::{ExportDescriptor, ExportKind, Import, ImportType}; + pub use wasmer_runtime_core::table::Table; + pub use wasmer_runtime_core::types::{ + FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type, Value, + }; +} + +pub mod import { + //! Types and functions for Wasm imports. + pub use wasmer_runtime_core::module::{Import, ImportType}; + pub use wasmer_runtime_core::{func, imports}; +} + +pub mod export { + //! Types and functions for Wasm exports. + pub use wasmer_runtime_core::module::{ExportDescriptor, ExportKind}; +} + +pub mod units { + //! Various unit types. + pub use wasmer_runtime_core::units::{Bytes, Pages}; +} + +pub mod types { + //! Types used in the Wasm runtime and conversion functions. + pub use wasmer_runtime_core::types::*; +} + +pub mod error { + //! Various error types returned by Wasmer APIs. + pub use wasmer_runtime_core::error::*; +} + +pub use prelude::*; + +/// Idea for generic trait; consider rename; it will need to be moved somewhere else +pub trait CompiledModule { + fn new(bytes: impl AsRef<[u8]>) -> error::CompileResult; + fn from_binary(bytes: impl AsRef<[u8]>) -> error::CompileResult; + fn from_binary_unchecked(bytes: impl AsRef<[u8]>) -> error::CompileResult; + fn from_file(file: impl AsRef) -> error::CompileResult; + + fn validate(bytes: impl AsRef<[u8]>) -> error::CompileResult<()>; +} + +use wasmer_runtime_core::backend::Compiler; + +/// Copied from runtime core; TODO: figure out what we want to do here +pub fn default_compiler() -> impl Compiler { + #[cfg(any( + all( + feature = "default-backend-llvm", + not(feature = "docs"), + any( + feature = "default-backend-cranelift", + feature = "default-backend-singlepass" + ) + ), + all( + not(feature = "docs"), + feature = "default-backend-cranelift", + feature = "default-backend-singlepass" + ) + ))] + compile_error!( + "The `default-backend-X` features are mutually exclusive. Please choose just one" + ); + + #[cfg(all(feature = "default-backend-llvm", not(feature = "docs")))] + use wasmer_llvm_backend::LLVMCompiler as DefaultCompiler; + + #[cfg(all(feature = "default-backend-singlepass", not(feature = "docs")))] + use wasmer_singlepass_backend::SinglePassCompiler as DefaultCompiler; + + #[cfg(any(feature = "default-backend-cranelift", feature = "docs"))] + use wasmer_clif_backend::CraneliftCompiler as DefaultCompiler; + + DefaultCompiler::new() +} + +// this implementation should be moved +impl CompiledModule for Module { + fn new(bytes: impl AsRef<[u8]>) -> error::CompileResult { + let bytes = bytes.as_ref(); + wasmer_runtime_core::compile_with(bytes, &default_compiler()) + } + + fn from_binary(_bytes: impl AsRef<[u8]>) -> error::CompileResult { + todo!("from_binary: how is this different from `new`?") + } + fn from_binary_unchecked(_bytes: impl AsRef<[u8]>) -> error::CompileResult { + todo!("from_binary_unchecked") + } + fn from_file(_file: impl AsRef) -> error::CompileResult { + todo!("from_file"); + /* + use std::fs; + use std::io::Read; + let path = file.as_ref(); + let mut f = + fs::File::open(path).map_err(|_| todo!("Current error enum can't handle this case"))?; + // TODO: ideally we can support a streaming compilation API and not have to read in the entire file + let mut bytes = vec![]; + f.read_to_end(&mut bytes) + .map_err(|_| todo!("Current error enum can't handle this case"))?; + + Module::from_binary(bytes.as_slice()) + */ + } + + fn validate(_bytes: impl AsRef<[u8]>) -> error::CompileResult<()> { + todo!("validate") + } +}