diff --git a/Cargo.lock b/Cargo.lock index de3f866d..46b4a66e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,9 +35,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" +checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" [[package]] name = "arrayref" @@ -403,7 +403,7 @@ dependencies = [ "parity-wasm", "pwasm-utils", "serde", - "wasmer-interface-types", + "wasmer-interface-types 0.17.0 (git+http://github.com/fluencelabs/interface-types?branch=struct_support)", "wasmer-runtime", "wasmer-runtime-core", "wasmer-wasi", @@ -415,7 +415,7 @@ name = "fce-wit-interfaces" version = "0.1.0" dependencies = [ "multimap", - "wasmer-interface-types", + "wasmer-interface-types 0.17.0 (git+https://github.com/fluencelabs/interface-types?branch=byte_array)", ] [[package]] @@ -434,10 +434,18 @@ dependencies = [ [[package]] name = "fluence" version = "0.2.0" -source = "git+https://github.com/fluencelabs/rust-sdk#79a5896015659ddb1c20526c4aa142b8afae35ad" +source = "git+https://github.com/fluencelabs/rust-sdk#e589d52c7247462f194800f50595029d224a5e43" dependencies = [ - "fluence-sdk-macro", - "fluence-sdk-main", + "fluence-sdk-macro 0.2.0 (git+https://github.com/fluencelabs/rust-sdk)", + "fluence-sdk-main 0.2.0 (git+https://github.com/fluencelabs/rust-sdk)", +] + +[[package]] +name = "fluence" +version = "0.2.0" +dependencies = [ + "fluence-sdk-macro 0.2.0", + "fluence-sdk-main 0.2.0", ] [[package]] @@ -459,15 +467,29 @@ dependencies = [ [[package]] name = "fluence-sdk-macro" version = "0.2.0" -source = "git+https://github.com/fluencelabs/rust-sdk#79a5896015659ddb1c20526c4aa142b8afae35ad" +source = "git+https://github.com/fluencelabs/rust-sdk#e589d52c7247462f194800f50595029d224a5e43" dependencies = [ - "fluence-sdk-wit", + "fluence-sdk-wit 0.2.0 (git+https://github.com/fluencelabs/rust-sdk)", +] + +[[package]] +name = "fluence-sdk-macro" +version = "0.2.0" +dependencies = [ + "fluence-sdk-wit 0.2.0", +] + +[[package]] +name = "fluence-sdk-main" +version = "0.2.0" +source = "git+https://github.com/fluencelabs/rust-sdk#e589d52c7247462f194800f50595029d224a5e43" +dependencies = [ + "log", ] [[package]] name = "fluence-sdk-main" version = "0.2.0" -source = "git+https://github.com/fluencelabs/rust-sdk#79a5896015659ddb1c20526c4aa142b8afae35ad" dependencies = [ "log", ] @@ -475,7 +497,19 @@ dependencies = [ [[package]] name = "fluence-sdk-wit" version = "0.2.0" -source = "git+https://github.com/fluencelabs/rust-sdk#79a5896015659ddb1c20526c4aa142b8afae35ad" +source = "git+https://github.com/fluencelabs/rust-sdk#e589d52c7247462f194800f50595029d224a5e43" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", + "uuid", +] + +[[package]] +name = "fluence-sdk-wit" +version = "0.2.0" dependencies = [ "proc-macro2", "quote", @@ -1010,6 +1044,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "safe-transmute" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b8b2cd387f744f69469aaed197954ba4c0ecdb31e02edf99b023e0df11178a" + [[package]] name = "scopeguard" version = "1.1.0" @@ -1107,9 +1147,9 @@ checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1" [[package]] name = "syn" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cae2873c940d92e697597c5eee105fb570cd5689c695806f672883653349b" +checksum = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0" dependencies = [ "proc-macro2", "quote", @@ -1311,14 +1351,14 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" name = "wasm_greeting" version = "0.1.0" dependencies = [ - "fluence", + "fluence 0.2.0 (git+https://github.com/fluencelabs/rust-sdk)", ] [[package]] name = "wasm_ipfs_node" version = "0.1.0" dependencies = [ - "fluence", + "fluence 0.2.0 (git+https://github.com/fluencelabs/rust-sdk)", "log", ] @@ -1326,7 +1366,7 @@ dependencies = [ name = "wasm_ipfs_rpc" version = "0.1.0" dependencies = [ - "fluence", + "fluence 0.2.0", "log", ] @@ -1384,13 +1424,25 @@ dependencies = [ [[package]] name = "wasmer-interface-types" version = "0.17.0" -source = "git+https://github.com/fluencelabs/interface-types?branch=byte_array#b65b34b0f627b3da5cd5761fe309cefc4980449b" +source = "git+https://github.com/fluencelabs/interface-types?branch=byte_array#4521e79a50e39dfa155d07e74ffc7d12203fa198" dependencies = [ "nom", "serde", "wast", ] +[[package]] +name = "wasmer-interface-types" +version = "0.17.0" +source = "git+http://github.com/fluencelabs/interface-types?branch=struct_support#053056c1eb5063b8aab480022301e4b5cd5baa81" +dependencies = [ + "nom", + "safe-transmute", + "serde", + "serde_json", + "wast", +] + [[package]] name = "wasmer-runtime" version = "0.17.0" @@ -1518,12 +1570,12 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" name = "wit-generator" version = "0.1.0" dependencies = [ - "fluence-sdk-wit", + "fluence-sdk-wit 0.2.0 (git+https://github.com/fluencelabs/rust-sdk)", "once_cell", "serde", "serde_json", "walrus", - "wasmer-interface-types", + "wasmer-interface-types 0.17.0 (git+http://github.com/fluencelabs/interface-types?branch=struct_support)", "wit-parser", ] @@ -1534,6 +1586,6 @@ dependencies = [ "anyhow", "fce-wit-interfaces", "walrus", - "wasmer-interface-types", + "wasmer-interface-types 0.17.0 (git+http://github.com/fluencelabs/interface-types?branch=struct_support)", "wasmer-runtime-core", ] diff --git a/README.md b/README.md index d5dcd409..01bb42cd 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ FCE is intended to run various Wasm binaries. At now, it is in the heavily devel - Init simple rust project `cargo init --bin` - `Config.toml`: -``` +```rust [[bin]] name = "wasm_application" path = "src/main.rs" @@ -33,7 +33,7 @@ fluence = { git = "https://github.com/fluencelabs/rust-sdk", features = ["logger ``` - Methods that will be exported from this module marked with `#[fce]` -``` +```rust use fluence::fce; #[fce] @@ -48,7 +48,7 @@ pub fn get(url: String) -> String { - Copy wasm file from `target/wasm32-wasi/debug` to directory with other modules - To import other wasm modules to your project use similar code: -``` +```rust #[fce] #[link(wasm_import_module = "wasm_curl.wasm")] extern "C" { @@ -68,7 +68,7 @@ extern "C" { - Create simple Rust project - Create `Config.toml` to describe existed wasm modules and give accesses to host binaries and local storage if needed: -``` +```rust core_modules_dir = "wasm/artifacts/modules" [[core_module]] @@ -97,7 +97,7 @@ core_modules_dir = "wasm/artifacts/modules" `curl = "/usr/bin/curl"` - gives possibility to call binary file `/usr/bin/curl` as method `curl` in Rust code Import example: -``` +```rust #[link(wasm_import_module = "host")] extern "C" { fn curl(args: String) -> String; @@ -112,7 +112,7 @@ Call binary with arguments: `curl("-vvv ya.ru")` `mapped_dirs` - mapping between paths Working with files as usual: -``` +```rust fs::write(PathBuf::from("/tmp/somefile"), vec!(1,2,3)); fs::read(...); ``` \ No newline at end of file diff --git a/crates/wit-generator/Cargo.toml b/crates/wit-generator/Cargo.toml index 5a9c3d0f..0bf2d712 100644 --- a/crates/wit-generator/Cargo.toml +++ b/crates/wit-generator/Cargo.toml @@ -9,6 +9,6 @@ wit-parser = { path = "../wit-parser" } walrus = "0.17.0" fluence-sdk-wit = { git = "https://github.com/fluencelabs/rust-sdk" } once_cell = "1.4.0" -wasmer-wit = { package = "wasmer-interface-types", git = "http://github.com/fluencelabs/interface-types", branch = "byte_array", features = ["serde"] } +wasmer-wit = { package = "wasmer-interface-types", git = "http://github.com/fluencelabs/interface-types", branch = "struct_support", features = ["serde"] } serde = { version = "1.0.110", features = ["derive"] } serde_json = "1.0.56" diff --git a/crates/wit-generator/src/errors.rs b/crates/wit-generator/src/errors.rs index f8e9bcba..42185f14 100644 --- a/crates/wit-generator/src/errors.rs +++ b/crates/wit-generator/src/errors.rs @@ -22,6 +22,9 @@ pub enum WITGeneratorError { /// An error related to serde deserialization. DeserializationError(SerdeDeserializationError), + /// Various errors related to records + CorruptedRecord(String), + /// Various errors occurred during the parsing/emitting a Wasm file. IOError(String), } @@ -36,6 +39,7 @@ impl std::fmt::Display for WITGeneratorError { "Embedded by rust-sdk metadata could't be parsed by serde: {:?}", err ), + WITGeneratorError::CorruptedRecord(err) => write!(f, "{:?}", err), WITGeneratorError::IOError(err) => write!(f, "I/O error occurred: {:?}", err), } } diff --git a/crates/wit-generator/src/instructions_generator.rs b/crates/wit-generator/src/instructions_generator.rs index 75b5519a..ff275427 100644 --- a/crates/wit-generator/src/instructions_generator.rs +++ b/crates/wit-generator/src/instructions_generator.rs @@ -19,34 +19,47 @@ mod foreign_mod_instructions; mod record_instructions; mod utils; -use fluence_sdk_wit::FCEAst; +use crate::Result; use wasmer_wit::types::InterfaceType as IType; use wasmer_wit::ast::Interfaces; -use wasmer_wit::interpreter::Instruction; -pub trait WITGenerator { - fn generate_wit<'a>(&'a self, interfaces: &mut Interfaces<'a>); +#[derive(PartialEq, Debug, Default)] +pub(crate) struct WITResolver<'a> { + pub(crate) types: std::collections::HashMap, + pub(crate) interfaces: Interfaces<'a>, } -trait FnInstructionGenerator { - fn generate_instructions_for_input_type(&self, arg_id: u32) -> Vec; +impl<'a> WITResolver<'a> { + pub(crate) fn get_record_type_id(&self, record_name: &str) -> Result { + match self.types.get(record_name) { + Some(type_index) => Ok(*type_index), + None => Err(crate::errors::WITGeneratorError::CorruptedRecord(format!( + "Can't find record with name='{}', don't you forget to wrap it with #[fce]", + record_name + ))), + } + } - fn generate_instructions_for_output_type(&self) -> Vec; -} - -trait ForeignModInstructionGenerator { - fn generate_instructions_for_input_type(&self, arg_id: u32) -> Vec; - - fn generate_instructions_for_output_type(&self) -> Vec; -} - -impl WITGenerator for FCEAst { - fn generate_wit<'a>(&'a self, interfaces: &mut Interfaces<'a>) { - match self { - FCEAst::Function(func) => func.generate_wit(interfaces), - FCEAst::ExternMod(extern_mod) => extern_mod.generate_wit(interfaces), - FCEAst::Record(record) => record.generate_wit(interfaces), + pub(crate) fn get_record_type( + &self, + record_name: &str, + ) -> Result { + match self.types.get(record_name) { + Some(type_index) => match &self.interfaces.types[*type_index as usize] { + wasmer_wit::ast::Type::Function { .. } => { + panic!("internal error inside WITResolver") + } + wasmer_wit::ast::Type::Record(record_type) => Ok(record_type.clone()), + }, + None => Err(crate::errors::WITGeneratorError::CorruptedRecord(format!( + "Can't find record with name='{}', don't you forget to wrap it with #[fce]", + record_name + ))), } } } + +pub(crate) trait WITGenerator { + fn generate_wit<'a>(&'a self, wit_resolver: &mut WITResolver<'a>) -> Result<()>; +} diff --git a/crates/wit-generator/src/instructions_generator/fn_instructions.rs b/crates/wit-generator/src/instructions_generator/fn_instructions.rs index ee85d9c3..5deaf918 100644 --- a/crates/wit-generator/src/instructions_generator/fn_instructions.rs +++ b/crates/wit-generator/src/instructions_generator/fn_instructions.rs @@ -15,17 +15,17 @@ */ use super::WITGenerator; -use super::Interfaces; -use super::FnInstructionGenerator; +use super::WITResolver; use super::utils::ptype_to_itype; use crate::default_export_api_config::*; +use crate::Result; use fluence_sdk_wit::AstFunctionItem; use fluence_sdk_wit::ParsedType; use wasmer_wit::interpreter::Instruction; impl WITGenerator for AstFunctionItem { - fn generate_wit<'a>(&'a self, interfaces: &mut Interfaces<'a>) { + fn generate_wit<'a>(&'a self, wit_resolver: &mut WITResolver<'a>) -> Result<()> { use wasmer_wit::ast::Type; use wasmer_wit::ast::Adapter; @@ -33,14 +33,15 @@ impl WITGenerator for AstFunctionItem { .signature .input_types .iter() - .map(ptype_to_itype) - .collect::>(); + .map(|input_type| ptype_to_itype(input_type, wit_resolver)) + .collect::>>()?; let outputs = match self.signature.output_type { - Some(ref output_type) => vec![ptype_to_itype(output_type)], + Some(ref output_type) => vec![ptype_to_itype(output_type, wit_resolver)?], None => vec![], }; + let interfaces = &mut wit_resolver.interfaces; interfaces.types.push(Type::Function { inputs: inputs.clone(), outputs: outputs.clone(), @@ -57,22 +58,27 @@ impl WITGenerator for AstFunctionItem { function_type: export_idx, }); - let mut instructions: Vec = self + // TODO: rewrite with try_fold + let mut instructions = self .signature .input_types .iter() .enumerate() - .map(|(id, input_type)| input_type.generate_instructions_for_input_type(id as _)) + .map(|(id, input_type)| { + input_type.generate_instructions_for_input_type(id as _, wit_resolver) + }) + .collect::>>()? + .into_iter() .flatten() - .collect(); + .collect::>(); - let export_function_index = (interfaces.exports.len() - 1) as u32; + let export_function_index = (wit_resolver.interfaces.exports.len() - 1) as u32; instructions.push(Instruction::CallCore { function_index: export_function_index, }); instructions.extend(match &self.signature.output_type { - Some(output_type) => output_type.generate_instructions_for_output_type(), + Some(output_type) => output_type.generate_instructions_for_output_type(wit_resolver)?, None => vec![], }); @@ -81,20 +87,37 @@ impl WITGenerator for AstFunctionItem { instructions, }; - interfaces.adapters.push(adapter); + wit_resolver.interfaces.adapters.push(adapter); let implementation = wasmer_wit::ast::Implementation { core_function_type: export_idx, adapter_function_type: adapter_idx, }; - interfaces.implementations.push(implementation); + wit_resolver.interfaces.implementations.push(implementation); + + Ok(()) } } +/// Generate WIT instructions for a function. +trait FnInstructionGenerator { + fn generate_instructions_for_input_type<'a>( + &self, + arg_id: u32, + wit_resolver: &mut WITResolver<'a>, + ) -> Result>; + + fn generate_instructions_for_output_type<'a>( + &self, + wit_resolver: &mut WITResolver<'a>, + ) -> Result>; +} + impl FnInstructionGenerator for ParsedType { #[rustfmt::skip] - fn generate_instructions_for_input_type(&self, index: u32) -> Vec { - match self { + fn generate_instructions_for_input_type<'a>(&self, index: u32, wit_resolver: &mut WITResolver<'a>) -> Result> { + let instructions = match self { + ParsedType::Boolean => vec![Instruction::ArgumentGet { index }], ParsedType::I8 => vec![Instruction::ArgumentGet { index }, Instruction::I32FromS8], ParsedType::I16 => vec![Instruction::ArgumentGet { index }, Instruction::I32FromS16], ParsedType::I32 => vec![Instruction::ArgumentGet { index }], @@ -119,13 +142,23 @@ impl FnInstructionGenerator for ParsedType { Instruction::ArgumentGet { index }, Instruction::ByteArrayLowerMemory, ], - _ => unimplemented!(), - } + ParsedType::Record(record_name) => { + let type_index = wit_resolver.get_record_type_id(record_name)?; + + vec! [ + Instruction::ArgumentGet { index }, + Instruction::RecordLowerMemory { type_index }, + ] + }, + }; + + Ok(instructions) } #[rustfmt::skip] - fn generate_instructions_for_output_type(&self) -> Vec { - match self { + fn generate_instructions_for_output_type<'a>(&self, wit_resolver: &mut WITResolver<'a>) -> Result> { + let instructions = match self { + ParsedType::Boolean => vec![], ParsedType::I8 => vec![Instruction::S8FromI32], ParsedType::I16 => vec![Instruction::S16FromI32], ParsedType::I32 => vec![], @@ -152,7 +185,16 @@ impl FnInstructionGenerator for ParsedType { Instruction::CallCore { function_index: GET_RESULT_SIZE_FUNC.id }, Instruction::CallCore { function_index: DEALLOCATE_FUNC.id }, ], - _ => unimplemented!(), - } + ParsedType::Record(record_name) => { + let type_index = wit_resolver.get_record_type_id(record_name)?; + + vec! [ + Instruction::CallCore { function_index: GET_RESULT_PTR_FUNC.id }, + Instruction::RecordLiftMemory { type_index }, + ] + }, + }; + + Ok(instructions) } } diff --git a/crates/wit-generator/src/instructions_generator/foreign_mod_instructions.rs b/crates/wit-generator/src/instructions_generator/foreign_mod_instructions.rs index 010f5aee..f3242faf 100644 --- a/crates/wit-generator/src/instructions_generator/foreign_mod_instructions.rs +++ b/crates/wit-generator/src/instructions_generator/foreign_mod_instructions.rs @@ -15,10 +15,10 @@ */ use super::WITGenerator; -use super::Interfaces; +use super::WITResolver; use super::utils::ptype_to_itype; -use super::ForeignModInstructionGenerator; use crate::default_export_api_config::*; +use crate::Result; use fluence_sdk_wit::AstExternModItem; use fluence_sdk_wit::AstExternFnItem; @@ -29,23 +29,25 @@ use crate::instructions_generator::utils::wtype_to_itype; const HOST_NAMESPACE_NAME: &str = "host"; impl WITGenerator for AstExternModItem { - fn generate_wit<'a>(&'a self, interfaces: &mut Interfaces<'a>) { + fn generate_wit<'a>(&'a self, wit_resolver: &mut WITResolver<'a>) -> Result<()> { // host imports should be left as is if self.namespace == HOST_NAMESPACE_NAME { - return; + return Ok(()); } for import in &self.imports { - generate_wit_for_import(import, &self.namespace, interfaces); + generate_wit_for_import(import, &self.namespace, wit_resolver)?; } + + Ok(()) } } fn generate_wit_for_import<'a>( import: &'a AstExternFnItem, namespace: &'a str, - interfaces: &mut Interfaces<'a>, -) { + wit_resolver: &mut WITResolver<'a>, +) -> Result<()> { use wasmer_wit::ast::Type; use wasmer_wit::ast::Adapter; @@ -53,14 +55,15 @@ fn generate_wit_for_import<'a>( .signature .input_types .iter() - .map(ptype_to_itype) - .collect::>(); + .map(|input_type| ptype_to_itype(input_type, wit_resolver)) + .collect::>>()?; let outputs = match import.signature.output_type { - Some(ref output_type) => vec![ptype_to_itype(output_type)], + Some(ref output_type) => vec![ptype_to_itype(output_type, wit_resolver)?], None => vec![], }; + let interfaces = &mut wit_resolver.interfaces; interfaces.types.push(Type::Function { inputs, outputs }); let raw_inputs = import @@ -116,19 +119,24 @@ fn generate_wit_for_import<'a>( .input_types .iter() .enumerate() - .map(|(id, input_type)| input_type.generate_instructions_for_input_type(id as _)) + .map(|(id, input_type)| { + input_type.generate_instructions_for_input_type(id as _, wit_resolver) + }) + .collect::>>()? + .into_iter() .flatten() .collect(); // TODO: refactor - let import_function_index = - (interfaces.exports.len() + interfaces.imports.len() / 2 - 1) as u32; + let import_function_index = (wit_resolver.interfaces.exports.len() + + wit_resolver.interfaces.imports.len() / 2 + - 1) as u32; instructions.push(Instruction::CallCore { function_index: import_function_index, }); instructions.extend(match &import.signature.output_type { - Some(output_type) => output_type.generate_instructions_for_output_type(), + Some(output_type) => output_type.generate_instructions_for_output_type(wit_resolver)?, None => vec![], }); @@ -136,18 +144,39 @@ fn generate_wit_for_import<'a>( function_type: adapter_idx, instructions, }; - interfaces.adapters.push(adapter); + wit_resolver.interfaces.adapters.push(adapter); let implementation = wasmer_wit::ast::Implementation { core_function_type: raw_import_idx, adapter_function_type: adapter_idx, }; - interfaces.implementations.push(implementation); + wit_resolver.interfaces.implementations.push(implementation); + + Ok(()) +} + +/// Generate WIT instructions for a foreign mod. +trait ForeignModInstructionGenerator { + fn generate_instructions_for_input_type<'a>( + &self, + arg_id: u32, + wit_resolver: &mut WITResolver<'a>, + ) -> Result>; + + fn generate_instructions_for_output_type<'a>( + &self, + wit_resolver: &mut WITResolver<'a>, + ) -> Result>; } impl ForeignModInstructionGenerator for ParsedType { - fn generate_instructions_for_input_type(&self, index: u32) -> Vec { - match self { + fn generate_instructions_for_input_type<'a>( + &self, + index: u32, + wit_resolver: &mut WITResolver<'a>, + ) -> Result> { + let instructions = match self { + ParsedType::Boolean => vec![Instruction::ArgumentGet { index }], ParsedType::I8 => vec![Instruction::ArgumentGet { index }, Instruction::S8FromI32], ParsedType::I16 => vec![Instruction::ArgumentGet { index }, Instruction::S16FromI32], ParsedType::I32 => vec![Instruction::ArgumentGet { index }], @@ -168,13 +197,23 @@ impl ForeignModInstructionGenerator for ParsedType { Instruction::ArgumentGet { index: index + 1 }, Instruction::StringLiftMemory, ], - _ => unimplemented!(), - } + ParsedType::Record(record_name) => { + let type_index = wit_resolver.get_record_type_id(record_name)?; + + vec![ + Instruction::ArgumentGet { index }, + Instruction::RecordLiftMemory { type_index }, + ] + } + }; + + Ok(instructions) } #[rustfmt::skip] - fn generate_instructions_for_output_type(&self) -> Vec { - match self { + fn generate_instructions_for_output_type<'a>(&self, wit_resolver: &mut WITResolver<'a>) -> Result> { + let instructions = match self { + ParsedType::Boolean => vec![], ParsedType::I8 => vec![Instruction::I32FromS8], ParsedType::I16 => vec![Instruction::I32FromS16], ParsedType::I32 => vec![], @@ -203,8 +242,17 @@ impl ForeignModInstructionGenerator for ParsedType { Instruction::CallCore { function_index: SET_RESULT_SIZE_FUNC.id }, Instruction::CallCore { function_index: SET_RESULT_PTR_FUNC.id }, ], - _ => unimplemented!(), - } + ParsedType::Record(record_name) => { + let type_index = wit_resolver.get_record_type_id(record_name)?; + + vec![ + Instruction::RecordLowerMemory {type_index}, + Instruction::CallCore { function_index: SET_RESULT_SIZE_FUNC.id }, + ] + }, + }; + + Ok(instructions) } } diff --git a/crates/wit-generator/src/instructions_generator/record_instructions.rs b/crates/wit-generator/src/instructions_generator/record_instructions.rs index 13d13f06..94f350c8 100644 --- a/crates/wit-generator/src/instructions_generator/record_instructions.rs +++ b/crates/wit-generator/src/instructions_generator/record_instructions.rs @@ -15,12 +15,40 @@ */ use super::WITGenerator; -use super::Interfaces; +use super::WITResolver; +use crate::Result; use fluence_sdk_wit::AstRecordItem; +use wasmer_wit::ast::Type; +use wasmer_wit::types::RecordType; +use wasmer_wit::vec1::Vec1; + impl WITGenerator for AstRecordItem { - fn generate_wit<'a>(&'a self, _interfaces: &mut Interfaces<'a>) { - unimplemented!() + fn generate_wit<'a>(&'a self, wit_resolver: &mut WITResolver<'a>) -> Result<()> { + let fields = self + .fields + .iter() + .map(|field| super::utils::ptype_to_itype(&field.ty, wit_resolver)) + .collect::>>()?; + + let fields = Vec1::new(fields).map_err(|_| { + crate::errors::WITGeneratorError::CorruptedRecord(format!( + "serialized record with name '{}' contains no fields", + self.name + )) + })?; + + wit_resolver + .interfaces + .types + .push(Type::Record(RecordType { fields })); + + wit_resolver.types.insert( + self.name.clone(), + (wit_resolver.interfaces.types.len() - 1) as _, + ); + + Ok(()) } } diff --git a/crates/wit-generator/src/instructions_generator/utils.rs b/crates/wit-generator/src/instructions_generator/utils.rs index e1583463..1e5310dd 100644 --- a/crates/wit-generator/src/instructions_generator/utils.rs +++ b/crates/wit-generator/src/instructions_generator/utils.rs @@ -15,25 +15,30 @@ */ use super::IType; +use crate::instructions_generator::WITResolver; +use crate::Result; + use fluence_sdk_wit::ParsedType; use fluence_sdk_wit::WasmType; -pub(crate) fn ptype_to_itype(pty: &ParsedType) -> IType { +pub(crate) fn ptype_to_itype(pty: &ParsedType, wit_resolver: &WITResolver) -> Result { match pty { - ParsedType::I8 => IType::S8, - ParsedType::I16 => IType::S16, - ParsedType::I32 => IType::S32, - ParsedType::I64 => IType::S64, - ParsedType::U8 => IType::U8, - ParsedType::U16 => IType::U16, - ParsedType::U32 => IType::U32, - ParsedType::U64 => IType::U64, - ParsedType::F32 => IType::F32, - ParsedType::F64 => IType::F64, - ParsedType::Boolean => IType::I32, - ParsedType::Utf8String => IType::String, - ParsedType::ByteVector => IType::ByteArray, - ParsedType::Record(_) => unimplemented!(), + ParsedType::I8 => Ok(IType::S8), + ParsedType::I16 => Ok(IType::S16), + ParsedType::I32 => Ok(IType::S32), + ParsedType::I64 => Ok(IType::S64), + ParsedType::U8 => Ok(IType::U8), + ParsedType::U16 => Ok(IType::U16), + ParsedType::U32 => Ok(IType::U32), + ParsedType::U64 => Ok(IType::U64), + ParsedType::F32 => Ok(IType::F32), + ParsedType::F64 => Ok(IType::F64), + ParsedType::Boolean => Ok(IType::I32), + ParsedType::Utf8String => Ok(IType::String), + ParsedType::ByteVector => Ok(IType::ByteArray), + ParsedType::Record(record_name) => { + Ok(IType::Record(wit_resolver.get_record_type(record_name)?)) + } } } diff --git a/crates/wit-generator/src/interface_generator.rs b/crates/wit-generator/src/interface_generator.rs index 34ec66a7..d9a44bfd 100644 --- a/crates/wit-generator/src/interface_generator.rs +++ b/crates/wit-generator/src/interface_generator.rs @@ -28,20 +28,30 @@ pub fn embed_wit(path: std::path::PathBuf) -> Result<()> { .parse_file(path.clone()) .map_err(|e| WITGeneratorError::IOError(format!("{:?} can't be parsed: {:?}", path, e)))?; - let ast_set = wasm_ast_extractor(&wasm_module)?; - let interfaces = generate_interfaces(&ast_set); + let module_ast = wasm_ast_extractor(&wasm_module)?; + let interfaces = generate_interfaces(&module_ast)?; let wasm_module = wit_parser::delete_wit_section(wasm_module); let mut wasm_module = wit_parser::embed_wit(wasm_module, &interfaces); wasm_module.emit_wasm_file(path).map_err(|e| { - WITGeneratorError::IOError(format!("resulted Wasm fule can't be emitted: {:?}", e)) + WITGeneratorError::IOError(format!("resulted Wasm file can't be emitted: {:?}", e)) }) } +pub(crate) struct ModuleAST { + pub(crate) records: Vec, + pub(crate) functions: Vec, + pub(crate) extern_mods: Vec, +} + /// Extract all custom AST types previously embedded by rust-sdk from compiled binary. -fn wasm_ast_extractor(wasm_module: &walrus::Module) -> Result> { - let mut extracted_ast = Vec::new(); +fn wasm_ast_extractor(wasm_module: &walrus::Module) -> Result { + use fluence_sdk_wit::*; + + let mut records: Vec = Vec::new(); + let mut functions: Vec = Vec::new(); + let mut extern_mods: Vec = Vec::new(); // consider only sections name of that starts with GENERATED_SECTION_PREFIX for custom_module in wasm_module.customs.iter().filter(|(_, section)| { @@ -51,22 +61,36 @@ fn wasm_ast_extractor(wasm_module: &walrus::Module) -> Result records.push(record), + FCEAst::Function(function) => functions.push(function), + FCEAst::ExternMod(extern_mod) => extern_mods.push(extern_mod), + } } - Ok(extracted_ast) + Ok(ModuleAST { + records, + functions, + extern_mods, + }) } -fn generate_interfaces(ast_set: &[FCEAst]) -> Interfaces<'_> { - let mut interfaces = Interfaces::default(); - generate_default_export_api(&mut interfaces); +fn generate_interfaces(module_ast: &ModuleAST) -> Result> { + let mut wit_resolver = crate::instructions_generator::WITResolver::default(); + generate_default_export_api(&mut wit_resolver.interfaces); - for ast in ast_set { - ast.generate_wit(&mut interfaces); + for record in &module_ast.records { + record.generate_wit(&mut wit_resolver)?; + } + for function in &module_ast.functions { + function.generate_wit(&mut wit_resolver)?; + } + for extern_mod in &module_ast.extern_mods { + extern_mod.generate_wit(&mut wit_resolver)?; } - interfaces + Ok(wit_resolver.interfaces) } fn generate_default_export_api(interfaces: &mut Interfaces) { diff --git a/crates/wit-parser/Cargo.toml b/crates/wit-parser/Cargo.toml index 325d8552..c6e821db 100644 --- a/crates/wit-parser/Cargo.toml +++ b/crates/wit-parser/Cargo.toml @@ -11,7 +11,7 @@ path = "src/lib.rs" [dependencies] walrus = "0.17.0" wasmer-core = { package = "wasmer-runtime-core", git = "https://github.com/fluencelabs/wasmer", branch = "fluence" } -wasmer-wit = { package = "wasmer-interface-types", git = "https://github.com/fluencelabs/interface-types", branch = "byte_array" } +wasmer-wit = { package = "wasmer-interface-types", git = "https://github.com/fluencelabs/interface-types", branch = "struct_support" } fce-wit-interfaces = { path = "../fce-wit-interfaces" } anyhow = "1.0.31" diff --git a/engine/Cargo.toml b/engine/Cargo.toml index e2fe7e7b..4f644eaa 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -11,7 +11,7 @@ wit-parser = { path = "../crates/wit-parser", version = "0.1.0" } wasmer-runtime = { git = "https://github.com/fluencelabs/wasmer", branch = "fluence" } # dynamicfunc-fat-closures allows using state inside DynamicFunc wasmer-core = { package = "wasmer-runtime-core", git = "http://github.com/fluencelabs/wasmer", branch = "fluence", features = ["dynamicfunc-fat-closures"] } -wasmer-wit = { package = "wasmer-interface-types", git = "http://github.com/fluencelabs/interface-types", branch = "byte_array", features = ["serde"] } +wasmer-wit = { package = "wasmer-interface-types", git = "http://github.com/fluencelabs/interface-types", branch = "struct_support", features = ["serde"] } wasmer-wasi = { git = "https://github.com/fluencelabs/wasmer", branch = "fluence" } serde = { version = "1.0.114", default-features = false, features = [ "derive" ] } diff --git a/examples/ipfs_node/wasm/ipfs_rpc/Cargo.toml b/examples/ipfs_node/wasm/ipfs_rpc/Cargo.toml index 526dd35a..c05a34d5 100644 --- a/examples/ipfs_node/wasm/ipfs_rpc/Cargo.toml +++ b/examples/ipfs_node/wasm/ipfs_rpc/Cargo.toml @@ -9,5 +9,5 @@ name = "ipfs_rpc" path = "src/main.rs" [dependencies] -fluence = { git = "https://github.com/fluencelabs/rust-sdk", features = ["logger"] } +fluence = { path = "/Users/mike/dev/work/fluence/wasm/rust-sdk", features = ["logger"] } log = "0.4.11" diff --git a/examples/ipfs_node/wasm/ipfs_rpc/expand.rs b/examples/ipfs_node/wasm/ipfs_rpc/expand.rs new file mode 100644 index 00000000..3ae35753 --- /dev/null +++ b/examples/ipfs_node/wasm/ipfs_rpc/expand.rs @@ -0,0 +1,205 @@ +#![feature(prelude_import)] +#[prelude_import] +use std::prelude::v1::*; +#[macro_use] +extern crate std; +use fluence::fce; +use fluence::WasmLogger; +use std::fs; +use std::path::PathBuf; +const RPC_TMP_FILEPATH: &str = "/tmp/ipfs_rpc_file"; +pub fn main() { + WasmLogger::init_with_level(log::Level::Info).unwrap(); +} +pub struct Asadasd { + pub a: i32, +} +#[cfg(target_arch = "wasm32")] +#[doc(hidden)] +#[allow(clippy::all)] +pub(crate) fn __fce_generated_record_serializer_Asadasd(record: Asadasd) -> i32 { + let mut raw_record = Vec::new(); + raw_record.push(record.a as u64); + let raw_record_ptr = raw_record.as_ptr(); + std::mem::forget(raw_record); + raw_record_ptr as _ +} +#[cfg(target_arch = "wasm32")] +#[doc(hidden)] +#[allow(clippy::all)] +unsafe fn __fce_generated_record_deserializer_Asadasd(offset: i32, size: i32) -> Asadasd { + let raw_record: Vec = Vec::from_raw_parts(offset as _, size as _, size as _); + let field_0 = raw_record[0usize] as i32; + Asadasd { a: field_0 } +} +#[cfg(target_arch = "wasm32")] +#[doc(hidden)] +#[allow(clippy::all)] +#[link_section = "__fce_generated_section__Asadasd"] +pub static __fce_generated_static_global_Asadasd: [u8; 73usize] = { + *b"{\"ast_type\":\"Record\",\"name\":\"Asadasd\",\"fields\":[{\"name\":\"a\",\"ty\":\"I32\"}]}" +}; +pub fn invoke(_a: Asadasd) -> String { + "IPFS_RPC wasm example, it allows to:\ninvoke\nput\nget".to_string() +} +#[export_name = "invoke"] +#[no_mangle] +#[doc(hidden)] +#[allow(clippy::all)] +pub unsafe fn __fce_generated_wrapper_func_invoke(arg_0: i32, arg_1: i32) { + let converted_arg_0 = __fce_generated_record_deserializer_Asadasd(arg_0, arg_1); + let result = invoke(converted_arg_0); + fluence::internal::set_result_ptr(result.as_ptr() as _); + fluence::internal::set_result_size(result.len() as _); + std::mem::forget(result); +} +#[cfg(target_arch = "wasm32")] +#[doc(hidden)] +#[allow(clippy::all)] +#[link_section = "__fce_generated_section__invoke"] +pub static __fce_generated_static_global_invoke: [u8; 117usize] = { + * b"{\"ast_type\":\"Function\",\"signature\":{\"name\":\"invoke\",\"input_types\":[{\"Record\":\"Asadasd\"}],\"output_type\":\"Utf8String\"}}" +}; +pub fn put(file_content: Vec) -> String { + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api_log( + ::core::fmt::Arguments::new_v1( + &["put called with "], + &match (&file_content,) { + (arg0,) => [::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Debug::fmt)], + }, + ), + lvl, + &( + "ipfs_rpc", + "ipfs_rpc", + "examples/ipfs_node/wasm/ipfs_rpc/src/main.rs", + 41u32, + ), + ); + } + }; + let rpc_tmp_filepath = RPC_TMP_FILEPATH.to_string(); + let r = fs::write(PathBuf::from(rpc_tmp_filepath.clone()), file_content); + if let Err(e) = r { + return { + let res = ::alloc::fmt::format(::core::fmt::Arguments::new_v1( + &["file can\'t be written: "], + &match (&e,) { + (arg0,) => [::core::fmt::ArgumentV1::new( + arg0, + ::core::fmt::Display::fmt, + )], + }, + )); + res + }; + } + ipfs_put(rpc_tmp_filepath) +} +#[export_name = "put"] +#[no_mangle] +#[doc(hidden)] +#[allow(clippy::all)] +pub unsafe fn __fce_generated_wrapper_func_put(arg_0: i32, arg_1: i32) { + let converted_arg_0 = Vec::from_raw_parts(arg_0 as _, arg_1 as _, arg_1 as _); + let result = put(converted_arg_0); + fluence::internal::set_result_ptr(result.as_ptr() as _); + fluence::internal::set_result_size(result.len() as _); + std::mem::forget(result); +} +#[cfg(target_arch = "wasm32")] +#[doc(hidden)] +#[allow(clippy::all)] +#[link_section = "__fce_generated_section__put"] +pub static __fce_generated_static_global_put: [u8; 106usize] = { + * b"{\"ast_type\":\"Function\",\"signature\":{\"name\":\"put\",\"input_types\":[\"ByteVector\"],\"output_type\":\"Utf8String\"}}" +}; +pub fn get(hash: String) -> Vec { + { + let lvl = ::log::Level::Info; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api_log( + ::core::fmt::Arguments::new_v1( + &["get called with hash: "], + &match (&hash,) { + (arg0,) => [::core::fmt::ArgumentV1::new( + arg0, + ::core::fmt::Display::fmt, + )], + }, + ), + lvl, + &( + "ipfs_rpc", + "ipfs_rpc", + "examples/ipfs_node/wasm/ipfs_rpc/src/main.rs", + 55u32, + ), + ); + } + }; + let file_path = ipfs_get(hash); + fs::read(file_path).unwrap_or_else(|_| b"error while reading file".to_vec()) +} +#[export_name = "get"] +#[no_mangle] +#[doc(hidden)] +#[allow(clippy::all)] +pub unsafe fn __fce_generated_wrapper_func_get(arg_0: i32, arg_1: i32) { + let converted_arg_0 = String::from_raw_parts(arg_0 as _, arg_1 as _, arg_1 as _); + let result = get(converted_arg_0); + fluence::internal::set_result_ptr(result.as_ptr() as _); + fluence::internal::set_result_size(result.len() as _); + std::mem::forget(result); +} +#[cfg(target_arch = "wasm32")] +#[doc(hidden)] +#[allow(clippy::all)] +#[link_section = "__fce_generated_section__get"] +pub static __fce_generated_static_global_get: [u8; 106usize] = { + * b"{\"ast_type\":\"Function\",\"signature\":{\"name\":\"get\",\"input_types\":[\"Utf8String\"],\"output_type\":\"ByteVector\"}}" +}; +#[link(wasm_import_module = "ipfs_node.wasm")] +#[cfg(target_arch = "wasm32")] +extern "C" { + #[link_name = "put"] + fn __fce_generated_wrapper_func__ipfs_put(arg_0: i32, arg_1: i32); + #[link_name = "get"] + fn __fce_generated_wrapper_func__ipfs_get(arg_0: i32, arg_1: i32); +} +#[cfg(target_arch = "wasm32")] +#[doc(hidden)] +#[allow(clippy::all)] +pub fn ipfs_put(arg_0: String) -> String { + unsafe { + let result = __fce_generated_wrapper_func__ipfs_put(arg_0.as_ptr() as _, arg_0.len() as _); + String::from_raw_parts( + fluence::internal::get_result_ptr() as _, + fluence::internal::get_result_size() as _, + fluence::internal::get_result_size() as _, + ) + } +} +#[cfg(target_arch = "wasm32")] +#[doc(hidden)] +#[allow(clippy::all)] +pub fn ipfs_get(arg_0: String) -> String { + unsafe { + let result = __fce_generated_wrapper_func__ipfs_get(arg_0.as_ptr() as _, arg_0.len() as _); + String::from_raw_parts( + fluence::internal::get_result_ptr() as _, + fluence::internal::get_result_size() as _, + fluence::internal::get_result_size() as _, + ) + } +} +#[cfg(target_arch = "wasm32")] +#[doc(hidden)] +#[allow(clippy::all)] +#[link_section = "__fce_generated_section__ipfs_node_wasm"] +pub static __fce_generated_static_global_ipfs_node_wasm: [u8; 281usize] = { + * b"{\"ast_type\":\"ExternMod\",\"namespace\":\"ipfs_node.wasm\",\"imports\":[{\"link_name\":\"put\",\"signature\":{\"name\":\"ipfs_put\",\"input_types\":[\"Utf8String\"],\"output_type\":\"Utf8String\"}},{\"link_name\":\"get\",\"signature\":{\"name\":\"ipfs_get\",\"input_types\":[\"Utf8String\"],\"output_type\":\"Utf8String\"}}]}" +}; diff --git a/examples/ipfs_node/wasm/ipfs_rpc/src/main.rs b/examples/ipfs_node/wasm/ipfs_rpc/src/main.rs index 395db17a..bc5aa8e2 100644 --- a/examples/ipfs_node/wasm/ipfs_rpc/src/main.rs +++ b/examples/ipfs_node/wasm/ipfs_rpc/src/main.rs @@ -27,7 +27,12 @@ pub fn main() { } #[fce] -pub fn invoke() -> String { +pub struct Asadasd { + pub a: i32, +} + +#[fce] +pub fn invoke(_a: Asadasd) -> String { "IPFS_RPC wasm example, it allows to:\ninvoke\nput\nget".to_string() }