diff --git a/Cargo.lock b/Cargo.lock index ce6c2f26..cf3974b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -712,6 +712,7 @@ version = "0.1.10" dependencies = [ "anyhow", "fce-wit-interfaces", + "serde", "walrus", "wasmer-interface-types-fl", "wasmer-runtime-core-fl", diff --git a/crates/wit-parser/Cargo.toml b/crates/wit-parser/Cargo.toml index 648eacf1..78091c8b 100644 --- a/crates/wit-parser/Cargo.toml +++ b/crates/wit-parser/Cargo.toml @@ -17,3 +17,5 @@ anyhow = "1.0.31" walrus = "0.17.0" wasmer-core = { package = "wasmer-runtime-core-fl", version = "0.17.0"} wasmer-wit = { package = "wasmer-interface-types-fl", version = "=0.17.19" } + +serde = "1.0.117" diff --git a/crates/wit-parser/src/errors.rs b/crates/wit-parser/src/errors.rs index 38dcac5d..dbe158f6 100644 --- a/crates/wit-parser/src/errors.rs +++ b/crates/wit-parser/src/errors.rs @@ -32,6 +32,9 @@ pub enum WITParserError { /// An error occurred while parsing WIT section. CorruptedWITSection, + // An error related to incorrect wit section. + IncorrectWIT(String), + /// An error occurred while parsing file in Wat format. CorruptedWATFile(WATError), @@ -59,6 +62,7 @@ impl std::fmt::Display for WITParserError { f, "WIT section remainder isn't empty - WIT section possibly corrupted" ), + WITParserError::IncorrectWIT(err_msg) => write!(f, "{}", err_msg), WITParserError::CorruptedWITSection => write!(f, "WIT section is corrupted"), WITParserError::CorruptedWATFile(err) => { write!(f, "an error occurred while parsing wat file: {}", err) diff --git a/crates/wit-parser/src/extractor.rs b/crates/wit-parser/src/extractor.rs index ac89137f..9e784397 100644 --- a/crates/wit-parser/src/extractor.rs +++ b/crates/wit-parser/src/extractor.rs @@ -14,61 +14,21 @@ * limitations under the License. */ -use super::custom::WIT_SECTION_NAME; -use super::errors::WITParserError; +mod functions; +mod wit; -use walrus::{IdsToIndices, ModuleConfig}; -use wasmer_wit::ast::Interfaces; -use wasmer_core::Module as WasmerModule; +pub use functions::*; +pub use wit::*; +use crate::Result; use std::path::PathBuf; -/// Extracts WIT section of provided Wasm binary and converts it to a string. -pub fn extract_text_wit(wasm_file_path: PathBuf) -> Result { - let wit_section_bytes = extract_wit_section_bytes(wasm_file_path)?; +pub fn module_interface(module_path: PathBuf) -> Result { + use fce_wit_interfaces::FCEWITInterfaces; + + let wit_section_bytes = extract_wit_section_bytes(module_path)?; let wit = extract_wit_with_fn(&wit_section_bytes)?; - Ok((&wit).to_string()) -} - -/// Extracts WIT section of provided Wasm binary and converts it to a FCEWITInterfaces. -pub fn extract_wit(wasmer_module: &WasmerModule) -> Result, WITParserError> { - let wit_sections = wasmer_module - .custom_sections(WIT_SECTION_NAME) - .ok_or(WITParserError::NoWITSection)?; - - if wit_sections.len() > 1 { - return Err(WITParserError::MultipleWITSections); - } - - extract_wit_with_fn(&wit_sections[0]) -} - -fn extract_wit_with_fn(wit_section_bytes: &[u8]) -> Result, WITParserError> { - match wasmer_wit::decoders::binary::parse::<()>(&wit_section_bytes) { - Ok((remainder, wit)) if remainder.is_empty() => Ok(wit), - Ok(_) => Err(WITParserError::WITRemainderNotEmpty), - Err(_) => Err(WITParserError::CorruptedWITSection), - } -} - -fn extract_wit_section_bytes(wasm_file_path: PathBuf) -> Result, WITParserError> { - let module = ModuleConfig::new() - .parse_file(wasm_file_path) - .map_err(WITParserError::CorruptedWasmFile)?; - - let sections = module - .customs - .iter() - .filter(|(_, section)| section.name() == WIT_SECTION_NAME) - .collect::>(); - - if sections.is_empty() { - return Err(WITParserError::NoWITSection); - } - if sections.len() > 1 { - return Err(WITParserError::MultipleWITSections); - } - - let default_ids = IdsToIndices::default(); - Ok(sections[0].1.data(&default_ids).into_owned()) + let fce_interface = FCEWITInterfaces::new(wit); + + get_interface(&fce_interface) } diff --git a/crates/wit-parser/src/extractor/functions.rs b/crates/wit-parser/src/extractor/functions.rs new file mode 100644 index 00000000..9b345c42 --- /dev/null +++ b/crates/wit-parser/src/extractor/functions.rs @@ -0,0 +1,211 @@ +/* + * 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::WITParserError; +use fce_wit_interfaces::FCEWITInterfaces; + +use wasmer_wit::types::RecordType as IRecordType; +use wasmer_wit::ast::FunctionArg as IFunctionArg; +use wasmer_wit::types::InterfaceType as IType; +use serde::Serialize; +use serde::Deserialize; + +use std::collections::HashMap; +use std::rc::Rc; + +pub type RecordTypes = HashMap>; + +#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)] +pub struct FCEFunctionSignature { + pub name: Rc, + pub arguments: Rc>, + pub outputs: Rc>, +} + +#[derive(PartialEq, Eq, Debug, Clone, Serialize)] +pub struct FCEModuleInterface { + pub record_types: RecordTypes, + pub function_signatures: Vec, +} + +pub fn get_interface(wit: &FCEWITInterfaces<'_>) -> Result { + let function_signatures = get_exports(wit)?; + let record_types = extract_record_types(wit); + + let fce_interface = FCEModuleInterface { + record_types, + function_signatures, + }; + + let service_interface = into_service_interface(fce_interface); + + Ok(service_interface) +} + +fn get_exports(wit: &FCEWITInterfaces<'_>) -> Result> { + use fce_wit_interfaces::WITAstType; + + wit.implementations() + .filter_map(|(adapter_function_type, core_function_type)| { + match wit.exports_by_type(*core_function_type) { + Some(export_function_name) => Some((adapter_function_type, export_function_name)), + // pass functions that aren't export + None => None, + } + }) + .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 wit_type = wit.type_by_idx_r(adapter_function_type).unwrap(); + + match wit_type { + WITAstType::Function { + arguments, + output_types, + } => { + let signature = FCEFunctionSignature { + name: Rc::new(export_function_name.to_string()), + arguments: arguments.clone(), + outputs: output_types.clone(), + }; + Ok(signature) + } + _ => Err(WITParserError::IncorrectWIT(format!( + "type with idx = {} isn't a function type", + adapter_function_type + ))), + } + }) + .collect::>>() +} + +fn extract_record_types(wit: &FCEWITInterfaces<'_>) -> RecordTypes { + use fce_wit_interfaces::WITAstType; + + let (record_types_by_id, _) = wit.types().fold( + (HashMap::new(), 0u64), + |(mut record_types_by_id, id), ty| { + match ty { + WITAstType::Record(record_type) => { + record_types_by_id.insert(id, record_type.clone()); + } + WITAstType::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(fce_interface: FCEModuleInterface) -> ServiceInterface { + let record_types = fce_interface.record_types; + + let function_signatures = fce_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 { + record_types, + function_signatures, + } +} + +fn serialize_function_signature( + signature: FCEFunctionSignature, + record_types: &RecordTypes, +) -> 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<'a, 'b>( + id: u64, + record: Rc, + record_types: &RecordTypes, +) -> 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: &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() + } + IType::Array(array_ty) => format!("Array<{}>", itype_text_view(array_ty, record_types)), + t => format!("{:?}", t), + } +} diff --git a/crates/wit-parser/src/extractor/wit.rs b/crates/wit-parser/src/extractor/wit.rs new file mode 100644 index 00000000..291e441e --- /dev/null +++ b/crates/wit-parser/src/extractor/wit.rs @@ -0,0 +1,78 @@ +/* + * 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::custom::WIT_SECTION_NAME; +use crate::errors::WITParserError; + +use walrus::{IdsToIndices, ModuleConfig}; +use wasmer_wit::ast::Interfaces; +use wasmer_core::Module as WasmerModule; + +use std::path::PathBuf; + +/// Extracts WIT section of provided Wasm binary and converts it to a string. +pub fn extract_text_wit(wasm_file_path: PathBuf) -> Result { + let wit_section_bytes = extract_wit_section_bytes(wasm_file_path)?; + let wit = extract_wit_with_fn(&wit_section_bytes)?; + Ok((&wit).to_string()) +} + +/// Extracts WIT section of provided Wasm binary and converts it to a FCEWITInterfaces. +pub fn extract_wit(wasmer_module: &WasmerModule) -> Result, WITParserError> { + let wit_sections = wasmer_module + .custom_sections(WIT_SECTION_NAME) + .ok_or(WITParserError::NoWITSection)?; + + if wit_sections.len() > 1 { + return Err(WITParserError::MultipleWITSections); + } + + extract_wit_with_fn(&wit_sections[0]) +} + +pub(crate) fn extract_wit_with_fn( + wit_section_bytes: &[u8], +) -> Result, WITParserError> { + match wasmer_wit::decoders::binary::parse::<()>(&wit_section_bytes) { + Ok((remainder, wit)) if remainder.is_empty() => Ok(wit), + Ok(_) => Err(WITParserError::WITRemainderNotEmpty), + Err(_) => Err(WITParserError::CorruptedWITSection), + } +} + +pub(crate) fn extract_wit_section_bytes( + wasm_file_path: PathBuf, +) -> Result, WITParserError> { + let module = ModuleConfig::new() + .parse_file(wasm_file_path) + .map_err(WITParserError::CorruptedWasmFile)?; + + let sections = module + .customs + .iter() + .filter(|(_, section)| section.name() == WIT_SECTION_NAME) + .collect::>(); + + if sections.is_empty() { + return Err(WITParserError::NoWITSection); + } + if sections.len() > 1 { + return Err(WITParserError::MultipleWITSections); + } + + let default_ids = IdsToIndices::default(); + Ok(sections[0].1.data(&default_ids).into_owned()) +} diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs index 4dc5d0e3..c29c5893 100644 --- a/crates/wit-parser/src/lib.rs +++ b/crates/wit-parser/src/lib.rs @@ -38,3 +38,6 @@ pub use embedder::embed_wit; pub use embedder::embed_text_wit; pub use extractor::extract_wit; pub use extractor::extract_text_wit; +pub use extractor::module_interface; + +pub(crate) type Result = std::result::Result;