refactor using of ABI

This commit is contained in:
vms 2020-04-29 20:42:28 +03:00
parent 893f01d22f
commit 44ac703e65
5 changed files with 118 additions and 157 deletions

View File

@ -35,7 +35,7 @@ use exitfailure::ExitFailure;
use std::fs; use std::fs;
fn main() -> Result<(), ExitFailure> { fn main() -> Result<(), ExitFailure> {
println!("Welcome to the Frank CLI:"); println!("Welcome to the FCE CLI:");
let mut rl = rustyline::Editor::<()>::new(); let mut rl = rustyline::Editor::<()>::new();
let mut frank = Frank::new(); let mut frank = Frank::new();
@ -49,17 +49,20 @@ fn main() -> Result<(), ExitFailure> {
"add" => { "add" => {
let module_name = cmd[1].to_string(); let module_name = cmd[1].to_string();
let wasm_bytes = fs::read(cmd[2]); let wasm_bytes = fs::read(cmd[2]);
if let None = wasm_bytes { if let Err(e) = wasm_bytes {
println!("incorrect path provided"); println!("failed to read wasm module: {}", e);
continue; continue;
} }
let config = Config::default(); let config = Config::default();
let result_msg = let result_msg = match frank.register_module(
match frank.register_module(module_name, &wasm_bytes.unwrap(), config) { module_name,
Ok(_) => "module successfully registered in Frank".to_string(), &wasm_bytes.unwrap(),
Err(e) => format!("module registration failed with: {:?}", e), config,
}; ) {
Ok(_) => "module successfully registered in Frank".to_string(),
Err(e) => format!("module registration failed with: {:?}", e),
};
println!("{}", result_msg); println!("{}", result_msg);
} }
"del" => { "del" => {

View File

@ -15,41 +15,94 @@
*/ */
use crate::vm::module::frank_result::FrankResult; use crate::vm::module::frank_result::FrankResult;
use crate::vm::module::{FrankModule, ModuleABI, ModuleAPI}; use crate::vm::module::{FrankModule, ModuleAPI};
use crate::vm::{config::Config, errors::FrankError, service::FrankService}; use crate::vm::{config::Config, errors::FrankError, service::FrankService};
use sha2::{digest::generic_array::GenericArray, digest::FixedOutput}; use sha2::{digest::generic_array::GenericArray, digest::FixedOutput};
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::collections::HashMap; use std::collections::HashMap;
use wasmer_runtime::{func, Ctx}; use wasmer_runtime::{func, Ctx};
use wasmer_runtime_core::import::ImportObject; use wasmer_runtime_core::import::{ImportObject, Namespace};
#[derive(Default)]
pub struct Dispatcher {
api: HashMap<String, ModuleABI>,
}
impl Dispatcher {
pub fn new() -> Self {
Self {
api: HashMap::new(),
}
}
}
#[derive(Default)]
pub struct Frank { pub struct Frank {
// set of modules registered inside Frank
modules: HashMap<String, FrankModule>, modules: HashMap<String, FrankModule>,
dispatcher: Dispatcher,
// contains ABI of each registered module in specific format for Wasmer
abi_import_object: ImportObject,
} }
impl Frank { impl Frank {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
modules: HashMap::new(), modules: HashMap::new(),
dispatcher: Dispatcher::new(), abi_import_object: ImportObject::new(),
} }
} }
/// Adds ABI of a module with provided module name to the abi_import_object.
fn create_import_object(module: &FrankModule, config: &Config) -> Namespace {
let mut namespace = Namespace::new();
// TODO: introduce a macro for such things
let allocate = module.abi.allocate.clone();
namespace.insert(
config.allocate_fn_name.clone(),
func!(move |_ctx: &mut Ctx, size: i32| -> i32 {
allocate
.as_ref()
.unwrap()
.call(size)
.expect("allocate failed")
}),
);
let invoke = module.abi.invoke.clone();
namespace.insert(
config.invoke_fn_name.clone(),
func!(move |_ctx: &mut Ctx, offset: i32, size: i32| -> i32 {
invoke
.as_ref()
.unwrap()
.call(offset, size)
.expect("invoke failed")
}),
);
let deallocate = module.abi.deallocate.clone();
namespace.insert(
config.deallocate_fn_name.clone(),
func!(move |_ctx: &mut Ctx, ptr: i32, size: i32| {
deallocate
.as_ref()
.unwrap()
.call(ptr, size)
.expect("deallocate failed");
}),
);
let store = module.abi.store.clone();
namespace.insert(
config.store_fn_name.clone(),
func!(move |_ctx: &mut Ctx, offset: i32, value: i32| {
store
.as_ref()
.unwrap()
.call(offset, value)
.expect("store failed")
}),
);
let load = module.abi.load.clone();
namespace.insert(
config.load_fn_name.clone(),
func!(move |_ctx: &mut Ctx, offset: i32| -> i32 {
load.as_ref().unwrap().call(offset).expect("load failed")
}),
);
namespace
}
} }
impl FrankService for Frank { impl FrankService for Frank {
@ -69,81 +122,23 @@ impl FrankService for Frank {
let prepared_wasm_bytes = let prepared_wasm_bytes =
crate::vm::prepare::prepare_module(wasm_bytes, config.mem_pages_count)?; crate::vm::prepare::prepare_module(wasm_bytes, config.mem_pages_count)?;
let mut import_object = ImportObject::new(); let module = FrankModule::new(
for (module, abi) in self.dispatcher.api.iter() { &prepared_wasm_bytes,
use wasmer_runtime_core::import::Namespace; config.clone(),
self.abi_import_object.clone_ref(),
)?;
// TODO: introduce a macro for such things // registers ABI of newly registered module in abi_import_object
let mut namespace = Namespace::new(); let namespace = Frank::create_import_object(&module, &config);
let allocate = abi.allocate.clone(); self.abi_import_object.register(module_name.clone(), namespace);
namespace.insert(
config.allocate_fn_name.clone(),
func!(move |_ctx: &mut Ctx, size: i32| -> i32 {
allocate
.as_ref()
.unwrap()
.call(size)
.expect("allocate failed")
}),
);
let invoke = abi.invoke.clone(); match self.modules.entry(module_name) {
namespace.insert( Entry::Vacant(entry) => {
config.invoke_fn_name.clone(), entry.insert(module);
func!(move |_ctx: &mut Ctx, offset: i32, size: i32| -> i32 { Ok(())
invoke },
.as_ref() Entry::Occupied(_) => Err(FrankError::NonUniqueModuleName)
.unwrap()
.call(offset, size)
.expect("invoke failed")
}),
);
let deallocate = abi.deallocate.clone();
namespace.insert(
config.deallocate_fn_name.clone(),
func!(move |_ctx: &mut Ctx, ptr: i32, size: i32| {
deallocate
.as_ref()
.unwrap()
.call(ptr, size)
.expect("deallocate failed");
}),
);
let store = abi.store.clone();
namespace.insert(
config.store_fn_name.clone(),
func!(move |_ctx: &mut Ctx, offset: i32, value: i32| {
store
.as_ref()
.unwrap()
.call(offset, value)
.expect("store failed")
}),
);
let load = abi.load.clone();
namespace.insert(
config.load_fn_name.clone(),
func!(move |_ctx: &mut Ctx, offset: i32| -> i32 {
load.as_ref().unwrap().call(offset).expect("load failed")
}),
);
import_object.register(module, namespace);
} }
let (module, module_abi) = FrankModule::new(&prepared_wasm_bytes, config, import_object)?;
match self.modules.entry(module_name.clone()) {
Entry::Vacant(entry) => entry.insert(module),
Entry::Occupied(_) => return Err(FrankError::NonUniqueModuleName),
};
// registers new abi in a dispatcher
self.dispatcher.api.insert(module_name, module_abi);
Ok(())
} }
fn unregister_module(&mut self, module_name: &str) -> Result<(), FrankError> { fn unregister_module(&mut self, module_name: &str) -> Result<(), FrankError> {

View File

@ -48,24 +48,3 @@ pub struct ModuleABI {
/// Loads one bytes from provided address. /// Loads one bytes from provided address.
pub(crate) load: Option<Func<'static, i32, i32>>, pub(crate) load: Option<Func<'static, i32, i32>>,
} }
impl Drop for ModuleABI {
// The manually drop is needed because at first we need to delete functions
// and only then instance.
fn drop(&mut self) {
#[allow(clippy::drop_copy)]
drop(self.allocate.as_ref());
#[allow(clippy::drop_copy)]
drop(self.deallocate.as_ref());
#[allow(clippy::drop_copy)]
drop(self.invoke.as_ref());
#[allow(clippy::drop_copy)]
drop(self.store.as_ref());
#[allow(clippy::drop_copy)]
drop(self.load.as_ref());
}
}

View File

@ -21,25 +21,14 @@ use crate::vm::module::{ModuleABI, ModuleAPI};
use sha2::digest::generic_array::GenericArray; use sha2::digest::generic_array::GenericArray;
use sha2::digest::FixedOutput; use sha2::digest::FixedOutput;
use wasmer_runtime::{compile, func, imports, Ctx, Func, Instance}; use wasmer_runtime::{compile, func, imports, Ctx, Instance};
use wasmer_runtime_core::import::ImportObject; use wasmer_runtime_core::import::ImportObject;
use wasmer_runtime_core::memory::ptr::{Array, WasmPtr}; use wasmer_runtime_core::memory::ptr::{Array, WasmPtr};
use wasmer_wasi::generate_import_object_for_version; use wasmer_wasi::generate_import_object_for_version;
pub struct FrankModule { pub(crate) struct FrankModule {
instance: &'static Instance, instance: &'static Instance,
pub(crate) abi: ModuleABI,
// It is safe to use unwrap() while calling these functions because Option is used here
// just to allow partially initialization. And all Option fields will contain Some if
// invoking Frank::new has been succeed.
/// Allocates a region of memory inside a module. Used for passing argument inside the module.
allocate: Option<Func<'static, i32, i32>>,
/// Deallocates previously allocated memory region.
deallocate: Option<Func<'static, (i32, i32), ()>>,
/// Calls the main entry point of a module called invoke.
invoke: Option<Func<'static, (i32, i32), i32>>,
} }
impl FrankModule { impl FrankModule {
@ -48,7 +37,7 @@ impl FrankModule {
wasm_bytes: &[u8], wasm_bytes: &[u8],
config: Config, config: Config,
imports: ImportObject, imports: ImportObject,
) -> Result<(Self, ModuleABI), FrankError> { ) -> Result<Self, FrankError> {
let logger_imports = imports! { let logger_imports = imports! {
"logger" => { "logger" => {
"log_utf8_string" => func!(FrankModule::logger_log_utf8_string), "log_utf8_string" => func!(FrankModule::logger_log_utf8_string),
@ -68,22 +57,15 @@ impl FrankModule {
let instance = compile(&wasm_bytes)?.instantiate(&import_object)?; let instance = compile(&wasm_bytes)?.instantiate(&import_object)?;
let instance: &'static mut Instance = Box::leak(Box::new(instance)); let instance: &'static mut Instance = Box::leak(Box::new(instance));
let abi = ModuleABI {
allocate: Some(instance.exports.get(&config.allocate_fn_name)?),
deallocate: Some(instance.exports.get(&config.deallocate_fn_name)?),
invoke: Some(instance.exports.get(&config.invoke_fn_name)?),
store: Some(instance.exports.get(&config.store_fn_name)?),
load: Some(instance.exports.get(&config.load_fn_name)?),
};
Ok(( Ok(Self { instance, abi })
Self {
instance,
allocate: Some(instance.exports.get(&config.allocate_fn_name)?),
deallocate: Some(instance.exports.get(&config.deallocate_fn_name)?),
invoke: Some(instance.exports.get(&config.invoke_fn_name)?),
},
ModuleABI {
allocate: Some(instance.exports.get(&config.allocate_fn_name)?),
deallocate: Some(instance.exports.get(&config.deallocate_fn_name)?),
invoke: Some(instance.exports.get(&config.invoke_fn_name)?),
store: Some(instance.exports.get(&config.store_fn_name)?),
load: Some(instance.exports.get(&config.load_fn_name)?),
},
))
} }
/// Prints utf8 string of the given size from the given offset. Called from the wasm. /// Prints utf8 string of the given size from the given offset. Called from the wasm.
@ -133,7 +115,7 @@ impl ModuleAPI for FrankModule {
// allocate memory for the given argument and write it to memory // allocate memory for the given argument and write it to memory
let argument_len = argument.len() as i32; let argument_len = argument.len() as i32;
let argument_address = if argument_len != 0 { let argument_address = if argument_len != 0 {
let address = self.allocate.as_ref().unwrap().call(argument_len)?; let address = self.abi.allocate.as_ref().unwrap().call(argument_len)?;
self.write_to_mem(address as usize, argument)?; self.write_to_mem(address as usize, argument)?;
address address
} else { } else {
@ -142,13 +124,15 @@ impl ModuleAPI for FrankModule {
// invoke a main module, read a result and deallocate it // invoke a main module, read a result and deallocate it
let result_address = self let result_address = self
.abi
.invoke .invoke
.as_ref() .as_ref()
.unwrap() .unwrap()
.call(argument_address, argument_len)?; .call(argument_address, argument_len)?;
let result = self.read_result_from_mem(result_address as _)?; let result = self.read_result_from_mem(result_address as _)?;
self.deallocate self.abi
.deallocate
.as_ref() .as_ref()
.unwrap() .unwrap()
.call(result_address, result.len() as i32)?; .call(result_address, result.len() as i32)?;

View File

@ -14,11 +14,11 @@
* limitations under the License. * limitations under the License.
*/ */
pub mod abi; pub(crate) mod abi;
pub mod api; pub(crate) mod api;
pub mod frank_module; pub(crate) mod frank_module;
pub mod frank_result; pub mod frank_result;
pub use abi::ModuleABI; pub(crate) use abi::ModuleABI;
pub use api::ModuleAPI; pub(crate) use api::ModuleAPI;
pub use frank_module::FrankModule; pub(crate) use frank_module::FrankModule;