diff --git a/Cargo.lock b/Cargo.lock index b3ada317..43872cb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,7 +17,7 @@ dependencies = [ "tokio", "wasmer-runtime 0.17.0 (git+http://github.com/fluencelabs/wasmer?branch=fluence)", "wasmer-runtime-core 0.17.0 (git+http://github.com/fluencelabs/wasmer?branch=fluence)", - "wasmer-wasi", + "wasmer-wasi 0.17.0 (git+http://github.com/fluencelabs/wasmer?branch=fluence)", ] [[package]] @@ -2143,6 +2143,26 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "wasmer-wasi" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bd5bfd3824c2e5cdef44db3c278b58bff917bf3347e79b34730cf2a952acc0" +dependencies = [ + "bincode", + "byteorder", + "generational-arena", + "getrandom", + "libc", + "log", + "serde", + "thiserror", + "time", + "typetag", + "wasmer-runtime-core 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8", +] + [[package]] name = "wasmer-win-exception-handler" version = "0.17.0" @@ -2255,12 +2275,15 @@ dependencies = [ [[package]] name = "wit_fce" -version = "0.1.0" +version = "0.2.0" dependencies = [ "multimap", + "parity-wasm", + "pwasm-utils", "wasmer-interface-types", "wasmer-runtime 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-runtime-core 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-wasi 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/fce/src/vm/module/api.rs b/fce/src/vm/module/api.rs index efdc010e..6668298e 100644 --- a/fce/src/vm/module/api.rs +++ b/fce/src/vm/module/api.rs @@ -20,7 +20,7 @@ use crate::vm::module::fce_result::FCEResult; use sha2::digest::generic_array::GenericArray; use sha2::digest::FixedOutput; -/// Application interface of a FCE module. Intended to use by FCE instance itself. +/// Application interface of a FCE module. Intended to use by FCE vm.instance itself. pub(crate) trait ModuleAPI { /// Invokes a module supplying byte array and expecting byte array with some outcome back. fn invoke(&mut self, argument: &[u8]) -> Result; diff --git a/wit_fce/Cargo.toml b/wit_fce/Cargo.toml index 718e7029..b2f79024 100644 --- a/wit_fce/Cargo.toml +++ b/wit_fce/Cargo.toml @@ -1,11 +1,15 @@ [package] name = "wit_fce" -version = "0.1.0" +version = "0.2.0" authors = ["Fluence Labs"] edition = "2018" [dependencies] wasmer-runtime = "0.17.0" -wasmer-runtime-core = { version = "0.17.0", features = ["dynamicfunc-fat-closures"] } -wasmer-interface-types = { git = "http://github.com/fluencelabs/interface-types" } +# dynamicfunc-fat-closures allows using state inside DynamicFunc +wasmer-core = { package = "wasmer-runtime-core", version = "0.17.0", features = ["dynamicfunc-fat-closures"] } +wasmer-wit = { package = "wasmer-interface-types", git = "http://github.com/fluencelabs/interface-types" } +wasmer-wasi = "0.17.0" multimap = "0.8.1" +parity-wasm = "0.41.0" +pwasm-utils = "0.12.0" diff --git a/wit_fce/src/main.rs b/wit_fce/src/main.rs index 2c2d74d6..848ea0d2 100644 --- a/wit_fce/src/main.rs +++ b/wit_fce/src/main.rs @@ -16,15 +16,13 @@ #![feature(get_mut_unchecked)] #![feature(new_uninit)] -mod instance; +mod vm; +mod misc; -use crate::instance::wit_module::WITModule; - -use std::collections::HashMap; -use std::sync::Arc; -use wasmer_interface_types::values::InterfaceValue; -use wasmer_runtime::{func, imports, ImportObject}; -use wasmer_runtime_core::vm::Ctx; +use vm::IValue; +use vm::FCE; +use vm::FCEModuleConfig; +use vm::FCEService; const IPFS_NODE: &str = "/Users/mike/dev/work/fluence/wasm/fce/target/wasm32-unknown-unknown/release/ipfs_node_wit.wasm"; @@ -35,34 +33,26 @@ const IPFS_RPC: &str = fn main() { let ipfs_node_bytes = std::fs::read(IPFS_NODE).unwrap(); let ipfs_rpc_bytes = std::fs::read(IPFS_RPC).unwrap(); - let imports = imports! { - "logger" => { - "log_utf8_string" => func!(logger_log_utf8_string), - }, - "host" => { - "ipfs" => func!(ipfs_call), - } - }; - let mut import_object = ImportObject::new(); - import_object.extend(imports); - let mut modules = HashMap::new(); + + let mut fce = FCE::new(); + let config = FCEModuleConfig::default(); println!("loading ipfs node module"); - let ipfs_node = WITModule::new(&ipfs_node_bytes, import_object.clone(), &modules) + fce.register_module("node", &ipfs_node_bytes, config.clone()) .expect("module successfully created"); - modules.insert("node".to_string(), Arc::new(ipfs_node)); println!("loading ipfs rpc module"); - let mut ipfs_rpc = WITModule::new(&ipfs_rpc_bytes, import_object, &modules) + fce.register_module("rpc", &ipfs_rpc_bytes, config.clone()) .expect("module successfully created"); - let result1 = ipfs_rpc - .call("invoke", &[InterfaceValue::String("0xffffff".to_string())]) + let result = fce + .call("node_rpc", "invoke", &[IValue::String("aaaa".to_string())]) .unwrap(); - println!("stack state {:?}", result1); + println!("execution result {:?}", result); } +/* fn logger_log_utf8_string(ctx: &mut Ctx, offset: i32, size: i32) { use wasmer_runtime_core::memory::ptr::{Array, WasmPtr}; @@ -73,6 +63,7 @@ fn logger_log_utf8_string(ctx: &mut Ctx, offset: i32, size: i32) { } } + fn ipfs_call(ctx: &mut Ctx, ptr: i32, size: i32) { use wasmer_runtime_core::memory::ptr::{Array, WasmPtr}; @@ -82,3 +73,4 @@ fn ipfs_call(ctx: &mut Ctx, ptr: i32, size: i32) { None => println!("fce logger: incorrect UTF8 string's been supplied to logger"), } } +*/ diff --git a/wit_fce/src/wit_fce.rs b/wit_fce/src/misc/mod.rs similarity index 71% rename from wit_fce/src/wit_fce.rs rename to wit_fce/src/misc/mod.rs index c04f4381..14d4f538 100644 --- a/wit_fce/src/wit_fce.rs +++ b/wit_fce/src/misc/mod.rs @@ -14,13 +14,6 @@ * limitations under the License. */ -use crate::instance::wit_module::WITModule; +mod slice_pretty_printer; -use std::collections::HashMap; -use wasmer_interface_types::values::InterfaceValue; -use wasmer_runtime::{func, imports, ImportObject}; -use wasmer_runtime_core::vm::Ctx; - -pub struct WITFCE { - -} +pub use slice_pretty_printer::SlicePrettyPrinter; diff --git a/wit_fce/src/misc/slice_pretty_printer.rs b/wit_fce/src/misc/slice_pretty_printer.rs new file mode 100644 index 00000000..f89e8f3c --- /dev/null +++ b/wit_fce/src/misc/slice_pretty_printer.rs @@ -0,0 +1,37 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pub struct SlicePrettyPrinter<'a>(pub &'a [u8]); + +impl<'a> std::fmt::LowerHex for SlicePrettyPrinter<'a> { + fn fmt(&self, fmtr: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + fmtr.write_fmt(format_args!("0x"))?; + for byte in self.0 { + fmtr.write_fmt(format_args!("{:02x}", byte))?; + } + Ok(()) + } +} + +impl<'a> std::fmt::UpperHex for SlicePrettyPrinter<'a> { + fn fmt(&self, fmtr: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + fmtr.write_fmt(format_args!("0x"))?; + for byte in self.0 { + fmtr.write_fmt(format_args!("{:02X}", byte))?; + } + Ok(()) + } +} diff --git a/wit_fce/src/vm/config.rs b/wit_fce/src/vm/config.rs new file mode 100644 index 00000000..22943fbb --- /dev/null +++ b/wit_fce/src/vm/config.rs @@ -0,0 +1,102 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use wasmer_wasi::WasiVersion; + +use std::path::PathBuf; +use wasmer_runtime::ImportObject; + +#[derive(Clone)] +pub struct FCEModuleConfig { + /// Maximum number of Wasm memory pages that loaded module can use. + /// Each Wasm pages is 65536 bytes long. + pub mem_pages_count: u32, + + /// If true, registers the logger Wasm module with name 'logger'. + /// This functionality is just for debugging, and this module will be disabled in future. + pub logger_enabled: bool, + + /// Import object that will be used in module instantiation process. + pub import_object: ImportObject, + + /// Desired WASI version. + pub wasi_version: WasiVersion, + + /// Environment variables for loaded modules. + pub wasi_envs: Vec>, + + /// List of available directories for loaded modules. + pub wasi_preopened_files: Vec, + + /// Mapping between paths. + pub wasi_mapped_dirs: Vec<(String, PathBuf)>, +} + +impl Default for FCEModuleConfig { + fn default() -> Self { + // some reasonable defaults + Self { + // 65536*1600 ~ 100 Mb + mem_pages_count: 1600, + logger_enabled: true, + import_object: ImportObject::new(), + wasi_version: WasiVersion::Latest, + wasi_envs: vec![], + wasi_preopened_files: vec![], + wasi_mapped_dirs: vec![] + } + } +} + +// TODO: implement debug for FCEModuleConfig + +impl FCEModuleConfig { + #[allow(dead_code)] + pub fn with_mem_pages_count(mut self, mem_pages_count: u32) -> Self { + self.mem_pages_count = mem_pages_count; + self + } + + #[allow(dead_code)] + pub fn with_logger_enable(mut self, logger_enable: bool) -> Self { + self.logger_enabled = logger_enable; + self + } + + #[allow(dead_code)] + pub fn with_wasi_version(mut self, wasi_version: WasiVersion) -> Self { + self.wasi_version = wasi_version; + self + } + + #[allow(dead_code)] + pub fn with_wasi_envs(mut self, envs: Vec>) -> Self { + self.wasi_envs = envs; + self + } + + #[allow(dead_code)] + pub fn with_wasi_preopened_files(mut self, preopened_files: Vec) -> Self { + self.wasi_preopened_files = preopened_files; + self + } + + #[allow(dead_code)] + pub fn with_wasi_mapped_dirs(mut self, mapped_dirs: Vec<(String, PathBuf)>) -> Self { + self.wasi_mapped_dirs = mapped_dirs; + self + } +} diff --git a/wit_fce/src/vm/errors.rs b/wit_fce/src/vm/errors.rs new file mode 100644 index 00000000..65f3cde4 --- /dev/null +++ b/wit_fce/src/vm/errors.rs @@ -0,0 +1,124 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use wasmer_runtime::error::{ + CallError, CompileError, CreationError, Error as WasmerError, ResolveError, RuntimeError, +}; + +use std::error::Error; + +#[derive(Debug)] +pub enum FCEError { + /// Errors for I/O errors raising while opening a file. + IOError(String), + + /// This error type is produced by Wasmer during resolving a Wasm function. + WasmerResolveError(String), + + /// Error related to calling a main Wasm module. + WasmerInvokeError(String), + + /// Error that raises during compilation Wasm code by Wasmer. + WasmerCreationError(String), + + /// Error that raises during creation of some Wasm objects (like table and memory) by Wasmer. + WasmerCompileError(String), + + /// Error that raises on the preparation step. + PrepareError(String), + + /// Indicates that there is already a module with such name. + NonUniqueModuleName, + + /// Returns where there is no module with such name. + NoSuchModule, + + /// Indicates that modules currently in use and couldn't be deleted. + ModuleInUse, +} + +impl Error for FCEError {} + +impl std::fmt::Display for FCEError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + FCEError::IOError(msg) => write!(f, "IOError: {}", msg), + FCEError::WasmerResolveError(msg) => write!(f, "WasmerResolveError: {}", msg), + FCEError::WasmerInvokeError(msg) => write!(f, "WasmerInvokeError: {}", msg), + FCEError::WasmerCompileError(msg) => write!(f, "WasmerCompileError: {}", msg), + FCEError::WasmerCreationError(msg) => write!(f, "WasmerCreationError: {}", msg), + FCEError::PrepareError(msg) => { + write!(f, "Prepare error: {}, probably module is mailformed", msg) + } + FCEError::NonUniqueModuleName => write!(f, "FCE already has module with such name"), + FCEError::NoSuchModule => write!(f, "FCE doesn't have a module with such name"), + FCEError::ModuleInUse => { + write!(f, "Module is used by other modules and couldn't be deleted") + } + } + } +} + +impl From for FCEError { + fn from(err: CreationError) -> Self { + FCEError::WasmerCreationError(format!("{}", err)) + } +} + +impl From for FCEError { + fn from(err: CompileError) -> Self { + FCEError::WasmerCompileError(format!("{}", err)) + } +} + +impl From for FCEError { + fn from(err: parity_wasm::elements::Error) -> Self { + FCEError::PrepareError(format!("{}", err)) + } +} + +impl From for FCEError { + fn from(err: CallError) -> Self { + match err { + CallError::Resolve(err) => FCEError::WasmerResolveError(format!("{}", err)), + CallError::Runtime(err) => FCEError::WasmerInvokeError(format!("{}", err)), + } + } +} + +impl From for FCEError { + fn from(err: ResolveError) -> Self { + FCEError::WasmerResolveError(format!("{}", err)) + } +} + +impl From for FCEError { + fn from(err: RuntimeError) -> Self { + FCEError::WasmerInvokeError(format!("{}", err)) + } +} + +impl From for FCEError { + fn from(err: WasmerError) -> Self { + FCEError::WasmerInvokeError(format!("{}", err)) + } +} + +impl From for FCEError { + fn from(err: std::io::Error) -> Self { + FCEError::IOError(format!("{}", err)) + } +} diff --git a/wit_fce/src/vm/fce.rs b/wit_fce/src/vm/fce.rs new file mode 100644 index 00000000..45462cbb --- /dev/null +++ b/wit_fce/src/vm/fce.rs @@ -0,0 +1,91 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::instance::FCEModule; +use super::*; + +use std::sync::Arc; +use std::collections::hash_map::Entry; +use std::collections::HashMap; + +pub struct FCE { + // set of modules registered inside FCE + modules: HashMap>, +} + +impl FCE { + pub fn new() -> Self { + Self { + modules: HashMap::new(), + } + } +} + +impl Default for FCE { + fn default() -> Self { + Self::new() + } +} + +impl FCEService for FCE { + fn call(&mut self, module_name: &str, func_name: &str, argument: &[IValue]) -> Result, FCEError> { + match self.modules.get_mut(module_name) { + // TODO: refactor errors + Some(mut module) => unsafe { + Ok(Arc::get_mut_unchecked(&mut module).call(func_name, argument).unwrap()) + }, + None => Err(FCEError::NoSuchModule), + } + } + + fn register_module( + &mut self, + module_name: S, + wasm_bytes: &[u8], + config: FCEModuleConfig, + ) -> Result<(), FCEError> + where + S: Into, + { + let prepared_wasm_bytes = + super::prepare::prepare_module(wasm_bytes, config.mem_pages_count)?; + + let module = FCEModule::new( + &prepared_wasm_bytes, + config.import_object, + &self.modules, + ).unwrap(); + + match self.modules.entry(module_name.into()) { + Entry::Vacant(entry) => { + entry.insert(Arc::new(module)); + Ok(()) + }, + Entry::Occupied(_) => Err(FCEError::NonUniqueModuleName), + } + } + + fn unregister_module(&mut self, module_name: &str) -> Result<(), FCEError> { + match self.modules.entry(module_name.to_string()) { + Entry::Vacant(_) => Err(FCEError::NoSuchModule), + + Entry::Occupied(module) => { + module.remove_entry(); + Ok(()) + } + } + } +} diff --git a/wit_fce/src/vm/fce_service.rs b/wit_fce/src/vm/fce_service.rs new file mode 100644 index 00000000..0316b29a --- /dev/null +++ b/wit_fce/src/vm/fce_service.rs @@ -0,0 +1,38 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::config::FCEModuleConfig; +use super::errors::FCEError; +use super::IValue; + +/// Describes a service behaviour in the Fluence network. +pub trait FCEService { + /// Invokes a module supplying byte array and expecting byte array with some outcome back. + fn call(&mut self, module_name: &str, function_name: &str, arguments: &[IValue]) -> Result, FCEError>; + + /// Registers new module in the FCE Service. + fn register_module( + &mut self, + module_name: S, + wasm_bytes: &[u8], + config: FCEModuleConfig, + ) -> Result<(), FCEError> + where + S: Into; + + /// Unregisters previously registered module. + fn unregister_module(&mut self, module_name: &str) -> Result<(), FCEError>; +} diff --git a/wit_fce/src/instance/errors.rs b/wit_fce/src/vm/instance/errors.rs similarity index 98% rename from wit_fce/src/instance/errors.rs rename to wit_fce/src/vm/instance/errors.rs index 78787214..721dff13 100644 --- a/wit_fce/src/instance/errors.rs +++ b/wit_fce/src/vm/instance/errors.rs @@ -14,12 +14,13 @@ * limitations under the License. */ -use std::error::Error; -use wasmer_interface_types::errors::InstructionError; +use wasmer_wit::errors::InstructionError; use wasmer_runtime::error::{ CallError, CompileError, CreationError, Error as WasmerError, ResolveError, RuntimeError, }; +use std::error::Error; + #[derive(Debug)] #[allow(unused)] pub enum WITFCEError { diff --git a/wit_fce/src/instance/exports.rs b/wit_fce/src/vm/instance/exports.rs similarity index 67% rename from wit_fce/src/instance/exports.rs rename to wit_fce/src/vm/instance/exports.rs index eb73a8e7..322b1f86 100644 --- a/wit_fce/src/instance/exports.rs +++ b/wit_fce/src/vm/instance/exports.rs @@ -14,15 +14,17 @@ * limitations under the License. */ -use wasmer_interface_types::interpreter::wasm; -use wasmer_interface_types::{types::InterfaceType, values::InterfaceValue}; +use super::{IValue, IType}; +use wasmer_wit::interpreter::wasm; -// In current implementation export simply does nothing. +// In current implementation export simply does nothing, because there is no more +// explicit instruction call-export in this version of wasmer-interface-types, +// but explicit Exports is still required by wasmer-interface-types::Interpreter. #[derive(Clone)] pub(crate) struct WITExport { - pub(crate) inputs: Vec, - pub(crate) outputs: Vec, - pub(crate) function: fn(arguments: &[InterfaceValue]) -> Result, ()>, + inputs: Vec, + outputs: Vec, + function: fn(arguments: &[IValue]) -> Result, ()>, } impl WITExport { @@ -45,15 +47,15 @@ impl wasm::structures::Export for WITExport { self.outputs.len() } - fn inputs(&self) -> &[InterfaceType] { + fn inputs(&self) -> &[IType] { &self.inputs } - fn outputs(&self) -> &[InterfaceType] { + fn outputs(&self) -> &[IType] { &self.outputs } - fn call(&self, arguments: &[InterfaceValue]) -> Result, ()> { + fn call(&self, arguments: &[IValue]) -> Result, ()> { (self.function)(arguments) } } diff --git a/wit_fce/src/instance/wit_module.rs b/wit_fce/src/vm/instance/fce_module.rs similarity index 77% rename from wit_fce/src/instance/wit_module.rs rename to wit_fce/src/vm/instance/fce_module.rs index 3f48cdf6..dfe19fbd 100644 --- a/wit_fce/src/instance/wit_module.rs +++ b/wit_fce/src/vm/instance/fce_module.rs @@ -14,91 +14,76 @@ * limitations under the License. */ -use crate::instance::errors::WITFCEError; -use crate::instance::exports::WITExport; -use crate::instance::memory::{WITMemory, WITMemoryView}; -use crate::instance::wit_function::WITFunction; -use crate::instance::wit_instance::WITInstance; +use super::wit_prelude::*; +use super::{IType, IValue, WValue}; -use wasmer_interface_types as wit; -use wasmer_interface_types::ast::{Interfaces, Type}; -use wasmer_interface_types::interpreter::Interpreter; -use wasmer_interface_types::values::InterfaceValue; +use wasmer_wit::ast::Interfaces; +use wasmer_wit::interpreter::Interpreter; use wasmer_runtime::{compile, ImportObject}; -use wasmer_runtime_core::Instance as WasmerInstance; +use wasmer_core::Module as WasmerModule; +use wasmer_core::Instance as WasmerInstance; +use wasmer_core::import::Namespace; use multimap::MultiMap; use std::collections::HashMap; use std::convert::TryInto; use std::mem::MaybeUninit; use std::sync::Arc; -use wasmer_interface_types::interpreter::stack::Stackable; -use wasmer_interface_types::types::InterfaceType; -use wasmer_runtime_core::import::Namespace; const WIT_SECTION_NAME: &str = "interface-types"; type WITInterpreter = Interpreter>; -type WITModuleFunc = (WITInterpreter, Vec, Vec); +// TODO: introduce new trait instead of type +type WITModuleFunc = (WITInterpreter, Vec, Vec); -pub struct WITModule { +pub struct FCEModule { + // it is needed because of WITInstance contains dynamic functions + // that internally keep pointer to Wasmer instance. #[allow(unused)] - instance: WasmerInstance, + wamser_instance: WasmerInstance, wit_instance: Arc, - funcs: HashMap, + exports_funcs: HashMap, } -impl WITModule { +impl FCEModule { pub fn new( wasm_bytes: &[u8], imports: ImportObject, - modules: &HashMap>, + modules: &HashMap>, ) -> Result { - let wasmer_instance = compile(&wasm_bytes)?; - - let wit_sections = wasmer_instance - .custom_sections(WIT_SECTION_NAME) - .ok_or_else(|| WITFCEError::NoWITSection)?; - - if wit_sections.len() > 1 { - return Err(WITFCEError::MultipleWITSections); - } - - let (remainder, interfaces) = wit::decoders::binary::parse::<()>(&wit_sections[0]) - .map_err(|_e| WITFCEError::WITParseError)?; - if remainder.len() > 1 { - return Err(WITFCEError::WITRemainderNotEmpty); - } + let wasmer_module = compile(&wasm_bytes)?; + let wit = Self::extract_wit(&wasmer_module)?; + let wit_exports = Self::instantiate_wit_exports(&wit)?; let mut wit_instance = Arc::new_uninit(); - - let callable_exports = Self::extract_wit_exports(&interfaces)?; - let mut import_object = Self::adjust_imports(&interfaces, wit_instance.clone())?; + let mut import_object = Self::adjust_imports(&wit, wit_instance.clone())?; import_object.extend(imports); - let wasmer_instance = wasmer_instance.instantiate(&import_object)?; + let wasmer_instance = wasmer_module.instantiate(&import_object)?; let wit_instance = unsafe { // get_mut_unchecked here is safe because currently only this modules have reference to // it and the environment is single-threaded *Arc::get_mut_unchecked(&mut wit_instance) = - MaybeUninit::new(WITInstance::new(&wasmer_instance, &interfaces, modules)?); + MaybeUninit::new(WITInstance::new(&wasmer_instance, &wit, modules)?); std::mem::transmute::<_, Arc>(wit_instance) }; Ok(Self { - instance: wasmer_instance, + wamser_instance: wasmer_instance, wit_instance, - funcs: callable_exports, + exports_funcs: wit_exports, }) } pub fn call( &mut self, function_name: &str, - args: &[InterfaceValue], - ) -> Result, WITFCEError> { - match self.funcs.get(function_name) { + args: &[IValue], + ) -> Result, WITFCEError> { + use wasmer_wit::interpreter::stack::Stackable; + + match self.exports_funcs.get(function_name) { Some(func) => { let result = func .0 @@ -117,8 +102,8 @@ impl WITModule { pub fn get_func_signature( &self, function_name: &str, - ) -> Result<(&Vec, &Vec), WITFCEError> { - match self.funcs.get(function_name) { + ) -> Result<(&Vec, &Vec), WITFCEError> { + match self.exports_funcs.get(function_name) { Some((_, inputs, outputs)) => Ok((inputs, outputs)), None => Err(WITFCEError::NoSuchFunction(format!( "{} has't been found during its signature looking up", @@ -127,23 +112,43 @@ impl WITModule { } } - fn extract_wit_exports( - interfaces: &Interfaces, + fn extract_wit(wasmer_instance: &WasmerModule) -> Result { + let wit_sections = wasmer_instance + .custom_sections(WIT_SECTION_NAME) + .ok_or_else(|| WITFCEError::NoWITSection)?; + + if wit_sections.len() > 1 { + return Err(WITFCEError::MultipleWITSections); + } + + let (remainder, interfaces) = wasmer_wit::decoders::binary::parse::<()>(&wit_sections[0]) + .map_err(|_e| WITFCEError::WITParseError)?; + if remainder.len() > 1 { + return Err(WITFCEError::WITRemainderNotEmpty); + } + + Ok(interfaces) + } + + fn instantiate_wit_exports( + wit: &Interfaces, ) -> Result, WITFCEError> { - let exports_type_to_names = interfaces + use super::IAstType; + + let exports_type_to_names = wit .exports .iter() .map(|export| (export.function_type, export.name.to_string())) .collect::>(); - let adapter_type_to_instructions = interfaces + let adapter_type_to_instructions = wit .adapters .iter() .map(|adapter| (adapter.function_type, &adapter.instructions)) .collect::>(); let mut wit_callable_exports = HashMap::new(); - for i in interfaces.implementations.iter() { + for i in wit.implementations.iter() { let export_function_names = match exports_type_to_names.get_vec(&i.core_function_type) { Some(export_function_names) => export_function_names, None => continue, @@ -156,7 +161,7 @@ impl WITModule { format!("adapter function with idx = {} hasn't been found during extracting exports by implementations", i.adapter_function_type) ))?; - if i.adapter_function_type >= interfaces.types.len() as u32 { + if i.adapter_function_type >= wit.types.len() as u32 { // TODO: change error type return Err(WITFCEError::NoSuchFunction(format!( "{} function id is bigger than WIT interface types count", @@ -164,8 +169,8 @@ impl WITModule { ))); }; - if let Type::Function { inputs, outputs } = - &interfaces.types[i.adapter_function_type as usize] + if let IAstType::Function { inputs, outputs } = + &wit.types[i.adapter_function_type as usize] { for export_function_name in export_function_names.iter() { // TODO: handle errors @@ -191,16 +196,16 @@ impl WITModule { interfaces: &Interfaces, wit_instance: Arc>, ) -> Result { - use crate::instance::{itype_to_wtype, wval_to_ival}; - use wasmer_interface_types::ast::Type as IType; - use wasmer_runtime_core::typed_func::DynamicFunc; - use wasmer_runtime_core::types::{FuncSig, Value}; - use wasmer_runtime_core::vm::Ctx; + use super::IAstType; + use super::type_converters::{itype_to_wtype, wval_to_ival}; + use wasmer_core::typed_func::DynamicFunc; + use wasmer_core::types::FuncSig; + use wasmer_core::vm::Ctx; // returns function that will be called from imports of Wasmer module - fn dyn_func_from_imports(inputs: Vec, func: F) -> DynamicFunc<'static> + fn dyn_func_from_imports(inputs: Vec, func: F) -> DynamicFunc<'static> where - F: Fn(&mut Ctx, &[Value]) -> Vec + 'static, + F: Fn(&mut Ctx, &[WValue]) -> Vec + 'static, { let signature = inputs.iter().map(itype_to_wtype).collect::>(); DynamicFunc::new(Arc::new(FuncSig::new(signature, vec![])), func) @@ -245,7 +250,7 @@ impl WITModule { ))); } - if let IType::Function { inputs, .. } = + if let IAstType::Function { inputs, .. } = &interfaces.types[adapter.function_type as usize] { let instructions = &adapter.instructions; @@ -253,7 +258,7 @@ impl WITModule { let wit_instance = wit_instance.clone(); let wit_inner_import = - Box::new(move |_: &mut Ctx, inputs: &[Value]| -> Vec { + Box::new(move |_: &mut Ctx, inputs: &[WValue]| -> Vec { // copy here to because otherwise wit_instance will be consumed by the closure let wit_instance_callable = wit_instance.clone(); let converted_inputs = inputs.iter().map(wval_to_ival).collect::>(); diff --git a/wit_fce/src/instance/memory.rs b/wit_fce/src/vm/instance/memory.rs similarity index 84% rename from wit_fce/src/instance/memory.rs rename to wit_fce/src/vm/instance/memory.rs index 451261d1..025ccdc3 100644 --- a/wit_fce/src/instance/memory.rs +++ b/wit_fce/src/vm/instance/memory.rs @@ -14,10 +14,10 @@ * limitations under the License. */ -use wasmer_interface_types::interpreter::wasm; -use wasmer_runtime_core::memory::{Memory, MemoryView}; +use wasmer_wit::interpreter::wasm; +use wasmer_core::memory::{Memory, MemoryView}; -pub struct WITMemoryView<'a>(pub MemoryView<'a, u8>); +pub(super) struct WITMemoryView<'a>(pub(super) MemoryView<'a, u8>); impl<'a> std::ops::Deref for WITMemoryView<'a> { type Target = [std::cell::Cell]; @@ -27,7 +27,7 @@ impl<'a> std::ops::Deref for WITMemoryView<'a> { } #[derive(Clone)] -pub struct WITMemory(pub Memory); +pub(super) struct WITMemory(pub(super) Memory); impl std::ops::Deref for WITMemory { type Target = Memory; @@ -40,7 +40,7 @@ impl wasm::structures::MemoryView for WITMemoryView<'_> {} impl<'a> wasm::structures::Memory> for WITMemory { fn view(&self) -> WITMemoryView<'a> { - use wasmer_runtime_core::vm::LocalMemory; + use wasmer_core::vm::LocalMemory; let LocalMemory { base, .. } = unsafe { *self.0.vm_local_memory() }; let length = self.0.size().bytes().0 / std::mem::size_of::(); diff --git a/wit_fce/src/vm/instance/mod.rs b/wit_fce/src/vm/instance/mod.rs new file mode 100644 index 00000000..5150b252 --- /dev/null +++ b/wit_fce/src/vm/instance/mod.rs @@ -0,0 +1,41 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod errors; +mod exports; +mod memory; +mod wit_function; +mod wit_instance; +mod type_converters; +mod fce_module; + +pub(crate) use fce_module::FCEModule; + +pub use wasmer_wit::types::InterfaceType as IType; +pub use wasmer_wit::values::InterfaceValue as IValue; +pub(self) use wasmer_wit::ast::Type as IAstType; +pub(self) use wasmer_core::types::Type as WType; +pub(self) use wasmer_core::types::Value as WValue; + +// types that often used together +pub(self) mod wit_prelude { + pub(super) use super::wit_instance::WITInstance; + pub(super) use super::exports::WITExport; + pub(super) use super::errors::WITFCEError; + pub(super) use super::wit_function::WITFunction; + pub(super) use super::memory::WITMemoryView; + pub(super) use super::memory::WITMemory; +} diff --git a/wit_fce/src/instance/mod.rs b/wit_fce/src/vm/instance/type_converters.rs similarity index 75% rename from wit_fce/src/instance/mod.rs rename to wit_fce/src/vm/instance/type_converters.rs index de602a7f..0db37d09 100644 --- a/wit_fce/src/instance/mod.rs +++ b/wit_fce/src/vm/instance/type_converters.rs @@ -14,18 +14,11 @@ * limitations under the License. */ -pub mod errors; -pub mod exports; -pub mod memory; -pub mod wit_function; -pub mod wit_instance; -pub mod wit_module; +/// Contains converters of types and values between Wasmer and wasmer_interface_types. -use wasmer_interface_types::types::InterfaceType as IType; -use wasmer_interface_types::values::InterfaceValue as IValue; -use wasmer_runtime_core::types::{Type as WType, Value as WValue}; +use super::{WType, WValue, IType, IValue}; -pub fn wtype_to_itype(ty: &WType) -> IType { +pub(super) fn wtype_to_itype(ty: &WType) -> IType { match ty { WType::I32 => IType::I32, WType::I64 => IType::I64, @@ -35,7 +28,7 @@ pub fn wtype_to_itype(ty: &WType) -> IType { } } -pub fn itype_to_wtype(ty: &IType) -> WType { +pub(super) fn itype_to_wtype(ty: &IType) -> WType { match ty { IType::I32 => WType::I32, IType::I64 => WType::I64, @@ -45,7 +38,7 @@ pub fn itype_to_wtype(ty: &IType) -> WType { } } -pub fn ival_to_wval(value: &IValue) -> WValue { +pub(super) fn ival_to_wval(value: &IValue) -> WValue { match value { IValue::I32(v) => WValue::I32(*v), IValue::I64(v) => WValue::I64(*v), @@ -55,7 +48,7 @@ pub fn ival_to_wval(value: &IValue) -> WValue { } } -pub fn wval_to_ival(value: &WValue) -> IValue { +pub(super) fn wval_to_ival(value: &WValue) -> IValue { match value { WValue::I32(v) => IValue::I32(*v), WValue::I64(v) => IValue::I64(*v), diff --git a/wit_fce/src/instance/wit_function.rs b/wit_fce/src/vm/instance/wit_function.rs similarity index 73% rename from wit_fce/src/instance/wit_function.rs rename to wit_fce/src/vm/instance/wit_function.rs index 2116820d..a89180b4 100644 --- a/wit_fce/src/instance/wit_function.rs +++ b/wit_fce/src/vm/instance/wit_function.rs @@ -14,38 +14,41 @@ * limitations under the License. */ -use crate::instance::errors::WITFCEError; -use crate::instance::wit_module::WITModule; +use super::errors::WITFCEError; +use super::fce_module::FCEModule; +use super::{IType, IValue, WValue}; + +use wasmer_wit::interpreter::wasm; +use wasmer_core::instance::DynFunc; + use std::sync::Arc; -use wasmer_interface_types::interpreter::wasm; -use wasmer_interface_types::{types::InterfaceType, values::InterfaceValue}; -use wasmer_runtime_core::instance::DynFunc; -use wasmer_runtime_core::types::Value; #[derive(Clone)] enum WITFunctionInner { Export { func: Arc>, - inputs: Vec, - outputs: Vec, + inputs: Vec, + outputs: Vec, }, Import { - // TODO: use WITInstance here instead of WITModule - wit_module: Arc, + // TODO: use dyn Callable here + wit_module: Arc, func_name: String, - inputs: Vec, - outputs: Vec, + inputs: Vec, + outputs: Vec, }, } +/// Represents all import and export functions that could be called from WIT context by call-core. #[derive(Clone)] -pub(crate) struct WITFunction { +pub(super) struct WITFunction { inner: WITFunctionInner, } impl WITFunction { - pub fn from_export(dyn_func: DynFunc<'static>) -> Result { - use super::wtype_to_itype; + /// Creates functions from a "usual" (not WIT) module export. + pub(super) fn from_export(dyn_func: DynFunc<'static>) -> Result { + use super::type_converters::wtype_to_itype; let signature = dyn_func.signature(); let inputs = signature @@ -68,7 +71,8 @@ impl WITFunction { Ok(Self { inner }) } - pub fn from_import(wit_module: Arc, func_name: String) -> Result { + /// Creates function from a module import. + pub(super) fn from_import(wit_module: Arc, func_name: String) -> Result { let func_type = wit_module.as_ref().get_func_signature(&func_name)?; let inputs = func_type.0.clone(); let outputs = func_type.1.clone(); @@ -84,16 +88,6 @@ impl WITFunction { } } -/* -impl std::ops::Deref for WITFuncs { - type Target = DynFunc<'static>; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - */ - impl wasm::structures::LocalImport for WITFunction { fn inputs_cardinality(&self) -> usize { match &self.inner { @@ -109,28 +103,28 @@ impl wasm::structures::LocalImport for WITFunction { } } - fn inputs(&self) -> &[InterfaceType] { + fn inputs(&self) -> &[IType] { match &self.inner { WITFunctionInner::Export { ref inputs, .. } => inputs, WITFunctionInner::Import { ref inputs, .. } => inputs, } } - fn outputs(&self) -> &[InterfaceType] { + fn outputs(&self) -> &[IType] { match &self.inner { WITFunctionInner::Export { ref outputs, .. } => outputs, WITFunctionInner::Import { ref outputs, .. } => outputs, } } - fn call(&self, arguments: &[InterfaceValue]) -> Result, ()> { - use super::{ival_to_wval, wval_to_ival}; + fn call(&self, arguments: &[IValue]) -> Result, ()> { + use super::type_converters::{ival_to_wval, wval_to_ival}; match &self.inner { WITFunctionInner::Export { func, .. } => func .as_ref() - .call(&arguments.iter().map(ival_to_wval).collect::>()) - .map(|results| results.iter().map(wval_to_ival).collect()) + .call(&arguments.iter().map(ival_to_wval).collect::>()) + .map(|result| result.iter().map(wval_to_ival).collect()) .map_err(|_| ()), WITFunctionInner::Import { wit_module, diff --git a/wit_fce/src/instance/wit_instance.rs b/wit_fce/src/vm/instance/wit_instance.rs similarity index 70% rename from wit_fce/src/instance/wit_instance.rs rename to wit_fce/src/vm/instance/wit_instance.rs index f0f9a453..82c35c2c 100644 --- a/wit_fce/src/instance/wit_instance.rs +++ b/wit_fce/src/vm/instance/wit_instance.rs @@ -14,38 +14,34 @@ * limitations under the License. */ -use crate::instance::errors::WITFCEError; -use crate::instance::exports::WITExport; -use crate::instance::memory::{WITMemory, WITMemoryView}; -use crate::instance::wit_function::WITFunction; +use super::wit_prelude::*; +use super::fce_module::FCEModule; -use wasmer_interface_types::interpreter::wasm; -use wasmer_runtime_core::Instance as WasmerInstance; +use wasmer_wit::interpreter::wasm; +use super::IAstType; +use wasmer_wit::ast::Interfaces; +use wasmer_wit::interpreter::wasm::structures::{ + LocalImportIndex, TypedIndex, +}; +use wasmer_core::Instance as WasmerInstance; -use crate::instance::wit_module::WITModule; use std::collections::HashMap; use std::sync::Arc; -use wasmer_interface_types::ast::Interfaces; -use wasmer_interface_types::ast::Type; -use wasmer_interface_types::interpreter::wasm::structures::{ - LocalImport, LocalImportIndex, TypedIndex, -}; -use wasmer_interface_types::types::InterfaceType; +/// Contains all import and export functions that could be called from WIT context by call-core. #[derive(Clone)] -pub struct WITInstance { - // represent all import and export functions that could be called from WIT context +pub(super) struct WITInstance { funcs: HashMap, memories: Vec, } impl WITInstance { - pub fn new( + pub(super) fn new( wasmer_instance: &WasmerInstance, interfaces: &Interfaces, - modules: &HashMap>, + modules: &HashMap>, ) -> Result { - let mut exports = Self::extract_exports(&wasmer_instance, interfaces)?; + let mut exports = Self::extract_raw_exports(&wasmer_instance, interfaces)?; let imports = Self::extract_imports(modules, interfaces, exports.len())?; let memories = Self::extract_memories(&wasmer_instance); @@ -55,25 +51,12 @@ impl WITInstance { Ok(Self { funcs, memories }) } - #[allow(unused)] - pub fn get_func_signature( - &self, - func_idx: usize, - ) -> Result<(Vec, Vec), WITFCEError> { - match self.funcs.get(&func_idx) { - Some(func) => Ok((func.inputs().to_owned(), func.outputs().to_owned())), - None => Err(WITFCEError::NoSuchFunction(format!( - "function with idx = {} hasn't been found during its signature looking up", - func_idx - ))), - } - } - - fn extract_exports( + fn extract_raw_exports( wasmer_instance: &WasmerInstance, interfaces: &Interfaces, ) -> Result, WITFCEError> { - use wasmer_runtime_core::DynFunc; + use wasmer_core::DynFunc; + let module_exports = &wasmer_instance.exports; interfaces @@ -95,7 +78,7 @@ impl WITInstance { /// Extracts only those imports that don't have implementations. fn extract_imports( - modules: &HashMap>, + modules: &HashMap>, interfaces: &Interfaces, start_index: usize, ) -> Result, WITFCEError> { @@ -104,7 +87,7 @@ impl WITInstance { .implementations .iter() .map(|i| (i.core_function_type, i.adapter_function_type)) - .collect::>(); + .collect::>(); let mut non_wit_callable_imports = HashMap::new(); @@ -127,7 +110,7 @@ impl WITInstance { } fn extract_memories(wasmer_instance: &WasmerInstance) -> Vec { - use wasmer_runtime_core::export::Export::Memory; + use wasmer_core::export::Export::Memory; let mut memories = wasmer_instance .exports() @@ -148,11 +131,11 @@ impl WITInstance { } } -impl<'instance> wasm::structures::Instance> +impl wasm::structures::Instance> for WITInstance { fn export(&self, _export_name: &str) -> Option<&WITExport> { - // exports aren't needed for this version of WIT + // exports aren't used in this version of WIT None } @@ -171,7 +154,7 @@ impl<'instance> wasm::structures::Instance Option<&Type> { + fn wit_type(&self, _index: u32) -> Option<&IAstType> { None } } diff --git a/wit_fce/src/vm/mod.rs b/wit_fce/src/vm/mod.rs new file mode 100644 index 00000000..17445a5e --- /dev/null +++ b/wit_fce/src/vm/mod.rs @@ -0,0 +1,28 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod fce; +mod instance; +mod fce_service; +mod config; +mod prepare; +mod errors; + +pub use fce::FCE; +pub use fce_service::FCEService; +pub use config::FCEModuleConfig; +pub use errors::FCEError; +pub use instance::{IType, IValue}; diff --git a/wit_fce/src/vm/prepare.rs b/wit_fce/src/vm/prepare.rs new file mode 100644 index 00000000..ff9763e9 --- /dev/null +++ b/wit_fce/src/vm/prepare.rs @@ -0,0 +1,80 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Similar to +// https://github.com/paritytech/substrate/blob/master/srml/contracts/src/wasm/prepare.rs +// https://github.com/nearprotocol/nearcore/blob/master/runtime/near-vm-runner/src/prepare.rs + +use super::errors::FCEError; + +use parity_wasm::{ + builder, elements, + elements::{MemorySection, MemoryType}, +}; + +struct ModuleBootstrapper { + module: elements::Module, +} + +impl<'a> ModuleBootstrapper { + fn init(module_code: &[u8]) -> Result { + let module = elements::deserialize_buffer(module_code)?; + + Ok(Self { module }) + } + + fn set_mem_pages_count(self, mem_pages_count: u32) -> Self { + let Self { mut module } = self; + + // At now, there is could be only one memory section, so + // it needs just to extract previous initial page count, + // delete an old entry and add create a new one with updated limits + let mem_initial = match module.memory_section_mut() { + Some(section) => match section.entries_mut().pop() { + Some(entry) => entry.limits().initial(), + None => 0, + }, + None => 0, + }; + + let memory_entry = MemoryType::new(mem_initial, Some(mem_pages_count)); + let mut default_mem_section = MemorySection::default(); + + module + .memory_section_mut() + .unwrap_or_else(|| &mut default_mem_section) + .entries_mut() + .push(memory_entry); + + let builder = builder::from_module(module); + + Self { + module: builder.build(), + } + } + + fn into_wasm(self) -> Result, FCEError> { + elements::serialize(self.module).map_err(Into::into) + } +} + +/// Prepares a Wasm module: +/// - set memory page count +pub fn prepare_module(module: &[u8], mem_pages_count: u32) -> Result, FCEError> { + ModuleBootstrapper::init(module)? + .set_mem_pages_count(mem_pages_count) + .into_wasm() +}