mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-24 22:21:32 +00:00
Merge #803
803: Add method to call function at index on Ctx r=MarkMcCaskey a=MarkMcCaskey For #638 and #670 ```Rust fn call_guest_fn(ctx: &mut Ctx, guest_fn: u32) -> u32 { println!("{}", guest_fn); let guest_fn_typed = unsafe { std::mem::transmute(guest_fn) }; let result = ctx.call_with_table_index(guest_fn_typed, &[]).unwrap(); println!(" -> {:?}", result); 0 } ``` is what this looks like from the Host side See `examples/callback.rs` for an example that doesn't require `transmute` # Review - [x] Create a short description of the the change in the CHANGELOG.md file Co-authored-by: Mark McCaskey <mark@wasmer.io> Co-authored-by: Mark McCaskey <markmccaskey@users.noreply.github.com>
This commit is contained in:
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@ -12,4 +12,4 @@ links to related issues, and the context of the PR.
|
|||||||
|
|
||||||
# Review
|
# Review
|
||||||
|
|
||||||
- [ ] Create a short description of the the change in the CHANGELOG.md file
|
- [ ] Add a short description of the the change to the CHANGELOG.md file
|
||||||
|
@ -6,6 +6,7 @@ Blocks of changes will separated by version increments.
|
|||||||
|
|
||||||
## **[Unreleased]**
|
## **[Unreleased]**
|
||||||
|
|
||||||
|
- [#803](https://github.com/wasmerio/wasmer/issues/803) Add method to `Ctx` to invoke functions by their `TableIndex`
|
||||||
- [#790](https://github.com/wasmerio/wasmer/pull/790) Fix flaky test failure with LLVM, switch to large code model.
|
- [#790](https://github.com/wasmerio/wasmer/pull/790) Fix flaky test failure with LLVM, switch to large code model.
|
||||||
- [#788](https://github.com/wasmerio/wasmer/pull/788) Use union merge on the changelog file.
|
- [#788](https://github.com/wasmerio/wasmer/pull/788) Use union merge on the changelog file.
|
||||||
- [#785](https://github.com/wasmerio/wasmer/pull/785) Include Apache license file for spectests.
|
- [#785](https://github.com/wasmerio/wasmer/pull/785) Include Apache license file for spectests.
|
||||||
|
@ -58,7 +58,7 @@ members = [
|
|||||||
"lib/wasi-tests",
|
"lib/wasi-tests",
|
||||||
"lib/emscripten-tests",
|
"lib/emscripten-tests",
|
||||||
"lib/middleware-common-tests",
|
"lib/middleware-common-tests",
|
||||||
"examples/plugin-for-example"
|
"examples/plugin-for-example",
|
||||||
]
|
]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
@ -105,3 +105,7 @@ managed = ["backend-singlepass", "wasmer-runtime-core/managed"]
|
|||||||
[[example]]
|
[[example]]
|
||||||
name = "plugin"
|
name = "plugin"
|
||||||
crate-type = ["bin"]
|
crate-type = ["bin"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "callback"
|
||||||
|
crate-type = ["bin"]
|
||||||
|
8
Makefile
8
Makefile
@ -1,4 +1,4 @@
|
|||||||
.PHONY: spectests emtests clean build install lint precommit docs
|
.PHONY: spectests emtests clean build install lint precommit docs examples
|
||||||
|
|
||||||
# Generate files
|
# Generate files
|
||||||
generate-spectests:
|
generate-spectests:
|
||||||
@ -111,12 +111,16 @@ test: spectests emtests middleware wasitests circleci-clean test-rest
|
|||||||
|
|
||||||
|
|
||||||
# Integration tests
|
# Integration tests
|
||||||
integration-tests: release-clif
|
integration-tests: release-clif examples
|
||||||
echo "Running Integration Tests"
|
echo "Running Integration Tests"
|
||||||
./integration_tests/lua/test.sh
|
./integration_tests/lua/test.sh
|
||||||
./integration_tests/nginx/test.sh
|
./integration_tests/nginx/test.sh
|
||||||
./integration_tests/cowsay/test.sh
|
./integration_tests/cowsay/test.sh
|
||||||
|
|
||||||
|
examples:
|
||||||
|
cargo run --example plugin
|
||||||
|
cargo run --example callback
|
||||||
|
|
||||||
|
|
||||||
# Utils
|
# Utils
|
||||||
lint:
|
lint:
|
||||||
|
5
examples/callback-guest/README.md
Normal file
5
examples/callback-guest/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Call back guest
|
||||||
|
|
||||||
|
This is part of the `callback` example. This Wasm module passes host imports and its own functions to the Wasm host to execute.
|
||||||
|
|
||||||
|
See `examples/callback.rs` for the host
|
24
examples/callback-guest/callback-guest.rs
Normal file
24
examples/callback-guest/callback-guest.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
extern "C" {
|
||||||
|
fn call_guest_fn(f: u32) -> u32;
|
||||||
|
fn call_guest_fn2(f: u32) -> u32;
|
||||||
|
fn host_callback() -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
fn test_callback() -> u32 {
|
||||||
|
42
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
fn test_callback2() -> u32 {
|
||||||
|
45
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
unsafe { call_guest_fn(test_callback as usize as u32) };
|
||||||
|
unsafe { call_guest_fn(host_callback as usize as u32) };
|
||||||
|
unsafe { call_guest_fn(test_callback2 as usize as u32) };
|
||||||
|
unsafe { call_guest_fn2(test_callback2 as usize as u32) };
|
||||||
|
unsafe { call_guest_fn2(test_callback as usize as u32) };
|
||||||
|
unsafe { call_guest_fn2(host_callback as usize as u32) };
|
||||||
|
}
|
BIN
examples/callback-guest/callback-guest.wasm
Executable file
BIN
examples/callback-guest/callback-guest.wasm
Executable file
Binary file not shown.
46
examples/callback.rs
Normal file
46
examples/callback.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/// 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_core::{structures::TypedIndex, types::TableIndex};
|
||||||
|
|
||||||
|
static WASM: &'static str = "examples/callback-guest/callback-guest.wasm";
|
||||||
|
|
||||||
|
/// This function matches our arbitrarily decided callback signature
|
||||||
|
/// in this example we'll only call functions that take no arguments and return one value
|
||||||
|
fn host_callback(_ctx: &mut Ctx) -> u32 {
|
||||||
|
55
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_guest_fn(ctx: &mut Ctx, guest_fn: u32) -> u32 {
|
||||||
|
// We get a TableIndex from our raw value passed in
|
||||||
|
let guest_fn_typed = TableIndex::new(guest_fn as usize);
|
||||||
|
// and use it to call the corresponding function
|
||||||
|
let result = ctx.call_with_table_index(guest_fn_typed, &[]).unwrap();
|
||||||
|
|
||||||
|
println!("Guest fn {} returned {:?}", guest_fn, result);
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let wasm_bytes =
|
||||||
|
std::fs::read(WASM).expect(&format!("Could not read in WASM plugin at {}", WASM));
|
||||||
|
|
||||||
|
let imports = imports! {
|
||||||
|
"env" => {
|
||||||
|
"call_guest_fn" => func!(call_guest_fn),
|
||||||
|
"call_guest_fn2" => func!(call_guest_fn),
|
||||||
|
"host_callback" => func!(host_callback),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let compiler = compiler_for_backend(Backend::default()).unwrap();
|
||||||
|
let module = compile_with(&wasm_bytes[..], compiler.as_ref()).unwrap();
|
||||||
|
let instance = module
|
||||||
|
.instantiate(&imports)
|
||||||
|
.expect("failed to instantiate wasm module");
|
||||||
|
|
||||||
|
let entry_point = instance.func::<(u32, u32), u32>("main").unwrap();
|
||||||
|
|
||||||
|
entry_point.call(0, 0).expect("START");
|
||||||
|
}
|
@ -527,29 +527,12 @@ fn call_func_with_index(
|
|||||||
args: &[Value],
|
args: &[Value],
|
||||||
rets: &mut Vec<Value>,
|
rets: &mut Vec<Value>,
|
||||||
) -> CallResult<()> {
|
) -> CallResult<()> {
|
||||||
rets.clear();
|
|
||||||
|
|
||||||
let sig_index = *info
|
let sig_index = *info
|
||||||
.func_assoc
|
.func_assoc
|
||||||
.get(func_index)
|
.get(func_index)
|
||||||
.expect("broken invariant, incorrect func index");
|
.expect("broken invariant, incorrect func index");
|
||||||
|
|
||||||
let signature = &info.signatures[sig_index];
|
let signature = &info.signatures[sig_index];
|
||||||
let num_results = signature.returns().len();
|
|
||||||
let num_results = num_results
|
|
||||||
+ signature
|
|
||||||
.returns()
|
|
||||||
.iter()
|
|
||||||
.filter(|&&ty| ty == Type::V128)
|
|
||||||
.count();
|
|
||||||
rets.reserve(num_results);
|
|
||||||
|
|
||||||
if !signature.check_param_value_types(args) {
|
|
||||||
Err(ResolveError::Signature {
|
|
||||||
expected: signature.clone(),
|
|
||||||
found: args.iter().map(|val| val.ty()).collect(),
|
|
||||||
})?
|
|
||||||
}
|
|
||||||
|
|
||||||
let func_ptr = match func_index.local_or_import(info) {
|
let func_ptr = match func_index.local_or_import(info) {
|
||||||
LocalOrImport::Local(local_func_index) => {
|
LocalOrImport::Local(local_func_index) => {
|
||||||
@ -567,6 +550,39 @@ fn call_func_with_index(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let wasm = runnable
|
||||||
|
.get_trampoline(info, sig_index)
|
||||||
|
.expect("wasm trampoline");
|
||||||
|
|
||||||
|
call_func_with_index_inner(ctx_ptr, func_ptr, signature, wasm, args, rets)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn call_func_with_index_inner(
|
||||||
|
ctx_ptr: *mut vm::Ctx,
|
||||||
|
func_ptr: NonNull<vm::Func>,
|
||||||
|
signature: &FuncSig,
|
||||||
|
wasm: Wasm,
|
||||||
|
args: &[Value],
|
||||||
|
rets: &mut Vec<Value>,
|
||||||
|
) -> CallResult<()> {
|
||||||
|
rets.clear();
|
||||||
|
|
||||||
|
let num_results = signature.returns().len();
|
||||||
|
let num_results = num_results
|
||||||
|
+ signature
|
||||||
|
.returns()
|
||||||
|
.iter()
|
||||||
|
.filter(|&&ty| ty == Type::V128)
|
||||||
|
.count();
|
||||||
|
rets.reserve(num_results);
|
||||||
|
|
||||||
|
if !signature.check_param_value_types(args) {
|
||||||
|
Err(ResolveError::Signature {
|
||||||
|
expected: signature.clone(),
|
||||||
|
found: args.iter().map(|val| val.ty()).collect(),
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
|
||||||
let mut raw_args: SmallVec<[u64; 8]> = SmallVec::new();
|
let mut raw_args: SmallVec<[u64; 8]> = SmallVec::new();
|
||||||
for v in args {
|
for v in args {
|
||||||
match v {
|
match v {
|
||||||
@ -598,9 +614,7 @@ fn call_func_with_index(
|
|||||||
trampoline,
|
trampoline,
|
||||||
invoke,
|
invoke,
|
||||||
invoke_env,
|
invoke_env,
|
||||||
} = runnable
|
} = wasm;
|
||||||
.get_trampoline(info, sig_index)
|
|
||||||
.expect("wasm trampoline");
|
|
||||||
|
|
||||||
let run_wasm = |result_space: *mut u64| unsafe {
|
let run_wasm = |result_space: *mut u64| unsafe {
|
||||||
let mut trap_info = WasmTrapInfo::Unknown;
|
let mut trap_info = WasmTrapInfo::Unknown;
|
||||||
|
@ -44,7 +44,7 @@ impl<'a> From<DynFunc<'a>> for Anyfunc<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct AnyfuncTable {
|
pub struct AnyfuncTable {
|
||||||
backing: Vec<vm::Anyfunc>,
|
pub(crate) backing: Vec<vm::Anyfunc>,
|
||||||
max: Option<u32>,
|
max: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ use std::{cell::RefCell, fmt, ptr, rc::Rc};
|
|||||||
mod anyfunc;
|
mod anyfunc;
|
||||||
|
|
||||||
pub use self::anyfunc::Anyfunc;
|
pub use self::anyfunc::Anyfunc;
|
||||||
use self::anyfunc::AnyfuncTable;
|
pub(crate) use self::anyfunc::AnyfuncTable;
|
||||||
use crate::error::GrowError;
|
use crate::error::GrowError;
|
||||||
|
|
||||||
pub enum Element<'a> {
|
pub enum Element<'a> {
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
pub use crate::backing::{ImportBacking, LocalBacking, INTERNALS_SIZE};
|
pub use crate::backing::{ImportBacking, LocalBacking, INTERNALS_SIZE};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
error::CallResult,
|
||||||
|
instance::call_func_with_index_inner,
|
||||||
memory::{Memory, MemoryType},
|
memory::{Memory, MemoryType},
|
||||||
module::{ModuleInfo, ModuleInner},
|
module::{ModuleInfo, ModuleInner},
|
||||||
|
sig_registry::SigRegistry,
|
||||||
structures::TypedIndex,
|
structures::TypedIndex,
|
||||||
types::{LocalOrImport, MemoryIndex},
|
types::{LocalOrImport, MemoryIndex, TableIndex, Value},
|
||||||
vmcalls,
|
vmcalls,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
cell::UnsafeCell,
|
cell::UnsafeCell,
|
||||||
ffi::c_void,
|
ffi::c_void,
|
||||||
mem, ptr,
|
mem,
|
||||||
|
ptr::{self, NonNull},
|
||||||
sync::atomic::{AtomicUsize, Ordering},
|
sync::atomic::{AtomicUsize, Ordering},
|
||||||
sync::Once,
|
sync::Once,
|
||||||
};
|
};
|
||||||
@ -393,6 +397,41 @@ impl Ctx {
|
|||||||
(*self.internal.internals)[field.index()] = value;
|
(*self.internal.internals)[field.index()] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calls a host or Wasm function at the given table index
|
||||||
|
pub fn call_with_table_index(
|
||||||
|
&mut self,
|
||||||
|
index: TableIndex,
|
||||||
|
args: &[Value],
|
||||||
|
) -> CallResult<Vec<Value>> {
|
||||||
|
let anyfunc_table =
|
||||||
|
unsafe { &*((**self.internal.tables).table as *mut crate::table::AnyfuncTable) };
|
||||||
|
let Anyfunc { func, ctx, sig_id } = anyfunc_table.backing[index.index()];
|
||||||
|
|
||||||
|
let signature = SigRegistry.lookup_signature(unsafe { std::mem::transmute(sig_id.0) });
|
||||||
|
let mut rets = vec![];
|
||||||
|
|
||||||
|
let wasm = {
|
||||||
|
let module = unsafe { &*self.module };
|
||||||
|
let runnable = &module.runnable_module;
|
||||||
|
|
||||||
|
let sig_index = SigRegistry.lookup_sig_index(signature.clone());
|
||||||
|
runnable
|
||||||
|
.get_trampoline(&module.info, sig_index)
|
||||||
|
.expect("wasm trampoline")
|
||||||
|
};
|
||||||
|
|
||||||
|
call_func_with_index_inner(
|
||||||
|
ctx,
|
||||||
|
NonNull::new(func as *mut _).unwrap(),
|
||||||
|
&signature,
|
||||||
|
wasm,
|
||||||
|
args,
|
||||||
|
&mut rets,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(rets)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
Reference in New Issue
Block a user