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 11 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`.
- TODO: update this when the method name changes. New way to get exports from an instance with `let func_name: Func<i32, i64> = instance.exports_new().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 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 @@

177 changes: 177 additions & 0 deletions lib/api-tests/tests/high_level_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
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: &[u8],
custom_section_name: &str,
custom_section_contents: &[u8],
) -> Vec<u8> {
let mut out = Vec::with_capacity(
// 1 for custom section id, 5 for max length of custom section, 5 for max length of name
wasm.len() + custom_section_name.len() + custom_section_contents.len() + 1 + 5 + 5,
);

out.extend(wasm);
out.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);

out.extend(&custom_section_length_as_leb128);
out.extend(&name_length_as_leb128);
out.extend(custom_section_name.as_bytes());
out.extend(custom_section_contents);

out
}

#[test]
fn it_works() {
MarkMcCaskey marked this conversation as resolved.
Show resolved Hide resolved
use wasmer::wasm::{Global, Value};
use wasmer::{export, func, imports, CompiledModule, Func, Module, Table};
let wasm_no_custom_section = wabt::wat2wasm(TEST_WAT).unwrap();
let wasm = append_custom_section(
&wasm_no_custom_section,
"test_custom_section",
b"hello, world!",
);
let wasm = append_custom_section(&wasm, "test_custom_section", b"goodbye, world!");
// 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 imports = module.imports();
assert_eq!(imports.len(), 2);
let num_exports = module.exports().count();
assert_eq!(num_exports, 6);
let (
mut test_table_seen,
mut test_global_seen,
mut test_ret_2_seen,
mut test_ret_4_seen,
mut set_test_global_seen,
mut update_outside_global_seen,
) = (false, false, false, false, false, false);
for export in module.exports() {
MarkMcCaskey marked this conversation as resolved.
Show resolved Hide resolved
match (export.name.as_ref(), export.kind) {
("test-table", export::ExportKind::Table) => {
assert!(!test_table_seen);
test_table_seen = true;
}
("test-global", export::ExportKind::Global) => {
assert!(!test_global_seen);
test_global_seen = true;
}
("ret_2", export::ExportKind::Function) => {
assert!(!test_ret_2_seen);
test_ret_2_seen = true;
}
("ret_4", export::ExportKind::Function) => {
assert!(!test_ret_4_seen);
test_ret_4_seen = true;
}
("set_test_global", export::ExportKind::Function) => {
assert!(!set_test_global_seen);
set_test_global_seen = true;
}
("update_outside_global", export::ExportKind::Function) => {
assert!(!update_outside_global_seen);
update_outside_global_seen = true;
}
_ => {
assert!(false, "Unknown export found!");
}
}
}
assert!(test_table_seen);
assert!(test_global_seen);
assert!(test_ret_2_seen);
assert!(test_ret_4_seen);
assert!(set_test_global_seen);
assert!(update_outside_global_seen);

assert_eq!(
module.custom_sections("test_custom_section"),
Some(&[b"hello, world!".to_vec(), b"goodbye, world!".to_vec()][..])
);
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_new().get("ret_2").unwrap();
let ret_4: Func<(), i32> = instance.exports_new().get("ret_4").unwrap();
let set_test_global: Func<i32> = instance.exports_new().get("set_test_global").unwrap();
let update_outside_global: Func = instance.exports_new().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_new().get("test-table").unwrap();
// TODO: when table get is stablized test this

let test_global: Global = instance.exports_new().get("test-global").unwrap();
// TODO: do we want to make global.get act like exports_new().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);
40 changes: 40 additions & 0 deletions lib/api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[package]
name = "wasmer"
version = "0.16.2"
authors = ["The Wasmer Engineering Team <[email protected]>"]
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"]
Loading