mirror of
https://github.com/fluencelabs/wasmer
synced 2025-07-01 01:21:32 +00:00
Function imports now fully working
This commit is contained in:
@ -2,15 +2,11 @@
|
||||
/// let func: fn(i32) -> i32 = get_instance_function!(instance, func_index);
|
||||
#[macro_export]
|
||||
macro_rules! get_instance_function {
|
||||
($instance:expr, $func_index:expr) => {
|
||||
{
|
||||
use std::mem;
|
||||
let func_addr = $instance.get_function_pointer($func_index);
|
||||
unsafe {
|
||||
mem::transmute(func_addr)
|
||||
}
|
||||
}
|
||||
};
|
||||
($instance:expr, $func_index:expr) => {{
|
||||
use std::mem;
|
||||
let func_addr = $instance.get_function_pointer($func_index);
|
||||
unsafe { mem::transmute(func_addr) }
|
||||
}};
|
||||
}
|
||||
|
||||
// #[cfg(feature = "debug")]
|
||||
|
@ -127,7 +127,18 @@ impl<'module> ScriptHandler for StoreCtrl<'module> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn module(&mut self, bytes: Vec<u8>, name: Option<String>) {
|
||||
let module_wrapped = instantiate(bytes, None);
|
||||
let mut import_object = HashMap::new();
|
||||
let mut test_import = import_object
|
||||
.entry("test".to_string())
|
||||
.or_insert_with(|| HashMap::new());
|
||||
fn identity(x: i32) -> i32 {
|
||||
x
|
||||
};
|
||||
test_import.insert("identity".to_string(), identity as *const u8);
|
||||
// let import_object = import_object!{
|
||||
// test.identity => fn(x: i32) {x},
|
||||
// }
|
||||
let module_wrapped = instantiate(bytes, Some(&import_object));
|
||||
let mut result = module_wrapped.expect("Module is invalid");
|
||||
// let module: &'module Module = result.module;
|
||||
self.last_module = Some(result);
|
||||
@ -163,10 +174,10 @@ impl<'module> ScriptHandler for StoreCtrl<'module> {
|
||||
}
|
||||
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
use crate::test::Bencher;
|
||||
use super::run_single_file;
|
||||
use crate::test::Bencher;
|
||||
use std::mem;
|
||||
use std::path::Path;
|
||||
#[macro_use]
|
||||
use crate::webassembly::{
|
||||
compile, instantiate, Error, ErrorKind, Export, Instance, Module, ResultObject,
|
||||
@ -199,29 +210,12 @@ mod tests {
|
||||
}
|
||||
|
||||
macro_rules! instantiate_from_wast {
|
||||
($x:expr) => {
|
||||
{
|
||||
pub const WAST_BYTES: &[u8] = include_bytes!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
$x
|
||||
));
|
||||
let wasm_bytes = wat2wasm(WAST_BYTES.to_vec()).expect("Can't convert wat to wasm");
|
||||
let result_object = instantiate(wasm_bytes, None).expect("Not compiled properly");
|
||||
result_object
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
wasm_tests!{
|
||||
_type,
|
||||
br_if,
|
||||
call,
|
||||
}
|
||||
|
||||
fn my_func () -> Vec<u8> {
|
||||
let x = String::from("hello");
|
||||
let bytes = x.into_bytes();
|
||||
return bytes;
|
||||
($x:expr) => {{
|
||||
pub const WAST_BYTES: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), $x));
|
||||
let wasm_bytes = wat2wasm(WAST_BYTES.to_vec()).expect("Can't convert wat to wasm");
|
||||
let result_object = instantiate(wasm_bytes, None).expect("Not compiled properly");
|
||||
result_object
|
||||
}};
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -234,9 +228,17 @@ mod tests {
|
||||
_ => panic!("Function not found"),
|
||||
};
|
||||
let func: fn(i32) -> i32 = get_instance_function!(instance, func_index);
|
||||
assert_eq!(func(1), 1, "Identity function not working.");
|
||||
assert_eq!(func(1), 1, "Identity function not working.");
|
||||
b.iter(|| {
|
||||
func(1);
|
||||
});
|
||||
}
|
||||
|
||||
wasm_tests!{
|
||||
_type,
|
||||
br_if,
|
||||
call,
|
||||
import,
|
||||
}
|
||||
|
||||
}
|
||||
|
8
src/spec/tests/import.wast
Normal file
8
src/spec/tests/import.wast
Normal file
@ -0,0 +1,8 @@
|
||||
(module
|
||||
(func $identity (import "test" "identity") (param i32) (result i32))
|
||||
(func (export "exported_func") (param i32) (result i32)
|
||||
(call $identity (get_local 0))
|
||||
)
|
||||
)
|
||||
|
||||
(assert_return (invoke "exported_func" (i32.const 42)) (i32.const 42))
|
@ -12,6 +12,7 @@ use cranelift_wasm::{FuncIndex, GlobalInit};
|
||||
use memmap::MmapMut;
|
||||
use region;
|
||||
use spin::RwLock;
|
||||
use std::collections::HashMap;
|
||||
use std::iter::Iterator;
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr::{self, write_unaligned};
|
||||
@ -43,15 +44,29 @@ pub fn protect_codebuf(code_buf: &Vec<u8>) -> Result<(), String> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_function_addr(
|
||||
base: *const (),
|
||||
functions: &[usize],
|
||||
fn get_function_addr(
|
||||
func_index: &FuncIndex,
|
||||
) -> *const () {
|
||||
let offset = functions[func_index.index()];
|
||||
(base as usize + offset) as _
|
||||
import_functions: &Vec<*const u8>,
|
||||
functions: &Vec<Vec<u8>>,
|
||||
) -> *const u8 {
|
||||
let index = func_index.index();
|
||||
let len = import_functions.len();
|
||||
let func_pointer = if index < len {
|
||||
import_functions[index]
|
||||
} else {
|
||||
(&functions[func_index.index() - len]).as_ptr()
|
||||
};
|
||||
func_pointer
|
||||
}
|
||||
|
||||
// pub fn get_function_addr(
|
||||
// functions: &[usize],
|
||||
// func_index: &FuncIndex,
|
||||
// ) -> *const () {
|
||||
// let offset = functions[func_index.index()];
|
||||
// (base as usize + offset) as _
|
||||
// }
|
||||
|
||||
/// Zero-sized, non-instantiable type.
|
||||
pub enum VmCtx {}
|
||||
|
||||
@ -84,6 +99,9 @@ pub struct UserData {
|
||||
pub instance: Instance,
|
||||
}
|
||||
|
||||
// The import object for the instance
|
||||
pub type ImportObject = HashMap<String, HashMap<String, *const u8>>;
|
||||
|
||||
/// An Instance of a WebAssembly module
|
||||
#[derive(Debug)]
|
||||
pub struct Instance {
|
||||
@ -101,6 +119,9 @@ pub struct Instance {
|
||||
// functions: Vec<usize>,
|
||||
functions: Vec<Vec<u8>>,
|
||||
|
||||
/// Imported functions
|
||||
import_functions: Vec<*const u8>,
|
||||
|
||||
/// The module start function
|
||||
start_func: Option<FuncIndex>,
|
||||
// Region start memory location
|
||||
@ -128,26 +149,47 @@ pub struct Instance {
|
||||
|
||||
// vmctx
|
||||
// }
|
||||
fn fake_fun(x: i32) -> i32 {
|
||||
return x * 2;
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
/// Create a new `Instance`.
|
||||
pub fn new(module: &Module) -> Result<Instance, ErrorKind> {
|
||||
pub fn new(
|
||||
module: &Module,
|
||||
import_object: Option<&ImportObject>,
|
||||
) -> Result<Instance, ErrorKind> {
|
||||
let mut tables: Vec<Vec<usize>> = Vec::new();
|
||||
let mut memories: Vec<LinearMemory> = Vec::new();
|
||||
let mut globals: Vec<u8> = Vec::new();
|
||||
let mut functions: Vec<Vec<u8>> = Vec::new();
|
||||
let mut import_functions: Vec<*const u8> = Vec::new();
|
||||
// let mut code_base: *const () = ptr::null();
|
||||
|
||||
// Instantiate functions
|
||||
{
|
||||
functions.reserve_exact(module.info.function_bodies.len());
|
||||
functions.reserve_exact(module.info.functions.len());
|
||||
let isa = isa::lookup(module.info.triple.clone())
|
||||
.unwrap()
|
||||
.finish(module.info.flags.clone());
|
||||
let mut relocations = Vec::new();
|
||||
|
||||
// let mut total_size: usize = 0;
|
||||
let mut context_and_offsets = Vec::with_capacity(module.info.function_bodies.len());
|
||||
let mut relocations = Vec::new();
|
||||
// let mut context_and_offsets = Vec::with_capacity(module.info.function_bodies.len());
|
||||
for (module, field) in module.info.imported_funcs.iter() {
|
||||
// let function = &import_object.map(|i| i.get(module).map(|m| m.get(field)));
|
||||
let mut function = fake_fun as *const u8;
|
||||
if let Some(import_object) = import_object {
|
||||
if let Some(module) = import_object.get(module) {
|
||||
if let Some(field_function) = module.get(field) {
|
||||
function = *field_function;
|
||||
}
|
||||
}
|
||||
}
|
||||
// println!("GET FUNC {:?}", function);
|
||||
import_functions.push(function);
|
||||
relocations.push(vec![]);
|
||||
}
|
||||
// Compile the functions (from cranelift IR to machine code)
|
||||
for function_body in module.info.function_bodies.values() {
|
||||
let mut func_context = Context::for_function(function_body.to_owned());
|
||||
@ -174,7 +216,7 @@ impl Instance {
|
||||
let func_offset = code_buf;
|
||||
functions.push(func_offset);
|
||||
|
||||
context_and_offsets.push(func_context);
|
||||
// context_and_offsets.push(func_context);
|
||||
relocations.push(reloc_sink.func_relocs);
|
||||
// println!("FUNCTION RELOCATIONS {:?}", reloc_sink.func_relocs)
|
||||
// total_size += code_size_offset;
|
||||
@ -189,7 +231,7 @@ impl Instance {
|
||||
for (ref reloc, ref reloc_type) in function_relocs {
|
||||
let target_func_address: isize = match reloc_type {
|
||||
RelocationType::Normal(func_index) => {
|
||||
functions[*func_index as usize].as_ptr() as isize
|
||||
get_function_addr(&FuncIndex::new(*func_index as usize), &import_functions, &functions) as isize
|
||||
},
|
||||
_ => unimplemented!()
|
||||
// RelocationType::Intrinsic(name) => {
|
||||
@ -204,18 +246,17 @@ impl Instance {
|
||||
// RelocationTarget::CurrentMemory => current_memory as isize,
|
||||
};
|
||||
// print!("FUNCTION {:?}", target_func_address);
|
||||
let body = &mut functions[i];
|
||||
let func_addr =
|
||||
get_function_addr(&FuncIndex::new(i), &import_functions, &functions);
|
||||
match reloc.reloc {
|
||||
Reloc::Abs8 => unsafe {
|
||||
let reloc_address =
|
||||
body.as_mut_ptr().offset(reloc.offset as isize) as i64;
|
||||
let reloc_address = func_addr.offset(reloc.offset as isize) as i64;
|
||||
let reloc_addend = reloc.addend;
|
||||
let reloc_abs = target_func_address as i64 + reloc_addend;
|
||||
write_unaligned(reloc_address as *mut i64, reloc_abs);
|
||||
},
|
||||
Reloc::X86PCRel4 => unsafe {
|
||||
let reloc_address =
|
||||
body.as_mut_ptr().offset(reloc.offset as isize) as isize;
|
||||
let reloc_address = func_addr.offset(reloc.offset as isize) as isize;
|
||||
let reloc_addend = reloc.addend as isize;
|
||||
// TODO: Handle overflow.
|
||||
let reloc_delta_i32 =
|
||||
@ -330,8 +371,8 @@ impl Instance {
|
||||
// to populate the table.
|
||||
|
||||
// let func_index = *elem_index - module.info.imported_funcs.len() as u32;
|
||||
let func_addr = functions[func_index.index()].as_ptr();
|
||||
// let func_addr = get_function_addr(code_base, &functions, *&func_index);
|
||||
// let func_addr = functions[func_index.index()].as_ptr();
|
||||
let func_addr = get_function_addr(&func_index, &import_functions, &functions);
|
||||
table[base + table_element.offset + i] = func_addr as _;
|
||||
}
|
||||
}
|
||||
@ -393,7 +434,8 @@ impl Instance {
|
||||
memories: Arc::new(memories.into_iter().collect()),
|
||||
globals: globals,
|
||||
functions: functions,
|
||||
start_func: start_func
|
||||
import_functions: import_functions,
|
||||
start_func: start_func,
|
||||
// code_base: code_base,
|
||||
})
|
||||
}
|
||||
@ -406,6 +448,10 @@ impl Instance {
|
||||
func_pointer.as_ptr()
|
||||
}
|
||||
|
||||
// pub fn is_imported_function(&self, func_index: FuncIndex) -> bool {
|
||||
// func_index.index() < self.import_functions.len()
|
||||
// }
|
||||
|
||||
/// Invoke a WebAssembly function given a FuncIndex and the
|
||||
/// arguments that the function should be called with
|
||||
pub fn get_function<T>(&self, func_index: FuncIndex) -> (fn() -> T) {
|
||||
@ -423,9 +469,9 @@ impl Instance {
|
||||
// the generated code. Thanks to this, we can transmute the code region into a first-class
|
||||
// Rust function and call it.
|
||||
// let func_pointer = get_function_addr(self.code_base, &self.functions, &func_index);
|
||||
let func_pointer = &self.functions[func_index.index()];
|
||||
let func_pointer = get_function_addr(&func_index, &self.import_functions, &self.functions);
|
||||
unsafe {
|
||||
let func = mem::transmute::<_, fn() -> T>(func_pointer.as_ptr());
|
||||
let func = mem::transmute::<_, fn() -> T>(func_pointer);
|
||||
func
|
||||
// let result = func(2);
|
||||
// println!("FUNCTION INVOKED, result {:?}", result);
|
||||
@ -496,6 +542,7 @@ impl Clone for Instance {
|
||||
globals: self.globals.clone(),
|
||||
functions: self.functions.clone(),
|
||||
start_func: self.start_func.clone(),
|
||||
import_functions: self.import_functions.clone(),
|
||||
// code_base: self.code_base,
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ use target_lexicon::{self, Triple};
|
||||
use wasmparser;
|
||||
|
||||
pub use self::errors::{Error, ErrorKind};
|
||||
pub use self::instance::Instance;
|
||||
pub use self::instance::{ImportObject, Instance};
|
||||
pub use self::memory::LinearMemory;
|
||||
pub use self::module::{Export, Module, ModuleInfo};
|
||||
|
||||
@ -27,8 +27,6 @@ pub struct ResultObject {
|
||||
pub instance: Instance,
|
||||
}
|
||||
|
||||
pub struct ImportObject {}
|
||||
|
||||
/// The webassembly::instantiate() function allows you to compile and
|
||||
/// instantiate WebAssembly code
|
||||
/// Params:
|
||||
@ -45,11 +43,11 @@ pub struct ImportObject {}
|
||||
/// webassembly::RuntimeError, depending on the cause of the failure.
|
||||
pub fn instantiate(
|
||||
buffer_source: Vec<u8>,
|
||||
import_object: Option<ImportObject>,
|
||||
import_object: Option<&ImportObject>,
|
||||
) -> Result<ResultObject, ErrorKind> {
|
||||
let module = compile(buffer_source)?;
|
||||
debug!("webassembly - creating instance");
|
||||
let instance = Instance::new(&module)?;
|
||||
let instance = Instance::new(&module, import_object)?;
|
||||
debug!("webassembly - instance created");
|
||||
Ok(ResultObject { module, instance })
|
||||
}
|
||||
|
Reference in New Issue
Block a user