Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add types and methods to provide updated API #1313

Merged
merged 22 commits into from
Mar 31, 2020
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
## **[Unreleased]**

- [#1320](https://github.com/wasmerio/wasmer/pull/1320) Change `custom_sections` field in `ModuleInfo` to be more standards compliant by allowing multiple custom sections with the same name. To get the old behavior with the new API, you can add `.last().unwrap()` to accesses. For example, `module_info.custom_sections["custom_section_name"].last().unwrap()`.
- [#1313](https://github.com/wasmerio/wasmer/pull/1313) Add new high-level public API through `wasmer` crate. Includes many updates including:
- Minor improvement: `imports!` macro now handles no trailing comma as well as a trailing comma in namespaces and between namespaces.
- New methods on `Module`: `exports`, `imports`, and `custom_sections`.
- New way to get exports from an instance with `let func_name: Func<i32, i64> = instance.exports.get("func_name");`.
- Improved `Table` APIs including `set` which now allows setting functions directly. TODO: update this more if `Table::get` gets made public in this PR
- TODO: finish the list of changes here
- [#1303](https://github.com/wasmerio/wasmer/pull/1303) NaN canonicalization for singlepass backend.
- [#1305](https://github.com/wasmerio/wasmer/pull/1305) Handle panics from DynamicFunc.
- [#1301](https://github.com/wasmerio/wasmer/pull/1301) Update supported stable Rust version to 1.41.1.
Expand Down
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "wasmer"
name = "wasmer-bin"
version = "0.16.2"
authors = ["The Wasmer Engineering Team <[email protected]>"]
edition = "2018"
Expand Down Expand Up @@ -27,6 +27,7 @@ fern = { version = "0.5", features = ["colored"], optional = true }
log = "0.4"
structopt = "0.3"
wabt = { version = "0.9.1", optional = true }
wasmer = { path = "lib/api" }
wasmer-clif-backend = { path = "lib/clif-backend", optional = true }
wasmer-singlepass-backend = { path = "lib/singlepass-backend", optional = true }
wasmer-middleware-common = { path = "lib/middleware-common" }
Expand All @@ -44,6 +45,8 @@ 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",
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm test-capi-ems
capi-test: test-capi

test-rest:
cargo test --release -p api-tests
cargo test --release -p wasmer-dev-utils
cargo test --release -p wasmer-interface-types
cargo test --release -p wasmer-kernel-loader
Expand Down
4 changes: 2 additions & 2 deletions examples/callback.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// This example demonstrates the use of callbacks: calling functions (Host and Wasm)
/// passed to us from the Wasm via hostcall
use wasmer_runtime::{compile_with, compiler_for_backend, func, imports, Backend, Ctx};
use wasmer_runtime::{compile_with, compiler_for_backend, func, imports, Backend, Ctx, Func};
use wasmer_runtime_core::{structures::TypedIndex, types::TableIndex};

static WASM: &'static str = "examples/callback-guest/callback-guest.wasm";
Expand Down Expand Up @@ -40,7 +40,7 @@ fn main() {
.instantiate(&imports)
.expect("failed to instantiate wasm module");

let entry_point = instance.func::<(u32, u32), u32>("main").unwrap();
let entry_point: Func<(u32, u32), u32> = instance.exports.get("main").unwrap();

entry_point.call(0, 0).expect("START");
}
9 changes: 6 additions & 3 deletions examples/parallel/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use rayon::prelude::*;
use wasmer_runtime::{compile_with, compiler_for_backend, func, imports, instantiate, Backend};
use wasmer_runtime::{
compile_with, compiler_for_backend, func, imports, instantiate, Backend, Func,
};
use wasmer_runtime_core::{
memory::ptr::{Array, WasmPtr},
vm::Ctx,
Expand Down Expand Up @@ -61,7 +63,8 @@ fn main() {
.clone()
.instantiate(&imports)
.expect("failed to instantiate wasm module");
let check_password = instance.func::<(u64, u64), u64>("check_password").unwrap();
let check_password: Func<(u64, u64), u64> =
instance.exports.get("check_password").unwrap();
let j = i * 10000;
let result = check_password.call(j, j + 10000).unwrap();
print!(".");
Expand Down Expand Up @@ -101,7 +104,7 @@ fn main() {
let instance =
instantiate(&wasm_bytes[..], &imports).expect("failed to instantiate wasm module");

let check_password = instance.func::<(u64, u64), u64>("check_password").unwrap();
let check_password: Func<(u64, u64), u64> = instance.exports.get("check_password").unwrap();

let mut out: Option<RetStr> = None;
for i in (0..=u64::max_value()).step_by(10000) {
Expand Down
4 changes: 2 additions & 2 deletions examples/plugin.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use wasmer_runtime::{compile, func, imports};
use wasmer_runtime::{compile, func, imports, Func};
use wasmer_runtime_core::vm::Ctx;
use wasmer_wasi::{
generate_import_object_for_version,
Expand Down Expand Up @@ -159,7 +159,7 @@ fn main() {
initialize(instance.context_mut());

// get a reference to the function "plugin_entrypoint" which takes an i32 and returns an i32
let entry_point = instance.func::<(i32), i32>("plugin_entrypoint").unwrap();
let entry_point: Func<i32, i32> = instance.exports.get("plugin_entrypoint").unwrap();
// call the "entry_point" function in WebAssembly with the number "2" as the i32 argument
let result = entry_point.call(2).expect("failed to execute plugin");
println!("result: {}", result);
Expand Down
4 changes: 2 additions & 2 deletions fuzz/fuzz_targets/validate_wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
#[macro_use]
extern crate libfuzzer_sys;

extern crate wasmer;
extern crate wasmer_bin;
extern crate wasmer_runtime_core;

use wasmer_runtime_core::backend::Features;

fuzz_target!(|data: &[u8]| {
let _ = wasmer::utils::is_wasm_binary(data);
let _ = wasmer_bin::utils::is_wasm_binary(data);
let _ = wasmer_runtime_core::validate_and_report_errors_with_features(
&data,
Features {
Expand Down
15 changes: 15 additions & 0 deletions lib/api-tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "api-tests"
version = "0.16.2"
authors = ["The Wasmer Engineering Team <[email protected]>"]
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"
1 change: 1 addition & 0 deletions lib/api-tests/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

211 changes: 211 additions & 0 deletions lib/api-tests/tests/high_level_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
static TEST_WAT: &str = r#"
(module
(import "env" "outside-global" (global $outside-global (mut i32)))
(import "env" "update-func" (func $update-func (param i32) (result i32)))
(table $test-table (export "test-table") 2 anyfunc)
(global $test-global (export "test-global") (mut i32) (i32.const 3))
(elem (;0;) (i32.const 0) $ret_2)
(func $ret_2 (export "ret_2") (result i32)
i32.const 2)
(func $ret_4 (export "ret_4") (result i32)
i32.const 4)
(func $set_test_global (export "set_test_global") (param i32)
(global.set $test-global
(local.get 0)))
(func $update-outside-global (export "update_outside_global")
(global.set $outside-global
(call $update-func (global.get $outside-global))))
)
"#;

fn append_custom_section(
wasm: &mut Vec<u8>,
custom_section_name: &str,
custom_section_contents: &[u8],
) {
wasm.reserve(
// 1 for custom section id, 5 for max length of custom section, 5 for max length of name
custom_section_name.len() + custom_section_contents.len() + 1 + 5 + 5,
);

wasm.push(0);

let name_length = custom_section_name.as_bytes().len() as u32;
let mut name_length_as_leb128 = vec![];
write_u32_leb128(&mut name_length_as_leb128, name_length);

let custom_section_length = (custom_section_contents.len()
+ custom_section_name.as_bytes().len()
+ name_length_as_leb128.len()) as u32;

let mut custom_section_length_as_leb128 = vec![];
write_u32_leb128(&mut custom_section_length_as_leb128, custom_section_length);

wasm.extend(&custom_section_length_as_leb128);
wasm.extend(&name_length_as_leb128);
wasm.extend(custom_section_name.as_bytes());
wasm.extend(custom_section_contents);
}

#[test]
fn custom_section_parsing_works() {
use wasmer::{CompiledModule, Module};
let wasm = {
let mut wasm = wabt::wat2wasm(TEST_WAT).unwrap();
append_custom_section(&mut wasm, "test_custom_section", b"hello, world!");
append_custom_section(&mut wasm, "test_custom_section", b"goodbye, world!");
append_custom_section(&mut wasm, "different_name", b"different value");
wasm
};

let module = Module::new(wasm).unwrap();

assert_eq!(
module.custom_sections("test_custom_section"),
Some(&[b"hello, world!".to_vec(), b"goodbye, world!".to_vec()][..])
);
}

#[test]
fn module_exports_are_ordered() {
use wasmer::{export, CompiledModule, Module};

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 exports = module.exports().collect::<Vec<_>>();
assert_eq!(
exports,
vec![
export::ExportDescriptor {
name: "test-table",
kind: export::ExportKind::Table,
},
export::ExportDescriptor {
name: "test-global",
kind: export::ExportKind::Global,
},
export::ExportDescriptor {
name: "ret_2",
kind: export::ExportKind::Function,
},
export::ExportDescriptor {
name: "ret_4",
kind: export::ExportKind::Function,
},
export::ExportDescriptor {
name: "set_test_global",
kind: export::ExportKind::Function,
},
export::ExportDescriptor {
name: "update_outside_global",
kind: export::ExportKind::Function,
},
]
);
}

#[test]
fn module_imports_are_correct() {
use wasmer::{CompiledModule, Module};

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();

// TODO: test this more later
let imports = module.imports();
assert_eq!(imports.len(), 2);
}

#[test]
fn module_can_be_instantiated() {
use wasmer::wasm::{Global, Value};
use wasmer::{func, imports, CompiledModule, Module};

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 outside_global = Global::new_mutable(Value::I32(15));
let import_object = imports! {
"env" => {
"update-func" => func!(|x: i32| x * 2),
"outside-global" => outside_global.clone(),
}
};
let instance = module.instantiate(&import_object);

assert!(instance.is_ok());
}

#[test]
fn exports_work() {
use wasmer::wasm::{Global, Value};
use wasmer::{func, 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 outside_global = Global::new_mutable(Value::I32(15));

let import_object = imports! {
"env" => {
"update-func" => func!(|x: i32| x * 2),
"outside-global" => outside_global.clone(),
}
};
let instance = module.instantiate(&import_object).unwrap();

let ret_2: Func<(), i32> = instance.exports.get("ret_2").unwrap();
let ret_4: Func<(), i32> = instance.exports.get("ret_4").unwrap();
let set_test_global: Func<i32> = instance.exports.get("set_test_global").unwrap();
let update_outside_global: Func = instance.exports.get("update_outside_global").unwrap();

assert_eq!(ret_2.call(), Ok(2));
assert_eq!(ret_4.call(), Ok(4));

let _test_table: Table = instance.exports.get("test-table").unwrap();
// TODO: when table get is stablized test this

let test_global: Global = instance.exports.get("test-global").unwrap();
// TODO: do we want to make global.get act like exports.get()?
assert_eq!(test_global.get(), Value::I32(3));
set_test_global.call(17).unwrap();
assert_eq!(test_global.get(), Value::I32(17));

assert_eq!(outside_global.get(), Value::I32(15));
update_outside_global.call().unwrap();
assert_eq!(outside_global.get(), Value::I32(15 * 2));
update_outside_global.call().unwrap();
assert_eq!(outside_global.get(), Value::I32(15 * 2 * 2));
}

// copied from Rust stdlib: https://doc.rust-lang.org/nightly/nightly-rustc/src/serialize/leb128.rs.html#4-14
macro_rules! impl_write_unsigned_leb128 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this is needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not strictly needed, but in case we needed the other functions, I figured it might be good to have around. I can macro-expand/inline the one function I need for the custom section writing if you'd prefer that!

($fn_name:ident, $int_ty:ident) => {
#[inline]
pub fn $fn_name(out: &mut Vec<u8>, mut value: $int_ty) {
loop {
if value < 0x80 {
out.push(value as u8);
break;
} else {
out.push(((value & 0x7f) | 0x80) as u8);
value >>= 7;
}
}
}
};
}

impl_write_unsigned_leb128!(write_u16_leb128, u16);
impl_write_unsigned_leb128!(write_u32_leb128, u32);
impl_write_unsigned_leb128!(write_u64_leb128, u64);
impl_write_unsigned_leb128!(write_u128_leb128, u128);
impl_write_unsigned_leb128!(write_usize_leb128, usize);
Loading