From 9d3d605190ccae595abe1b49738f4ac4ed65ee69 Mon Sep 17 00:00:00 2001 From: vms Date: Mon, 1 Jun 2020 02:45:04 +0300 Subject: [PATCH] linking --- Cargo.lock | 14 ++ Cargo.toml | 1 + wit_fce/Cargo.toml | 11 +- wit_fce/examples/export_test/wit | 4 +- wit_fce/examples/ipfs_node/Cargo.toml | 10 + wit_fce/examples/ipfs_node/src/lib.rs | 62 +++++ wit_fce/examples/ipfs_node/src/mem.rs | 33 +++ wit_fce/examples/ipfs_node/src/result.rs | 41 ++++ wit_fce/examples/ipfs_node/wit | 44 ++++ wit_fce/examples/ipfs_rpc/src/lib.rs | 14 +- wit_fce/examples/ipfs_rpc/src/result.rs | 2 +- wit_fce/examples/ipfs_rpc/wit | 69 ++++-- wit_fce/src/instance/errors.rs | 11 +- wit_fce/src/instance/locals_imports.rs | 86 ------- wit_fce/src/instance/memory.rs | 1 + wit_fce/src/instance/mod.rs | 12 +- wit_fce/src/instance/wit_function.rs | 153 +++++++++++++ wit_fce/src/instance/wit_instance.rs | 92 +++++++- wit_fce/src/instance/wit_module.rs | 273 +++++++++++++++++------ wit_fce/src/main.rs | 50 +++-- wit_fce/src/wit_fce.rs | 26 +++ 21 files changed, 801 insertions(+), 208 deletions(-) create mode 100644 wit_fce/examples/ipfs_node/Cargo.toml create mode 100644 wit_fce/examples/ipfs_node/src/lib.rs create mode 100644 wit_fce/examples/ipfs_node/src/mem.rs create mode 100644 wit_fce/examples/ipfs_node/src/result.rs create mode 100644 wit_fce/examples/ipfs_node/wit delete mode 100644 wit_fce/src/instance/locals_imports.rs create mode 100644 wit_fce/src/instance/wit_function.rs create mode 100644 wit_fce/src/wit_fce.rs diff --git a/Cargo.lock b/Cargo.lock index 92081fe4..b3ada317 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -806,6 +806,10 @@ dependencies = [ "libc", ] +[[package]] +name = "ipfs_node" +version = "0.1.0" + [[package]] name = "ipfs_rpc" version = "0.1.0" @@ -968,6 +972,15 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "multimap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8883adfde9756c1d30b0f519c9b8c502a94b41ac62f696453c37c7fc0a958ce" +dependencies = [ + "serde", +] + [[package]] name = "native-tls" version = "0.2.4" @@ -2244,6 +2257,7 @@ dependencies = [ name = "wit_fce" version = "0.1.0" dependencies = [ + "multimap", "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)", diff --git a/Cargo.toml b/Cargo.toml index f9f10a0e..25eabfdc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,6 @@ members = [ "wit_fce", "wit_embedder", "wit_fce/examples/export_test", + "wit_fce/examples/ipfs_node", "wit_fce/examples/ipfs_rpc", ] diff --git a/wit_fce/Cargo.toml b/wit_fce/Cargo.toml index 41acfd67..0591b155 100644 --- a/wit_fce/Cargo.toml +++ b/wit_fce/Cargo.toml @@ -6,5 +6,14 @@ edition = "2018" [dependencies] wasmer-runtime = "0.17.0" -wasmer-runtime-core = "0.17.0" +wasmer-runtime-core = { version = "0.17.0", features = ["dynamicfunc-fat-closures"] } wasmer-interface-types = { git = "http://github.com/fluencelabs/interface-types" } +multimap = "0.8.1" + +[profile.release] +opt-level = 3 +debug = false +lto = true +debug-assertions = false +overflow-checks = false +panic = "abort" diff --git a/wit_fce/examples/export_test/wit b/wit_fce/examples/export_test/wit index eab8e92b..0215a0df 100644 --- a/wit_fce/examples/export_test/wit +++ b/wit_fce/examples/export_test/wit @@ -45,5 +45,5 @@ ) ;; Implementations -(@interface implement (func 0) (func 2)) -(@interface implement (func 1) (func 3)) +(@interface implement (func 2) (func 2)) +(@interface implement (func 3) (func 3)) diff --git a/wit_fce/examples/ipfs_node/Cargo.toml b/wit_fce/examples/ipfs_node/Cargo.toml new file mode 100644 index 00000000..0cbfa3a7 --- /dev/null +++ b/wit_fce/examples/ipfs_node/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "ipfs_node" +version = "0.1.0" +authors = ["Fluence Labs"] +edition = "2018" + +[lib] +name = "ipfs_node" +path = "src/lib.rs" +crate-type = ["cdylib"] diff --git a/wit_fce/examples/ipfs_node/src/lib.rs b/wit_fce/examples/ipfs_node/src/lib.rs new file mode 100644 index 00000000..27d54f81 --- /dev/null +++ b/wit_fce/examples/ipfs_node/src/lib.rs @@ -0,0 +1,62 @@ +/* + * 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 mem; +mod result; + +use crate::result::{RESULT_PTR, RESULT_SIZE}; + +#[no_mangle] +pub unsafe fn put(file_content_ptr: *mut u8, file_content_size: usize) { + let file_content = String::from_raw_parts( + file_content_ptr, + file_content_size, + file_content_size + ); + + let msg = format!("from Wasm node: file content is {}\n", file_content); + log_utf8_string(msg.as_ptr() as _, msg.len() as _); + + let cmd = format!("put {}", file_content); + ipfs(file_content.as_ptr() as _, file_content.len() as _); +} + +#[no_mangle] +pub unsafe fn get(hash_ptr: *mut u8, hash_size: usize, t: i32) { + let hash = String::from_raw_parts( + hash_ptr, + hash_size, + hash_size + ); + + let msg = format!("from Wasm node: file content is {}\n", hash); + log_utf8_string(msg.as_ptr() as _, msg.len() as _); + + let cmd = format!("get {}", hash); + ipfs(cmd.as_ptr() as _, cmd.len() as _); +} + +#[link(wasm_import_module = "logger")] +extern "C" { + /// Writes a byte string of size bytes that starts from ptr to a logger. + fn log_utf8_string(ptr: i32, size: i32); +} + +#[link(wasm_import_module = "host")] +extern "C" { + /// Put a file to ipfs, returns ipfs hash of the file. + fn ipfs(ptr: i32, size: i32); +} diff --git a/wit_fce/examples/ipfs_node/src/mem.rs b/wit_fce/examples/ipfs_node/src/mem.rs new file mode 100644 index 00000000..e39eb89a --- /dev/null +++ b/wit_fce/examples/ipfs_node/src/mem.rs @@ -0,0 +1,33 @@ +/* + * 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 std::alloc::{alloc as global_alloc, dealloc as global_dealloc, Layout}; +use std::ptr::NonNull; + +/// Allocates memory area of specified size and returns its address. +#[no_mangle] +pub unsafe fn allocate(size: usize) -> NonNull { + let layout: Layout = Layout::from_size_align(size, std::mem::align_of::()).unwrap(); + NonNull::new_unchecked(global_alloc(layout)) +} + +/// Deallocates memory area for provided memory pointer and size. +#[no_mangle] +pub unsafe fn deallocate(ptr: NonNull, size: usize) { + let layout = Layout::from_size_align(size, std::mem::align_of::()).unwrap(); + global_dealloc(ptr.as_ptr(), layout); +} diff --git a/wit_fce/examples/ipfs_node/src/result.rs b/wit_fce/examples/ipfs_node/src/result.rs new file mode 100644 index 00000000..2530e47a --- /dev/null +++ b/wit_fce/examples/ipfs_node/src/result.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. + */ + + +use std::sync::atomic::AtomicUsize; + +pub static mut RESULT_PTR: AtomicUsize = AtomicUsize::new(0); +pub static mut RESULT_SIZE: AtomicUsize = AtomicUsize::new(0); + +#[no_mangle] +pub unsafe fn get_result_ptr() -> usize { + *RESULT_PTR.get_mut() +} + +#[no_mangle] +pub unsafe fn get_result_size() -> usize { + *RESULT_SIZE.get_mut() +} + +#[no_mangle] +pub unsafe fn set_result_ptr(ptr: usize) { + *RESULT_PTR.get_mut() = ptr; +} + +#[no_mangle] +pub unsafe fn set_result_size(size: usize) { + *RESULT_SIZE.get_mut() = size; +} diff --git a/wit_fce/examples/ipfs_node/wit b/wit_fce/examples/ipfs_node/wit new file mode 100644 index 00000000..22155a13 --- /dev/null +++ b/wit_fce/examples/ipfs_node/wit @@ -0,0 +1,44 @@ +;; allocate function type +(@interface type (func (param i32) (result i32))) ;; 0 + +;; deallocate function +(@interface type (func (param i32 i32))) ;; 1 + +;; invoke function +(@interface type (func (param string) (result string))) ;; 2 + +;; result extractor functions +(@interface type (func (result i32))) ;; 3 + +;; result setter functions +(@interface type (func (param string))) ;; 4 + +;; import ipfs put/get function +(@interface type (func (param string) (result string))) ;; 5 + +;; import ipfs put/get function +(@interface type (func (param string) (result string))) ;; 6 + +;; import ipfs put/get function +(@interface type (func (param string) (result string))) ;; 7 + +(@interface export "allocate" (func 0)) ;; 0 +(@interface export "deallocate" (func 1)) ;; 1 +(@interface export "get_result_size" (func 3)) ;; 3 +(@interface export "get_result_ptr" (func 3)) ;; 4 +(@interface export "set_result_size" (func 4)) ;; 5 +(@interface export "set_result_ptr" (func 4)) ;; 6 + +(@interface export "put" (func 5)) ;; 8 +(@interface export "get" (func 5)) ;; 7 + +(@interface func (type 6) + arg.get 0 + string.lower_memory + call-core 9 ;; call node.get + call-core 5 ;; call set_result_size + call-core 6 ;; call set_result_ptr +) + +;; Implementations +(@interface implement (func 5) (func 6)) diff --git a/wit_fce/examples/ipfs_rpc/src/lib.rs b/wit_fce/examples/ipfs_rpc/src/lib.rs index 8077db13..33348394 100644 --- a/wit_fce/examples/ipfs_rpc/src/lib.rs +++ b/wit_fce/examples/ipfs_rpc/src/lib.rs @@ -21,14 +21,26 @@ use crate::result::{RESULT_PTR, RESULT_SIZE}; #[no_mangle] pub unsafe fn invoke(file_content_ptr: *mut u8, file_content_size: usize) { + let file_content = String::from_raw_parts( + file_content_ptr, + file_content_size, + file_content_size, + ); + let msg = format!("from Wasm rpc: file_content is {}\n", file_content); + log_utf8_string(msg.as_ptr() as _, msg.len() as _); + put(file_content_ptr as _, file_content_size as _); + + /* let hash = String::from_raw_parts( *RESULT_PTR.get_mut(), *RESULT_SIZE.get_mut(), *RESULT_SIZE.get_mut(), ); + let msg = format!("from Wasm rpc: hash is {}\n", hash); - log_utf8_string(hash.as_ptr() as _, hash.len() as _); + log_utf8_string(msg.as_ptr() as _, msg.len() as _); + */ } #[link(wasm_import_module = "logger")] diff --git a/wit_fce/examples/ipfs_rpc/src/result.rs b/wit_fce/examples/ipfs_rpc/src/result.rs index 8def1085..2530e47a 100644 --- a/wit_fce/examples/ipfs_rpc/src/result.rs +++ b/wit_fce/examples/ipfs_rpc/src/result.rs @@ -36,6 +36,6 @@ pub unsafe fn set_result_ptr(ptr: usize) { } #[no_mangle] -pub unsafe fn set_result_size(ptr: usize) { +pub unsafe fn set_result_size(size: usize) { *RESULT_SIZE.get_mut() = size; } diff --git a/wit_fce/examples/ipfs_rpc/wit b/wit_fce/examples/ipfs_rpc/wit index 0b2a77f3..55f89534 100644 --- a/wit_fce/examples/ipfs_rpc/wit +++ b/wit_fce/examples/ipfs_rpc/wit @@ -1,46 +1,69 @@ ;; allocate function type -(@interface type (func (param i32) (result i32))) +(@interface type (func (param i32) (result i32))) ;; 0 ;; deallocate function -(@interface type (func (param i32 i32))) +(@interface type (func (param i32 i32))) ;; 1 ;; invoke function -(@interface type (func (param string) (result string))) +(@interface type (func (param string) (result string))) ;; 2 ;; result extractor functions -(@interface type (func (result i32))) +(@interface type (func (result i32))) ;; 3 ;; result setter functions -(@interface type (func (param i32))) +(@interface type (func (param i32))) ;; 4 ;; import ipfs put/get function -(@interface type (func (param string) (param string))) +(@interface type (func (param i32 i32))) ;; 5 -(@interface export "allocate" (func 0)) -(@interface export "deallocate" (func 1)) -(@interface export "invoke" (func 2)) -(@interface export "get_result_size" (func 3)) -(@interface export "get_result_ptr" (func 3)) -(@interface export "set_result_size" (func 4)) -(@interface export "set_result_ptr" (func 4)) +;; import ipfs put/get function +(@interface type (func (param string) (result string))) ;; 6 -(@interface import "node" "get" (func 5)) -(@interface import "node" "put" (func 5)) +;; import ipfs put/get function +(@interface type (func (param string) (result string))) ;; 7 + +;; import ipfs put/get function +(@interface type (func (param i64 i32) (result i64 i32))) ;; 8 + +(@interface export "allocate" (func 0)) ;; 0 +(@interface export "deallocate" (func 1)) ;; 1 +(@interface export "invoke" (func 2)) ;; 2 +(@interface export "get_result_size" (func 3)) ;; 3 +(@interface export "get_result_ptr" (func 3)) ;; 4 +(@interface export "set_result_size" (func 4)) ;; 5 +(@interface export "set_result_ptr" (func 4)) ;; 6 + +(@interface import "node" "get" (func (type 5))) +(@interface import "node" "put" (func (type 5))) + +(@interface import "node" "get" (func (type 7))) ;; 7 +(@interface import "node" "put" (func (type 8))) ;; 8 (@interface func (type 2) arg.get 0 string.size - call-core 0 ;; call allocate + call-core 0 ;; call allocate arg.get 0 string.lower_memory - call-core 2 ;; call greeting - call-core 3 ;; call get_result_size - call-core 4 ;; call get_result_ptr + call-core 2 ;; call invoke + call-core 3 ;; call get_result_size + call-core 4 ;; call get_result_ptr string.lift_memory - call-core 3 ;; call get_result_size - call-core 4 ;; call get_result_ptr - call-core 1 ;; call deallocate + call-core 3 ;; call get_result_size + call-core 4 ;; call get_result_ptr + call-core 1 ;; call deallocate +) + +(@interface func (type 5) + arg.get 0 + arg.get 1 + string.lift_memory + call-core 7 ;; call node.get that returns string + string.lower_memory + call-core 5 ;; call set_result_size + call-core 6 ;; call set_result_ptr ) ;; Implementations -(@interface implement (func 0) (func 2)) +(@interface implement (func 2) (func 2)) +(@interface implement (func 5) (func 5)) diff --git a/wit_fce/src/instance/errors.rs b/wit_fce/src/instance/errors.rs index 2f885228..78787214 100644 --- a/wit_fce/src/instance/errors.rs +++ b/wit_fce/src/instance/errors.rs @@ -45,7 +45,7 @@ pub enum WITFCEError { NonUniqueModuleName, /// Returns when there is no module with such name. - NoSuchFunction, + NoSuchFunction(String), /// Returns when there is no module with such name. NoSuchModule, @@ -79,8 +79,13 @@ impl std::fmt::Display for WITFCEError { WITFCEError::PrepareError(msg) => { write!(f, "Prepare error: {}, probably module is mailformed", msg) } - WITFCEError::NonUniqueModuleName => write!(f, "FCE already has module with such name"), - WITFCEError::NoSuchModule => write!(f, "FCE doesn't have a module with such name"), + WITFCEError::NonUniqueModuleName => { + write!(f, "FCE already has module with such a name") + } + WITFCEError::NoSuchFunction(msg) => { + write!(f, "FCE doesn't have a function with such a name: {}", msg) + } + WITFCEError::NoSuchModule => write!(f, "FCE doesn't have a module with such a name"), WITFCEError::ModuleInUse => { write!(f, "Module is used by other modules and couldn't be deleted") } diff --git a/wit_fce/src/instance/locals_imports.rs b/wit_fce/src/instance/locals_imports.rs deleted file mode 100644 index 12643b43..00000000 --- a/wit_fce/src/instance/locals_imports.rs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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 crate::instance::errors::WITFCEError; -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; - -pub(crate) struct WITLocalImport { - inner: DynFunc<'static>, - inputs: Vec, - outputs: Vec, -} - -impl WITLocalImport { - pub fn new(dyn_func: DynFunc<'static>) -> Result { - use super::wtype_to_itype; - - let signature = dyn_func.signature(); - let inputs = signature - .params() - .iter() - .map(wtype_to_itype) - .collect::>(); - let outputs = signature - .returns() - .iter() - .map(wtype_to_itype) - .collect::>(); - - Ok(Self { - inner: dyn_func, - inputs, - outputs, - }) - } -} - -impl std::ops::Deref for WITLocalImport { - type Target = DynFunc<'static>; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl wasm::structures::LocalImport for WITLocalImport { - fn inputs_cardinality(&self) -> usize { - self.inputs.len() - } - - fn outputs_cardinality(&self) -> usize { - self.outputs.len() - } - - fn inputs(&self) -> &[InterfaceType] { - &self.inputs - } - - fn outputs(&self) -> &[InterfaceType] { - &self.outputs - } - - fn call(&self, arguments: &[InterfaceValue]) -> Result, ()> { - use super::{ival_to_wval, wval_to_ival}; - - self.inner - .call(&arguments.iter().map(ival_to_wval).collect::>()) - .map(|results| results.iter().map(wval_to_ival).collect()) - .map_err(|_| ()) - } -} diff --git a/wit_fce/src/instance/memory.rs b/wit_fce/src/instance/memory.rs index 2d6e72aa..451261d1 100644 --- a/wit_fce/src/instance/memory.rs +++ b/wit_fce/src/instance/memory.rs @@ -26,6 +26,7 @@ impl<'a> std::ops::Deref for WITMemoryView<'a> { } } +#[derive(Clone)] pub struct WITMemory(pub Memory); impl std::ops::Deref for WITMemory { type Target = Memory; diff --git a/wit_fce/src/instance/mod.rs b/wit_fce/src/instance/mod.rs index 5c954dc3..de602a7f 100644 --- a/wit_fce/src/instance/mod.rs +++ b/wit_fce/src/instance/mod.rs @@ -16,8 +16,8 @@ pub mod errors; pub mod exports; -pub mod locals_imports; pub mod memory; +pub mod wit_function; pub mod wit_instance; pub mod wit_module; @@ -35,6 +35,16 @@ pub fn wtype_to_itype(ty: &WType) -> IType { } } +pub fn itype_to_wtype(ty: &IType) -> WType { + match ty { + IType::I32 => WType::I32, + IType::I64 => WType::I64, + IType::F32 => WType::F32, + IType::F64 => WType::F64, + _ => unimplemented!(), + } +} + pub fn ival_to_wval(value: &IValue) -> WValue { match value { IValue::I32(v) => WValue::I32(*v), diff --git a/wit_fce/src/instance/wit_function.rs b/wit_fce/src/instance/wit_function.rs new file mode 100644 index 00000000..98ca40bc --- /dev/null +++ b/wit_fce/src/instance/wit_function.rs @@ -0,0 +1,153 @@ +/* + * 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 crate::instance::errors::WITFCEError; +use crate::instance::wit_module::WITModule; +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, + }, + Import { + wit_module: Arc, + func_name: String, + inputs: Vec, + outputs: Vec, + }, +} + +#[derive(Clone)] +pub(crate) struct WITFunction { + inner: WITFunctionInner, +} + +impl WITFunction { + pub fn from_export(dyn_func: DynFunc<'static>) -> Result { + use super::wtype_to_itype; + + let signature = dyn_func.signature(); + let inputs = signature + .params() + .iter() + .map(wtype_to_itype) + .collect::>(); + let outputs = signature + .returns() + .iter() + .map(wtype_to_itype) + .collect::>(); + + let inner = WITFunctionInner::Export { + func: Arc::new(dyn_func), + inputs, + outputs, + }; + + Ok(Self { inner }) + } + + pub fn from_import(wit_module: Arc, func_name: String) -> Result { + let (inputs, outputs) = wit_module.as_ref().get_func_signature(&func_name)?; + println!("from_import: {:?}", inputs); + + let inner = WITFunctionInner::Import { + wit_module, + func_name, + inputs, + outputs, + }; + + Ok(Self { inner }) + } +} + +/* +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 { + WITFunctionInner::Export { ref inputs, .. } => inputs.len(), + WITFunctionInner::Import { ref inputs, .. } => inputs.len(), + } + } + + fn outputs_cardinality(&self) -> usize { + match &self.inner { + WITFunctionInner::Export { ref outputs, .. } => outputs.len(), + WITFunctionInner::Import { ref outputs, .. } => outputs.len(), + } + } + + fn inputs(&self) -> &[InterfaceType] { + match &self.inner { + WITFunctionInner::Export { ref inputs, .. } => inputs, + WITFunctionInner::Import { ref inputs, .. } => inputs, + } + } + + fn outputs(&self) -> &[InterfaceType] { + 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}; + + match &self.inner { + WITFunctionInner::Export { func, .. } => { + println!("calling with {:?}", arguments); + func.as_ref() + .call(&arguments.iter().map(ival_to_wval).collect::>()) + .map(|results| results.iter().map(wval_to_ival).collect()) + .map_err(|_| ()) + } + WITFunctionInner::Import { + wit_module, + func_name, + .. + } => { + println!("calling {} with {:?}", func_name, arguments); + let mut tt = wit_module.clone(); + unsafe { + let result = Arc::get_mut_unchecked(&mut tt) + .call(func_name, arguments) + .map_err(|_| ()); + + println!("result is {:?}", result); + result + } + } + } + } +} diff --git a/wit_fce/src/instance/wit_instance.rs b/wit_fce/src/instance/wit_instance.rs index c207e53b..bf508053 100644 --- a/wit_fce/src/instance/wit_instance.rs +++ b/wit_fce/src/instance/wit_instance.rs @@ -16,20 +16,26 @@ use crate::instance::errors::WITFCEError; use crate::instance::exports::WITExport; -use crate::instance::locals_imports::WITLocalImport; use crate::instance::memory::{WITMemory, WITMemoryView}; +use crate::instance::wit_function::WITFunction; use wasmer_interface_types::interpreter::wasm; use wasmer_runtime_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::{LocalImportIndex, TypedIndex}; +use wasmer_interface_types::interpreter::wasm::structures::{ + LocalImport, LocalImportIndex, TypedIndex, +}; +use wasmer_interface_types::types::InterfaceType; +#[derive(Clone)] pub struct WITInstance { - // represent all import and export functions - funcs: HashMap, + // represent all import and export functions that could be called from WIT context + funcs: HashMap, memories: Vec, } @@ -37,17 +43,37 @@ impl WITInstance { pub fn new( wasmer_instance: &WasmerInstance, interfaces: &Interfaces, + modules: &HashMap>, ) -> Result { - let funcs = Self::extract_funcs(&wasmer_instance, interfaces)?; + let mut exports = Self::extract_exports(&wasmer_instance, interfaces)?; + println!("exports count {}", exports.len()); + let imports = Self::extract_imports(modules, interfaces, exports.len())?; + println!("imports count {}", imports.len()); let memories = Self::extract_memories(&wasmer_instance); + exports.extend(imports); + let funcs = exports; + Ok(Self { funcs, memories }) } - fn extract_funcs( + 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( wasmer_instance: &WasmerInstance, interfaces: &Interfaces, - ) -> Result, WITFCEError> { + ) -> Result, WITFCEError> { use wasmer_runtime_core::DynFunc; let module_exports = &wasmer_instance.exports; @@ -62,12 +88,58 @@ impl WITInstance { // here it is safe because dyn func is never lives WITInstance let export_func = std::mem::transmute::, DynFunc<'static>>(export_func); - Ok((export_id, WITLocalImport::new(export_func)?)) + let tt = WITFunction::from_export(export_func)?; + println!( + "{}, {} - {:?}", + export_id, + export.name, + tt.inputs() + ); + Ok((export_id, tt)) } }) .collect() } + /// Extracts only those imports that don't have implementations. + fn extract_imports( + modules: &HashMap>, + interfaces: &Interfaces, + start_index: usize, + ) -> Result, WITFCEError> { + // uses to filter import functions that have an adapter implementation + let core_to_adapter = interfaces + .implementations + .iter() + .map(|i| (i.core_function_type, i.adapter_function_type)) + .collect::>(); + + let mut non_wit_callable_imports = HashMap::new(); + + for import in interfaces.imports.iter() { + if let Some(_) = core_to_adapter.get(&import.function_type) { + continue; + } + + match modules.get(import.namespace) { + Some(module) => { + let func = WITFunction::from_import(module.clone(), import.name.to_string())?; + println!( + "{}, {} - {:?}", + start_index + non_wit_callable_imports.len(), + import.name, + func.inputs() + ); + non_wit_callable_imports + .insert(start_index + non_wit_callable_imports.len() as usize, func); + } + None => return Err(WITFCEError::NoSuchModule), + } + } + + Ok(non_wit_callable_imports) + } + fn extract_memories(wasmer_instance: &WasmerInstance) -> Vec { use wasmer_runtime_core::export::Export::Memory; @@ -90,7 +162,7 @@ impl WITInstance { } } -impl<'instance> wasm::structures::Instance> +impl<'instance> wasm::structures::Instance> for WITInstance { fn export(&self, _export_name: &str) -> Option<&WITExport> { @@ -101,7 +173,7 @@ impl<'instance> wasm::structures::Instance( &mut self, index: I, - ) -> Option<&WITLocalImport> { + ) -> Option<&WITFunction> { self.funcs.get(&index.index()) } diff --git a/wit_fce/src/instance/wit_module.rs b/wit_fce/src/instance/wit_module.rs index b6c207dc..2c834557 100644 --- a/wit_fce/src/instance/wit_module.rs +++ b/wit_fce/src/instance/wit_module.rs @@ -16,41 +16,47 @@ use crate::instance::errors::WITFCEError; use crate::instance::exports::WITExport; -use crate::instance::locals_imports::WITLocalImport; use crate::instance::memory::{WITMemory, WITMemoryView}; +use crate::instance::wit_function::WITFunction; use crate::instance::wit_instance::WITInstance; use wasmer_interface_types as wit; +use wasmer_interface_types::ast::Interfaces; use wasmer_interface_types::interpreter::Interpreter; use wasmer_interface_types::values::InterfaceValue; use wasmer_runtime::{compile, ImportObject}; use wasmer_runtime_core::Instance as WasmerInstance; +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>; pub struct WITModule { instance: WasmerInstance, - wit_instance: WITInstance, - exports: HashMap< - String, - Interpreter>, - >, - import_object: ImportObject, + wit_instance: Arc, + func_name_to_idx: HashMap, + funcs: HashMap, } impl WITModule { - pub fn new(wasm_bytes: &[u8], imports: &ImportObject) -> Result { - let wasmer_instance = compile(&wasm_bytes)?.instantiate(imports)?; + pub fn new( + wasm_bytes: &[u8], + imports: ImportObject, + modules: &HashMap>, + ) -> Result { + let wasmer_instance = compile(&wasm_bytes)?; let wit_sections = wasmer_instance - .module - .info - .custom_sections - .get(WIT_SECTION_NAME) + .custom_sections(WIT_SECTION_NAME) .ok_or_else(|| WITFCEError::NoWITSection)?; if wit_sections.len() > 1 { @@ -63,61 +69,25 @@ impl WITModule { return Err(WITFCEError::WITRemainderNotEmpty); } - let wit_instance = WITInstance::new(&wasmer_instance, &interfaces)?; + let mut wit_instance = Arc::new_uninit(); - let wit_export_names = interfaces - .imports - .iter() - .map(|export| (export.function_type, export.name.to_string())) - .collect::>(); + let callable_exports = Self::extract_exports(&interfaces)?; + let mut import_object = Self::adjust_imports(&interfaces, wit_instance.clone())?; + import_object.extend(imports); - let callable_exports = interfaces - .adapters - .iter() - .map(|adapter| { - let export_func_name = wit_export_names - .get(&adapter.function_type) - .ok_or_else(|| WITFCEError::NoSuchFunction)?; - let instructions = &adapter.instructions; + let wasmer_instance = wasmer_instance.instantiate(&import_object)?; - let interpreter: Interpreter< - WITInstance, - WITExport, - WITLocalImport, - WITMemory, - WITMemoryView<'static>, - > = instructions.try_into().unwrap(); - - Ok((export_func_name.to_owned(), interpreter)) - }) - .collect::, WITFCEError>>()?; - - - let callable_imports = interfaces - .adapters - .iter() - .map(|adapter| { - let import_func_name = wit_export_names - .get(&adapter.function_type) - .ok_or_else(|| WITFCEError::NoSuchFunction)?; - let instructions = &adapter.instructions; - - let interpreter: Interpreter< - WITInstance, - WITExport, - WITLocalImport, - WITMemory, - WITMemoryView<'static>, - > = instructions.try_into().unwrap(); - - Ok((export_func_name.to_owned(), interpreter)) - }) - .collect::, WITFCEError>>()?; + let wit_instance = unsafe { + *Arc::get_mut_unchecked(&mut wit_instance) = + MaybeUninit::new(WITInstance::new(&wasmer_instance, &interfaces, modules)?); + std::mem::transmute::<_, Arc>(wit_instance) + }; Ok(Self { instance: wasmer_instance, wit_instance, - exports: callable_exports, + func_name_to_idx: HashMap::new(), + funcs: callable_exports, }) } @@ -126,15 +96,184 @@ impl WITModule { function_name: &str, args: &[InterfaceValue], ) -> Result, WITFCEError> { - match self.exports.get(function_name) { + println!("here, func name is {}, args = {:?}", function_name, args); + match self.funcs.get(function_name) { Some(func) => { - let result = func - .run(args, &mut self.wit_instance)? - .as_slice() - .to_owned(); + let tt = Arc::make_mut(&mut self.wit_instance); + + let result = func.run(args, tt)?.as_slice().to_owned(); + println!("here {:?}", result); Ok(result) } - None => Err(WITFCEError::NoSuchFunction), + None => { + println!("no func"); + Err(WITFCEError::NoSuchFunction(format!( + "{} hasn't been found while calling", + function_name + ))) + } } } + + pub fn get_func_signature( + &self, + function_name: &str, + ) -> Result<(Vec, Vec), WITFCEError> { + match self.func_name_to_idx.get(function_name) { + Some(func_idx) => { + println!("func_idx: {}", func_idx); + self.wit_instance.as_ref().get_func_signature(*func_idx) + }, + None => Err(WITFCEError::NoSuchFunction(format!( + "{} has't been found during its signature looking up", + function_name + ))), + } + } + + fn extract_exports( + interfaces: &Interfaces, + ) -> Result, WITFCEError> { + let exports_type_to_names = interfaces + .exports + .iter() + .map(|export| (export.function_type, export.name.to_string())) + .collect::>(); + + let adapter_type_to_instructions = interfaces + .adapters + .iter() + .map(|adapter| (adapter.function_type, &adapter.instructions)) + .collect::>(); + + let mut wit_callable_exports = HashMap::new(); + for i in interfaces.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, + }; + + // * just to remove reference + let adapter_instructions = *adapter_type_to_instructions + .get(&i.adapter_function_type) + .ok_or_else(|| WITFCEError::NoSuchFunction( + format!("adapter function with idx = {} hasn't been found during extracting exports by implementations", i.adapter_function_type) + ))?; + + for export_function_name in export_function_names.iter() { + println!("export func name {}", export_function_name); + + // TODO: handle errors + let interpreter: WITInterpreter = adapter_instructions.try_into().unwrap(); + wit_callable_exports.insert(export_function_name.to_owned(), interpreter); + } + } + + Ok(wit_callable_exports) + } + + // this function deals only with import functions that have an adaptor implementation + fn adjust_imports( + 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, Type as WType, Value}; + use wasmer_runtime_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> + where + F: Fn(&mut Ctx, &[Value]) -> Vec + 'static, + { + let signature = inputs.iter().map(itype_to_wtype).collect::>(); + DynamicFunc::new(Arc::new(FuncSig::new(signature, vec![])), func) + } + + // uses to filter out import functions that have an adapter implementation + let adapter_to_core = interfaces + .implementations + .iter() + .map(|i| (i.adapter_function_type, i.core_function_type)) + .collect::>(); + + // all wit imports + let mut export_type_to_name = interfaces + .imports + .iter() + .map(|import| { + ( + import.function_type, + (import.namespace.to_string(), import.name.to_string()), + ) + }) + .collect::>(); + + let mut import_namespaces: HashMap = HashMap::new(); + + for adapter in interfaces.adapters.iter() { + let core_function_idx = adapter_to_core + .get(&adapter.function_type) + .ok_or_else(|| WITFCEError::NoSuchFunction(format!("function with idx = {} hasn't been found during adjusting imports in WIT implementation", adapter.function_type)))?; + + let (namespace, func_name) = match export_type_to_name.remove(core_function_idx) { + Some(v) => (v.0, v.1), + None => continue, + }; + + if adapter.function_type >= interfaces.types.len() as u32 { + // TODO: change error type + return Err(WITFCEError::NoSuchFunction(format!( + "{} function id is bigger than WIT interface types count", + adapter.function_type + ))); + } + + if let IType::Function { inputs, .. } = + &interfaces.types[adapter.function_type as usize] + { + let instructions = &adapter.instructions; + let interpreter: WITInterpreter = instructions.try_into().unwrap(); + + let wit_instance = wit_instance.clone(); + let inner_import = Box::new(move |_: &mut Ctx, inputs: &[Value]| -> Vec { + println!("calling from import with {:?}", inputs); + + let tt = wit_instance.clone(); + let converted_inputs = inputs.iter().map(wval_to_ival).collect::>(); + //let mut wit_instance_copy = Arc::make_mut(tt).unwrap(); + unsafe { + let r = interpreter + .run(&converted_inputs, Arc::make_mut(&mut tt.assume_init())); + println!("import interpreter result is {:?}", r); + } + + vec![] + }); + + let linking_import = dyn_func_from_imports(inputs.clone(), inner_import); + + let mut n = Namespace::new(); + n.insert(func_name.clone(), linking_import); + + import_namespaces.insert(namespace, n); + } else { + // TODO: change error type + return Err(WITFCEError::WasmerResolveError(format!( + "WIT type with idx = {} doesn't refer to function", + adapter.function_type + ))); + } + } + + let mut import_object = ImportObject::new(); + + for (namespace_name, namespace) in import_namespaces.into_iter() { + import_object.register(namespace_name, namespace); + } + + Ok(import_object) + } } diff --git a/wit_fce/src/main.rs b/wit_fce/src/main.rs index 1e838521..327a86c9 100644 --- a/wit_fce/src/main.rs +++ b/wit_fce/src/main.rs @@ -13,40 +13,54 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![feature(get_mut_unchecked)] +#![feature(new_uninit)] mod instance; 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; -const FILE_NAME: &str = - "/Users/mike/dev/work/fluence/wasm/fce/target/wasm32-unknown-unknown/release/export_test_wit.wasm"; +const IPFS_NODE: &str = + "/Users/mike/dev/work/fluence/wasm/fce/target/wasm32-unknown-unknown/release/ipfs_node_wit.wasm"; + +const IPFS_RPC: &str = + "/Users/mike/dev/work/fluence/wasm/fce/target/wasm32-unknown-unknown/release/ipfs_rpc_wit.wasm"; fn main() { - let wasm_bytes = std::fs::read(FILE_NAME).unwrap(); - let logger_imports = imports! { + 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(logger_imports); + import_object.extend(imports); + let mut modules = HashMap::new(); - let mut module = - WITModule::new(&wasm_bytes, &import_object).expect("module successfully created"); + println!("loading ipfs node module"); + let ipfs_node = WITModule::new(&ipfs_node_bytes, import_object.clone(), &modules) + .expect("module successfully created"); + modules.insert("node".to_string(), Arc::new(ipfs_node)); - let result1 = module - .call("strlen", &[InterfaceValue::String("aaaaaa".to_string())]) - .unwrap(); - let result2 = module - .call("greeting", &[InterfaceValue::String("Mike".to_string())]) + println!("loading ipfs rpc module"); + let mut ipfs_rpc = WITModule::new(&ipfs_rpc_bytes, import_object, &modules) + .expect("module successfully created"); + + let result1 = ipfs_rpc + .call("invoke", &[InterfaceValue::String("aaaaaa".to_string())]) .unwrap(); println!("stack state {:?}", result1); - println!("stack state {:?}", result2); } fn logger_log_utf8_string(ctx: &mut Ctx, offset: i32, size: i32) { @@ -58,3 +72,13 @@ fn logger_log_utf8_string(ctx: &mut Ctx, offset: i32, size: i32) { None => print!("fce logger: incorrect UTF8 string's been supplied to logger"), } } + +fn ipfs_call(ctx: &mut Ctx, ptr: i32, size: i32) { + use wasmer_runtime_core::memory::ptr::{Array, WasmPtr}; + + let wasm_ptr = WasmPtr::::new(ptr as _); + match wasm_ptr.get_utf8_string(ctx.memory(0), size as _) { + Some(msg) => print!("ipfs_call {}", msg), + None => print!("fce logger: incorrect UTF8 string's been supplied to logger"), + } +} diff --git a/wit_fce/src/wit_fce.rs b/wit_fce/src/wit_fce.rs new file mode 100644 index 00000000..c04f4381 --- /dev/null +++ b/wit_fce/src/wit_fce.rs @@ -0,0 +1,26 @@ +/* + * 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 crate::instance::wit_module::WITModule; + +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 { + +}