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