From 3d5be0e798bc6069e2b2677c47d0dcf69190a85f Mon Sep 17 00:00:00 2001 From: vms Date: Sat, 30 May 2020 01:55:39 +0300 Subject: [PATCH] introduce wit_fce --- Cargo.lock | 114 +++++++++++++++-- Cargo.toml | 3 + wit_fce/Cargo.toml | 10 ++ wit_fce/examples/export_test/Cargo.toml | 10 ++ wit_fce/examples/export_test/src/lib.rs | 44 +++++++ wit_fce/examples/export_test/src/mem.rs | 33 +++++ wit_fce/examples/export_test/src/result.rs | 31 +++++ wit_fce/examples/export_test/wit | 49 +++++++ wit_fce/examples/ipfs_rpc/Cargo.toml | 10 ++ wit_fce/examples/ipfs_rpc/src/lib.rs | 47 +++++++ wit_fce/examples/ipfs_rpc/src/mem.rs | 33 +++++ wit_fce/examples/ipfs_rpc/src/result.rs | 41 ++++++ wit_fce/examples/ipfs_rpc/wit | 46 +++++++ wit_fce/src/instance/errors.rs | 141 +++++++++++++++++++++ wit_fce/src/instance/exports.rs | 59 +++++++++ wit_fce/src/instance/locals_imports.rs | 86 +++++++++++++ wit_fce/src/instance/memory.rs | 49 +++++++ wit_fce/src/instance/mod.rs | 56 ++++++++ wit_fce/src/instance/wit_instance.rs | 119 +++++++++++++++++ wit_fce/src/instance/wit_module.rs | 140 ++++++++++++++++++++ wit_fce/src/main.rs | 60 +++++++++ 21 files changed, 1173 insertions(+), 8 deletions(-) create mode 100644 wit_fce/Cargo.toml create mode 100644 wit_fce/examples/export_test/Cargo.toml create mode 100644 wit_fce/examples/export_test/src/lib.rs create mode 100644 wit_fce/examples/export_test/src/mem.rs create mode 100644 wit_fce/examples/export_test/src/result.rs create mode 100644 wit_fce/examples/export_test/wit create mode 100644 wit_fce/examples/ipfs_rpc/Cargo.toml create mode 100644 wit_fce/examples/ipfs_rpc/src/lib.rs create mode 100644 wit_fce/examples/ipfs_rpc/src/mem.rs create mode 100644 wit_fce/examples/ipfs_rpc/src/result.rs create mode 100644 wit_fce/examples/ipfs_rpc/wit create mode 100644 wit_fce/src/instance/errors.rs create mode 100644 wit_fce/src/instance/exports.rs create mode 100644 wit_fce/src/instance/locals_imports.rs create mode 100644 wit_fce/src/instance/memory.rs create mode 100644 wit_fce/src/instance/mod.rs create mode 100644 wit_fce/src/instance/wit_instance.rs create mode 100644 wit_fce/src/instance/wit_module.rs create mode 100644 wit_fce/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 0f74fbee..92081fe4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,8 +15,8 @@ dependencies = [ "rustyline", "sha2", "tokio", - "wasmer-runtime", - "wasmer-runtime-core", + "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", ] @@ -470,6 +470,10 @@ dependencies = [ "failure", ] +[[package]] +name = "export_test" +version = "0.1.0" + [[package]] name = "failure" version = "0.1.8" @@ -802,6 +806,10 @@ dependencies = [ "libc", ] +[[package]] +name = "ipfs_rpc" +version = "0.1.0" + [[package]] name = "itoa" version = "0.4.5" @@ -1951,8 +1959,34 @@ dependencies = [ "target-lexicon", "wasmer-clif-fork-frontend", "wasmer-clif-fork-wasm", - "wasmer-runtime-core", - "wasmer-win-exception-handler", + "wasmer-runtime-core 0.17.0 (git+http://github.com/fluencelabs/wasmer?branch=fluence)", + "wasmer-win-exception-handler 0.17.0 (git+http://github.com/fluencelabs/wasmer?branch=fluence)", + "wasmparser 0.51.4", + "winapi 0.3.8", +] + +[[package]] +name = "wasmer-clif-backend" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "691ea323652d540a10722066dbf049936f4367bb22a96f8992a262a942a8b11b" +dependencies = [ + "byteorder", + "cranelift-codegen", + "cranelift-entity", + "cranelift-native", + "libc", + "nix 0.15.0", + "rayon", + "serde", + "serde-bench", + "serde_bytes", + "serde_derive", + "target-lexicon", + "wasmer-clif-fork-frontend", + "wasmer-clif-fork-wasm", + "wasmer-runtime-core 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-win-exception-handler 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmparser 0.51.4", "winapi 0.3.8", ] @@ -2002,8 +2036,22 @@ dependencies = [ "memmap", "serde", "serde_derive", - "wasmer-clif-backend", - "wasmer-runtime-core", + "wasmer-clif-backend 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)", +] + +[[package]] +name = "wasmer-runtime" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30259003902716aa4fb86fd66a2de555116adef545cbc5ab70afb74e74b44fc3" +dependencies = [ + "lazy_static", + "memmap", + "serde", + "serde_derive", + "wasmer-clif-backend 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)", ] [[package]] @@ -2034,6 +2082,35 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "wasmer-runtime-core" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45d4253f097502423d8b19d54cb18745f61b984b9dbce32424cba7945cfef367" +dependencies = [ + "bincode", + "blake3", + "cc", + "digest", + "errno", + "hex", + "indexmap", + "lazy_static", + "libc", + "nix 0.15.0", + "page_size", + "parking_lot", + "rustc_version", + "serde", + "serde-bench", + "serde_bytes", + "serde_derive", + "smallvec", + "target-lexicon", + "wasmparser 0.51.4", + "winapi 0.3.8", +] + [[package]] name = "wasmer-wasi" version = "0.17.0" @@ -2049,7 +2126,7 @@ dependencies = [ "thiserror", "time", "typetag", - "wasmer-runtime-core", + "wasmer-runtime-core 0.17.0 (git+http://github.com/fluencelabs/wasmer?branch=fluence)", "winapi 0.3.8", ] @@ -2060,7 +2137,19 @@ source = "git+http://github.com/fluencelabs/wasmer?branch=fluence#0575a7ed3f8742 dependencies = [ "cc", "libc", - "wasmer-runtime-core", + "wasmer-runtime-core 0.17.0 (git+http://github.com/fluencelabs/wasmer?branch=fluence)", + "winapi 0.3.8", +] + +[[package]] +name = "wasmer-win-exception-handler" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf22ce6dc66d893099aac853d451bf9443fa8f5443f5bf4fc63f3aebd7b592b1" +dependencies = [ + "cc", + "libc", + "wasmer-runtime-core 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8", ] @@ -2151,6 +2240,15 @@ dependencies = [ "wasmer-interface-types", ] +[[package]] +name = "wit_fce" +version = "0.1.0" +dependencies = [ + "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)", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index fd52f9b5..f9f10a0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,8 @@ [workspace] members = [ "fce", + "wit_fce", "wit_embedder", + "wit_fce/examples/export_test", + "wit_fce/examples/ipfs_rpc", ] diff --git a/wit_fce/Cargo.toml b/wit_fce/Cargo.toml new file mode 100644 index 00000000..41acfd67 --- /dev/null +++ b/wit_fce/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "wit_fce" +version = "0.1.0" +authors = ["Fluence Labs"] +edition = "2018" + +[dependencies] +wasmer-runtime = "0.17.0" +wasmer-runtime-core = "0.17.0" +wasmer-interface-types = { git = "http://github.com/fluencelabs/interface-types" } diff --git a/wit_fce/examples/export_test/Cargo.toml b/wit_fce/examples/export_test/Cargo.toml new file mode 100644 index 00000000..c813af87 --- /dev/null +++ b/wit_fce/examples/export_test/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "export_test" +version = "0.1.0" +authors = ["Fluence Labs"] +edition = "2018" + +[lib] +name = "export_test" +path = "src/lib.rs" +crate-type = ["cdylib"] diff --git a/wit_fce/examples/export_test/src/lib.rs b/wit_fce/examples/export_test/src/lib.rs new file mode 100644 index 00000000..9e9db848 --- /dev/null +++ b/wit_fce/examples/export_test/src/lib.rs @@ -0,0 +1,44 @@ +/* + * 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 strlen(str_ptr: *mut u8, str_len: usize) -> usize { + let user_name = String::from_raw_parts(str_ptr, str_len, str_len); + user_name.len() +} + +#[no_mangle] +pub unsafe fn greeting(user_name_ptr: *mut u8, user_name_size: usize) { + let user_name = String::from_raw_parts(user_name_ptr, user_name_size, user_name_size); + let user_name = format!("Hi, {}\n", user_name); + + log_utf8_string(user_name.as_ptr() as i32, user_name.len() as i32); + + *RESULT_PTR.get_mut() = user_name.as_ptr() as _; + *RESULT_SIZE.get_mut() = user_name.len(); + std::mem::forget(user_name); +} + +#[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); +} diff --git a/wit_fce/examples/export_test/src/mem.rs b/wit_fce/examples/export_test/src/mem.rs new file mode 100644 index 00000000..e39eb89a --- /dev/null +++ b/wit_fce/examples/export_test/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/export_test/src/result.rs b/wit_fce/examples/export_test/src/result.rs new file mode 100644 index 00000000..872ef773 --- /dev/null +++ b/wit_fce/examples/export_test/src/result.rs @@ -0,0 +1,31 @@ +/* + * 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() +} diff --git a/wit_fce/examples/export_test/wit b/wit_fce/examples/export_test/wit new file mode 100644 index 00000000..eab8e92b --- /dev/null +++ b/wit_fce/examples/export_test/wit @@ -0,0 +1,49 @@ +;; allocate function +(@interface type (func (param i32) (result i32))) + +;; deallocate function +(@interface type (func (param i32 i32))) + +;; greeting function +(@interface type (func (param string) (result string))) + +;; strlen function +(@interface type (func (param string) (result i32))) + +;; result extractor functions +(@interface type (func (result i32))) + +(@interface export "allocate" (func 0)) +(@interface export "deallocate" (func 1)) +(@interface export "greeting" (func 2)) +(@interface export "strlen" (func 3)) +(@interface export "get_result_size" (func 4)) +(@interface export "get_result_ptr" (func 4)) + +(@interface func (type 2) + arg.get 0 + string.size + call-core 0 ;; call allocate + arg.get 0 + string.lower_memory + call-core 2 ;; call greeting + call-core 5 ;; call get_result_ptr + call-core 4 ;; call get_result_size + string.lift_memory + call-core 5 ;; call get_result_ptr + call-core 4 ;; call get_result_size + call-core 1 ;; call deallocate +) + +(@interface func (type 3) + arg.get 0 + string.size + call-core 0 ;; call allocate + arg.get 0 + string.lower_memory + call-core 3 ;; call strlen +) + +;; Implementations +(@interface implement (func 0) (func 2)) +(@interface implement (func 1) (func 3)) diff --git a/wit_fce/examples/ipfs_rpc/Cargo.toml b/wit_fce/examples/ipfs_rpc/Cargo.toml new file mode 100644 index 00000000..f70c0215 --- /dev/null +++ b/wit_fce/examples/ipfs_rpc/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "ipfs_rpc" +version = "0.1.0" +authors = ["Fluence Labs"] +edition = "2018" + +[lib] +name = "ipfs_rpc" +path = "src/lib.rs" +crate-type = ["cdylib"] diff --git a/wit_fce/examples/ipfs_rpc/src/lib.rs b/wit_fce/examples/ipfs_rpc/src/lib.rs new file mode 100644 index 00000000..8077db13 --- /dev/null +++ b/wit_fce/examples/ipfs_rpc/src/lib.rs @@ -0,0 +1,47 @@ +/* + * 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 invoke(file_content_ptr: *mut u8, file_content_size: usize) { + 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(), + ); + + log_utf8_string(hash.as_ptr() as _, hash.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 = "node")] +extern "C" { + /// Put a file to ipfs, returns ipfs hash of the file. + fn put(ptr: i32, size: i32); + + /// Get file from ipfs by hash. + fn get(ptr: i32, size: i32); +} diff --git a/wit_fce/examples/ipfs_rpc/src/mem.rs b/wit_fce/examples/ipfs_rpc/src/mem.rs new file mode 100644 index 00000000..e39eb89a --- /dev/null +++ b/wit_fce/examples/ipfs_rpc/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_rpc/src/result.rs b/wit_fce/examples/ipfs_rpc/src/result.rs new file mode 100644 index 00000000..8def1085 --- /dev/null +++ b/wit_fce/examples/ipfs_rpc/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(ptr: usize) { + *RESULT_SIZE.get_mut() = size; +} diff --git a/wit_fce/examples/ipfs_rpc/wit b/wit_fce/examples/ipfs_rpc/wit new file mode 100644 index 00000000..0b2a77f3 --- /dev/null +++ b/wit_fce/examples/ipfs_rpc/wit @@ -0,0 +1,46 @@ +;; allocate function type +(@interface type (func (param i32) (result i32))) + +;; deallocate function +(@interface type (func (param i32 i32))) + +;; invoke function +(@interface type (func (param string) (result string))) + +;; result extractor functions +(@interface type (func (result i32))) + +;; result setter functions +(@interface type (func (param i32))) + +;; import ipfs put/get function +(@interface type (func (param string) (param string))) + +(@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)) + +(@interface import "node" "get" (func 5)) +(@interface import "node" "put" (func 5)) + +(@interface func (type 2) + arg.get 0 + string.size + 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 + string.lift_memory + call-core 3 ;; call get_result_size + call-core 4 ;; call get_result_ptr + call-core 1 ;; call deallocate +) + +;; Implementations +(@interface implement (func 0) (func 2)) diff --git a/wit_fce/src/instance/errors.rs b/wit_fce/src/instance/errors.rs new file mode 100644 index 00000000..2f885228 --- /dev/null +++ b/wit_fce/src/instance/errors.rs @@ -0,0 +1,141 @@ +/* + * 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::error::Error; +use wasmer_interface_types::errors::InstructionError; +use wasmer_runtime::error::{ + CallError, CompileError, CreationError, Error as WasmerError, ResolveError, RuntimeError, +}; + +#[derive(Debug)] +#[allow(unused)] +pub enum WITFCEError { + /// 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 when there is no module with such name. + NoSuchFunction, + + /// Returns when there is no module with such name. + NoSuchModule, + + /// WIT section is absent. + NoWITSection, + + /// Multiple WIT sections. + MultipleWITSections, + + /// WIT section remainder isn't empty. + WITRemainderNotEmpty, + + /// An error occurred while parsing WIT section. + WITParseError, + + /// Indicates that modules currently in use and couldn't be deleted. + ModuleInUse, +} + +impl Error for WITFCEError {} + +impl std::fmt::Display for WITFCEError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + WITFCEError::IOError(msg) => write!(f, "IOError: {}", msg), + WITFCEError::WasmerResolveError(msg) => write!(f, "WasmerResolveError: {}", msg), + WITFCEError::WasmerInvokeError(msg) => write!(f, "WasmerInvokeError: {}", msg), + WITFCEError::WasmerCompileError(msg) => write!(f, "WasmerCompileError: {}", msg), + WITFCEError::WasmerCreationError(msg) => write!(f, "WasmerCreationError: {}", msg), + 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::ModuleInUse => { + write!(f, "Module is used by other modules and couldn't be deleted") + } + _ => unimplemented!(), + } + } +} + +impl From for WITFCEError { + fn from(err: CreationError) -> Self { + WITFCEError::WasmerCreationError(format!("{}", err)) + } +} + +impl From for WITFCEError { + fn from(err: CompileError) -> Self { + WITFCEError::WasmerCompileError(format!("{}", err)) + } +} + +impl From for WITFCEError { + fn from(err: CallError) -> Self { + match err { + CallError::Resolve(err) => WITFCEError::WasmerResolveError(format!("{}", err)), + CallError::Runtime(err) => WITFCEError::WasmerInvokeError(format!("{}", err)), + } + } +} + +impl From for WITFCEError { + fn from(err: ResolveError) -> Self { + WITFCEError::WasmerResolveError(format!("{}", err)) + } +} + +impl From for WITFCEError { + fn from(err: RuntimeError) -> Self { + WITFCEError::WasmerInvokeError(format!("{}", err)) + } +} + +impl From for WITFCEError { + fn from(err: WasmerError) -> Self { + WITFCEError::WasmerInvokeError(format!("{}", err)) + } +} + +impl From for WITFCEError { + fn from(err: std::io::Error) -> Self { + WITFCEError::IOError(format!("{}", err)) + } +} + +impl From for WITFCEError { + fn from(err: InstructionError) -> Self { + WITFCEError::WasmerInvokeError(format!("{}", err)) + } +} diff --git a/wit_fce/src/instance/exports.rs b/wit_fce/src/instance/exports.rs new file mode 100644 index 00000000..eb73a8e7 --- /dev/null +++ b/wit_fce/src/instance/exports.rs @@ -0,0 +1,59 @@ +/* + * 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_interface_types::interpreter::wasm; +use wasmer_interface_types::{types::InterfaceType, values::InterfaceValue}; + +// In current implementation export simply does nothing. +#[derive(Clone)] +pub(crate) struct WITExport { + pub(crate) inputs: Vec, + pub(crate) outputs: Vec, + pub(crate) function: fn(arguments: &[InterfaceValue]) -> Result, ()>, +} + +impl WITExport { + #[allow(unused)] + pub(crate) fn new() -> Self { + Self { + inputs: vec![], + outputs: vec![], + function: |_| -> _ { Ok(vec![]) }, + } + } +} + +impl wasm::structures::Export for WITExport { + fn inputs_cardinality(&self) -> usize { + self.inputs.len() as usize + } + + 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, ()> { + (self.function)(arguments) + } +} diff --git a/wit_fce/src/instance/locals_imports.rs b/wit_fce/src/instance/locals_imports.rs new file mode 100644 index 00000000..12643b43 --- /dev/null +++ b/wit_fce/src/instance/locals_imports.rs @@ -0,0 +1,86 @@ +/* + * 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 new file mode 100644 index 00000000..2d6e72aa --- /dev/null +++ b/wit_fce/src/instance/memory.rs @@ -0,0 +1,49 @@ +/* + * 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_interface_types::interpreter::wasm; +use wasmer_runtime_core::memory::{Memory, MemoryView}; + +pub struct WITMemoryView<'a>(pub MemoryView<'a, u8>); +impl<'a> std::ops::Deref for WITMemoryView<'a> { + type Target = [std::cell::Cell]; + + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +pub struct WITMemory(pub Memory); +impl std::ops::Deref for WITMemory { + type Target = Memory; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl wasm::structures::MemoryView for WITMemoryView<'_> {} + +impl<'a> wasm::structures::Memory> for WITMemory { + fn view(&self) -> WITMemoryView<'a> { + use wasmer_runtime_core::vm::LocalMemory; + + let LocalMemory { base, .. } = unsafe { *self.0.vm_local_memory() }; + let length = self.0.size().bytes().0 / std::mem::size_of::(); + + unsafe { WITMemoryView(MemoryView::new(base as _, length as u32)) } + } +} diff --git a/wit_fce/src/instance/mod.rs b/wit_fce/src/instance/mod.rs new file mode 100644 index 00000000..5c954dc3 --- /dev/null +++ b/wit_fce/src/instance/mod.rs @@ -0,0 +1,56 @@ +/* + * 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 mod errors; +pub mod exports; +pub mod locals_imports; +pub mod memory; +pub mod wit_instance; +pub mod wit_module; + +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}; + +pub fn wtype_to_itype(ty: &WType) -> IType { + match ty { + WType::I32 => IType::I32, + WType::I64 => IType::I64, + WType::F32 => IType::F32, + WType::F64 => IType::F64, + WType::V128 => unimplemented!(), + } +} + +pub fn ival_to_wval(value: &IValue) -> WValue { + match value { + IValue::I32(v) => WValue::I32(*v), + IValue::I64(v) => WValue::I64(*v), + IValue::F32(v) => WValue::F32(*v), + IValue::F64(v) => WValue::F64(*v), + _ => unimplemented!(), + } +} + +pub fn wval_to_ival(value: &WValue) -> IValue { + match value { + WValue::I32(v) => IValue::I32(*v), + WValue::I64(v) => IValue::I64(*v), + WValue::F32(v) => IValue::F32(*v), + WValue::F64(v) => IValue::F64(*v), + _ => unimplemented!(), + } +} diff --git a/wit_fce/src/instance/wit_instance.rs b/wit_fce/src/instance/wit_instance.rs new file mode 100644 index 00000000..c207e53b --- /dev/null +++ b/wit_fce/src/instance/wit_instance.rs @@ -0,0 +1,119 @@ +/* + * 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::exports::WITExport; +use crate::instance::locals_imports::WITLocalImport; +use crate::instance::memory::{WITMemory, WITMemoryView}; + +use wasmer_interface_types::interpreter::wasm; +use wasmer_runtime_core::Instance as WasmerInstance; + +use std::collections::HashMap; +use wasmer_interface_types::ast::Interfaces; +use wasmer_interface_types::ast::Type; +use wasmer_interface_types::interpreter::wasm::structures::{LocalImportIndex, TypedIndex}; + +pub struct WITInstance { + // represent all import and export functions + funcs: HashMap, + memories: Vec, +} + +impl WITInstance { + pub fn new( + wasmer_instance: &WasmerInstance, + interfaces: &Interfaces, + ) -> Result { + let funcs = Self::extract_funcs(&wasmer_instance, interfaces)?; + let memories = Self::extract_memories(&wasmer_instance); + + Ok(Self { funcs, memories }) + } + + fn extract_funcs( + wasmer_instance: &WasmerInstance, + interfaces: &Interfaces, + ) -> Result, WITFCEError> { + use wasmer_runtime_core::DynFunc; + let module_exports = &wasmer_instance.exports; + + interfaces + .exports + .iter() + .enumerate() + .map(|(export_id, export)| { + let export_func = module_exports.get(export.name)?; + unsafe { + // TODO: refactor this with new Wasmer API when it is ready + // 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)?)) + } + }) + .collect() + } + + fn extract_memories(wasmer_instance: &WasmerInstance) -> Vec { + use wasmer_runtime_core::export::Export::Memory; + + let mut memories = wasmer_instance + .exports() + .filter_map(|(_, export)| match export { + Memory(memory) => Some(WITMemory(memory)), + _ => None, + }) + .collect::>(); + + if let Some(Memory(memory)) = wasmer_instance + .import_object + .maybe_with_namespace("env", |env| env.get_export("memory")) + { + memories.push(WITMemory(memory)); + } + + memories + } +} + +impl<'instance> wasm::structures::Instance> + for WITInstance +{ + fn export(&self, _export_name: &str) -> Option<&WITExport> { + // exports aren't needed for this version of WIT + None + } + + fn local_or_import( + &mut self, + index: I, + ) -> Option<&WITLocalImport> { + self.funcs.get(&index.index()) + } + + fn memory(&self, index: usize) -> Option<&WITMemory> { + if index >= self.memories.len() { + None + } else { + Some(&self.memories[index]) + } + } + + fn wit_type(&self, _index: u32) -> Option<&Type> { + None + } +} diff --git a/wit_fce/src/instance/wit_module.rs b/wit_fce/src/instance/wit_module.rs new file mode 100644 index 00000000..b6c207dc --- /dev/null +++ b/wit_fce/src/instance/wit_module.rs @@ -0,0 +1,140 @@ +/* + * 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::exports::WITExport; +use crate::instance::locals_imports::WITLocalImport; +use crate::instance::memory::{WITMemory, WITMemoryView}; +use crate::instance::wit_instance::WITInstance; + +use wasmer_interface_types as wit; +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 std::collections::HashMap; +use std::convert::TryInto; +use wasmer_interface_types::interpreter::stack::Stackable; + +const WIT_SECTION_NAME: &str = "interface-types"; + +pub struct WITModule { + instance: WasmerInstance, + wit_instance: WITInstance, + exports: HashMap< + String, + Interpreter>, + >, + import_object: ImportObject, +} + +impl WITModule { + pub fn new(wasm_bytes: &[u8], imports: &ImportObject) -> Result { + let wasmer_instance = compile(&wasm_bytes)?.instantiate(imports)?; + + let wit_sections = wasmer_instance + .module + .info + .custom_sections + .get(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 wit_instance = WITInstance::new(&wasmer_instance, &interfaces)?; + + let wit_export_names = interfaces + .imports + .iter() + .map(|export| (export.function_type, export.name.to_string())) + .collect::>(); + + 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 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>>()?; + + Ok(Self { + instance: wasmer_instance, + wit_instance, + exports: callable_exports, + }) + } + + pub fn call( + &mut self, + function_name: &str, + args: &[InterfaceValue], + ) -> Result, WITFCEError> { + match self.exports.get(function_name) { + Some(func) => { + let result = func + .run(args, &mut self.wit_instance)? + .as_slice() + .to_owned(); + Ok(result) + } + None => Err(WITFCEError::NoSuchFunction), + } + } +} diff --git a/wit_fce/src/main.rs b/wit_fce/src/main.rs new file mode 100644 index 00000000..1e838521 --- /dev/null +++ b/wit_fce/src/main.rs @@ -0,0 +1,60 @@ +/* + * 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 instance; + +use crate::instance::wit_module::WITModule; + +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"; + +fn main() { + let wasm_bytes = std::fs::read(FILE_NAME).unwrap(); + let logger_imports = imports! { + "logger" => { + "log_utf8_string" => func!(logger_log_utf8_string), + }, + }; + let mut import_object = ImportObject::new(); + import_object.extend(logger_imports); + + let mut module = + WITModule::new(&wasm_bytes, &import_object).expect("module successfully created"); + + let result1 = module + .call("strlen", &[InterfaceValue::String("aaaaaa".to_string())]) + .unwrap(); + let result2 = module + .call("greeting", &[InterfaceValue::String("Mike".to_string())]) + .unwrap(); + + println!("stack state {:?}", result1); + println!("stack state {:?}", result2); +} + +fn logger_log_utf8_string(ctx: &mut Ctx, offset: i32, size: i32) { + use wasmer_runtime_core::memory::ptr::{Array, WasmPtr}; + + let wasm_ptr = WasmPtr::::new(offset as _); + match wasm_ptr.get_utf8_string(ctx.memory(0), size as _) { + Some(msg) => print!("{}", msg), + None => print!("fce logger: incorrect UTF8 string's been supplied to logger"), + } +}