mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-24 06:01:33 +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
|
||||
|
||||
- [ ] 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]**
|
||||
|
||||
- [#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.
|
||||
- [#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.
|
||||
|
@ -58,7 +58,7 @@ members = [
|
||||
"lib/wasi-tests",
|
||||
"lib/emscripten-tests",
|
||||
"lib/middleware-common-tests",
|
||||
"examples/plugin-for-example"
|
||||
"examples/plugin-for-example",
|
||||
]
|
||||
|
||||
[build-dependencies]
|
||||
@ -105,3 +105,7 @@ managed = ["backend-singlepass", "wasmer-runtime-core/managed"]
|
||||
[[example]]
|
||||
name = "plugin"
|
||||
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-spectests:
|
||||
@ -111,12 +111,16 @@ test: spectests emtests middleware wasitests circleci-clean test-rest
|
||||
|
||||
|
||||
# Integration tests
|
||||
integration-tests: release-clif
|
||||
integration-tests: release-clif examples
|
||||
echo "Running Integration Tests"
|
||||
./integration_tests/lua/test.sh
|
||||
./integration_tests/nginx/test.sh
|
||||
./integration_tests/cowsay/test.sh
|
||||
|
||||
examples:
|
||||
cargo run --example plugin
|
||||
cargo run --example callback
|
||||
|
||||
|
||||
# Utils
|
||||
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],
|
||||
rets: &mut Vec<Value>,
|
||||
) -> CallResult<()> {
|
||||
rets.clear();
|
||||
|
||||
let sig_index = *info
|
||||
.func_assoc
|
||||
.get(func_index)
|
||||
.expect("broken invariant, incorrect func 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) {
|
||||
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();
|
||||
for v in args {
|
||||
match v {
|
||||
@ -598,9 +614,7 @@ fn call_func_with_index(
|
||||
trampoline,
|
||||
invoke,
|
||||
invoke_env,
|
||||
} = runnable
|
||||
.get_trampoline(info, sig_index)
|
||||
.expect("wasm trampoline");
|
||||
} = wasm;
|
||||
|
||||
let run_wasm = |result_space: *mut u64| unsafe {
|
||||
let mut trap_info = WasmTrapInfo::Unknown;
|
||||
|
@ -44,7 +44,7 @@ impl<'a> From<DynFunc<'a>> for Anyfunc<'a> {
|
||||
}
|
||||
|
||||
pub struct AnyfuncTable {
|
||||
backing: Vec<vm::Anyfunc>,
|
||||
pub(crate) backing: Vec<vm::Anyfunc>,
|
||||
max: Option<u32>,
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ use std::{cell::RefCell, fmt, ptr, rc::Rc};
|
||||
mod anyfunc;
|
||||
|
||||
pub use self::anyfunc::Anyfunc;
|
||||
use self::anyfunc::AnyfuncTable;
|
||||
pub(crate) use self::anyfunc::AnyfuncTable;
|
||||
use crate::error::GrowError;
|
||||
|
||||
pub enum Element<'a> {
|
||||
|
@ -1,15 +1,19 @@
|
||||
pub use crate::backing::{ImportBacking, LocalBacking, INTERNALS_SIZE};
|
||||
use crate::{
|
||||
error::CallResult,
|
||||
instance::call_func_with_index_inner,
|
||||
memory::{Memory, MemoryType},
|
||||
module::{ModuleInfo, ModuleInner},
|
||||
sig_registry::SigRegistry,
|
||||
structures::TypedIndex,
|
||||
types::{LocalOrImport, MemoryIndex},
|
||||
types::{LocalOrImport, MemoryIndex, TableIndex, Value},
|
||||
vmcalls,
|
||||
};
|
||||
use std::{
|
||||
cell::UnsafeCell,
|
||||
ffi::c_void,
|
||||
mem, ptr,
|
||||
mem,
|
||||
ptr::{self, NonNull},
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
sync::Once,
|
||||
};
|
||||
@ -393,6 +397,41 @@ impl Ctx {
|
||||
(*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)]
|
||||
|
Reference in New Issue
Block a user