introduce service-like functionality

This commit is contained in:
vms 2020-06-02 17:20:00 +03:00
parent 880bb6f87e
commit b258caa8d6
20 changed files with 735 additions and 204 deletions

27
Cargo.lock generated
View File

@ -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]]

View File

@ -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<FCEResult, FCEError>;

View File

@ -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"

View File

@ -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"),
}
}
*/

View File

@ -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;

View File

@ -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(())
}
}

102
wit_fce/src/vm/config.rs Normal file
View File

@ -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<Vec<u8>>,
/// List of available directories for loaded modules.
pub wasi_preopened_files: Vec<PathBuf>,
/// 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<Vec<u8>>) -> Self {
self.wasi_envs = envs;
self
}
#[allow(dead_code)]
pub fn with_wasi_preopened_files(mut self, preopened_files: Vec<PathBuf>) -> 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
}
}

124
wit_fce/src/vm/errors.rs Normal file
View File

@ -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<CreationError> for FCEError {
fn from(err: CreationError) -> Self {
FCEError::WasmerCreationError(format!("{}", err))
}
}
impl From<CompileError> for FCEError {
fn from(err: CompileError) -> Self {
FCEError::WasmerCompileError(format!("{}", err))
}
}
impl From<parity_wasm::elements::Error> for FCEError {
fn from(err: parity_wasm::elements::Error) -> Self {
FCEError::PrepareError(format!("{}", err))
}
}
impl From<CallError> 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<ResolveError> for FCEError {
fn from(err: ResolveError) -> Self {
FCEError::WasmerResolveError(format!("{}", err))
}
}
impl From<RuntimeError> for FCEError {
fn from(err: RuntimeError) -> Self {
FCEError::WasmerInvokeError(format!("{}", err))
}
}
impl From<WasmerError> for FCEError {
fn from(err: WasmerError) -> Self {
FCEError::WasmerInvokeError(format!("{}", err))
}
}
impl From<std::io::Error> for FCEError {
fn from(err: std::io::Error) -> Self {
FCEError::IOError(format!("{}", err))
}
}

91
wit_fce/src/vm/fce.rs Normal file
View File

@ -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<String, Arc<FCEModule>>,
}
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<Vec<IValue>, 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<S>(
&mut self,
module_name: S,
wasm_bytes: &[u8],
config: FCEModuleConfig,
) -> Result<(), FCEError>
where
S: Into<String>,
{
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(())
}
}
}
}

View File

@ -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<Vec<IValue>, FCEError>;
/// Registers new module in the FCE Service.
fn register_module<S>(
&mut self,
module_name: S,
wasm_bytes: &[u8],
config: FCEModuleConfig,
) -> Result<(), FCEError>
where
S: Into<String>;
/// Unregisters previously registered module.
fn unregister_module(&mut self, module_name: &str) -> Result<(), FCEError>;
}

View File

@ -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 {

View File

@ -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<InterfaceType>,
pub(crate) outputs: Vec<InterfaceType>,
pub(crate) function: fn(arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()>,
inputs: Vec<IType>,
outputs: Vec<IType>,
function: fn(arguments: &[IValue]) -> Result<Vec<IValue>, ()>,
}
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<Vec<InterfaceValue>, ()> {
fn call(&self, arguments: &[IValue]) -> Result<Vec<IValue>, ()> {
(self.function)(arguments)
}
}

View File

@ -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<WITInstance, WITExport, WITFunction, WITMemory, WITMemoryView<'static>>;
type WITModuleFunc = (WITInterpreter, Vec<InterfaceType>, Vec<InterfaceType>);
// TODO: introduce new trait instead of type
type WITModuleFunc = (WITInterpreter, Vec<IType>, Vec<IType>);
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<WITInstance>,
funcs: HashMap<String, WITModuleFunc>,
exports_funcs: HashMap<String, WITModuleFunc>,
}
impl WITModule {
impl FCEModule {
pub fn new(
wasm_bytes: &[u8],
imports: ImportObject,
modules: &HashMap<String, Arc<WITModule>>,
modules: &HashMap<String, Arc<FCEModule>>,
) -> Result<Self, WITFCEError> {
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<WITInstance>>(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<Vec<InterfaceValue>, WITFCEError> {
match self.funcs.get(function_name) {
args: &[IValue],
) -> Result<Vec<IValue>, 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<InterfaceType>, &Vec<InterfaceType>), WITFCEError> {
match self.funcs.get(function_name) {
) -> Result<(&Vec<IType>, &Vec<IType>), 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<Interfaces, WITFCEError> {
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<HashMap<String, WITModuleFunc>, 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::<MultiMap<_, _>>();
let adapter_type_to_instructions = interfaces
let adapter_type_to_instructions = wit
.adapters
.iter()
.map(|adapter| (adapter.function_type, &adapter.instructions))
.collect::<HashMap<_, _>>();
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<MaybeUninit<WITInstance>>,
) -> Result<ImportObject, WITFCEError> {
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<F>(inputs: Vec<InterfaceType>, func: F) -> DynamicFunc<'static>
fn dyn_func_from_imports<F>(inputs: Vec<IType>, func: F) -> DynamicFunc<'static>
where
F: Fn(&mut Ctx, &[Value]) -> Vec<Value> + 'static,
F: Fn(&mut Ctx, &[WValue]) -> Vec<WValue> + 'static,
{
let signature = inputs.iter().map(itype_to_wtype).collect::<Vec<_>>();
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<Value> {
Box::new(move |_: &mut Ctx, inputs: &[WValue]| -> Vec<WValue> {
// 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::<Vec<_>>();

View File

@ -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<u8>];
@ -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<WITMemoryView<'a>> 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::<u8>();

View File

@ -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;
}

View File

@ -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),

View File

@ -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<DynFunc<'static>>,
inputs: Vec<InterfaceType>,
outputs: Vec<InterfaceType>,
inputs: Vec<IType>,
outputs: Vec<IType>,
},
Import {
// TODO: use WITInstance here instead of WITModule
wit_module: Arc<WITModule>,
// TODO: use dyn Callable here
wit_module: Arc<FCEModule>,
func_name: String,
inputs: Vec<InterfaceType>,
outputs: Vec<InterfaceType>,
inputs: Vec<IType>,
outputs: Vec<IType>,
},
}
/// 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<Self, WITFCEError> {
use super::wtype_to_itype;
/// Creates functions from a "usual" (not WIT) module export.
pub(super) fn from_export(dyn_func: DynFunc<'static>) -> Result<Self, WITFCEError> {
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<WITModule>, func_name: String) -> Result<Self, WITFCEError> {
/// Creates function from a module import.
pub(super) fn from_import(wit_module: Arc<FCEModule>, func_name: String) -> Result<Self, WITFCEError> {
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<Vec<InterfaceValue>, ()> {
use super::{ival_to_wval, wval_to_ival};
fn call(&self, arguments: &[IValue]) -> Result<Vec<IValue>, ()> {
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::<Vec<Value>>())
.map(|results| results.iter().map(wval_to_ival).collect())
.call(&arguments.iter().map(ival_to_wval).collect::<Vec<WValue>>())
.map(|result| result.iter().map(wval_to_ival).collect())
.map_err(|_| ()),
WITFunctionInner::Import {
wit_module,

View File

@ -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<usize, WITFunction>,
memories: Vec<WITMemory>,
}
impl WITInstance {
pub fn new(
pub(super) fn new(
wasmer_instance: &WasmerInstance,
interfaces: &Interfaces,
modules: &HashMap<String, Arc<WITModule>>,
modules: &HashMap<String, Arc<FCEModule>>,
) -> Result<Self, WITFCEError> {
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<InterfaceType>, Vec<InterfaceType>), 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<HashMap<usize, WITFunction>, 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<String, Arc<WITModule>>,
modules: &HashMap<String, Arc<FCEModule>>,
interfaces: &Interfaces,
start_index: usize,
) -> Result<HashMap<usize, WITFunction>, WITFCEError> {
@ -104,7 +87,7 @@ impl WITInstance {
.implementations
.iter()
.map(|i| (i.core_function_type, i.adapter_function_type))
.collect::<HashMap<_, _>>();
.collect::<HashMap<u32, u32>>();
let mut non_wit_callable_imports = HashMap::new();
@ -127,7 +110,7 @@ impl WITInstance {
}
fn extract_memories(wasmer_instance: &WasmerInstance) -> Vec<WITMemory> {
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<WITExport, WITFunction, WITMemory, WITMemoryView<'_>>
impl wasm::structures::Instance<WITExport, WITFunction, WITMemory, WITMemoryView<'_>>
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<WITExport, WITFunction, WITMemory, WI
}
}
fn wit_type(&self, _index: u32) -> Option<&Type> {
fn wit_type(&self, _index: u32) -> Option<&IAstType> {
None
}
}

28
wit_fce/src/vm/mod.rs Normal file
View File

@ -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};

80
wit_fce/src/vm/prepare.rs Normal file
View File

@ -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<Self, FCEError> {
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<Vec<u8>, 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<Vec<u8>, FCEError> {
ModuleBootstrapper::init(module)?
.set_mem_pages_count(mem_pages_count)
.into_wasm()
}