mirror of
https://github.com/fluencelabs/wasmer
synced 2025-04-25 02:12:13 +00:00
Merge #1313
1313: Add types and methods to provide updated API r=MarkMcCaskey a=MarkMcCaskey Improving the API in a number of ways. <details><summary>Current status </summary> - [x] Get exports from Instance - [x] Func - [x] Documented - [x] Tested - [x] DynFunc - [x] Documented - [x] Tested - [x] Memory - [x] Documented - [x] Tested - [x] Table - [x] Documented - [ ] Tested ; fully testing this now will be difficult, blocked on Table API being completed - [x] Global - [x] Documented - [x] Tested - [x] Field syntax (fairly non-trivial) - [x] Get imports from Module - [ ] Maybe update this to be an iterator instead of a Vec (side note, we may want to have a way to access specific types of imports too) - [x] Documented - [x] Tested - [x] Get exports from Module - [x] Documented - [x] Tested - [x] Get custom section from Module - [x] Figure out correct solution - [x] Ship separate PR that updates custom section code - [x] Documented - [x] Tested - [ ] Updated Memory API - [x] Added conversion methods to bytes/pages with From - [ ] Documented - [ ] Tested - [ ] Table APIs ; blocked on `wrap` being not linear time update (this update should also make it possible to retrieve a `Func` from `vm::Anyfunc`) - [ ] Table set (implemented needs to be checked) - [ ] Documented with examples - [x] Implemented - [ ] Table grow (implemented needs to be checked) - [ ] Documented with examples - [x] Implemented - [ ] Table get - [x] Structure implemented - [ ] Documented - [ ] Tested - [ ] Module APIs (probably separate) - [x] Update import object macro to handle a lack of trailing commas as well - [x] Add line in changelog about it - [x] Tested </details> # Review - [ ] Add a short description of the the change to the CHANGELOG.md file Co-authored-by: Mark McCaskey <mark@wasmer.io> Co-authored-by: Mark McCaskey <5770194+MarkMcCaskey@users.noreply.github.com>
This commit is contained in:
commit
ecafa7564d
@ -7,6 +7,12 @@
|
||||
- [#1329](https://github.com/wasmerio/wasmer/pull/1329) New numbers and strings instructions for WIT
|
||||
- [#1332](https://github.com/wasmerio/wasmer/pull/1332) Add option to `CompilerConfig` to force compiler IR verification off even when `debug_assertions` are enabled. This can be used to make debug builds faster, which may be important if you're creating a library that wraps Wasmer and depend on the speed of debug builds.
|
||||
- [#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.
|
||||
- [#1292](https://github.com/wasmerio/wasmer/pull/1292) Experimental Support for Android (x86_64 and AArch64)
|
||||
- [#1305](https://github.com/wasmerio/wasmer/pull/1305) Handle panics from DynamicFunc.
|
||||
|
19
Cargo.lock
generated
19
Cargo.lock
generated
@ -30,6 +30,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"
|
||||
@ -2718,6 +2726,16 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "wasmer"
|
||||
version = "0.16.2"
|
||||
dependencies = [
|
||||
"wasmer-clif-backend",
|
||||
"wasmer-llvm-backend",
|
||||
"wasmer-runtime-core",
|
||||
"wasmer-singlepass-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmer-bin"
|
||||
version = "0.16.2"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"byteorder",
|
||||
@ -2730,6 +2748,7 @@ dependencies = [
|
||||
"structopt",
|
||||
"typetag",
|
||||
"wabt",
|
||||
"wasmer",
|
||||
"wasmer-clif-backend",
|
||||
"wasmer-dev-utils",
|
||||
"wasmer-emscripten",
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "wasmer"
|
||||
name = "wasmer-bin"
|
||||
version = "0.16.2"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
edition = "2018"
|
||||
@ -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" }
|
||||
@ -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",
|
||||
|
1
Makefile
1
Makefile
@ -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
|
||||
|
@ -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";
|
||||
@ -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");
|
||||
}
|
||||
|
@ -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,
|
||||
@ -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!(".");
|
||||
@ -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) {
|
||||
|
@ -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,
|
||||
@ -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);
|
||||
|
@ -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 {
|
||||
|
15
lib/api-tests/Cargo.toml
Normal file
15
lib/api-tests/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "api-tests"
|
||||
version = "0.16.2"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
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
lib/api-tests/src/lib.rs
Normal file
1
lib/api-tests/src/lib.rs
Normal file
@ -0,0 +1 @@
|
||||
|
219
lib/api-tests/tests/high_level_api.rs
Normal file
219
lib/api-tests/tests/high_level_api.rs
Normal file
@ -0,0 +1,219 @@
|
||||
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::types::{ElementType, FuncSig, GlobalDescriptor, TableDescriptor, Type};
|
||||
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();
|
||||
assert_eq!(
|
||||
exports,
|
||||
vec![
|
||||
export::ExportDescriptor {
|
||||
name: "test-table",
|
||||
ty: export::ExternDescriptor::Table(TableDescriptor {
|
||||
element: ElementType::Anyfunc,
|
||||
minimum: 2,
|
||||
maximum: None,
|
||||
}),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "test-global",
|
||||
ty: export::ExternDescriptor::Global(GlobalDescriptor {
|
||||
mutable: true,
|
||||
ty: Type::I32,
|
||||
}),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "ret_2",
|
||||
ty: export::ExternDescriptor::Function(FuncSig::new(vec![], vec![Type::I32])),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "ret_4",
|
||||
ty: export::ExternDescriptor::Function(FuncSig::new(vec![], vec![Type::I32])),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "set_test_global",
|
||||
ty: export::ExternDescriptor::Function(FuncSig::new(vec![Type::I32], vec![])),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "update_outside_global",
|
||||
ty: export::ExternDescriptor::Function(FuncSig::new(vec![], vec![])),
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[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 {
|
||||
($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
lib/api/Cargo.toml
Normal file
40
lib/api/Cargo.toml
Normal file
@ -0,0 +1,40 @@
|
||||
[package]
|
||||
name = "wasmer"
|
||||
version = "0.16.2"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
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"]
|
219
lib/api/src/lib.rs
Normal file
219
lib/api/src/lib.rs
Normal file
@ -0,0 +1,219 @@
|
||||
#![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
|
||||
|
||||
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<ExportDescriptor> = 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::types::{ExportDescriptor, ExternDescriptor, ImportDescriptor};
|
||||
// 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::table::Table;
|
||||
pub use wasmer_runtime_core::types::{ExportDescriptor, ExternDescriptor, ImportDescriptor};
|
||||
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::types::{ExternDescriptor, ImportDescriptor};
|
||||
pub use wasmer_runtime_core::{func, imports};
|
||||
}
|
||||
|
||||
pub mod export {
|
||||
//! Types and functions for Wasm exports.
|
||||
pub use wasmer_runtime_core::types::{ExportDescriptor, ExternDescriptor};
|
||||
}
|
||||
|
||||
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::{
|
||||
ElementType, FuncDescriptor, FuncSig, GlobalDescriptor, GlobalInit, MemoryDescriptor,
|
||||
TableDescriptor, Type, Value, ValueType,
|
||||
};
|
||||
}
|
||||
|
||||
pub mod error {
|
||||
//! Various error types returned by Wasmer APIs.
|
||||
pub use wasmer_runtime_core::error::{CompileError, CompileResult};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CompileFromFileError {
|
||||
CompileError(CompileError),
|
||||
IoError(std::io::Error),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CompileFromFileError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
CompileFromFileError::CompileError(ce) => write!(f, "{}", ce),
|
||||
CompileFromFileError::IoError(ie) => write!(f, "{}", ie),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CompileFromFileError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
CompileFromFileError::CompileError(ce) => Some(ce),
|
||||
CompileFromFileError::IoError(ie) => Some(ie),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CompileError> for CompileFromFileError {
|
||||
fn from(other: CompileError) -> Self {
|
||||
CompileFromFileError::CompileError(other)
|
||||
}
|
||||
}
|
||||
impl From<std::io::Error> for CompileFromFileError {
|
||||
fn from(other: std::io::Error) -> Self {
|
||||
CompileFromFileError::IoError(other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<Module>;
|
||||
fn from_binary(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module>;
|
||||
fn from_binary_unchecked(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module>;
|
||||
fn from_file(file: impl AsRef<std::path::Path>) -> Result<Module, error::CompileFromFileError>;
|
||||
|
||||
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<Module> {
|
||||
let bytes = bytes.as_ref();
|
||||
wasmer_runtime_core::compile_with(bytes, &default_compiler())
|
||||
}
|
||||
|
||||
fn from_binary(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module> {
|
||||
let bytes = bytes.as_ref();
|
||||
wasmer_runtime_core::compile_with(bytes, &default_compiler())
|
||||
}
|
||||
|
||||
fn from_binary_unchecked(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module> {
|
||||
// TODO: optimize this
|
||||
Self::from_binary(bytes)
|
||||
}
|
||||
|
||||
fn from_file(file: impl AsRef<std::path::Path>) -> Result<Module, error::CompileFromFileError> {
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
let path = file.as_ref();
|
||||
let mut f = fs::File::open(path)?;
|
||||
// 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)?;
|
||||
|
||||
Ok(Module::from_binary(bytes.as_slice())?)
|
||||
}
|
||||
|
||||
fn validate(bytes: impl AsRef<[u8]>) -> error::CompileResult<()> {
|
||||
// TODO: optimize this
|
||||
let _ = Self::from_binary(bytes)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@ use wasmer_runtime_core::{
|
||||
types::{ElementType, FuncSig, MemoryDescriptor, TableDescriptor, Type, Value},
|
||||
units::Pages,
|
||||
vm::Ctx,
|
||||
Func, Instance, IsExport, Module,
|
||||
DynFunc, Func, Instance, IsExport, Module,
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
@ -181,75 +181,89 @@ impl<'a> EmscriptenData<'a> {
|
||||
globals: &'a EmscriptenGlobalsData,
|
||||
mapped_dirs: HashMap<String, PathBuf>,
|
||||
) -> EmscriptenData<'a> {
|
||||
let malloc = instance.func("_malloc").or(instance.func("malloc")).ok();
|
||||
let free = instance.func("_free").or(instance.func("free")).ok();
|
||||
let memalign = instance
|
||||
.func("_memalign")
|
||||
.or(instance.func("memalign"))
|
||||
let malloc = instance
|
||||
.exports
|
||||
.get("_malloc")
|
||||
.or(instance.exports.get("malloc"))
|
||||
.ok();
|
||||
let memset = instance.func("_memset").or(instance.func("memset")).ok();
|
||||
let stack_alloc = instance.func("stackAlloc").ok();
|
||||
let free = instance
|
||||
.exports
|
||||
.get("_free")
|
||||
.or(instance.exports.get("free"))
|
||||
.ok();
|
||||
let memalign = instance
|
||||
.exports
|
||||
.get("_memalign")
|
||||
.or(instance.exports.get("memalign"))
|
||||
.ok();
|
||||
let memset = instance
|
||||
.exports
|
||||
.get("_memset")
|
||||
.or(instance.exports.get("memset"))
|
||||
.ok();
|
||||
let stack_alloc = instance.exports.get("stackAlloc").ok();
|
||||
|
||||
let dyn_call_i = instance.func("dynCall_i").ok();
|
||||
let dyn_call_ii = instance.func("dynCall_ii").ok();
|
||||
let dyn_call_iii = instance.func("dynCall_iii").ok();
|
||||
let dyn_call_iiii = instance.func("dynCall_iiii").ok();
|
||||
let dyn_call_iifi = instance.func("dynCall_iifi").ok();
|
||||
let dyn_call_v = instance.func("dynCall_v").ok();
|
||||
let dyn_call_vi = instance.func("dynCall_vi").ok();
|
||||
let dyn_call_vii = instance.func("dynCall_vii").ok();
|
||||
let dyn_call_viii = instance.func("dynCall_viii").ok();
|
||||
let dyn_call_viiii = instance.func("dynCall_viiii").ok();
|
||||
let dyn_call_i = instance.exports.get("dynCall_i").ok();
|
||||
let dyn_call_ii = instance.exports.get("dynCall_ii").ok();
|
||||
let dyn_call_iii = instance.exports.get("dynCall_iii").ok();
|
||||
let dyn_call_iiii = instance.exports.get("dynCall_iiii").ok();
|
||||
let dyn_call_iifi = instance.exports.get("dynCall_iifi").ok();
|
||||
let dyn_call_v = instance.exports.get("dynCall_v").ok();
|
||||
let dyn_call_vi = instance.exports.get("dynCall_vi").ok();
|
||||
let dyn_call_vii = instance.exports.get("dynCall_vii").ok();
|
||||
let dyn_call_viii = instance.exports.get("dynCall_viii").ok();
|
||||
let dyn_call_viiii = instance.exports.get("dynCall_viiii").ok();
|
||||
|
||||
// round 2
|
||||
let dyn_call_dii = instance.func("dynCall_dii").ok();
|
||||
let dyn_call_diiii = instance.func("dynCall_diiii").ok();
|
||||
let dyn_call_iiiii = instance.func("dynCall_iiiii").ok();
|
||||
let dyn_call_iiiiii = instance.func("dynCall_iiiiii").ok();
|
||||
let dyn_call_iiiiiii = instance.func("dynCall_iiiiiii").ok();
|
||||
let dyn_call_iiiiiiii = instance.func("dynCall_iiiiiiii").ok();
|
||||
let dyn_call_iiiiiiiii = instance.func("dynCall_iiiiiiiii").ok();
|
||||
let dyn_call_iiiiiiiiii = instance.func("dynCall_iiiiiiiiii").ok();
|
||||
let dyn_call_iiiiiiiiiii = instance.func("dynCall_iiiiiiiiiii").ok();
|
||||
let dyn_call_vd = instance.func("dynCall_vd").ok();
|
||||
let dyn_call_viiiii = instance.func("dynCall_viiiii").ok();
|
||||
let dyn_call_viiiiii = instance.func("dynCall_viiiiii").ok();
|
||||
let dyn_call_viiiiiii = instance.func("dynCall_viiiiiii").ok();
|
||||
let dyn_call_viiiiiiii = instance.func("dynCall_viiiiiiii").ok();
|
||||
let dyn_call_viiiiiiiii = instance.func("dynCall_viiiiiiiii").ok();
|
||||
let dyn_call_viiiiiiiiii = instance.func("dynCall_viiiiiiiiii").ok();
|
||||
let dyn_call_iij = instance.func("dynCall_iij").ok();
|
||||
let dyn_call_iji = instance.func("dynCall_iji").ok();
|
||||
let dyn_call_iiji = instance.func("dynCall_iiji").ok();
|
||||
let dyn_call_iiijj = instance.func("dynCall_iiijj").ok();
|
||||
let dyn_call_j = instance.func("dynCall_j").ok();
|
||||
let dyn_call_ji = instance.func("dynCall_ji").ok();
|
||||
let dyn_call_jii = instance.func("dynCall_jii").ok();
|
||||
let dyn_call_jij = instance.func("dynCall_jij").ok();
|
||||
let dyn_call_jjj = instance.func("dynCall_jjj").ok();
|
||||
let dyn_call_viiij = instance.func("dynCall_viiij").ok();
|
||||
let dyn_call_viiijiiii = instance.func("dynCall_viiijiiii").ok();
|
||||
let dyn_call_viiijiiiiii = instance.func("dynCall_viiijiiiiii").ok();
|
||||
let dyn_call_viij = instance.func("dynCall_viij").ok();
|
||||
let dyn_call_viiji = instance.func("dynCall_viiji").ok();
|
||||
let dyn_call_viijiii = instance.func("dynCall_viijiii").ok();
|
||||
let dyn_call_viijj = instance.func("dynCall_viijj").ok();
|
||||
let dyn_call_vj = instance.func("dynCall_vj").ok();
|
||||
let dyn_call_vjji = instance.func("dynCall_vjji").ok();
|
||||
let dyn_call_vij = instance.func("dynCall_vij").ok();
|
||||
let dyn_call_viji = instance.func("dynCall_viji").ok();
|
||||
let dyn_call_vijiii = instance.func("dynCall_vijiii").ok();
|
||||
let dyn_call_vijj = instance.func("dynCall_vijj").ok();
|
||||
let dyn_call_viid = instance.func("dynCall_viid").ok();
|
||||
let dyn_call_vidd = instance.func("dynCall_vidd").ok();
|
||||
let dyn_call_viidii = instance.func("dynCall_viidii").ok();
|
||||
let dyn_call_viidddddddd = instance.func("dynCall_viidddddddd").ok();
|
||||
let dyn_call_dii = instance.exports.get("dynCall_dii").ok();
|
||||
let dyn_call_diiii = instance.exports.get("dynCall_diiii").ok();
|
||||
let dyn_call_iiiii = instance.exports.get("dynCall_iiiii").ok();
|
||||
let dyn_call_iiiiii = instance.exports.get("dynCall_iiiiii").ok();
|
||||
let dyn_call_iiiiiii = instance.exports.get("dynCall_iiiiiii").ok();
|
||||
let dyn_call_iiiiiiii = instance.exports.get("dynCall_iiiiiiii").ok();
|
||||
let dyn_call_iiiiiiiii = instance.exports.get("dynCall_iiiiiiiii").ok();
|
||||
let dyn_call_iiiiiiiiii = instance.exports.get("dynCall_iiiiiiiiii").ok();
|
||||
let dyn_call_iiiiiiiiiii = instance.exports.get("dynCall_iiiiiiiiiii").ok();
|
||||
let dyn_call_vd = instance.exports.get("dynCall_vd").ok();
|
||||
let dyn_call_viiiii = instance.exports.get("dynCall_viiiii").ok();
|
||||
let dyn_call_viiiiii = instance.exports.get("dynCall_viiiiii").ok();
|
||||
let dyn_call_viiiiiii = instance.exports.get("dynCall_viiiiiii").ok();
|
||||
let dyn_call_viiiiiiii = instance.exports.get("dynCall_viiiiiiii").ok();
|
||||
let dyn_call_viiiiiiiii = instance.exports.get("dynCall_viiiiiiiii").ok();
|
||||
let dyn_call_viiiiiiiiii = instance.exports.get("dynCall_viiiiiiiiii").ok();
|
||||
let dyn_call_iij = instance.exports.get("dynCall_iij").ok();
|
||||
let dyn_call_iji = instance.exports.get("dynCall_iji").ok();
|
||||
let dyn_call_iiji = instance.exports.get("dynCall_iiji").ok();
|
||||
let dyn_call_iiijj = instance.exports.get("dynCall_iiijj").ok();
|
||||
let dyn_call_j = instance.exports.get("dynCall_j").ok();
|
||||
let dyn_call_ji = instance.exports.get("dynCall_ji").ok();
|
||||
let dyn_call_jii = instance.exports.get("dynCall_jii").ok();
|
||||
let dyn_call_jij = instance.exports.get("dynCall_jij").ok();
|
||||
let dyn_call_jjj = instance.exports.get("dynCall_jjj").ok();
|
||||
let dyn_call_viiij = instance.exports.get("dynCall_viiij").ok();
|
||||
let dyn_call_viiijiiii = instance.exports.get("dynCall_viiijiiii").ok();
|
||||
let dyn_call_viiijiiiiii = instance.exports.get("dynCall_viiijiiiiii").ok();
|
||||
let dyn_call_viij = instance.exports.get("dynCall_viij").ok();
|
||||
let dyn_call_viiji = instance.exports.get("dynCall_viiji").ok();
|
||||
let dyn_call_viijiii = instance.exports.get("dynCall_viijiii").ok();
|
||||
let dyn_call_viijj = instance.exports.get("dynCall_viijj").ok();
|
||||
let dyn_call_vj = instance.exports.get("dynCall_vj").ok();
|
||||
let dyn_call_vjji = instance.exports.get("dynCall_vjji").ok();
|
||||
let dyn_call_vij = instance.exports.get("dynCall_vij").ok();
|
||||
let dyn_call_viji = instance.exports.get("dynCall_viji").ok();
|
||||
let dyn_call_vijiii = instance.exports.get("dynCall_vijiii").ok();
|
||||
let dyn_call_vijj = instance.exports.get("dynCall_vijj").ok();
|
||||
let dyn_call_viid = instance.exports.get("dynCall_viid").ok();
|
||||
let dyn_call_vidd = instance.exports.get("dynCall_vidd").ok();
|
||||
let dyn_call_viidii = instance.exports.get("dynCall_viidii").ok();
|
||||
let dyn_call_viidddddddd = instance.exports.get("dynCall_viidddddddd").ok();
|
||||
|
||||
let stack_save = instance.func("stackSave").ok();
|
||||
let stack_restore = instance.func("stackRestore").ok();
|
||||
let stack_save = instance.exports.get("stackSave").ok();
|
||||
let stack_restore = instance.exports.get("stackRestore").ok();
|
||||
let set_threw = instance
|
||||
.func("_setThrew")
|
||||
.or(instance.func("setThrew"))
|
||||
.exports
|
||||
.get("_setThrew")
|
||||
.or(instance.exports.get("setThrew"))
|
||||
.ok();
|
||||
|
||||
EmscriptenData {
|
||||
@ -335,12 +349,15 @@ impl<'a> EmscriptenData<'a> {
|
||||
pub fn set_up_emscripten(instance: &mut Instance) -> CallResult<()> {
|
||||
// ATINIT
|
||||
// (used by C++)
|
||||
if let Ok(_func) = instance.dyn_func("globalCtors") {
|
||||
instance.call("globalCtors", &[])?;
|
||||
if let Ok(func) = instance.exports.get::<DynFunc>("globalCtors") {
|
||||
func.call(&[])?;
|
||||
}
|
||||
|
||||
if let Ok(_func) = instance.dyn_func("___emscripten_environ_constructor") {
|
||||
instance.call("___emscripten_environ_constructor", &[])?;
|
||||
if let Ok(func) = instance
|
||||
.exports
|
||||
.get::<DynFunc>("___emscripten_environ_constructor")
|
||||
{
|
||||
func.call(&[])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -350,9 +367,9 @@ pub fn set_up_emscripten(instance: &mut Instance) -> CallResult<()> {
|
||||
///
|
||||
/// If you don't want to set it up yourself, consider using [`run_emscripten_instance`].
|
||||
pub fn emscripten_call_main(instance: &mut Instance, path: &str, args: &[&str]) -> CallResult<()> {
|
||||
let (func_name, main_func) = match instance.dyn_func("_main") {
|
||||
let (func_name, main_func) = match instance.exports.get::<DynFunc>("_main") {
|
||||
Ok(func) => Ok(("_main", func)),
|
||||
Err(_e) => match instance.dyn_func("main") {
|
||||
Err(_e) => match instance.exports.get::<DynFunc>("main") {
|
||||
Ok(func) => Ok(("main", func)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
@ -363,13 +380,12 @@ pub fn emscripten_call_main(instance: &mut Instance, path: &str, args: &[&str])
|
||||
let mut new_args = vec![path];
|
||||
new_args.extend(args);
|
||||
let (argc, argv) = store_module_arguments(instance.context_mut(), new_args);
|
||||
instance.call(
|
||||
func_name,
|
||||
&[Value::I32(argc as i32), Value::I32(argv as i32)],
|
||||
)?;
|
||||
let func: DynFunc = instance.exports.get(func_name)?;
|
||||
func.call(&[Value::I32(argc as i32), Value::I32(argv as i32)])?;
|
||||
}
|
||||
0 => {
|
||||
instance.call(func_name, &[])?;
|
||||
let func: DynFunc = instance.exports.get(func_name)?;
|
||||
func.call(&[])?;
|
||||
}
|
||||
_ => {
|
||||
return Err(CallError::Resolve(ResolveError::ExportWrongType {
|
||||
@ -403,7 +419,8 @@ pub fn run_emscripten_instance(
|
||||
debug!("Running entry point: {}", &ep);
|
||||
let arg = unsafe { allocate_cstr_on_stack(instance.context_mut(), args[0]).0 };
|
||||
//let (argc, argv) = store_module_arguments(instance.context_mut(), args);
|
||||
instance.call(&ep, &[Value::I32(arg as i32)])?;
|
||||
let func: DynFunc = instance.exports.get(&ep)?;
|
||||
func.call(&[Value::I32(arg as i32)])?;
|
||||
} else {
|
||||
emscripten_call_main(instance, path, &args)?;
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ fn bench_metering(c: &mut Criterion) {
|
||||
let module = compile_with(&wasm_binary, &compiler).unwrap();
|
||||
let import_object = imports! {};
|
||||
let instance = module.instantiate(&import_object).unwrap();
|
||||
let add_to: Func<(i32, i32), i32> = instance.func("add_to").unwrap();
|
||||
let add_to: Func<(i32, i32), i32> = instance.exports.get("add_to").unwrap();
|
||||
b.iter(|| black_box(add_to.call(100, 4)))
|
||||
})
|
||||
.with_function("Gas Metering", |b| {
|
||||
@ -203,7 +203,7 @@ fn bench_metering(c: &mut Criterion) {
|
||||
},
|
||||
};
|
||||
let gas_instance = gas_module.instantiate(&gas_import_object).unwrap();
|
||||
let gas_add_to: Func<(i32, i32), i32> = gas_instance.func("add_to").unwrap();
|
||||
let gas_add_to: Func<(i32, i32), i32> = gas_instance.exports.get("add_to").unwrap();
|
||||
b.iter(|| black_box(gas_add_to.call(100, 4)))
|
||||
})
|
||||
.with_function("Built-in Metering", |b| {
|
||||
@ -215,7 +215,8 @@ fn bench_metering(c: &mut Criterion) {
|
||||
.instantiate(&metering_import_object)
|
||||
.unwrap();
|
||||
metering::set_points_used(&mut metering_instance, 0u64);
|
||||
let metering_add_to: Func<(i32, i32), i32> = metering_instance.func("add_to").unwrap();
|
||||
let metering_add_to: Func<(i32, i32), i32> =
|
||||
metering_instance.exports.get("add_to").unwrap();
|
||||
b.iter(|| black_box(metering_add_to.call(100, 4)))
|
||||
}),
|
||||
);
|
||||
|
@ -103,7 +103,7 @@ mod tests {
|
||||
|
||||
set_points_used(&mut instance, 0u64);
|
||||
|
||||
let add_to: Func<(i32, i32), i32> = instance.func("add_to").unwrap();
|
||||
let add_to: Func<(i32, i32), i32> = instance.exports.get("add_to").unwrap();
|
||||
|
||||
let cv_pushed = if let Some(msm) = instance.module.runnable_module.get_module_state_map() {
|
||||
push_code_version(CodeVersion {
|
||||
@ -145,7 +145,7 @@ mod tests {
|
||||
|
||||
set_points_used(&mut instance, 0u64);
|
||||
|
||||
let add_to: Func<(i32, i32), i32> = instance.func("add_to").unwrap();
|
||||
let add_to: Func<(i32, i32), i32> = instance.exports.get("add_to").unwrap();
|
||||
|
||||
let cv_pushed = if let Some(msm) = instance.module.runnable_module.get_module_state_map() {
|
||||
push_code_version(CodeVersion {
|
||||
|
@ -2,19 +2,48 @@ use std::{convert::TryInto, sync::Arc};
|
||||
use wasmer_runtime_core::{
|
||||
compile_with,
|
||||
error::RuntimeError,
|
||||
global::Global,
|
||||
imports,
|
||||
memory::Memory,
|
||||
typed_func::{DynamicFunc, Func},
|
||||
types::{FuncSig, MemoryDescriptor, Type, Value},
|
||||
units::Pages,
|
||||
vm, Instance,
|
||||
vm, DynFunc, Instance,
|
||||
};
|
||||
use wasmer_runtime_core_tests::{get_compiler, wat2wasm};
|
||||
|
||||
#[test]
|
||||
fn new_api_works() {
|
||||
let wasm = r#"
|
||||
(module
|
||||
(type $type (func (param i32) (result i32)))
|
||||
(global (export "my_global") i32 (i32.const 45))
|
||||
(func (export "add_one") (type $type)
|
||||
(i32.add (get_local 0)
|
||||
(i32.const 1)))
|
||||
(func (export "double") (type $type)
|
||||
(i32.mul (get_local 0)
|
||||
(i32.const 2)))
|
||||
)"#;
|
||||
let wasm_binary = wat2wasm(wasm.as_bytes()).expect("WAST not valid or malformed");
|
||||
let module = compile_with(&wasm_binary, &get_compiler()).unwrap();
|
||||
let import_object = imports! {};
|
||||
let instance = module.instantiate(&import_object).unwrap();
|
||||
|
||||
let my_global: Global = instance.exports.get("my_global").unwrap();
|
||||
assert_eq!(my_global.get(), Value::I32(45));
|
||||
let double: Func<i32, i32> = instance.exports.get("double").unwrap();
|
||||
assert_eq!(double.call(5).unwrap(), 10);
|
||||
let add_one: DynFunc = instance.exports.get("add_one").unwrap();
|
||||
assert_eq!(add_one.call(&[Value::I32(5)]).unwrap(), &[Value::I32(6)]);
|
||||
let add_one_memory: Result<DynFunc, _> = instance.exports.get("my_global");
|
||||
assert!(add_one_memory.is_err());
|
||||
}
|
||||
|
||||
macro_rules! call_and_assert {
|
||||
($instance:ident, $function:ident( $( $inputs:ty ),* ) -> $output:ty, ( $( $arguments:expr ),* ) == $expected_value:expr) => {
|
||||
#[allow(unused_parens)]
|
||||
let $function: Func<( $( $inputs ),* ), $output> = $instance.func(stringify!($function)).expect(concat!("Failed to get the `", stringify!($function), "` export function."));
|
||||
let $function: Func<( $( $inputs ),* ), $output> = $instance.exports.get(stringify!($function)).expect(concat!("Failed to get the `", stringify!($function), "` export function."));
|
||||
|
||||
let result = $function.call( $( $arguments ),* );
|
||||
|
||||
|
@ -1,9 +1,15 @@
|
||||
//! The export module contains the implementation data structures and helper functions used to
|
||||
//! manipulate and access a wasm module's exports including memories, tables, globals, and
|
||||
//! functions.
|
||||
//! This module contains types to manipulate and access a Wasm module's exports
|
||||
//! including memories, tables, globals, and functions.
|
||||
use crate::{
|
||||
global::Global, instance::InstanceInner, memory::Memory, module::ExportIndex,
|
||||
module::ModuleInner, table::Table, types::FuncSig, vm,
|
||||
error,
|
||||
global::Global,
|
||||
instance::{Exports, InstanceInner},
|
||||
memory::Memory,
|
||||
module::ExportIndex,
|
||||
module::ModuleInner,
|
||||
table::Table,
|
||||
types::FuncSig,
|
||||
vm,
|
||||
};
|
||||
use indexmap::map::Iter as IndexMapIter;
|
||||
use std::{ptr::NonNull, sync::Arc};
|
||||
@ -92,3 +98,10 @@ impl<'a> Iterator for ExportIter<'a> {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait is used to mark types as gettable from an [`Instance`].
|
||||
pub trait Exportable<'a>: Sized {
|
||||
/// Implementation of how to get the export corresponding to the implementing type
|
||||
/// from an [`Instance`] by name.
|
||||
fn get_self(exports: &'a Exports, name: &str) -> error::ResolveResult<Self>;
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
//! The instance module contains the implementation data structures and helper functions used to
|
||||
//! manipulate and access wasm instances.
|
||||
//! This module contains types for manipulating and accessing Wasm instances.
|
||||
//!
|
||||
//! An "instance", or "instantiated module", is a compiled WebAssembly [`Module`] with its
|
||||
//! corresponding imports (via [`ImportObject`]) that is ready to execute.
|
||||
use crate::{
|
||||
backend::RunnableModule,
|
||||
backing::{ImportBacking, LocalBacking},
|
||||
error::{CallError, CallResult, ResolveError, ResolveResult, Result, RuntimeError},
|
||||
export::{Context, Export, ExportIter, FuncPointer},
|
||||
error::{CallResult, ResolveError, ResolveResult, Result, RuntimeError},
|
||||
export::{Context, Export, ExportIter, Exportable, FuncPointer},
|
||||
global::Global,
|
||||
import::{ImportObject, LikeNamespace},
|
||||
loader::Loader,
|
||||
@ -19,6 +21,7 @@ use crate::{
|
||||
};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
mem,
|
||||
pin::Pin,
|
||||
ptr::{self, NonNull},
|
||||
@ -53,6 +56,8 @@ pub struct Instance {
|
||||
/// Reference to the module used to instantiate this instance.
|
||||
pub module: Arc<ModuleInner>,
|
||||
inner: Pin<Box<InstanceInner>>,
|
||||
/// The exports of this instance.
|
||||
pub exports: Exports,
|
||||
#[allow(dead_code)]
|
||||
import_object: ImportObject,
|
||||
}
|
||||
@ -89,9 +94,15 @@ impl Instance {
|
||||
};
|
||||
Box::leak(vmctx);
|
||||
|
||||
let exports = Exports {
|
||||
module: module.clone(),
|
||||
instance_inner: &*inner as *const InstanceInner,
|
||||
};
|
||||
|
||||
let instance = Instance {
|
||||
module,
|
||||
inner,
|
||||
exports,
|
||||
import_object: imports.clone_ref(),
|
||||
};
|
||||
|
||||
@ -170,103 +181,21 @@ impl Instance {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[deprecated(
|
||||
since = "0.17.0",
|
||||
note = "Please use `instance.exports.get(name)` instead"
|
||||
)]
|
||||
pub fn func<Args, Rets>(&self, name: &str) -> ResolveResult<Func<Args, Rets, Wasm>>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
let export_index =
|
||||
self.module
|
||||
.info
|
||||
.exports
|
||||
.get(name)
|
||||
.ok_or_else(|| ResolveError::ExportNotFound {
|
||||
name: name.to_string(),
|
||||
})?;
|
||||
|
||||
if let ExportIndex::Func(func_index) = export_index {
|
||||
let sig_index = *self
|
||||
.module
|
||||
.info
|
||||
.func_assoc
|
||||
.get(*func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
let signature =
|
||||
SigRegistry.lookup_signature_ref(&self.module.info.signatures[sig_index]);
|
||||
|
||||
if signature.params() != Args::types() || signature.returns() != Rets::types() {
|
||||
Err(ResolveError::Signature {
|
||||
expected: (*signature).clone(),
|
||||
found: Args::types().to_vec(),
|
||||
})?;
|
||||
}
|
||||
|
||||
let ctx = match func_index.local_or_import(&self.module.info) {
|
||||
LocalOrImport::Local(_) => self.inner.vmctx,
|
||||
LocalOrImport::Import(imported_func_index) => unsafe {
|
||||
self.inner.import_backing.vm_functions[imported_func_index]
|
||||
.func_ctx
|
||||
.as_ref()
|
||||
}
|
||||
.vmctx
|
||||
.as_ptr(),
|
||||
};
|
||||
|
||||
let func_wasm_inner = self
|
||||
.module
|
||||
.runnable_module
|
||||
.get_trampoline(&self.module.info, sig_index)
|
||||
.unwrap();
|
||||
|
||||
let (func_ptr, func_env) = match func_index.local_or_import(&self.module.info) {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
self.module
|
||||
.runnable_module
|
||||
.get_func(&self.module.info, local_func_index)
|
||||
.unwrap(),
|
||||
None,
|
||||
),
|
||||
LocalOrImport::Import(import_func_index) => {
|
||||
let imported_func = &self.inner.import_backing.vm_functions[import_func_index];
|
||||
|
||||
(
|
||||
NonNull::new(imported_func.func as *mut _).unwrap(),
|
||||
unsafe { imported_func.func_ctx.as_ref() }.func_env,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let typed_func: Func<Args, Rets, Wasm> =
|
||||
unsafe { Func::from_raw_parts(func_wasm_inner, func_ptr, func_env, ctx) };
|
||||
|
||||
Ok(typed_func)
|
||||
} else {
|
||||
Err(ResolveError::ExportWrongType {
|
||||
name: name.to_string(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
self.exports.get(name)
|
||||
}
|
||||
|
||||
/// Resolve a function by name.
|
||||
pub fn resolve_func(&self, name: &str) -> ResolveResult<usize> {
|
||||
let export_index =
|
||||
self.module
|
||||
.info
|
||||
.exports
|
||||
.get(name)
|
||||
.ok_or_else(|| ResolveError::ExportNotFound {
|
||||
name: name.to_string(),
|
||||
})?;
|
||||
|
||||
if let ExportIndex::Func(func_index) = export_index {
|
||||
Ok(func_index.index())
|
||||
} else {
|
||||
Err(ResolveError::ExportWrongType {
|
||||
name: name.to_string(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
resolve_func_index(&*self.module, name).map(|fi| fi.index())
|
||||
}
|
||||
|
||||
/// This returns the representation of a function that can be called
|
||||
@ -283,38 +212,12 @@ impl Instance {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[deprecated(
|
||||
since = "0.17.0",
|
||||
note = "Please use `instance.exports.get(name)` instead"
|
||||
)]
|
||||
pub fn dyn_func(&self, name: &str) -> ResolveResult<DynFunc> {
|
||||
let export_index =
|
||||
self.module
|
||||
.info
|
||||
.exports
|
||||
.get(name)
|
||||
.ok_or_else(|| ResolveError::ExportNotFound {
|
||||
name: name.to_string(),
|
||||
})?;
|
||||
|
||||
if let ExportIndex::Func(func_index) = export_index {
|
||||
let sig_index = *self
|
||||
.module
|
||||
.info
|
||||
.func_assoc
|
||||
.get(*func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
let signature =
|
||||
SigRegistry.lookup_signature_ref(&self.module.info.signatures[sig_index]);
|
||||
|
||||
Ok(DynFunc {
|
||||
signature,
|
||||
module: &self.module,
|
||||
instance_inner: &self.inner,
|
||||
func_index: *func_index,
|
||||
})
|
||||
} else {
|
||||
Err(ResolveError::ExportWrongType {
|
||||
name: name.to_string(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
self.exports.get(name)
|
||||
}
|
||||
|
||||
/// Call an exported WebAssembly function given the export name.
|
||||
@ -327,6 +230,23 @@ impl Instance {
|
||||
/// This returns `CallResult<Vec<Value>>` in order to support
|
||||
/// the future multi-value returns WebAssembly feature.
|
||||
///
|
||||
/// Consider using the more explicit [`Exports::get`]` with [`DynFunc::call`]
|
||||
/// instead. For example:
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer_runtime_core::types::Value;
|
||||
/// # use wasmer_runtime_core::error::Result;
|
||||
/// # use wasmer_runtime_core::Instance;
|
||||
/// # use wasmer_runtime_core::DynFunc;
|
||||
/// # fn call_foo(instance: &mut Instance) -> Result<()> {
|
||||
/// // ...
|
||||
/// let foo: DynFunc = instance.exports.get("foo")?;
|
||||
/// let results = foo.call(&[Value::I32(42)])?;
|
||||
/// // ...
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Usage:
|
||||
/// ```
|
||||
/// # use wasmer_runtime_core::types::Value;
|
||||
@ -340,37 +260,8 @@ impl Instance {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn call(&self, name: &str, params: &[Value]) -> CallResult<Vec<Value>> {
|
||||
let export_index =
|
||||
self.module
|
||||
.info
|
||||
.exports
|
||||
.get(name)
|
||||
.ok_or_else(|| ResolveError::ExportNotFound {
|
||||
name: name.to_string(),
|
||||
})?;
|
||||
|
||||
let func_index = if let ExportIndex::Func(func_index) = export_index {
|
||||
*func_index
|
||||
} else {
|
||||
return Err(CallError::Resolve(ResolveError::ExportWrongType {
|
||||
name: name.to_string(),
|
||||
})
|
||||
.into());
|
||||
};
|
||||
|
||||
let mut results = Vec::new();
|
||||
|
||||
call_func_with_index(
|
||||
&self.module.info,
|
||||
&**self.module.runnable_module,
|
||||
&self.inner.import_backing,
|
||||
self.inner.vmctx,
|
||||
func_index,
|
||||
params,
|
||||
&mut results,
|
||||
)?;
|
||||
|
||||
Ok(results)
|
||||
let func: DynFunc = self.exports.get(name)?;
|
||||
func.call(params)
|
||||
}
|
||||
|
||||
/// Returns an immutable reference to the
|
||||
@ -411,6 +302,27 @@ impl Instance {
|
||||
}
|
||||
}
|
||||
|
||||
/// Private function used to find the [`FuncIndex`] of a given export.
|
||||
fn resolve_func_index(module: &ModuleInner, name: &str) -> ResolveResult<FuncIndex> {
|
||||
let export_index =
|
||||
module
|
||||
.info
|
||||
.exports
|
||||
.get(name)
|
||||
.ok_or_else(|| ResolveError::ExportNotFound {
|
||||
name: name.to_string(),
|
||||
})?;
|
||||
|
||||
if let ExportIndex::Func(func_index) = export_index {
|
||||
Ok(*func_index)
|
||||
} else {
|
||||
Err(ResolveError::ExportWrongType {
|
||||
name: name.to_string(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl InstanceInner {
|
||||
pub(crate) fn get_export_from_index(
|
||||
&self,
|
||||
@ -814,6 +726,219 @@ impl<'a> DynFunc<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Memory {
|
||||
fn get_self(exports: &'a Exports, name: &str) -> ResolveResult<Self> {
|
||||
let (inst_inner, module) = exports.get_inner();
|
||||
let export_index =
|
||||
module
|
||||
.info
|
||||
.exports
|
||||
.get(name)
|
||||
.ok_or_else(|| ResolveError::ExportNotFound {
|
||||
name: name.to_string(),
|
||||
})?;
|
||||
if let ExportIndex::Memory(idx) = export_index {
|
||||
Ok(inst_inner.get_memory_from_index(module, *idx))
|
||||
} else {
|
||||
Err(ResolveError::ExportWrongType {
|
||||
name: name.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Table {
|
||||
fn get_self(exports: &'a Exports, name: &str) -> ResolveResult<Self> {
|
||||
let (inst_inner, module) = exports.get_inner();
|
||||
let export_index =
|
||||
module
|
||||
.info
|
||||
.exports
|
||||
.get(name)
|
||||
.ok_or_else(|| ResolveError::ExportNotFound {
|
||||
name: name.to_string(),
|
||||
})?;
|
||||
if let ExportIndex::Table(idx) = export_index {
|
||||
Ok(inst_inner.get_table_from_index(module, *idx))
|
||||
} else {
|
||||
Err(ResolveError::ExportWrongType {
|
||||
name: name.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Global {
|
||||
fn get_self(exports: &'a Exports, name: &str) -> ResolveResult<Self> {
|
||||
let (inst_inner, module) = exports.get_inner();
|
||||
let export_index =
|
||||
module
|
||||
.info
|
||||
.exports
|
||||
.get(name)
|
||||
.ok_or_else(|| ResolveError::ExportNotFound {
|
||||
name: name.to_string(),
|
||||
})?;
|
||||
if let ExportIndex::Global(idx) = export_index {
|
||||
Ok(inst_inner.get_global_from_index(module, *idx))
|
||||
} else {
|
||||
Err(ResolveError::ExportWrongType {
|
||||
name: name.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for DynFunc<'a> {
|
||||
fn get_self(exports: &'a Exports, name: &str) -> ResolveResult<Self> {
|
||||
let (inst_inner, module) = exports.get_inner();
|
||||
let func_index = resolve_func_index(module, name)?;
|
||||
|
||||
let sig_index = *module
|
||||
.info
|
||||
.func_assoc
|
||||
.get(func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
let signature = SigRegistry.lookup_signature_ref(&module.info.signatures[sig_index]);
|
||||
|
||||
Ok(DynFunc {
|
||||
signature,
|
||||
module: &module,
|
||||
instance_inner: &inst_inner,
|
||||
func_index: func_index,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Args: WasmTypeList, Rets: WasmTypeList> Exportable<'a> for Func<'a, Args, Rets, Wasm> {
|
||||
fn get_self(exports: &'a Exports, name: &str) -> ResolveResult<Self> {
|
||||
let (inst_inner, module) = exports.get_inner();
|
||||
|
||||
let func_index = resolve_func_index(module, name)?;
|
||||
|
||||
let sig_index = *module
|
||||
.info
|
||||
.func_assoc
|
||||
.get(func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
let signature = SigRegistry.lookup_signature_ref(&module.info.signatures[sig_index]);
|
||||
|
||||
if signature.params() != Args::types() || signature.returns() != Rets::types() {
|
||||
Err(ResolveError::Signature {
|
||||
expected: (*signature).clone(),
|
||||
found: Args::types().to_vec(),
|
||||
})?;
|
||||
}
|
||||
|
||||
let ctx = match func_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(_) => inst_inner.vmctx,
|
||||
LocalOrImport::Import(imported_func_index) => unsafe {
|
||||
inst_inner.import_backing.vm_functions[imported_func_index]
|
||||
.func_ctx
|
||||
.as_ref()
|
||||
}
|
||||
.vmctx
|
||||
.as_ptr(),
|
||||
};
|
||||
|
||||
let func_wasm_inner = module
|
||||
.runnable_module
|
||||
.get_trampoline(&module.info, sig_index)
|
||||
.unwrap();
|
||||
|
||||
let (func_ptr, func_env) = match func_index.local_or_import(&module.info) {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
module
|
||||
.runnable_module
|
||||
.get_func(&module.info, local_func_index)
|
||||
.unwrap(),
|
||||
None,
|
||||
),
|
||||
LocalOrImport::Import(import_func_index) => {
|
||||
let imported_func = &inst_inner.import_backing.vm_functions[import_func_index];
|
||||
|
||||
(
|
||||
NonNull::new(imported_func.func as *mut _).unwrap(),
|
||||
unsafe { imported_func.func_ctx.as_ref() }.func_env,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let typed_func: Func<Args, Rets, Wasm> =
|
||||
unsafe { Func::from_raw_parts(func_wasm_inner, func_ptr, func_env, ctx) };
|
||||
|
||||
Ok(typed_func)
|
||||
}
|
||||
}
|
||||
|
||||
/// `Exports` is used to get exports like [`Func`]s, [`DynFunc`]s, [`Memory`]s,
|
||||
/// [`Global`]s, and [`Table`]s from an [`Instance`].
|
||||
///
|
||||
/// Use `Instance.exports` to get an `Exports` from an [`Instance`].
|
||||
pub struct Exports {
|
||||
// We want to avoid the borrow checker here.
|
||||
// This is safe because
|
||||
// 1. `Exports` can't be constructed, its fields inspected (directly or via methods),
|
||||
// or copied outside of this module/in Instance, so it can't safely outlive `Instance`.
|
||||
// 2. `InstanceInner` is `Pin<Box<>>`, thus we know that it will not move.
|
||||
instance_inner: *const InstanceInner,
|
||||
module: Arc<ModuleInner>,
|
||||
}
|
||||
|
||||
// this is safe because the lifetime of `Exports` is tied to `Instance` and
|
||||
// `*const InstanceInner` comes from a `Pin<Box<InstanceInner>>`
|
||||
unsafe impl Send for Exports {}
|
||||
|
||||
impl Exports {
|
||||
/// Get an export.
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer_runtime_core::{DynFunc, Func, Instance};
|
||||
/// # use wasmer_runtime_core::global::Global;
|
||||
/// # use wasmer_runtime_core::types::Value;
|
||||
/// # use wasmer_runtime_core::error::ResolveResult;
|
||||
/// # fn example_fn(instance: &Instance) -> ResolveResult<()> {
|
||||
/// // We can get a function as a static `Func`
|
||||
/// let func: Func<i32, i32> = instance.exports.get("my_func")?;
|
||||
/// let _result = func.call(42);
|
||||
///
|
||||
/// // Or we can get it as a dynamic `DynFunc`
|
||||
/// let dyn_func: DynFunc = instance.exports.get("my_func")?;
|
||||
/// let _result= dyn_func.call(&[Value::I32(42)]);
|
||||
///
|
||||
/// // We can also get other exports like `Global`s, `Memory`s, and `Table`s
|
||||
/// let _counter: Global = instance.exports.get("counter")?;
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> ResolveResult<T> {
|
||||
T::get_self(self, name)
|
||||
}
|
||||
|
||||
/// This method must remain private for `Exports` to be sound.
|
||||
fn get_inner(&self) -> (&InstanceInner, &ModuleInner) {
|
||||
let inst_inner = unsafe { &*self.instance_inner };
|
||||
let module = self.module.borrow();
|
||||
(inst_inner, module)
|
||||
}
|
||||
|
||||
/// Iterate the exports.
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmer_runtime_core::instance::Instance;
|
||||
/// # fn iterate_exports_example(instance: &Instance) {
|
||||
/// for (export_name, export_value) in instance.exports.into_iter() {
|
||||
/// println!("Found export `{}` with value `{:?}`", export_name, export_value);
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn into_iter(&self) -> ExportIter {
|
||||
let (inst_inner, module) = self.get_inner();
|
||||
ExportIter::new(&module, &inst_inner)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
@ -76,7 +76,7 @@ macro_rules! func {
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! imports {
|
||||
( $( $ns_name:expr => $ns:tt, )* ) => {{
|
||||
( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {{
|
||||
use $crate::{
|
||||
import::{ImportObject, Namespace},
|
||||
};
|
||||
@ -91,7 +91,7 @@ macro_rules! imports {
|
||||
|
||||
import_object
|
||||
}};
|
||||
($state_gen:expr, $( $ns_name:expr => $ns:tt, )* ) => {{
|
||||
($state_gen:expr, $( $ns_name:expr => $ns:tt ),* $(,)? ) => {{
|
||||
use $crate::{
|
||||
import::{ImportObject, Namespace},
|
||||
};
|
||||
@ -111,7 +111,7 @@ macro_rules! imports {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __imports_internal {
|
||||
( { $( $imp_name:expr => $import_item:expr, )* } ) => {{
|
||||
( { $( $imp_name:expr => $import_item:expr ),* $(,)? } ) => {{
|
||||
let mut ns = Namespace::new();
|
||||
$(
|
||||
ns.insert($imp_name, $import_item);
|
||||
@ -126,7 +126,7 @@ macro_rules! __imports_internal {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! namespace {
|
||||
( $( $imp_name:expr => $import_item:expr, )* ) => {{
|
||||
( $( $imp_name:expr => $import_item:expr ),* $(,)? ) => {{
|
||||
let mut ns = $crate::import::Namespace::new();
|
||||
$(
|
||||
ns.insert($imp_name, $import_item);
|
||||
@ -134,3 +134,85 @@ macro_rules! namespace {
|
||||
ns
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
fn func(arg: i32) -> i32 {
|
||||
arg + 1
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn imports_macro_allows_trailing_comma_and_none() {
|
||||
let _ = imports! {
|
||||
"env" => {
|
||||
"func" => func!(func),
|
||||
},
|
||||
};
|
||||
let _ = imports! {
|
||||
"env" => {
|
||||
"func" => func!(func),
|
||||
}
|
||||
};
|
||||
let _ = imports! {
|
||||
"env" => {
|
||||
"func" => func!(func),
|
||||
},
|
||||
"abc" => {
|
||||
"def" => func!(func),
|
||||
}
|
||||
};
|
||||
let _ = imports! {
|
||||
"env" => {
|
||||
"func" => func!(func)
|
||||
},
|
||||
};
|
||||
let _ = imports! {
|
||||
"env" => {
|
||||
"func" => func!(func)
|
||||
}
|
||||
};
|
||||
let _ = imports! {
|
||||
"env" => {
|
||||
"func1" => func!(func),
|
||||
"func2" => func!(func)
|
||||
}
|
||||
};
|
||||
let _ = imports! {
|
||||
"env" => {
|
||||
"func1" => func!(func),
|
||||
"func2" => func!(func),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn imports_macro_allows_trailing_comma_and_none_with_state() {
|
||||
use std::{ffi, ptr};
|
||||
|
||||
fn dtor(_arg: *mut ffi::c_void) {}
|
||||
fn state_creator() -> (*mut ffi::c_void, fn(*mut ffi::c_void)) {
|
||||
(ptr::null_mut() as *mut ffi::c_void, dtor)
|
||||
}
|
||||
let _ = imports! {
|
||||
state_creator,
|
||||
"env" => {
|
||||
"func1" => func!(func),
|
||||
"func2" => func!(func),
|
||||
}
|
||||
};
|
||||
let _ = imports! {
|
||||
state_creator,
|
||||
"env" => {
|
||||
"func1" => func!(func),
|
||||
"func2" => func!(func)
|
||||
},
|
||||
};
|
||||
let _ = imports! {
|
||||
state_creator,
|
||||
"env" => {
|
||||
"func1" => func!(func),
|
||||
"func2" => func!(func),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
//! The module module contains the implementation data structures and helper functions used to
|
||||
//! manipulate and access wasm modules.
|
||||
//! This module contains the types to manipulate and access Wasm modules.
|
||||
//!
|
||||
//! A Wasm module is the artifact of compiling WebAssembly. Wasm modules are not executable
|
||||
//! until they're instantiated with imports (via [`ImportObject`]).
|
||||
use crate::{
|
||||
backend::RunnableModule,
|
||||
cache::{Artifact, Error as CacheError},
|
||||
@ -7,10 +9,10 @@ use crate::{
|
||||
import::ImportObject,
|
||||
structures::{Map, TypedIndex},
|
||||
types::{
|
||||
FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, ImportedFuncIndex,
|
||||
ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex, Initializer,
|
||||
LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryDescriptor, MemoryIndex,
|
||||
SigIndex, TableDescriptor, TableIndex,
|
||||
ExportDescriptor, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit,
|
||||
ImportDescriptor, ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex,
|
||||
ImportedTableIndex, Initializer, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex,
|
||||
MemoryDescriptor, MemoryIndex, SigIndex, TableDescriptor, TableIndex,
|
||||
},
|
||||
Instance,
|
||||
};
|
||||
@ -52,6 +54,10 @@ pub struct ModuleInfo {
|
||||
pub imported_globals: Map<ImportedGlobalIndex, (ImportName, GlobalDescriptor)>,
|
||||
|
||||
/// Map of string to export index.
|
||||
// Implementation note: this should maintain the order that the exports appear in the
|
||||
// Wasm module. Be careful not to use APIs that may break the order!
|
||||
// Side note, because this is public we can't actually guarantee that it will remain
|
||||
// in order.
|
||||
pub exports: IndexMap<String, ExportIndex>,
|
||||
|
||||
/// Vector of data initializers.
|
||||
@ -164,6 +170,130 @@ impl Module {
|
||||
pub fn info(&self) -> &ModuleInfo {
|
||||
&self.inner.info
|
||||
}
|
||||
|
||||
/// Iterate over the [`ExportDescriptor`]s of the exports that this module provides.
|
||||
pub(crate) fn exports_iter(&self) -> impl Iterator<Item = ExportDescriptor> + '_ {
|
||||
self.info()
|
||||
.exports
|
||||
.iter()
|
||||
.map(move |(name, &ei)| ExportDescriptor {
|
||||
name,
|
||||
ty: match ei {
|
||||
ExportIndex::Func(f_idx) => {
|
||||
let sig_idx = self.info().func_assoc[f_idx].into();
|
||||
self.info().signatures[sig_idx].clone().into()
|
||||
}
|
||||
ExportIndex::Global(g_idx) => {
|
||||
let info = self.info();
|
||||
let local_global_idx =
|
||||
LocalGlobalIndex::new(g_idx.index() - info.imported_globals.len());
|
||||
info.globals[local_global_idx].desc.into()
|
||||
}
|
||||
ExportIndex::Memory(m_idx) => {
|
||||
let info = self.info();
|
||||
let local_memory_idx =
|
||||
LocalMemoryIndex::new(m_idx.index() - info.imported_memories.len());
|
||||
info.memories[local_memory_idx].into()
|
||||
}
|
||||
ExportIndex::Table(t_idx) => {
|
||||
let info = self.info();
|
||||
let local_table_idx =
|
||||
LocalTableIndex::new(t_idx.index() - info.imported_tables.len());
|
||||
info.tables[local_table_idx].into()
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the [`ExportDescriptor`]s of the exports this [`Module`] provides.
|
||||
pub fn exports(&self) -> Vec<ExportDescriptor> {
|
||||
self.exports_iter().collect()
|
||||
}
|
||||
|
||||
/// Get the [`ImportDescriptor`]s describing the imports this [`Module`]
|
||||
/// requires to be instantiated.
|
||||
pub fn imports(&self) -> Vec<ImportDescriptor> {
|
||||
let mut out = Vec::with_capacity(
|
||||
self.inner.info.imported_functions.len()
|
||||
+ self.inner.info.imported_memories.len()
|
||||
+ self.inner.info.imported_tables.len()
|
||||
+ self.inner.info.imported_globals.len(),
|
||||
);
|
||||
|
||||
/// Lookup the (namespace, name) in the [`ModuleInfo`] by index.
|
||||
fn get_import_name(
|
||||
info: &ModuleInfo,
|
||||
&ImportName {
|
||||
namespace_index,
|
||||
name_index,
|
||||
}: &ImportName,
|
||||
) -> (String, String) {
|
||||
let namespace = info.namespace_table.get(namespace_index).to_string();
|
||||
let name = info.name_table.get(name_index).to_string();
|
||||
|
||||
(namespace, name)
|
||||
}
|
||||
|
||||
let info = &self.inner.info;
|
||||
|
||||
let imported_functions = info.imported_functions.iter().map(|(idx, import_name)| {
|
||||
let (namespace, name) = get_import_name(info, import_name);
|
||||
let sig = info
|
||||
.signatures
|
||||
.get(*info.func_assoc.get(FuncIndex::new(idx.index())).unwrap())
|
||||
.unwrap();
|
||||
ImportDescriptor {
|
||||
namespace,
|
||||
name,
|
||||
ty: sig.into(),
|
||||
}
|
||||
});
|
||||
let imported_memories =
|
||||
info.imported_memories
|
||||
.values()
|
||||
.map(|(import_name, memory_descriptor)| {
|
||||
let (namespace, name) = get_import_name(info, import_name);
|
||||
ImportDescriptor {
|
||||
namespace,
|
||||
name,
|
||||
ty: memory_descriptor.into(),
|
||||
}
|
||||
});
|
||||
let imported_tables =
|
||||
info.imported_tables
|
||||
.values()
|
||||
.map(|(import_name, table_descriptor)| {
|
||||
let (namespace, name) = get_import_name(info, import_name);
|
||||
ImportDescriptor {
|
||||
namespace,
|
||||
name,
|
||||
ty: table_descriptor.into(),
|
||||
}
|
||||
});
|
||||
let imported_globals =
|
||||
info.imported_globals
|
||||
.values()
|
||||
.map(|(import_name, global_descriptor)| {
|
||||
let (namespace, name) = get_import_name(info, import_name);
|
||||
ImportDescriptor {
|
||||
namespace,
|
||||
name,
|
||||
ty: global_descriptor.into(),
|
||||
}
|
||||
});
|
||||
|
||||
out.extend(imported_functions);
|
||||
out.extend(imported_memories);
|
||||
out.extend(imported_tables);
|
||||
out.extend(imported_globals);
|
||||
out
|
||||
}
|
||||
|
||||
/// Get the custom sections matching the given name.
|
||||
pub fn custom_sections(&self, key: impl AsRef<str>) -> Option<&[Vec<u8>]> {
|
||||
let key = key.as_ref();
|
||||
self.inner.info.custom_sections.get(key).map(|v| v.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Module {
|
||||
|
@ -7,6 +7,7 @@ use crate::{
|
||||
vm,
|
||||
};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::{ptr, sync::Arc};
|
||||
|
||||
enum AnyfuncInner<'a> {
|
||||
@ -45,6 +46,46 @@ impl<'a> From<DynFunc<'a>> for Anyfunc<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<Anyfunc<'a>> for DynFunc<'a> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(anyfunc: Anyfunc<'a>) -> Result<Self, Self::Error> {
|
||||
match anyfunc.inner {
|
||||
AnyfuncInner::Managed(df) => Ok(df),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: implement this when `vm::Anyfunc` is updated (aka avoiding the linear scan in `wrap`)
|
||||
impl<'a, Args: WasmTypeList, Rets: WasmTypeList> TryFrom<Anyfunc<'a>> for Func<'a, Args, Rets> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(anyfunc: Anyfunc<'a>) -> Result<Self, Self::Error> {
|
||||
match anyfunc.inner {
|
||||
AnyfuncInner::Host {
|
||||
ptr,
|
||||
ctx,
|
||||
signature,
|
||||
} => {
|
||||
// TODO: return more specific error
|
||||
let ptr = NonNull::new(ptr as _).ok_or(())?;
|
||||
if signature.params() != Args::types() || signature.returns() != Rets::types() {
|
||||
// TODO: return more specific error
|
||||
return Err(());
|
||||
}
|
||||
let wasm = todo!("Figure out how to get typed_func::Wasm");
|
||||
// TODO: handle func_env
|
||||
let func_env = None;
|
||||
Ok(unsafe { Func::from_raw_parts(wasm, ptr, func_env, ctx) })
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub struct AnyfuncTable {
|
||||
pub(crate) backing: Vec<vm::Anyfunc>,
|
||||
max: Option<u32>,
|
||||
@ -98,6 +139,25 @@ impl AnyfuncTable {
|
||||
Some(starting_len)
|
||||
}
|
||||
|
||||
// hidden and `pub(crate)` due to incomplete implementation (blocked on `wrap` issue)
|
||||
#[doc(hidden)]
|
||||
/// Get The vm::AnyFunc at the given index.
|
||||
pub(crate) fn get<'outer_table>(&self, index: u32) -> Option<Anyfunc<'outer_table>> {
|
||||
let vm_any_func = self.backing.get(index as usize)?;
|
||||
let signature = SigRegistry.lookup_signature(vm_any_func.sig_id.into());
|
||||
// TODO: this function should take a generic type param indicating what type of
|
||||
// anyfunc we want `host` or `managed` (or perhaps we should just return DynFunc/Func directly here).
|
||||
//
|
||||
// The issue with the current implementation is that through `StorableInTable`, we'll call
|
||||
// `TryFrom<Anyfuc> for Dynfunc` which will always fail because we always return a `Host` function here.
|
||||
Some(Anyfunc {
|
||||
inner: AnyfuncInner::Host {
|
||||
ptr: vm_any_func.func,
|
||||
signature,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set(&mut self, index: u32, element: Anyfunc) -> Result<(), ()> {
|
||||
if let Some(slot) = self.backing.get_mut(index as usize) {
|
||||
let anyfunc = match element.inner {
|
||||
|
@ -8,6 +8,7 @@ use crate::{
|
||||
vm,
|
||||
};
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
fmt, ptr,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
@ -18,12 +19,76 @@ pub use self::anyfunc::Anyfunc;
|
||||
pub(crate) use self::anyfunc::AnyfuncTable;
|
||||
use crate::error::GrowError;
|
||||
|
||||
/// Error type indicating why a table access failed.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum TableAccessError {
|
||||
/// The index wasn't valid, so no element could be accessed.
|
||||
IndexError,
|
||||
|
||||
// we'll need this error when we support tables holding more types
|
||||
#[allow(dead_code)]
|
||||
/// The type of the table was incorrect, so no element could be accessed.
|
||||
TypeError,
|
||||
}
|
||||
|
||||
/// Trait indicates types that can be stored in tables
|
||||
pub trait StorableInTable: Sized {
|
||||
/// Attempt to lookup self in the given table.
|
||||
fn unwrap_self(storage: &TableStorage, index: u32) -> Result<Self, TableAccessError>;
|
||||
|
||||
/// Wrap value to be stored in a table.
|
||||
fn wrap_self(self, storage: &mut TableStorage, index: u32) -> Result<(), TableAccessError>;
|
||||
}
|
||||
|
||||
impl<'a, F: Into<Anyfunc<'a>> + TryFrom<Anyfunc<'a>>> StorableInTable for F {
|
||||
fn unwrap_self(storage: &TableStorage, index: u32) -> Result<Self, TableAccessError> {
|
||||
match storage {
|
||||
TableStorage::Anyfunc(ref anyfunc_table) => {
|
||||
let anyfunc = anyfunc_table
|
||||
.get(index)
|
||||
.ok_or(TableAccessError::IndexError)?;
|
||||
// Should this be a different error value because it's not a table type error?
|
||||
F::try_from(anyfunc).map_err(|_| TableAccessError::TypeError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn wrap_self(self, storage: &mut TableStorage, index: u32) -> Result<(), TableAccessError> {
|
||||
let anyfunc: Anyfunc = self.into();
|
||||
|
||||
match storage {
|
||||
TableStorage::Anyfunc(ref mut anyfunc_table) => anyfunc_table
|
||||
.set(index, anyfunc)
|
||||
.map_err(|_| TableAccessError::IndexError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Kind of table element.
|
||||
// note to implementors: all types in `Element` should implement `StorableInTable`.
|
||||
pub enum Element<'a> {
|
||||
/// Anyfunc.
|
||||
Anyfunc(Anyfunc<'a>),
|
||||
}
|
||||
|
||||
// delegation implementation for `Element`
|
||||
impl<'a> StorableInTable for Element<'a> {
|
||||
fn unwrap_self(storage: &TableStorage, index: u32) -> Result<Self, TableAccessError> {
|
||||
match storage {
|
||||
TableStorage::Anyfunc(ref anyfunc_table) => anyfunc_table
|
||||
.get(index)
|
||||
.map(Element::Anyfunc)
|
||||
.ok_or(TableAccessError::IndexError),
|
||||
}
|
||||
}
|
||||
|
||||
fn wrap_self(self, storage: &mut TableStorage, index: u32) -> Result<(), TableAccessError> {
|
||||
match self {
|
||||
Element::Anyfunc(af) => af.wrap_self(storage, index),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Kind of table storage.
|
||||
// #[derive(Debug)]
|
||||
pub enum TableStorage {
|
||||
@ -89,17 +154,22 @@ impl Table {
|
||||
self.desc
|
||||
}
|
||||
|
||||
// Made `pub(crate)` because this API is incomplete, see `anyfunc::AnyfuncTable::get`
|
||||
// for more information.
|
||||
#[allow(dead_code)]
|
||||
/// Get the raw table value at index. A return value of `None` means either that
|
||||
/// the index or the type wasn't valid.
|
||||
pub(crate) fn get<T: StorableInTable>(&self, index: u32) -> Result<T, TableAccessError> {
|
||||
let guard = self.storage.lock().unwrap();
|
||||
let (storage, _) = &*guard;
|
||||
T::unwrap_self(storage, index)
|
||||
}
|
||||
|
||||
/// Set the element at index.
|
||||
pub fn set(&self, index: u32, element: Element) -> Result<(), ()> {
|
||||
let mut storage = self.storage.lock().unwrap();
|
||||
match &mut *storage {
|
||||
(TableStorage::Anyfunc(ref mut anyfunc_table), _) => {
|
||||
match element {
|
||||
Element::Anyfunc(anyfunc) => anyfunc_table.set(index, anyfunc),
|
||||
// _ => panic!("wrong element type for anyfunc table"),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn set<T: StorableInTable>(&self, index: u32, element: T) -> Result<(), TableAccessError> {
|
||||
let mut guard = self.storage.lock().unwrap();
|
||||
let (storage, _) = &mut *guard;
|
||||
T::wrap_self(element, storage, index)
|
||||
}
|
||||
|
||||
pub(crate) fn anyfunc_direct_access_mut<F, R>(&self, f: F) -> R
|
||||
|
@ -257,6 +257,7 @@ where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
// TODO: document the invariants `unsafe` requires here
|
||||
pub(crate) unsafe fn from_raw_parts(
|
||||
inner: Wasm,
|
||||
func: NonNull<vm::Func>,
|
||||
|
@ -254,7 +254,7 @@ pub enum ElementType {
|
||||
|
||||
/// Describes the properties of a table including the element types, minimum and optional maximum,
|
||||
/// number of elements in the table.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct TableDescriptor {
|
||||
/// Type of data stored in this table.
|
||||
pub element: ElementType,
|
||||
@ -359,6 +359,9 @@ pub struct FuncSig {
|
||||
returns: Cow<'static, [Type]>,
|
||||
}
|
||||
|
||||
/// Information about a function.
|
||||
pub type FuncDescriptor = FuncSig;
|
||||
|
||||
impl FuncSig {
|
||||
/// Creates a new function signatures with the given parameter and return types.
|
||||
pub fn new<Params, Returns>(params: Params, returns: Returns) -> Self
|
||||
@ -549,6 +552,81 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about an import such as its type and metadata.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ExternDescriptor {
|
||||
/// The import is a function.
|
||||
Function(FuncDescriptor),
|
||||
/// The import is a global variable.
|
||||
Global(GlobalDescriptor),
|
||||
/// The import is a Wasm linear memory.
|
||||
Memory(MemoryDescriptor),
|
||||
/// The import is a Wasm table.
|
||||
Table(TableDescriptor),
|
||||
}
|
||||
|
||||
impl From<FuncDescriptor> for ExternDescriptor {
|
||||
fn from(other: FuncDescriptor) -> Self {
|
||||
ExternDescriptor::Function(other)
|
||||
}
|
||||
}
|
||||
impl From<&FuncDescriptor> for ExternDescriptor {
|
||||
fn from(other: &FuncDescriptor) -> Self {
|
||||
ExternDescriptor::Function(other.clone())
|
||||
}
|
||||
}
|
||||
impl From<MemoryDescriptor> for ExternDescriptor {
|
||||
fn from(other: MemoryDescriptor) -> Self {
|
||||
ExternDescriptor::Memory(other)
|
||||
}
|
||||
}
|
||||
impl From<&MemoryDescriptor> for ExternDescriptor {
|
||||
fn from(other: &MemoryDescriptor) -> Self {
|
||||
ExternDescriptor::Memory(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TableDescriptor> for ExternDescriptor {
|
||||
fn from(other: TableDescriptor) -> Self {
|
||||
ExternDescriptor::Table(other)
|
||||
}
|
||||
}
|
||||
impl From<&TableDescriptor> for ExternDescriptor {
|
||||
fn from(other: &TableDescriptor) -> Self {
|
||||
ExternDescriptor::Table(*other)
|
||||
}
|
||||
}
|
||||
impl From<GlobalDescriptor> for ExternDescriptor {
|
||||
fn from(other: GlobalDescriptor) -> Self {
|
||||
ExternDescriptor::Global(other)
|
||||
}
|
||||
}
|
||||
impl From<&GlobalDescriptor> for ExternDescriptor {
|
||||
fn from(other: &GlobalDescriptor) -> Self {
|
||||
ExternDescriptor::Global(*other)
|
||||
}
|
||||
}
|
||||
|
||||
/// A type describing an import that a [`Module`] needs to be instantiated.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ImportDescriptor {
|
||||
/// The namespace that this import is in.
|
||||
pub namespace: String,
|
||||
/// The name of the import.
|
||||
pub name: String,
|
||||
/// The type of the import and information about the import.
|
||||
pub ty: ExternDescriptor,
|
||||
}
|
||||
|
||||
/// Type describing an export that the [`Module`] provides.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ExportDescriptor<'a> {
|
||||
/// The name identifying the export.
|
||||
pub name: &'a str,
|
||||
/// The type of the export.
|
||||
pub ty: ExternDescriptor,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::types::NativeWasmType;
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! The units module provides common WebAssembly units like [`Pages`] and conversion functions into
|
||||
//! This module provides common WebAssembly units like [`Pages`] and conversion functions into
|
||||
//! other units.
|
||||
use crate::error::PageError;
|
||||
use std::{
|
||||
@ -45,6 +45,12 @@ impl fmt::Debug for Pages {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Pages {
|
||||
fn from(other: u32) -> Self {
|
||||
Pages(other)
|
||||
}
|
||||
}
|
||||
|
||||
/// Units of WebAssembly memory in terms of 8-bit bytes.
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Bytes(pub usize);
|
||||
@ -61,6 +67,12 @@ impl From<Pages> for Bytes {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Bytes {
|
||||
fn from(other: usize) -> Self {
|
||||
Bytes(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Sub<T> for Pages
|
||||
where
|
||||
T: Into<Pages>,
|
||||
|
@ -716,10 +716,19 @@ impl LocalGlobal {
|
||||
}
|
||||
|
||||
/// Identifier for a function signature.
|
||||
///
|
||||
/// A transparent `SigIndex`
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct SigId(pub u32);
|
||||
|
||||
use crate::types::SigIndex;
|
||||
impl From<SigId> for SigIndex {
|
||||
fn from(other: SigId) -> SigIndex {
|
||||
SigIndex::new(other.0 as _)
|
||||
}
|
||||
}
|
||||
|
||||
/// Caller-checked anyfunc
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
|
@ -71,6 +71,7 @@ static WASM: &'static [u8] = &[
|
||||
|
||||
use wasmer_runtime::{
|
||||
instantiate,
|
||||
DynFunc,
|
||||
Value,
|
||||
imports,
|
||||
error,
|
||||
@ -83,7 +84,8 @@ fn main() -> error::Result<()> {
|
||||
let instance = instantiate(WASM, &import_object)?;
|
||||
|
||||
let values = instance
|
||||
.dyn_func("add_one")?
|
||||
.exports
|
||||
.get::<DynFunc>("add_one")?
|
||||
.call(&[Value::I32(42)])?;
|
||||
|
||||
assert_eq!(values[0], Value::I32(43));
|
||||
|
@ -4,7 +4,7 @@ use criterion::Criterion;
|
||||
use tempfile::tempdir;
|
||||
use wasmer_runtime::{
|
||||
cache::{Cache, FileSystemCache, WasmHash},
|
||||
compile, func, imports, instantiate, validate,
|
||||
compile, func, imports, instantiate, validate, Func,
|
||||
};
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
|
||||
@ -71,7 +71,7 @@ fn calling_fn_benchmark(c: &mut Criterion) {
|
||||
);
|
||||
let instance = instantiate(SIMPLE_WASM, &imports).unwrap();
|
||||
c.bench_function("calling fn", move |b| {
|
||||
let entry_point = instance.func::<i32, i32>("plugin_entrypoint").unwrap();
|
||||
let entry_point: Func<i32, i32> = instance.exports.get("plugin_entrypoint").unwrap();
|
||||
b.iter(|| entry_point.call(2).unwrap())
|
||||
});
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ fn main() -> Result<(), error::Error> {
|
||||
},
|
||||
})?;
|
||||
|
||||
let foo: Func<(), i32> = instance.func("dbz")?;
|
||||
let foo: Func<(), i32> = instance.exports.get("dbz")?;
|
||||
|
||||
let result = foo.call();
|
||||
|
||||
|
@ -187,7 +187,7 @@ mod tests {
|
||||
|
||||
let import_object = imports! {};
|
||||
let instance = cached_module.instantiate(&import_object).unwrap();
|
||||
let add_one: Func<i32, i32> = instance.func("add_one").unwrap();
|
||||
let add_one: Func<i32, i32> = instance.exports.get("add_one").unwrap();
|
||||
|
||||
let value = add_one.call(42).unwrap();
|
||||
|
||||
|
@ -66,7 +66,7 @@
|
||||
//!
|
||||
//! let mut instance = instantiate(WASM, &import_object)?;
|
||||
//!
|
||||
//! let add_one: Func<i32, i32> = instance.func("add_one")?;
|
||||
//! let add_one: Func<i32, i32> = instance.exports.get("add_one")?;
|
||||
//!
|
||||
//! let value = add_one.call(42)?;
|
||||
//!
|
||||
|
@ -36,7 +36,7 @@ fn error_propagation() {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let foo: Func<(), ()> = instance.func("call_err").unwrap();
|
||||
let foo: Func<(), ()> = instance.exports.get("call_err").unwrap();
|
||||
|
||||
let result = foo.call();
|
||||
|
||||
|
@ -35,7 +35,7 @@ fn serializing_works() {
|
||||
let state_bytes = {
|
||||
let instance = module.instantiate(&import_object).unwrap();
|
||||
|
||||
let start: Func<(), ()> = instance.func("_start").unwrap();
|
||||
let start: Func<(), ()> = instance.exports.get("_start").unwrap();
|
||||
start.call().unwrap();
|
||||
let state = get_wasi_state(instance.context());
|
||||
|
||||
@ -52,7 +52,7 @@ fn serializing_works() {
|
||||
|
||||
instance.context_mut().data = Box::into_raw(wasi_state) as *mut c_void;
|
||||
|
||||
let second_entry: Func<(), i32> = instance.func("second_entry").unwrap();
|
||||
let second_entry: Func<(), i32> = instance.exports.get("second_entry").unwrap();
|
||||
let result = second_entry.call().unwrap();
|
||||
assert_eq!(result, true as i32);
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ fn handle_client(mut stream: UnixStream) {
|
||||
let mut wasm_binary: Vec<u8> = Vec::with_capacity(binary_size as usize);
|
||||
unsafe { wasm_binary.set_len(binary_size as usize) };
|
||||
stream.read_exact(&mut wasm_binary).unwrap();
|
||||
use wasmer::webassembly;
|
||||
use wasmer_bin::webassembly;
|
||||
use wasmer_runtime_core::{
|
||||
backend::{CompilerConfig, MemoryBoundCheckMode},
|
||||
loader::Instance,
|
||||
|
@ -22,7 +22,7 @@ use std::str::FromStr;
|
||||
|
||||
use structopt::{clap, StructOpt};
|
||||
|
||||
use wasmer::*;
|
||||
use wasmer_bin::*;
|
||||
#[cfg(feature = "backend-cranelift")]
|
||||
use wasmer_clif_backend::CraneliftCompiler;
|
||||
#[cfg(feature = "backend-llvm")]
|
||||
@ -31,7 +31,7 @@ use wasmer_llvm_backend::{
|
||||
};
|
||||
use wasmer_runtime::{
|
||||
cache::{Cache as BaseCache, FileSystemCache, WasmHash},
|
||||
Backend, Value, VERSION,
|
||||
Backend, DynFunc, Value, VERSION,
|
||||
};
|
||||
#[cfg(feature = "managed")]
|
||||
use wasmer_runtime_core::tiering::{run_tiering, InteractiveShellContext, ShellExitOperation};
|
||||
@ -437,8 +437,10 @@ fn execute_wasi(
|
||||
.instantiate(&import_object)
|
||||
.map_err(|e| format!("Can't instantiate WASI module: {:?}", e))?;
|
||||
|
||||
let start: wasmer_runtime::Func<(), ()> =
|
||||
instance.func("_start").map_err(|e| format!("{:?}", e))?;
|
||||
let start: wasmer_runtime::Func<(), ()> = instance
|
||||
.exports
|
||||
.get("_start")
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
|
||||
#[cfg(feature = "managed")]
|
||||
{
|
||||
@ -506,7 +508,8 @@ fn execute_wasi(
|
||||
eprintln!("WARNING: Invoking aribtrary functions with WASI is not officially supported in the WASI standard yet. Use this feature at your own risk!");
|
||||
let args = options.parse_args(&module, invoke_fn)?;
|
||||
let invoke_result = instance
|
||||
.dyn_func(invoke_fn)
|
||||
.exports
|
||||
.get::<DynFunc>(invoke_fn)
|
||||
.map_err(|e| format!("Invoke failed: {:?}", e))?
|
||||
.call(&args)
|
||||
.map_err(|e| format!("Calling invoke fn failed: {:?}", e))?;
|
||||
@ -900,7 +903,8 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
|
||||
};
|
||||
|
||||
let result = instance
|
||||
.dyn_func(&invoke_fn)
|
||||
.exports
|
||||
.get::<DynFunc>(&invoke_fn)
|
||||
.map_err(|e| format!("{:?}", e))?
|
||||
.call(&args)
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
|
Loading…
x
Reference in New Issue
Block a user