diff --git a/Cargo.lock b/Cargo.lock index 408641ae..7d11dc6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "addr2line" version = "0.15.1" @@ -713,6 +723,8 @@ dependencies = [ [[package]] name = "fluence-app-service" version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3b1d2c199d6bc140c9ec5ffa323ebd34ac134fb37828bbfd7aeed0339cab78" dependencies = [ "fluence-faas 0.7.2", "log", @@ -726,11 +738,9 @@ dependencies = [ [[package]] name = "fluence-app-service" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3b1d2c199d6bc140c9ec5ffa323ebd34ac134fb37828bbfd7aeed0339cab78" +version = "0.7.3" dependencies = [ - "fluence-faas 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fluence-faas 0.7.3", "log", "maplit", "serde", @@ -740,32 +750,6 @@ dependencies = [ "wasmer-wasi-fl", ] -[[package]] -name = "fluence-faas" -version = "0.7.2" -dependencies = [ - "cmd_lib", - "env_logger 0.7.1", - "fluence", - "fluence-sdk-main", - "itertools 0.9.0", - "log", - "marine-runtime 0.5.0", - "marine-utils 0.2.0", - "once_cell", - "pretty_assertions", - "safe-transmute", - "serde", - "serde_derive", - "serde_json", - "thiserror", - "toml", - "wasmer-interface-types-fl", - "wasmer-runtime-core-fl", - "wasmer-runtime-fl", - "wasmer-wasi-fl", -] - [[package]] name = "fluence-faas" version = "0.7.2" @@ -791,6 +775,33 @@ dependencies = [ "wasmer-wasi-fl", ] +[[package]] +name = "fluence-faas" +version = "0.7.3" +dependencies = [ + "cmd_lib", + "env_logger 0.7.1", + "fluence", + "fluence-sdk-main", + "itertools 0.9.0", + "log", + "marine-module-interface", + "marine-runtime 0.5.0", + "marine-utils 0.2.0", + "once_cell", + "pretty_assertions", + "safe-transmute", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "toml", + "wasmer-interface-types-fl", + "wasmer-runtime-core-fl", + "wasmer-runtime-fl", + "wasmer-wasi-fl", +] + [[package]] name = "fluence-it-types" version = "0.3.0" @@ -830,11 +841,11 @@ dependencies = [ [[package]] name = "fluence-test" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be0d7928e5e6a74a8e303b7f539116fdb4043f5788f78e9eaf32c53700c4c18" +checksum = "886ac15e0e95754d5d9339b541a5119dc47cc1a8d80101dc4e3e238d06a4b28b" dependencies = [ - "fluence-app-service 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fluence-app-service 0.7.2", "marine-test-macro", "serde", "serde_json", @@ -994,9 +1005,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if 1.0.0", "libc", @@ -1064,8 +1075,8 @@ dependencies = [ "http", "indexmap", "slab", - "tokio 1.5.0", - "tokio-util 0.6.6", + "tokio 1.6.0", + "tokio-util 0.6.7", "tracing", ] @@ -1200,7 +1211,7 @@ dependencies = [ "itoa", "pin-project", "socket2 0.4.0", - "tokio 1.5.0", + "tokio 1.6.0", "tower-service", "tracing", "want", @@ -1228,7 +1239,7 @@ dependencies = [ "bytes 1.0.1", "hyper 0.14.7", "native-tls", - "tokio 1.5.0", + "tokio 1.6.0", "tokio-native-tls", ] @@ -1446,14 +1457,15 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "marine" -version = "0.6.1" +version = "0.6.3" dependencies = [ + "Inflector", "anyhow", "check-latest", "clap", "exitfailure", - "marine-it-generator 0.5.1", - "marine-it-parser 0.6.0", + "marine-it-generator 0.5.2", + "marine-it-parser 0.6.1", "marine-module-info-parser 0.1.0", "semver 0.11.0", "serde", @@ -1483,11 +1495,11 @@ dependencies = [ [[package]] name = "marine-it-generator" -version = "0.5.1" +version = "0.5.2" dependencies = [ "cargo_toml", "it-lilo", - "marine-it-parser 0.6.0", + "marine-it-parser 0.6.1", "marine-macro-impl", "once_cell", "serde", @@ -1534,10 +1546,12 @@ dependencies = [ [[package]] name = "marine-it-parser" -version = "0.6.0" +version = "0.6.1" dependencies = [ "anyhow", + "itertools 0.10.0", "marine-it-interfaces 0.4.0", + "marine-module-interface", "nom", "semver 0.11.0", "serde", @@ -1600,6 +1614,22 @@ dependencies = [ "wasmer-runtime-core-fl", ] +[[package]] +name = "marine-module-interface" +version = "0.1.0" +dependencies = [ + "anyhow", + "itertools 0.10.0", + "marine-it-interfaces 0.4.0", + "nom", + "semver 0.11.0", + "serde", + "thiserror", + "walrus", + "wasmer-interface-types-fl", + "wasmer-runtime-core-fl", +] + [[package]] name = "marine-runtime" version = "0.5.0" @@ -1609,10 +1639,11 @@ dependencies = [ "bytes 0.5.6", "it-lilo", "log", - "marine-it-generator 0.5.1", + "marine-it-generator 0.5.2", "marine-it-interfaces 0.4.0", - "marine-it-parser 0.6.0", + "marine-it-parser 0.6.1", "marine-module-info-parser 0.1.0", + "marine-module-interface", "marine-utils 0.2.0", "multimap", "once_cell", @@ -1670,9 +1701,9 @@ dependencies = [ [[package]] name = "marine-test-macro" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709eebc4ee6c8e6aa49066ab066c9f137d9cf164f298f578a67bfb11a0508f65" +checksum = "089a1d98ebee2fa24928e23019b477222562beda663f8625d3d490dce290e532" dependencies = [ "marine-test-macro-impl", "proc-macro-error", @@ -1683,12 +1714,12 @@ dependencies = [ [[package]] name = "marine-test-macro-impl" -version = "0.1.5" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2432fd5233a088981c79324668b76f3ee2ae6d313d4522b5490226e5e7827ce" +checksum = "b6e79cfbde74677cbeb60fe5944c345617739aae37dc5ac5ef5f12d487472071" dependencies = [ "darling 0.12.4", - "fluence-app-service 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fluence-app-service 0.7.2", "marine-it-parser 0.5.0", "proc-macro-error", "proc-macro2", @@ -1829,13 +1860,13 @@ dependencies = [ [[package]] name = "mrepl" -version = "0.7.2" +version = "0.7.3" dependencies = [ "anyhow", "check-latest", "clap", "env_logger 0.7.1", - "fluence-app-service 0.7.2", + "fluence-app-service 0.7.3", "fluence-sdk-main", "itertools 0.9.0", "log", @@ -2181,9 +2212,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" dependencies = [ "unicode-xid", ] @@ -2242,7 +2273,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ - "getrandom 0.2.2", + "getrandom 0.2.3", ] [[package]] @@ -2267,9 +2298,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" dependencies = [ "autocfg", "crossbeam-deque", @@ -2279,9 +2310,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -2355,7 +2386,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ - "getrandom 0.2.2", + "getrandom 0.2.3", "redox_syscall 0.2.8", ] @@ -2446,7 +2477,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "tokio 1.5.0", + "tokio 1.6.0", "tokio-native-tls", "url", "wasm-bindgen", @@ -2934,9 +2965,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5" +checksum = "bd3076b5c8cc18138b8f8814895c11eb4de37114a5d127bafdc5e55798ceef37" dependencies = [ "autocfg", "bytes 1.0.1", @@ -2965,7 +2996,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" dependencies = [ "native-tls", - "tokio 1.5.0", + "tokio 1.6.0", ] [[package]] @@ -2994,16 +3025,16 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940a12c99365c31ea8dd9ba04ec1be183ffe4920102bb7122c2f515437601e8e" +checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592" dependencies = [ "bytes 1.0.1", "futures-core", "futures-sink", "log", "pin-project-lite 0.2.6", - "tokio 1.5.0", + "tokio 1.6.0", ] [[package]] @@ -3183,7 +3214,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.2", + "getrandom 0.2.3", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2c35fa0a..f6a202f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "crates/it-interfaces", "crates/it-parser", "crates/module-info-parser", + "crates/module-interface", "crates/utils", "examples/call_parameters", "examples/greeting", diff --git a/crates/it-generator/src/instructions_generator/foreign_mod_instructions.rs b/crates/it-generator/src/instructions_generator/foreign_mod_instructions.rs index 418115c8..a2c2c706 100644 --- a/crates/it-generator/src/instructions_generator/foreign_mod_instructions.rs +++ b/crates/it-generator/src/instructions_generator/foreign_mod_instructions.rs @@ -148,7 +148,7 @@ fn generate_it_instructions<'f>( Ok(()) } -pub(crate) fn generate_raw_args<'f>(signature: &FnSignature) -> Rc> { +pub(crate) fn generate_raw_args(signature: &FnSignature) -> Rc> { let raw_inputs = signature .arguments .iter() @@ -159,7 +159,7 @@ pub(crate) fn generate_raw_args<'f>(signature: &FnSignature) -> Rc(signature: &FnSignature) -> Rc> { +pub(crate) fn generate_raw_output_type(signature: &FnSignature) -> Rc> { let raw_outputs = signature .output_types .iter() diff --git a/crates/it-parser/Cargo.toml b/crates/it-parser/Cargo.toml index 731d36f3..f7bf2a14 100644 --- a/crates/it-parser/Cargo.toml +++ b/crates/it-parser/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "marine-it-parser" description = "Fluence Marine interface types parser" -version = "0.6.0" +version = "0.6.1" authors = ["Fluence Labs"] license = "Apache-2.0" edition = "2018" @@ -12,6 +12,7 @@ path = "src/lib.rs" [dependencies] marine-it-interfaces = { path = "../it-interfaces", version = "0.4.0" } +marine-module-interface = { path = "../module-interface", version = "0.1.0" } anyhow = "1.0.31" walrus = "0.18.0" @@ -19,6 +20,7 @@ wasmer-core = { package = "wasmer-runtime-core-fl", version = "0.17.0"} wasmer-it = { package = "wasmer-interface-types-fl", version = "0.20.0" } nom = "5.1" +itertools = "0.10.0" semver = "0.11.0" serde = "=1.0.118" thiserror = "1.0.24" diff --git a/crates/it-parser/src/embedder.rs b/crates/it-parser/src/embedder.rs index e9836966..3e6897f2 100644 --- a/crates/it-parser/src/embedder.rs +++ b/crates/it-parser/src/embedder.rs @@ -16,7 +16,7 @@ use super::custom::ITCustomSection; use super::errors::ITParserError; -use crate::Result; +use crate::ParserResult; use walrus::ModuleConfig; use wasmer_it::{ @@ -28,7 +28,7 @@ use wasmer_it::ToBytes; use std::path::Path; /// Embed provided IT to a Wasm file by path. -pub fn embed_text_it(in_wasm_path: I, out_wasm_path: O, it: &str) -> Result<()> +pub fn embed_text_it(in_wasm_path: I, out_wasm_path: O, it: &str) -> ParserResult<()> where I: AsRef, O: AsRef, diff --git a/crates/it-parser/src/errors.rs b/crates/it-parser/src/errors.rs index 4abe8f8f..d19a0e50 100644 --- a/crates/it-parser/src/errors.rs +++ b/crates/it-parser/src/errors.rs @@ -14,6 +14,9 @@ * limitations under the License. */ +use marine_module_interface::interface::InterfaceError; +use marine_module_interface::it_interface::ITInterfaceError; + use wasmer_it::decoders::wat::Error as WATError; use thiserror::Error as ThisError; @@ -45,6 +48,14 @@ pub enum ITParserError { #[error("{0}")] IncorrectITFormat(String), + /// An error occurred while processing module interface. + #[error("{0}")] + ModuleInterfaceError(#[from] InterfaceError), + + /// An error occurred while processing module IT interface. + #[error("{0}")] + ModuleITInterfaceError(#[from] ITInterfaceError), + /// An error occurred while parsing file in Wat format. #[error("provided file with IT definitions is corrupted: {0}")] CorruptedITFile(#[from] WATError), diff --git a/crates/it-parser/src/extractor.rs b/crates/it-parser/src/extractor.rs index 7811ca60..7f4636cd 100644 --- a/crates/it-parser/src/extractor.rs +++ b/crates/it-parser/src/extractor.rs @@ -14,38 +14,41 @@ * limitations under the License. */ -mod functions; mod it; -pub use functions::*; pub use it::*; -use crate::Result; +use crate::interface::ModuleInterface; +use crate::it_interface::IModuleInterface; +use crate::ParserResult; use crate::ITParserError; +use marine_module_interface::it_interface; +use marine_module_interface::interface; use marine_it_interfaces::MITInterfaces; use std::path::Path; -pub fn module_interface

(module_path: P) -> Result +pub fn module_interface

(module_path: P) -> ParserResult where P: AsRef, { - create_mit_with(module_path, |it| get_interface(&it)) + create_mit_with(module_path, |it| interface::get_interface(&it)) } -pub fn module_raw_interface

(module_path: P) -> Result +pub fn module_it_interface

(module_path: P) -> ParserResult where P: AsRef, { - create_mit_with(module_path, |it| get_raw_interface(&it)) + create_mit_with(module_path, |it| it_interface::get_interface(&it)) } -fn create_mit_with( +fn create_mit_with( module_path: P, - transformer: impl FnOnce(MITInterfaces<'_>) -> Result, -) -> Result + transformer: impl FnOnce(MITInterfaces<'_>) -> std::result::Result, +) -> ParserResult where P: AsRef, + ITParserError: From, { let module = walrus::ModuleConfig::new() .parse_file(module_path) @@ -56,5 +59,5 @@ where let mit = MITInterfaces::new(it); - transformer(mit) + transformer(mit).map_err(Into::into) } diff --git a/crates/it-parser/src/extractor/functions.rs b/crates/it-parser/src/extractor/functions.rs deleted file mode 100644 index 3f65676b..00000000 --- a/crates/it-parser/src/extractor/functions.rs +++ /dev/null @@ -1,213 +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::Result; -use crate::ITParserError; -use marine_it_interfaces::MITInterfaces; - -use wasmer_it::IRecordType; -use wasmer_it::ast::FunctionArg as IFunctionArg; -use wasmer_it::IType; -use serde::Serialize; -use serde::Deserialize; - -use std::collections::HashMap; -use std::rc::Rc; - -pub type MRecordTypes = HashMap>; - -#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)] -pub struct MFunctionSignature { - pub name: Rc, - pub arguments: Rc>, - pub outputs: Rc>, -} - -#[derive(PartialEq, Eq, Debug, Clone, Serialize)] -pub struct MModuleInterface { - pub record_types: MRecordTypes, - pub function_signatures: Vec, -} - -pub fn get_interface(mit: &MITInterfaces<'_>) -> Result { - let marine_interface = get_raw_interface(mit)?; - let service_interface = into_service_interface(marine_interface); - - Ok(service_interface) -} - -pub fn get_raw_interface(mit: &MITInterfaces<'_>) -> Result { - let function_signatures = get_exports(mit)?; - let record_types = extract_record_types(mit); - - let mm_interface = MModuleInterface { - record_types, - function_signatures, - }; - - Ok(mm_interface) -} - -fn get_exports(it: &MITInterfaces<'_>) -> Result> { - use marine_it_interfaces::ITAstType; - - it.implementations() - .filter_map(|(adapter_function_type, core_function_type)| { - it.exports_by_type(*core_function_type) - .map(|export_function_name| (adapter_function_type, export_function_name)) - }) - .map(|(adapter_function_type, export_function_names)| { - export_function_names - .iter() - .map(move |export_function_name| (*adapter_function_type, export_function_name)) - }) - .flatten() - .map(|(adapter_function_type, export_function_name)| { - let it_type = it.type_by_idx_r(adapter_function_type).unwrap(); - - match it_type { - ITAstType::Function { - arguments, - output_types, - } => { - let signature = MFunctionSignature { - name: Rc::new(export_function_name.to_string()), - arguments: arguments.clone(), - outputs: output_types.clone(), - }; - Ok(signature) - } - _ => Err(ITParserError::IncorrectITFormat(format!( - "type with idx = {} isn't a function type", - adapter_function_type - ))), - } - }) - .collect::>>() -} - -fn extract_record_types(it: &MITInterfaces<'_>) -> MRecordTypes { - use marine_it_interfaces::ITAstType; - - let (record_types_by_id, _) = it.types().fold( - (HashMap::new(), 0u64), - |(mut record_types_by_id, id), ty| { - match ty { - ITAstType::Record(record_type) => { - record_types_by_id.insert(id, record_type.clone()); - } - ITAstType::Function { .. } => {} - }; - (record_types_by_id, id + 1) - }, - ); - - record_types_by_id -} - -#[derive(Serialize)] -pub struct FunctionSignature { - pub name: String, - pub arguments: Vec<(String, String)>, - pub output_types: Vec, -} - -#[derive(Serialize)] -pub struct RecordType { - pub name: String, - pub id: u64, - pub fields: Vec<(String, String)>, -} - -#[derive(Serialize)] -pub struct ServiceInterface { - pub function_signatures: Vec, - pub record_types: Vec, -} - -pub(crate) fn into_service_interface(mm_interface: MModuleInterface) -> ServiceInterface { - let record_types = mm_interface.record_types; - - let function_signatures = mm_interface - .function_signatures - .into_iter() - .map(|sign| serialize_function_signature(sign, &record_types)) - .collect(); - - let record_types = record_types - .iter() - .map(|(id, record)| serialize_record_type(*id, record.clone(), &record_types)) - .collect::>(); - - ServiceInterface { - function_signatures, - record_types, - } -} - -fn serialize_function_signature( - signature: MFunctionSignature, - record_types: &MRecordTypes, -) -> FunctionSignature { - let arguments = signature - .arguments - .iter() - .map(|arg| (arg.name.clone(), itype_text_view(&arg.ty, record_types))) - .collect(); - - let output_types = signature - .outputs - .iter() - .map(|itype| itype_text_view(itype, record_types)) - .collect(); - - FunctionSignature { - name: signature.name.to_string(), - arguments, - output_types, - } -} - -fn serialize_record_type( - id: u64, - record: Rc, - record_types: &MRecordTypes, -) -> RecordType { - let fields = record - .fields - .iter() - .map(|field| (field.name.clone(), itype_text_view(&field.ty, record_types))) - .collect::>(); - - RecordType { - name: record.name.clone(), - id, - fields, - } -} - -fn itype_text_view(arg_ty: &IType, record_types: &MRecordTypes) -> String { - match arg_ty { - IType::Record(record_type_id) => { - // unwrap is safe because FaaSInterface here is well-formed - // (it was checked on the module startup stage) - let record = record_types.get(record_type_id).unwrap(); - record.name.clone() - } - IType::Array(array_ty) => format!("Array<{}>", itype_text_view(array_ty, record_types)), - t => format!("{:?}", t), - } -} diff --git a/crates/it-parser/src/extractor/it.rs b/crates/it-parser/src/extractor/it.rs index e623d998..e0bb38e3 100644 --- a/crates/it-parser/src/extractor/it.rs +++ b/crates/it-parser/src/extractor/it.rs @@ -16,7 +16,7 @@ use crate::custom::IT_SECTION_NAME; use crate::errors::ITParserError; -use crate::Result; +use crate::ParserResult; use walrus::IdsToIndices; use wasmer_it::ast::Interfaces; @@ -26,7 +26,7 @@ use std::borrow::Cow; use std::path::Path; /// Extracts IT section of provided Wasm binary and converts it to a string. -pub fn extract_text_it

(wasm_file_path: P) -> Result +pub fn extract_text_it

(wasm_file_path: P) -> ParserResult where P: AsRef, { @@ -42,7 +42,7 @@ where } /// Extracts IT section of provided Wasm binary and converts it to a MITInterfaces. -pub fn extract_it_from_module(wasmer_module: &WasmerModule) -> Result> { +pub fn extract_it_from_module(wasmer_module: &WasmerModule) -> ParserResult> { let wit_sections = wasmer_module .custom_sections(IT_SECTION_NAME) .ok_or(ITParserError::NoITSection)?; @@ -54,7 +54,7 @@ pub fn extract_it_from_module(wasmer_module: &WasmerModule) -> Result Result { +pub fn extract_version_from_module(module: &walrus::Module) -> ParserResult { let raw_custom_section = extract_custom_section(&module)?; let wit_section_bytes = raw_custom_section.as_ref(); let it = extract_it_from_bytes(wit_section_bytes)?; @@ -62,7 +62,7 @@ pub fn extract_version_from_module(module: &walrus::Module) -> Result Result> { +pub(crate) fn extract_it_from_bytes(wit_section_bytes: &[u8]) -> ParserResult> { match wasmer_it::decoders::binary::parse::<(&[u8], nom::error::ErrorKind)>(wit_section_bytes) { Ok((remainder, it)) if remainder.is_empty() => Ok(it), Ok(_) => Err(ITParserError::ITRemainderNotEmpty), @@ -70,7 +70,7 @@ pub(crate) fn extract_it_from_bytes(wit_section_bytes: &[u8]) -> Result Result> { +pub(crate) fn extract_custom_section(module: &walrus::Module) -> ParserResult> { let sections = module .customs .iter() diff --git a/crates/it-parser/src/lib.rs b/crates/it-parser/src/lib.rs index 33fe3c0c..9f817d2c 100644 --- a/crates/it-parser/src/lib.rs +++ b/crates/it-parser/src/lib.rs @@ -42,16 +42,20 @@ pub use extractor::extract_it_from_module; pub use extractor::extract_version_from_module; pub use extractor::extract_text_it; pub use extractor::module_interface; -pub use extractor::module_raw_interface; +pub use extractor::module_it_interface; pub mod interface { - pub use crate::extractor::ServiceInterface; - pub use crate::extractor::RecordType; - pub use crate::extractor::FunctionSignature; + pub use marine_module_interface::interface::ModuleInterface; + pub use marine_module_interface::interface::RecordType; + pub use marine_module_interface::interface::RecordField; + pub use marine_module_interface::interface::FunctionSignature; +} + +pub mod it_interface { + pub use marine_module_interface::it_interface::IModuleInterface; + pub use marine_module_interface::it_interface::IRecordTypes; + pub use marine_module_interface::it_interface::IFunctionSignature; - pub use crate::extractor::MModuleInterface; - pub use crate::extractor::MRecordTypes; - pub use crate::extractor::MFunctionSignature; pub mod it { pub use wasmer_it::IType; pub use wasmer_it::ast::FunctionArg as IFunctionArg; @@ -60,4 +64,4 @@ pub mod interface { } } -pub(crate) type Result = std::result::Result; +pub(crate) type ParserResult = std::result::Result; diff --git a/crates/module-interface/Cargo.toml b/crates/module-interface/Cargo.toml new file mode 100644 index 00000000..2b6d80d4 --- /dev/null +++ b/crates/module-interface/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "marine-module-interface" +description = "Fluence Marine module interface" +version = "0.1.0" +authors = ["Fluence Labs"] +license = "Apache-2.0" +edition = "2018" + +[lib] +name = "marine_module_interface" +path = "src/lib.rs" + +[dependencies] +marine-it-interfaces = { path = "../it-interfaces", version = "0.4.0" } +#marine-it-generator = { path = "../it-generator", verision = "0.5.2"} + +anyhow = "1.0.31" +walrus = "0.18.0" +wasmer-core = { package = "wasmer-runtime-core-fl", version = "0.17.0"} +wasmer-it = { package = "wasmer-interface-types-fl", version = "0.20.0" } +nom = "5.1" + +itertools = "0.10.0" +semver = "0.11.0" +serde = "=1.0.118" +thiserror = "1.0.24" diff --git a/crates/module-interface/src/interface/errors.rs b/crates/module-interface/src/interface/errors.rs new file mode 100644 index 00000000..4b72cf0c --- /dev/null +++ b/crates/module-interface/src/interface/errors.rs @@ -0,0 +1,27 @@ +/* + * 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::it_interface::ITInterfaceError; +use thiserror::Error as ThisError; + +#[derive(Debug, ThisError)] +pub enum InterfaceError { + #[error("record type with type id {0} not found")] + NotFoundRecordTypeId(u64), + + #[error("{0}")] + ITInterfaceError(#[from] ITInterfaceError), +} diff --git a/crates/module-interface/src/interface/interface_transformer.rs b/crates/module-interface/src/interface/interface_transformer.rs new file mode 100644 index 00000000..857332d7 --- /dev/null +++ b/crates/module-interface/src/interface/interface_transformer.rs @@ -0,0 +1,67 @@ +/* + * Copyright 2021 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::ModuleInterface; +use super::InterfaceResult; +use super::FunctionSignature; +use super::records_transformer::RecordsTransformer; +use crate::it_interface::IModuleInterface; +use crate::it_interface::IFunctionSignature; +use crate::it_interface::IRecordTypes; + +pub fn it_to_module_interface(mm_interface: IModuleInterface) -> InterfaceResult { + let record_types = mm_interface.export_record_types; + + let function_signatures = mm_interface + .function_signatures + .into_iter() + .map(|sign| serialize_function_signature(sign, &record_types)) + .collect(); + + let record_types = RecordsTransformer::transform(&record_types)?; + + let interface = ModuleInterface { + function_signatures, + record_types, + }; + + Ok(interface) +} + +fn serialize_function_signature( + signature: IFunctionSignature, + record_types: &IRecordTypes, +) -> FunctionSignature { + use super::itype_text_view; + + let arguments = signature + .arguments + .iter() + .map(|arg| (arg.name.clone(), itype_text_view(&arg.ty, record_types))) + .collect(); + + let output_types = signature + .outputs + .iter() + .map(|itype| itype_text_view(itype, record_types)) + .collect(); + + FunctionSignature { + name: signature.name.to_string(), + arguments, + output_types, + } +} diff --git a/crates/module-interface/src/interface/itype_to_text.rs b/crates/module-interface/src/interface/itype_to_text.rs new file mode 100644 index 00000000..3083e5f5 --- /dev/null +++ b/crates/module-interface/src/interface/itype_to_text.rs @@ -0,0 +1,50 @@ +/* + * Copyright 2021 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::it_interface::IRecordTypes; + +use wasmer_it::IType; + +/// Converts the supplied IType to a Aqua0compatible text representation. +/// +/// SAFETY: +/// It's assumed that arguments are well-formed and all records have a corresponded type in +/// record_types. +pub fn itype_text_view(arg_ty: &IType, record_types: &IRecordTypes) -> String { + match arg_ty { + IType::Record(record_type_id) => { + // assumed that this functions called with well-formed args + let record = record_types.get(record_type_id).unwrap(); + record.name.clone() + } + IType::Array(array_ty) => format!("[]{}", itype_text_view(array_ty, record_types)), + IType::Boolean => "bool".to_string(), + IType::S8 => "i8".to_string(), + IType::S16 => "i16".to_string(), + IType::S32 => "i32".to_string(), + IType::S64 => "i64".to_string(), + IType::U8 => "u8".to_string(), + IType::U16 => "u16".to_string(), + IType::U32 => "u32".to_string(), + IType::U64 => "u64".to_string(), + IType::F32 => "f32".to_string(), + IType::F64 => "f64".to_string(), + IType::String => "string".to_string(), + IType::ByteArray => "[]u8".to_string(), + IType::I32 => "i32".to_string(), + IType::I64 => "i64".to_string(), + } +} diff --git a/crates/module-interface/src/interface/mod.rs b/crates/module-interface/src/interface/mod.rs new file mode 100644 index 00000000..a502f93f --- /dev/null +++ b/crates/module-interface/src/interface/mod.rs @@ -0,0 +1,38 @@ +/* + * Copyright 2021 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 interface_transformer; +mod itype_to_text; +mod module_interface; +mod records_transformer; + +pub use errors::InterfaceError; +pub use interface_transformer::it_to_module_interface; +pub use itype_to_text::*; +pub use module_interface::*; + +pub type InterfaceResult = std::result::Result; + +use marine_it_interfaces::MITInterfaces; + +/// Returns interface of a Marine module. +pub fn get_interface(mit: &MITInterfaces<'_>) -> InterfaceResult { + let it_interface = crate::it_interface::get_interface(mit)?; + let interface = it_to_module_interface(it_interface)?; + + Ok(interface) +} diff --git a/crates/module-interface/src/interface/module_interface.rs b/crates/module-interface/src/interface/module_interface.rs new file mode 100644 index 00000000..c21f4697 --- /dev/null +++ b/crates/module-interface/src/interface/module_interface.rs @@ -0,0 +1,78 @@ +/* + * Copyright 2021 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 serde::Serialize; +use serde::Deserialize; + +#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)] +pub struct FunctionSignature { + pub name: String, + pub arguments: Vec<(String, String)>, + pub output_types: Vec, +} + +#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)] +pub struct RecordField { + pub name: String, + pub ty: String, +} + +#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)] +pub struct RecordType { + pub name: String, + pub id: u64, + pub fields: Vec, +} + +#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)] +pub struct ModuleInterface { + pub function_signatures: Vec, + // record types are guaranteed to be topological sorted + pub record_types: Vec, +} + +use std::fmt; + +impl fmt::Display for FunctionSignature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use itertools::Itertools; + + let output = match self.output_types.len() { + 0 => "()", + 1 => &self.output_types[0], + _ => unimplemented!("more than 1 output type is unsupported"), + }; + + if self.arguments.is_empty() { + writeln!(f, "{}: -> {}", self.name, output) + } else { + let args = self.arguments.iter().map(|(_, ty)| ty).format(","); + writeln!(f, "{}: {} -> {}", self.name, args, output) + } + } +} + +impl fmt::Display for RecordType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "data {}:", self.name)?; + + for field in self.fields.iter() { + writeln!(f, " {}: {}", field.name, field.ty)?; + } + + Ok(()) + } +} diff --git a/crates/module-interface/src/interface/records_transformer.rs b/crates/module-interface/src/interface/records_transformer.rs new file mode 100644 index 00000000..602ee4e3 --- /dev/null +++ b/crates/module-interface/src/interface/records_transformer.rs @@ -0,0 +1,128 @@ +/* + * 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::RecordType; +use super::RecordField; +use super::InterfaceResult; +use super::InterfaceError; +use crate::it_interface::IRecordTypes; + +use wasmer_it::IRecordType; +use wasmer_it::IType; + +use std::collections::HashSet; +use std::rc::Rc; + +pub(crate) struct RecordsTransformer { + used: HashSet, + sorted_order: Vec, +} + +impl RecordsTransformer { + pub(crate) fn transform(record_types: &IRecordTypes) -> InterfaceResult> { + let records_count = record_types.len(); + + let mut transformer = Self { + used: HashSet::with_capacity(records_count), + sorted_order: Vec::with_capacity(records_count), + }; + + // TODO: check for cycles + transformer.topological_sort(record_types)?; + let record_types = transformer.into_transformed_records(record_types); + + Ok(record_types) + } + + fn topological_sort(&mut self, exported_records: &IRecordTypes) -> InterfaceResult<()> { + for (id, record) in exported_records { + self.dfs(*id, record, exported_records)?; + } + + Ok(()) + } + + fn dfs( + &mut self, + record_id: u64, + record: &Rc, + exported_records: &IRecordTypes, + ) -> InterfaceResult<()> { + if !self.used.insert(record_id) { + return Ok(()); + } + + for field in (&record.fields).iter() { + self.type_dfs(&field.ty, exported_records)?; + } + + self.sorted_order.push(record_id); + + Ok(()) + } + + fn type_dfs( + &mut self, + field_ty: &IType, + exported_records: &IRecordTypes, + ) -> InterfaceResult<()> { + match field_ty { + IType::Record(type_id) => { + let child_record = exported_records + .get(type_id) + .ok_or(InterfaceError::NotFoundRecordTypeId(*type_id))?; + + self.dfs(*type_id, child_record, exported_records) + } + IType::Array(ty) => self.type_dfs(ty, exported_records), + _ => Ok(()), + } + } + + fn into_transformed_records(self, record_types: &IRecordTypes) -> Vec { + self.sorted_order + .into_iter() + .map(|id| { + // unwrap is safe here because sorted_order is constructed based on record_types + let record = record_types.get(&id).unwrap(); + Self::convert_record(id, record, &record_types) + }) + .collect::>() + } + + fn convert_record( + id: u64, + record: &Rc, + record_types: &IRecordTypes, + ) -> RecordType { + use super::itype_text_view; + + let fields = record + .fields + .iter() + .map(|field| RecordField { + name: field.name.clone(), + ty: itype_text_view(&field.ty, record_types), + }) + .collect::>(); + + RecordType { + name: record.name.clone(), + id, + fields, + } + } +} diff --git a/crates/module-interface/src/it_interface/errors.rs b/crates/module-interface/src/it_interface/errors.rs new file mode 100644 index 00000000..8eec4069 --- /dev/null +++ b/crates/module-interface/src/it_interface/errors.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 marine_it_interfaces::MITInterfacesError; +use thiserror::Error as ThisError; + +#[derive(Debug, ThisError)] +pub enum ITInterfaceError { + #[error("type with idx = {0} isn't a function type")] + ITTypeNotFunction(u32), + + #[error("record type with type id {0} not found")] + NotFoundRecordTypeId(u64), + + #[error("mailformed module: a record contains more recursion level then allowed")] + TooManyRecursionLevels, + + #[error("{0}")] + MITInterfacesError(#[from] MITInterfacesError), +} diff --git a/crates/module-interface/src/it_interface/export_it_functions.rs b/crates/module-interface/src/it_interface/export_it_functions.rs new file mode 100644 index 00000000..9b2544f2 --- /dev/null +++ b/crates/module-interface/src/it_interface/export_it_functions.rs @@ -0,0 +1,86 @@ +/* + * Copyright 2021 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::IFunctionSignature; +use super::ITInterfaceError; +use super::RIResult; + +use marine_it_interfaces::MITInterfaces; + +use std::rc::Rc; + +pub struct ITExportFuncDescriptor<'n> { + pub adapter_function_type: u32, + pub name: &'n str, +} + +/// Returns all exported IT functions descriptors. +pub fn get_export_funcs_descriptors<'i>( + mit: &'i MITInterfaces<'_>, +) -> Vec> { + // An IT function is exported if it lies in export functions and have implementation. + // An export IT function without implementation is a hack and used to call core function from + // a Wasm module. This hack is needed because there is only one call instruction in the + // interface-types crates and it's needed to distinguish somehow between calling export IT or + // core functions. This scheme is a kind of mess and it needs to be refactored one day. + mit.implementations() + .filter_map(|(adapter_function_type, core_function_type)| { + mit.exports_by_type(*core_function_type) + .map(|export_function_name| (adapter_function_type, export_function_name)) + }) + .map(|(&adapter_function_type, export_function_names)| { + export_function_names + .iter() + .map(move |name| ITExportFuncDescriptor { + adapter_function_type, + name, + }) + }) + .flatten() + .collect::>() +} + +/// Returns all exported IT functions. +pub fn get_export_funcs(mit: &MITInterfaces<'_>) -> RIResult> { + use marine_it_interfaces::ITAstType; + + let funcs_descriptors = get_export_funcs_descriptors(mit); + + funcs_descriptors + .into_iter() + .map(|descriptor| { + let it_type = mit.type_by_idx_r(descriptor.adapter_function_type)?; + + match it_type { + ITAstType::Function { + arguments, + output_types, + } => { + let signature = IFunctionSignature { + name: Rc::new(descriptor.name.to_string()), + arguments: arguments.clone(), + outputs: output_types.clone(), + adapter_function_type: descriptor.adapter_function_type, + }; + Ok(signature) + } + _ => Err(ITInterfaceError::ITTypeNotFunction( + descriptor.adapter_function_type, + )), + } + }) + .collect::>>() +} diff --git a/crates/module-interface/src/it_interface/export_it_records.rs b/crates/module-interface/src/it_interface/export_it_records.rs new file mode 100644 index 00000000..af8660c4 --- /dev/null +++ b/crates/module-interface/src/it_interface/export_it_records.rs @@ -0,0 +1,129 @@ +/* + * Copyright 2021 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::IRecordTypes; +use super::RIResult; +use super::ITInterfaceError; +use super::IFunctionSignature; + +use marine_it_interfaces::MITInterfaces; +use wasmer_it::IType; + +use std::collections::HashMap; + +const TYPE_RESOLVE_RECURSION_LIMIT: u32 = 1024; + +pub struct FullRecordTypes { + pub record_types: IRecordTypes, + pub export_record_types: IRecordTypes, +} + +pub fn get_record_types<'f>( + mit: &MITInterfaces<'_>, + export_funcs: impl ExactSizeIterator, +) -> RIResult { + let all_record_types = get_all_records(mit); + let mut export_record_types = HashMap::new(); + + let itypes = export_funcs.flat_map(|s| { + s.arguments + .as_ref() + .iter() + .map(|a| &a.ty) + .chain(s.outputs.as_ref().iter()) + }); + + for itype in itypes { + handle_itype(itype, &all_record_types, &mut export_record_types, 0)?; + } + + let full_record_types = FullRecordTypes { + record_types: all_record_types, + export_record_types, + }; + + Ok(full_record_types) +} + +fn handle_itype( + itype: &IType, + all_record_types: &IRecordTypes, + export_record_types: &mut IRecordTypes, + recursion_level: u32, +) -> RIResult<()> { + if recursion_level > TYPE_RESOLVE_RECURSION_LIMIT { + return Err(ITInterfaceError::TooManyRecursionLevels); + } + + match itype { + IType::Record(record_type_id) => handle_record_type( + *record_type_id, + all_record_types, + export_record_types, + recursion_level + 1, + )?, + IType::Array(array_ty) => handle_itype( + array_ty, + all_record_types, + export_record_types, + recursion_level + 1, + )?, + _ => {} + } + + Ok(()) +} + +fn handle_record_type( + record_type_id: u64, + all_record_types: &IRecordTypes, + export_record_types: &mut IRecordTypes, + recursion_level: u32, +) -> RIResult<()> { + let record_type = all_record_types + .get(&record_type_id) + .ok_or(ITInterfaceError::NotFoundRecordTypeId(record_type_id))?; + + export_record_types.insert(record_type_id, record_type.clone()); + + for field in record_type.fields.iter() { + handle_itype( + &field.ty, + all_record_types, + export_record_types, + recursion_level + 1, + )?; + } + + Ok(()) +} + +fn get_all_records(mit: &MITInterfaces<'_>) -> IRecordTypes { + use marine_it_interfaces::ITAstType; + + mit.types() + .enumerate() + .fold(HashMap::new(), |mut record_types_by_id, (id, ty)| { + match ty { + ITAstType::Record(record_type) => { + record_types_by_id.insert(id as u64, record_type.clone()); + } + ITAstType::Function { .. } => {} + }; + + record_types_by_id + }) +} diff --git a/crates/module-interface/src/it_interface/it_module_interface.rs b/crates/module-interface/src/it_interface/it_module_interface.rs new file mode 100644 index 00000000..bff7b977 --- /dev/null +++ b/crates/module-interface/src/it_interface/it_module_interface.rs @@ -0,0 +1,44 @@ +/* + * Copyright 2021 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_it::IType; +use wasmer_it::ast::FunctionArg as IFunctionArg; +use wasmer_it::IRecordType; + +use serde::Serialize; +use serde::Deserialize; + +use std::collections::HashMap; +use std::rc::Rc; + +pub type IRecordTypes = HashMap>; + +/// Represent a function type inside Marine module. +#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)] +pub struct IFunctionSignature { + pub name: Rc, + pub arguments: Rc>, + pub outputs: Rc>, + pub adapter_function_type: u32, +} + +/// Represent an interface of a Wasm module. +#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] +pub struct IModuleInterface { + pub export_record_types: IRecordTypes, + pub record_types: IRecordTypes, + pub function_signatures: Vec, +} diff --git a/crates/module-interface/src/it_interface/mod.rs b/crates/module-interface/src/it_interface/mod.rs new file mode 100644 index 00000000..9c4edcca --- /dev/null +++ b/crates/module-interface/src/it_interface/mod.rs @@ -0,0 +1,46 @@ +/* + * Copyright 2021 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 export_it_functions; +mod export_it_records; +mod it_module_interface; + +pub use errors::*; +pub use export_it_functions::*; +pub use export_it_records::*; +pub use it_module_interface::*; + +pub type RIResult = std::result::Result; + +use marine_it_interfaces::MITInterfaces; + +/// Returns Marine module interface that includes both export and all record types. +pub fn get_interface(mit: &MITInterfaces<'_>) -> RIResult { + let function_signatures = get_export_funcs(mit)?; + let FullRecordTypes { + record_types, + export_record_types, + } = get_record_types(mit, function_signatures.iter())?; + + let mm_interface = IModuleInterface { + record_types, + export_record_types, + function_signatures, + }; + + Ok(mm_interface) +} diff --git a/crates/module-interface/src/lib.rs b/crates/module-interface/src/lib.rs new file mode 100644 index 00000000..2a9d1b14 --- /dev/null +++ b/crates/module-interface/src/lib.rs @@ -0,0 +1,29 @@ +/* + * Copyright 2021 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. + */ + +#![warn(rust_2018_idioms)] +#![deny( + dead_code, + nonstandard_style, + unused_imports, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] + +pub mod interface; +pub mod it_interface; diff --git a/fluence-app-service/Cargo.toml b/fluence-app-service/Cargo.toml index e91e8868..21b3bd96 100644 --- a/fluence-app-service/Cargo.toml +++ b/fluence-app-service/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "fluence-app-service" description = "Fluence Application Service" -version = "0.7.2" +version = "0.7.3" authors = ["Fluence Labs"] license = "Apache-2.0" edition = "2018" [dependencies] -fluence-faas = { path = "../fluence-faas", version = "0.7.2" } +fluence-faas = { path = "../fluence-faas", version = "0.7.3" } maplit = "1.0.2" log = "0.4.8" diff --git a/fluence-app-service/src/service_interface.rs b/fluence-app-service/src/service_interface.rs index 1c665685..e50346c3 100644 --- a/fluence-app-service/src/service_interface.rs +++ b/fluence-app-service/src/service_interface.rs @@ -17,7 +17,7 @@ use fluence_faas::FaaSModuleInterface; use fluence_faas::FaaSFunctionSignature; use fluence_faas::IRecordType; -use fluence_faas::RecordTypes; +use fluence_faas::MRecordTypes; use fluence_faas::itype_text_view; use serde::Serialize; @@ -66,7 +66,7 @@ pub(crate) fn into_service_interface(faas_interface: FaaSModuleInterface<'_>) -> fn serialize_function_signature( signature: FaaSFunctionSignature, - record_types: &RecordTypes, + record_types: &MRecordTypes, ) -> FunctionSignature { let arguments = signature .arguments @@ -90,7 +90,7 @@ fn serialize_function_signature( fn serialize_record_type( id: u64, record: Rc, - record_types: &RecordTypes, + record_types: &MRecordTypes, ) -> RecordType { let fields = record .fields diff --git a/fluence-faas/Cargo.toml b/fluence-faas/Cargo.toml index 9821bc1f..8b1e4924 100644 --- a/fluence-faas/Cargo.toml +++ b/fluence-faas/Cargo.toml @@ -1,13 +1,14 @@ [package] name = "fluence-faas" description = "Fluence FaaS" -version = "0.7.2" +version = "0.7.3" authors = ["Fluence Labs"] license = "Apache-2.0" edition = "2018" [dependencies] marine-runtime = { path = "../runtime", version = "0.5.0" } +marine-module-interface = { path = "../crates/module-interface", version = "0.1.0" } marine-utils = { path = "../crates/utils", version = "0.2.0" } fluence-sdk-main = { version = "0.6.9", features = ["logger"] } fluence = { version = "0.6.3", features = ["logger"] } diff --git a/fluence-faas/src/faas.rs b/fluence-faas/src/faas.rs index edd37127..a3a7f55e 100644 --- a/fluence-faas/src/faas.rs +++ b/fluence-faas/src/faas.rs @@ -28,7 +28,7 @@ use crate::host_imports::logger::WASM_LOG_ENV_NAME; use marine::Marine; use marine::IFunctionArg; use marine_utils::SharedString; -use marine::RecordTypes; +use marine::MRecordTypes; use fluence::CallParameters; use serde_json::Value as JValue; @@ -39,7 +39,7 @@ use std::rc::Rc; struct ModuleInterface { function_signatures: HashMap>, Rc>)>, - record_types: Rc, + record_types: Rc, } // TODO: remove and use mutex instead @@ -186,7 +186,7 @@ impl FluenceFaaS { &'faas mut self, module_name: &str, func_name: &str, - ) -> Result<(Rc>, Rc>, Rc)> { + ) -> Result<(Rc>, Rc>, Rc)> { use FaaSError::NoSuchModule; use FaaSError::MissingFunctionError; diff --git a/fluence-faas/src/faas_interface.rs b/fluence-faas/src/faas_interface.rs index 000888fd..772c0b70 100644 --- a/fluence-faas/src/faas_interface.rs +++ b/fluence-faas/src/faas_interface.rs @@ -14,17 +14,15 @@ * limitations under the License. */ -use super::IType; use super::IRecordType; +use super::itype_text_view; use crate::FaaSModuleInterface; -use marine::RecordTypes; use itertools::Itertools; use serde::Serialize; use std::fmt; use std::collections::HashMap; -use std::collections::HashSet; #[derive(Debug, PartialEq, Eq, Clone, Serialize)] pub struct FaaSInterface<'a> { @@ -33,78 +31,80 @@ pub struct FaaSInterface<'a> { impl<'a> fmt::Display for FaaSInterface<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut printed_record_types: HashSet<&IRecordType> = HashSet::new(); - - for (_, module_interface) in self.modules.iter() { - for (_, record_type) in module_interface.record_types.iter() { - if !printed_record_types.insert(record_type) { - // do not print record if it has been already printed - continue; - } - - writeln!(f, "{} {{", record_type.name)?; - - for field in record_type.fields.iter() { - writeln!( - f, - " {}: {}", - field.name, - itype_text_view(&field.ty, &module_interface.record_types) - )?; - } - - writeln!(f, "}}")?; - } - } - - for (name, module_interface) in self.modules.iter() { - writeln!(f, "\n{}:", *name)?; - - for function_signature in module_interface.function_signatures.iter() { - write!(f, " fn {}(", function_signature.name)?; - - let args = function_signature - .arguments - .iter() - .map(|arg| { - format!( - "{}: {}", - arg.name, - itype_text_view(&arg.ty, &module_interface.record_types) - ) - }) - .join(", "); - - let outputs = &function_signature.outputs; - if outputs.is_empty() { - writeln!(f, "{})", args)?; - } else if outputs.len() == 1 { - writeln!( - f, - "{}) -> {}", - args, - itype_text_view(&outputs[0], &module_interface.record_types) - )?; - } else { - // At now, multi values aren't supported - only one output type is possible - unimplemented!() - } - } - } - - Ok(()) + print_record_types(self.modules.values(), f)?; + print_functions_sign(self.modules.iter(), f) } } -pub fn itype_text_view(arg_ty: &IType, record_types: &RecordTypes) -> String { - match arg_ty { - IType::Record(record_type_id) => { - // unwrap is safe because FaaSInterface here is well-formed - // (it was checked on the module startup stage) - let record = record_types.get(record_type_id).unwrap(); - record.name.clone() +fn print_record_types<'r>( + modules: impl Iterator>, + f: &mut fmt::Formatter<'_>, +) -> fmt::Result { + use std::collections::HashSet; + + let mut printed_record_types: HashSet<&IRecordType> = HashSet::new(); + + for module in modules { + for (_, record_type) in module.record_types.iter() { + if !printed_record_types.insert(record_type) { + // do not print record if it has been already printed + continue; + } + + writeln!(f, "data {}:", record_type.name)?; + + for field in record_type.fields.iter() { + writeln!( + f, + " {}: {}", + field.name, + itype_text_view(&field.ty, &module.record_types) + )?; + } } - IType::Array(array_ty) => format!("Array<{}>", itype_text_view(array_ty, record_types)), - t => format!("{:?}", t), } + + writeln!(f) +} + +fn print_functions_sign<'r>( + modules: impl Iterator)>, + f: &mut fmt::Formatter<'_>, +) -> fmt::Result { + for (name, module_interface) in modules { + writeln!(f, "{}:", *name)?; + + for function_signature in module_interface.function_signatures.iter() { + write!(f, " fn {}(", function_signature.name)?; + + let args = function_signature + .arguments + .iter() + .map(|arg| { + format!( + "{}: {}", + arg.name, + itype_text_view(&arg.ty, &module_interface.record_types) + ) + }) + .join(", "); + + let outputs = &function_signature.outputs; + if outputs.is_empty() { + writeln!(f, "{})", args)?; + } else if outputs.len() == 1 { + writeln!( + f, + "{}) -> {}", + args, + itype_text_view(&outputs[0], &module_interface.record_types) + )?; + } else { + // At now, multi values aren't supported - only one output type is possible + unimplemented!() + } + } + } + + Ok(()) } diff --git a/fluence-faas/src/json/ivalues_to_json.rs b/fluence-faas/src/json/ivalues_to_json.rs index f35f272c..42785345 100644 --- a/fluence-faas/src/json/ivalues_to_json.rs +++ b/fluence-faas/src/json/ivalues_to_json.rs @@ -19,13 +19,13 @@ use crate::IType; use crate::Result; use crate::errors::FaaSError::JsonOutputSerializationError as OutputDeError; -use marine::RecordTypes; +use marine::MRecordTypes; use serde_json::Value as JValue; pub(crate) fn ivalues_to_json( mut ivalues: Vec, outputs: &[IType], - record_types: &RecordTypes, + record_types: &MRecordTypes, ) -> Result { if outputs.len() != ivalues.len() { return Err(OutputDeError(format!( @@ -42,7 +42,7 @@ pub(crate) fn ivalues_to_json( } } -fn ivalue_to_json(ivalue: IValue, output: &IType, record_types: &RecordTypes) -> Result { +fn ivalue_to_json(ivalue: IValue, output: &IType, record_types: &MRecordTypes) -> Result { use serde_json::json; // clone here needed because binding by-value and by-ref in the same pattern in unstable diff --git a/fluence-faas/src/json/json_to_ivalues.rs b/fluence-faas/src/json/json_to_ivalues.rs index f19bd7bd..60082cf5 100644 --- a/fluence-faas/src/json/json_to_ivalues.rs +++ b/fluence-faas/src/json/json_to_ivalues.rs @@ -19,7 +19,7 @@ use crate::IType; use crate::Result; use crate::FaaSError::JsonArgumentsDeserializationError as ArgDeError; -use marine::RecordTypes; +use marine::MRecordTypes; use serde_json::Value as JValue; use wasmer_it::NEVec; @@ -30,7 +30,7 @@ use std::iter::ExactSizeIterator; pub(crate) fn json_to_ivalues<'a, 'b>( json_args: JValue, arg_types: impl Iterator + ExactSizeIterator, - record_types: &'b RecordTypes, + record_types: &'b MRecordTypes, ) -> Result> { let ivalues = match json_args { JValue::Object(json_map) => json_map_to_ivalues(json_map, arg_types, &record_types)?, @@ -48,7 +48,7 @@ pub(crate) fn json_to_ivalues<'a, 'b>( fn json_map_to_ivalues<'a, 'b>( mut json_map: serde_json::Map, arg_types: impl Iterator, - record_types: &'b RecordTypes, + record_types: &'b MRecordTypes, ) -> Result> { let mut iargs = Vec::new(); @@ -75,7 +75,7 @@ fn json_map_to_ivalues<'a, 'b>( fn json_array_to_ivalues<'a, 'b>( json_array: Vec, arg_types: impl Iterator + ExactSizeIterator, - record_types: &'b RecordTypes, + record_types: &'b MRecordTypes, ) -> Result> { if json_array.len() != arg_types.len() { return Err(ArgDeError(format!( @@ -129,7 +129,7 @@ fn json_null_to_ivalues<'a>( } /// Convert one JValue to an array of ivalues according to the supplied argument type. -fn jvalue_to_ivalue(jvalue: JValue, ty: &IType, record_types: &RecordTypes) -> Result { +fn jvalue_to_ivalue(jvalue: JValue, ty: &IType, record_types: &MRecordTypes) -> Result { macro_rules! to_ivalue( ($json_value:expr, $ty:ident) => { { @@ -211,7 +211,7 @@ fn jvalue_to_ivalue(jvalue: JValue, ty: &IType, record_types: &RecordTypes) -> R fn json_record_type_to_ivalue( json_value: JValue, record_type_id: &u64, - record_types: &RecordTypes, + record_types: &MRecordTypes, ) -> Result> { let record_type = record_types.get(record_type_id).ok_or_else(|| { ArgDeError(format!( diff --git a/fluence-faas/src/lib.rs b/fluence-faas/src/lib.rs index a9cdaab0..3cf748b3 100644 --- a/fluence-faas/src/lib.rs +++ b/fluence-faas/src/lib.rs @@ -36,7 +36,6 @@ pub(crate) type Result = std::result::Result; pub use faas::FluenceFaaS; pub use faas_interface::FaaSInterface; -pub use faas_interface::itype_text_view; pub use config::FaaSConfig; pub use config::FaaSModuleConfig; @@ -57,7 +56,7 @@ pub use marine::IFunctionArg; pub use marine::IType; pub use marine::MModuleInterface as FaaSModuleInterface; pub use marine::MFunctionSignature as FaaSFunctionSignature; -pub use marine::RecordTypes; +pub use marine::MRecordTypes; pub use marine::HostExportedFunc; pub use marine::HostImportDescriptor; pub use marine::HostImportError; @@ -67,6 +66,8 @@ pub use marine::ne_vec; pub use marine::min_sdk_version; pub use marine::min_it_version; +pub use marine_module_interface::interface::itype_text_view; + pub use fluence::CallParameters; pub use fluence::SecurityTetraplet; diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 6ba0aa3c..c6756f78 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -15,6 +15,7 @@ marine-module-info-parser = { path = "../crates/module-info-parser", version = " marine-it-interfaces = { path = "../crates/it-interfaces", version = "0.4.0" } marine-it-parser = { path = "../crates/it-parser", version = "0.6.0" } marine-it-generator = { path = "../crates/it-generator", version = "0.5.0" } +marine-module-interface = { path = "../crates/module-interface", version = "0.1.0" } marine-utils = { path = "../crates/utils", version = "0.2.0" } wasmer-runtime = { package = "wasmer-runtime-fl", version = "0.17.0" } diff --git a/runtime/src/engine.rs b/runtime/src/engine.rs index e5947175..93480ddf 100644 --- a/runtime/src/engine.rs +++ b/runtime/src/engine.rs @@ -16,7 +16,7 @@ use super::*; use crate::module::MModule; -use crate::module::RecordTypes; +use crate::module::MRecordTypes; use serde::Serialize; @@ -27,7 +27,7 @@ use std::rc::Rc; /// Represent Marine module interface. #[derive(PartialEq, Eq, Debug, Clone, Serialize)] pub struct MModuleInterface<'a> { - pub record_types: &'a RecordTypes, + pub record_types: &'a MRecordTypes, pub function_signatures: Vec, } @@ -119,7 +119,7 @@ impl Marine { } /// Return record types exported by module with given name. - pub fn module_record_types>(&self, module_name: S) -> Option<&RecordTypes> { + pub fn module_record_types>(&self, module_name: S) -> Option<&MRecordTypes> { self.modules .get(module_name.as_ref()) .map(|module| module.export_record_types()) diff --git a/runtime/src/errors.rs b/runtime/src/errors.rs index afb7e8c1..196439f5 100644 --- a/runtime/src/errors.rs +++ b/runtime/src/errors.rs @@ -18,6 +18,7 @@ use crate::HostImportError; use marine_it_interfaces::MITInterfacesError; use marine_it_parser::ITParserError; use marine_module_info_parser::ModuleInfoError; +use marine_module_interface::it_interface::ITInterfaceError; use wasmer_runtime::error as wasmer_error; @@ -63,6 +64,10 @@ pub enum MError { #[error("{0}")] WASIPrepareError(String), + /// Errors occurred inside marine-module-interface crate. + #[error("{0}")] + ModuleInterfaceError(#[from] ITInterfaceError), + /// Error arisen during execution of Wasm modules (especially, interface types). #[error("Execution error: {0}")] ITInstructionError(#[from] wasmer_it::errors::InstructionError), diff --git a/runtime/src/host_imports/imports.rs b/runtime/src/host_imports/imports.rs index 2632ccba..a89a99f7 100644 --- a/runtime/src/host_imports/imports.rs +++ b/runtime/src/host_imports/imports.rs @@ -22,7 +22,7 @@ use super::lowering::LoHelper; use super::utils::itypes_args_to_wtypes; use super::utils::itypes_output_to_wtypes; -use crate::RecordTypes; +use crate::MRecordTypes; use crate::init_wasm_func_once; use crate::call_wasm_func; use crate::HostImportDescriptor; @@ -41,7 +41,7 @@ use std::ops::Deref; pub(crate) fn create_host_import_func( descriptor: HostImportDescriptor, - record_types: Rc, + record_types: Rc, ) -> DynamicFunc<'static> { let allocate_func: AllocateFunc = Box::new(RefCell::new(None)); let set_result_ptr_func: SetResultPtrFunc = Box::new(RefCell::new(None)); diff --git a/runtime/src/host_imports/lifting/li_helper.rs b/runtime/src/host_imports/lifting/li_helper.rs index d2b55b0c..f5f94f71 100644 --- a/runtime/src/host_imports/lifting/li_helper.rs +++ b/runtime/src/host_imports/lifting/li_helper.rs @@ -14,7 +14,7 @@ * limitations under the License. */ -use crate::RecordTypes; +use crate::MRecordTypes; use crate::IRecordType; use it_lilo::traits::RecordResolvable; use it_lilo::traits::RecordResolvableError; @@ -22,11 +22,11 @@ use it_lilo::traits::RecordResolvableError; use std::rc::Rc; pub(crate) struct LiHelper { - record_types: Rc, + record_types: Rc, } impl LiHelper { - pub(crate) fn new(record_types: Rc) -> Self { + pub(crate) fn new(record_types: Rc) -> Self { Self { record_types } } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 6c7196d7..26379a52 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -45,7 +45,7 @@ pub use module::IValue; pub use module::IRecordType; pub use module::IFunctionArg; pub use module::IType; -pub use module::RecordTypes; +pub use module::MRecordTypes; pub use module::MFunctionSignature; pub use module::from_interface_values; pub use module::to_interface_value; diff --git a/runtime/src/module/marine_module.rs b/runtime/src/module/marine_module.rs index a68a7335..bd66fb3f 100644 --- a/runtime/src/module/marine_module.rs +++ b/runtime/src/module/marine_module.rs @@ -15,8 +15,9 @@ */ use super::wit_prelude::*; +use super::MFunctionSignature; +use super::MRecordTypes; use super::{IType, IRecordType, IFunctionArg, IValue, WValue}; -use super::RecordTypes; use crate::MResult; use crate::MModuleConfig; @@ -29,9 +30,6 @@ use wasmer_runtime::compile; use wasmer_runtime::ImportObject; use wasmer_it::interpreter::Interpreter; -use serde::Serialize; -use serde::Deserialize; - use std::collections::HashMap; use std::convert::TryInto; use std::mem::MaybeUninit; @@ -48,18 +46,10 @@ pub(super) struct ITModuleFunc { pub(super) output_types: Rc>, } -/// Represent a function type inside Marine module. -#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)] -pub struct MFunctionSignature { - pub name: Rc, - pub arguments: Rc>, - pub outputs: Rc>, -} - #[derive(Clone)] pub(super) struct Callable { - pub(super) wit_instance: Arc, - pub(super) wit_module_func: ITModuleFunc, + pub(super) it_instance: Arc, + pub(super) it_module_func: ITModuleFunc, } impl Callable { @@ -67,9 +57,9 @@ impl Callable { use wasmer_it::interpreter::stack::Stackable; let result = self - .wit_module_func + .it_module_func .interpreter - .run(args, Arc::make_mut(&mut self.wit_instance))? + .run(args, Arc::make_mut(&mut self.it_instance))? .as_slice() .to_owned(); @@ -105,7 +95,7 @@ pub(crate) struct MModule { // TODO: save refs instead copying of a record types HashMap. /// Record types used in exported functions as arguments or return values. - export_record_types: RecordTypes, + export_record_types: MRecordTypes, } impl MModule { @@ -130,7 +120,7 @@ impl MModule { Self::create_import_objects(config, &mit, wit_import_object.clone())?; let wasmer_instance = wasmer_module.instantiate(&wasi_import_object)?; - let wit_instance = unsafe { + let it_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) = @@ -138,8 +128,7 @@ impl MModule { std::mem::transmute::<_, Arc>(wit_instance) }; - let export_funcs = Self::instantiate_wit_exports(&wit_instance, &mit)?; - let export_record_types = Self::extract_export_record_types(&export_funcs, &wit_instance)?; + let (export_funcs, export_record_types) = Self::instantiate_exports(&it_instance, &mit)?; // call _start to populate the WASI state of the module #[rustfmt::skip] @@ -179,12 +168,12 @@ impl MModule { .iter() .map(|(func_name, func)| MFunctionSignature { name: func_name.0.clone(), - arguments: func.wit_module_func.arguments.clone(), - outputs: func.wit_module_func.output_types.clone(), + arguments: func.it_module_func.arguments.clone(), + outputs: func.it_module_func.output_types.clone(), }) } - pub(crate) fn export_record_types(&self) -> &RecordTypes { + pub(crate) fn export_record_types(&self) -> &MRecordTypes { &self.export_record_types } @@ -260,54 +249,36 @@ impl MModule { Ok((wasi_import_object, host_closures_import_object)) } - fn instantiate_wit_exports( - wit_instance: &Arc, - wit: &MITInterfaces<'_>, - ) -> MResult { - use marine_it_interfaces::ITAstType; + fn instantiate_exports( + it_instance: &Arc, + mit: &MITInterfaces<'_>, + ) -> MResult<(ExportFunctions, MRecordTypes)> { + let module_interface = marine_module_interface::it_interface::get_interface(mit)?; - wit.implementations() - .filter_map(|(adapter_function_type, core_function_type)| { - wit.exports_by_type(*core_function_type) - .map(|export_function_name| (adapter_function_type, export_function_name)) + let export_funcs = module_interface + .function_signatures + .into_iter() + .map(|sign| { + let adapter_instructions = mit.adapter_by_type_r(sign.adapter_function_type)?; + + let interpreter: ITInterpreter = adapter_instructions.clone().try_into()?; + let it_module_func = ITModuleFunc { + interpreter: Arc::new(interpreter), + arguments: sign.arguments.clone(), + output_types: sign.outputs.clone(), + }; + + let shared_string = SharedString(sign.name); + let callable = Rc::new(Callable { + it_instance: it_instance.clone(), + it_module_func, + }); + + Ok((shared_string, callable)) }) - .map(|(adapter_function_type, export_function_names)| { - export_function_names - .iter() - .map(move |export_function_name| (*adapter_function_type, export_function_name)) - }) - .flatten() - .map(|(adapter_function_type, export_function_name)| { - let adapter_instructions = wit.adapter_by_type_r(adapter_function_type)?; - let wit_type = wit.type_by_idx_r(adapter_function_type)?; + .collect::>()?; - match wit_type { - ITAstType::Function { - arguments, - output_types, - } => { - let interpreter: ITInterpreter = adapter_instructions.clone().try_into()?; - let wit_module_func = ITModuleFunc { - interpreter: Arc::new(interpreter), - arguments: arguments.clone(), - output_types: output_types.clone(), - }; - - let shared_string = SharedString(Rc::new(export_function_name.to_string())); - let callable = Rc::new(Callable { - wit_instance: wit_instance.clone(), - wit_module_func, - }); - - Ok((shared_string, callable)) - } - _ => Err(MError::IncorrectWIT(format!( - "type with idx = {} isn't a function type", - adapter_function_type - ))), - } - }) - .collect::>() + Ok((export_funcs, module_interface.export_record_types)) } // this function deals only with import functions that have an adaptor implementation @@ -442,92 +413,4 @@ impl MModule { Ok(import_object) } - - // TODO : move it to a separate crate - fn extract_export_record_types( - export_funcs: &ExportFunctions, - wit_instance: &Arc, - ) -> MResult { - use marine_it_generator::TYPE_RESOLVE_RECURSION_LIMIT; - use MError::RecordResolveError; - - fn handle_itype( - itype: &IType, - wit_instance: &Arc, - export_record_types: &mut RecordTypes, - recursion_level: u32, - ) -> MResult<()> { - use wasmer_it::interpreter::wasm::structures::Instance; - - if recursion_level > TYPE_RESOLVE_RECURSION_LIMIT { - return Err(RecordResolveError(String::from( - "mailformed module: a record contains more recursion level then allowed", - ))); - } - - fn handle_record_type( - record_type_id: u64, - wit_instance: &Arc, - export_record_types: &mut RecordTypes, - recursion_level: u32, - ) -> MResult<()> { - let record_type = - wit_instance - .wit_record_by_id(record_type_id) - .ok_or_else(|| { - RecordResolveError(format!( - "record type with type id {} not found", - record_type_id - )) - })?; - export_record_types.insert(record_type_id, record_type.clone()); - - for field in record_type.fields.iter() { - handle_itype( - &field.ty, - wit_instance, - export_record_types, - recursion_level + 1, - )?; - } - - Ok(()) - } - - match itype { - IType::Record(record_type_id) => handle_record_type( - *record_type_id, - wit_instance, - export_record_types, - recursion_level + 1, - )?, - IType::Array(array_ty) => handle_itype( - array_ty, - wit_instance, - export_record_types, - recursion_level + 1, - )?, - _ => {} - } - - Ok(()) - } - - let mut export_record_types = HashMap::new(); - - let itypes = export_funcs.iter().flat_map(|(_, ref mut callable)| { - callable - .wit_module_func - .arguments - .iter() - .map(|arg| &arg.ty) - .chain(callable.wit_module_func.output_types.iter()) - }); - - for itype in itypes { - handle_itype(itype, wit_instance, &mut export_record_types, 0)?; - } - - Ok(export_record_types) - } } diff --git a/runtime/src/module/mod.rs b/runtime/src/module/mod.rs index 81ff40fc..c85cd699 100644 --- a/runtime/src/module/mod.rs +++ b/runtime/src/module/mod.rs @@ -21,16 +21,26 @@ mod wit_function; mod wit_instance; mod type_converters; -pub use wit_instance::RecordTypes; - +pub use wit_instance::MRecordTypes; pub use wasmer_it::IType; pub use wasmer_it::IRecordType; pub use wasmer_it::ast::FunctionArg as IFunctionArg; pub use wasmer_it::IValue; -pub use marine_module::MFunctionSignature; pub use wasmer_it::from_interface_values; pub use wasmer_it::to_interface_value; +use serde::Serialize; +use serde::Deserialize; +use std::rc::Rc; + +/// Represent a function type inside Marine module. +#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)] +pub struct MFunctionSignature { + pub name: Rc, + pub arguments: Rc>, + pub outputs: Rc>, +} + pub(crate) use marine_module::MModule; pub(self) use wasmer_core::types::Type as WType; pub(self) use wasmer_core::types::Value as WValue; diff --git a/runtime/src/module/wit_instance.rs b/runtime/src/module/wit_instance.rs index bf42fbb0..a2da0e1f 100644 --- a/runtime/src/module/wit_instance.rs +++ b/runtime/src/module/wit_instance.rs @@ -29,7 +29,7 @@ use std::collections::HashMap; use std::cell::Cell; use std::rc::Rc; -pub type RecordTypes = HashMap>; +pub type MRecordTypes = HashMap>; /// Contains all import and export functions that could be called from IT context by call-core. #[derive(Clone)] @@ -41,7 +41,7 @@ pub(super) struct ITInstance { memories: Vec, /// All record types that instance contains. - record_types_by_id: RecordTypes, + record_types_by_id: MRecordTypes, } impl ITInstance { @@ -158,7 +158,7 @@ impl ITInstance { memories } - fn extract_record_types(wit: &MITInterfaces<'_>) -> RecordTypes { + fn extract_record_types(wit: &MITInterfaces<'_>) -> MRecordTypes { let (record_types_by_id, _) = wit.types().fold( (HashMap::new(), 0u64), |(mut record_types_by_id, id), ty| { diff --git a/tools/cli/Cargo.toml b/tools/cli/Cargo.toml index 4a5cca4f..4a4fbd19 100644 --- a/tools/cli/Cargo.toml +++ b/tools/cli/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "marine" description = "Fluence Marine command line tool" -version = "0.6.2" +version = "0.6.3" authors = ["Fluence Labs"] repository = "https://github.com/fluencelabs/marine/tools/cli" license = "Apache-2.0" @@ -13,11 +13,12 @@ path = "src/main.rs" [dependencies] marine-it-generator = { path = "../../crates/it-generator", version = "0.5.1" } -marine-it-parser = { path = "../../crates/it-parser", version = "0.6.0" } +marine-it-parser = { path = "../../crates/it-parser", version = "0.6.1" } marine-module-info-parser = { path = "../../crates/module-info-parser", version = "0.1.0" } semver = "0.11.0" walrus = "0.18.0" +Inflector = "0.11.4" thiserror = "1.0.24" anyhow = "1.0.31" diff --git a/tools/cli/src/args.rs b/tools/cli/src/args.rs index ff85da94..cb47c143 100644 --- a/tools/cli/src/args.rs +++ b/tools/cli/src/args.rs @@ -23,9 +23,27 @@ pub const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION"); pub const IN_WASM_PATH: &str = "in-wasm-path"; pub const IT_PATH: &str = "it-path"; pub const OUT_WASM_PATH: &str = "out-wasm-path"; +pub const SERVICE_NAME: &str = "service-name"; pub const SDK_VERSION: &str = "sdk-version"; +pub fn aqua<'a, 'b>() -> App<'a, 'b> { + SubCommand::with_name("aqua") + .about("Shows data types of provided module in a format suitable for Aqua") + .args(&[ + Arg::with_name(IN_WASM_PATH) + .required(true) + .takes_value(true) + .index(1) + .help("a path to a Wasm file"), + Arg::with_name(SERVICE_NAME) + .required(false) + .takes_value(true) + .short("s") + .help("optional service name"), + ]) +} + pub fn build<'a, 'b>() -> App<'a, 'b> { SubCommand::with_name("build") .about("Builds provided Rust project to Wasm") diff --git a/tools/cli/src/main.rs b/tools/cli/src/main.rs index f014a173..58f97c1b 100644 --- a/tools/cli/src/main.rs +++ b/tools/cli/src/main.rs @@ -25,13 +25,13 @@ unreachable_patterns )] +use marine_module_info_parser::manifest; +use marine_module_info_parser::sdk_version; + mod args; mod build; mod errors; -use marine_module_info_parser::manifest; -use marine_module_info_parser::sdk_version; - pub(crate) type CLIResult = std::result::Result; pub fn main() -> Result<(), anyhow::Error> { @@ -39,6 +39,7 @@ pub fn main() -> Result<(), anyhow::Error> { .version(args::VERSION) .author(args::AUTHORS) .setting(clap::AppSettings::ArgRequiredElseHelp) + .subcommand(args::aqua()) .subcommand(args::build()) .subcommand(args::set()) .subcommand(args::show_manifest()) @@ -47,6 +48,7 @@ pub fn main() -> Result<(), anyhow::Error> { let arg_matches = app.get_matches(); match arg_matches.subcommand() { + ("aqua", Some(args)) => aqua(args), ("build", Some(args)) => build(args), ("set", Some(args)) => set(args), ("it", Some(args)) => it(args), @@ -76,6 +78,36 @@ pub fn main() -> Result<(), anyhow::Error> { Ok(()) } +fn aqua(args: &clap::ArgMatches<'_>) -> Result<(), anyhow::Error> { + use inflector::Inflector; + + let wasm_path = args.value_of(args::IN_WASM_PATH).unwrap(); + let wasm_path = std::path::Path::new(wasm_path); + + let module_interface = marine_it_parser::module_interface(wasm_path)?; + for record in module_interface.record_types.iter() { + println!("{}", record); + } + + match args.value_of(args::SERVICE_NAME) { + Some(service_name) => println!("service {}:", service_name.to_title_case()), + None => { + let service_name = wasm_path + .file_stem() + .ok_or(anyhow::Error::msg("provided path isn't a path to a file"))?; + let service_name = service_name.to_string_lossy().to_title_case(); + + println!("service {}:", service_name); + } + } + + for sign in module_interface.function_signatures { + println!(" {}", sign); + } + + Ok(()) +} + fn build(args: &clap::ArgMatches<'_>) -> Result<(), anyhow::Error> { let trailing_args: Vec<&str> = args.values_of("optional").unwrap_or_default().collect(); diff --git a/tools/repl/Cargo.toml b/tools/repl/Cargo.toml index 94f91f08..4807208d 100644 --- a/tools/repl/Cargo.toml +++ b/tools/repl/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "mrepl" description = "Fluence Marine REPL intended for testing purposes" -version = "0.7.2" +version = "0.7.3" authors = ["Fluence Labs"] repository = "https://github.com/fluencelabs/marine/tools/repl" license = "Apache-2.0" @@ -12,7 +12,7 @@ name = "mrepl" path = "src/main.rs" [dependencies] -fluence-app-service = { path = "../../fluence-app-service", version = "0.7.1", features = ["raw-module-api"] } +fluence-app-service = { path = "../../fluence-app-service", version = "0.7.2", features = ["raw-module-api"] } fluence-sdk-main = { version = "0.6.9", features = ["logger"] } anyhow = "1.0.31"