From 2c68bb7a2c7d20dcdd3b1e669e3861f9dd2313d7 Mon Sep 17 00:00:00 2001 From: vms Date: Thu, 2 Jul 2020 22:46:54 +0300 Subject: [PATCH] update function generation --- crates/macro/Cargo.toml | 2 + crates/macro/src/fce_ast_types.rs | 7 + crates/macro/src/lib.rs | 1 + crates/macro/src/macro_impl.rs | 48 +----- .../src/parse_macro_input/item_record.rs | 66 ++++++- crates/macro/src/parsed_type.rs | 162 +++++++++++++++--- crates/macro/src/token_stream_generator.rs | 2 + .../token_stream_generator/fn_generator.rs | 32 +++- crates/macro/src/wasm_type.rs | 15 ++ 9 files changed, 263 insertions(+), 72 deletions(-) create mode 100644 crates/macro/src/wasm_type.rs diff --git a/crates/macro/Cargo.toml b/crates/macro/Cargo.toml index b729c96..1ab94c2 100644 --- a/crates/macro/Cargo.toml +++ b/crates/macro/Cargo.toml @@ -21,5 +21,7 @@ proc-macro = true syn = { version = '1.0.33', features = ['full'] } quote = "1.0.7" proc-macro2 = "1.0.18" +serde = { version = "1.0.110", features = ["derive"] } +serde_json = "1.0.56" fluence-sdk-main = { path = "../main", version = "=0.1.11" } diff --git a/crates/macro/src/fce_ast_types.rs b/crates/macro/src/fce_ast_types.rs index 76945c9..1da1b4d 100644 --- a/crates/macro/src/fce_ast_types.rs +++ b/crates/macro/src/fce_ast_types.rs @@ -16,6 +16,10 @@ use crate::parsed_type::ParsedType; +use serde::Serialize; +use serde::Deserialize; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub(crate) struct AstFunctionItem { pub(crate) name: String, pub(crate) input_types: Vec, @@ -24,16 +28,19 @@ pub(crate) struct AstFunctionItem { pub(crate) output_type: ParsedType, } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub(crate) struct AstRecordItem { pub(crate) fields: Vec, } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub(crate) struct AstExternModItem { pub(crate) namespace: String, // only imports are possible here pub(crate) imports: Vec, } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub(crate) enum FCEAst { Function(AstFunctionItem), Record(AstRecordItem), diff --git a/crates/macro/src/lib.rs b/crates/macro/src/lib.rs index c4fae0a..d7b86e3 100644 --- a/crates/macro/src/lib.rs +++ b/crates/macro/src/lib.rs @@ -31,6 +31,7 @@ mod parsed_type; mod macro_impl; mod parse_macro_input; mod token_stream_generator; +mod wasm_type; use macro_impl::fce_impl; use proc_macro::TokenStream; diff --git a/crates/macro/src/macro_impl.rs b/crates/macro/src/macro_impl.rs index 50aea6b..e15a9c0 100644 --- a/crates/macro/src/macro_impl.rs +++ b/crates/macro/src/macro_impl.rs @@ -26,52 +26,6 @@ pub(super) fn fce_impl(tokens: TokenStream) -> Result { fce_ast_item.generate_token_stream() /* - let input_type = match sig.inputs.len() { - 0 => ParsedType::Empty, - 1 => ParsedType::from_fn_arg(sig.inputs.first().unwrap())?, - _ => { - return Err(Error::new( - sig.inputs.span(), - "The invocation handler shouldn't have more than one argument", - )) - } - }; - let output_type = ParsedType::from_return_type(&sig.output)?; - if output_type == ParsedType::Empty { - return Err(Error::new( - sig.output.span(), - "The invocation handler should have the return value", - )); - } - - let ident = &sig.ident; - let prolog = input_type.generate_fn_prolog(); - let prolog = match input_type { - ParsedType::Empty => quote! { - #prolog - - let result = #ident(); - }, - _ => quote! { - #prolog - - let result = #ident(arg); - }, - }; - let epilog = output_type.generate_fn_epilog(); - - let resulted_invoke = quote! { - #fn_item - - #[no_mangle] - pub unsafe fn invoke(ptr: *mut u8, len: usize) -> std::ptr::NonNull { - #prolog - - #epilog - } - }; - - Ok(resulted_invoke) - */ + */ } diff --git a/crates/macro/src/parse_macro_input/item_record.rs b/crates/macro/src/parse_macro_input/item_record.rs index a4ddbc6..650bd5a 100644 --- a/crates/macro/src/parse_macro_input/item_record.rs +++ b/crates/macro/src/parse_macro_input/item_record.rs @@ -14,12 +14,76 @@ * limitations under the License. */ use super::ParseMacroInput; +use crate::fce_ast_types; use crate::fce_ast_types::FCEAst; +use syn::Error; use syn::Result; +use syn::spanned::Spanned; +use crate::parsed_type::ParsedType; impl ParseMacroInput for syn::ItemStruct { fn parse_macro_input(self) -> Result { - unimplemented!() + check_record(&self)?; + + let fields = match self.fields { + syn::Fields::Named(named_field) => named_field, + _ => return Err(Error::new(self.span(), "only named field allowed")), + }; + + let fields = fields + .named + .iter() + .map(|field| { + check_field(field)?; + ParsedType::from_type(&field.ty) + }) + .collect::>>()?; + + let ast_record_item = fce_ast_types::AstRecordItem { fields }; + + Ok(FCEAst::Record(ast_record_item)) } } + +fn check_record(record: &syn::ItemStruct) -> Result<()> { + match record.vis { + syn::Visibility::Public(_) => {} + _ => { + return Err(Error::new( + record.span(), + "#[fce] could be applied only to public struct", + )) + } + }; + + if record.generics.lt_token.is_some() + || record.generics.gt_token.is_some() + || record.generics.where_clause.is_some() + { + return Err(Error::new( + record.span(), + "#[fce] couldn't be applied to a struct with generics", + )); + } + + Ok(()) +} + +fn check_field(field: &syn::Field) -> Result<()> { + match field.vis { + syn::Visibility::Public(_) => {} + _ => { + return Err(Error::new( + field.span(), + "#[fce] could be applied only to struct with all public fields", + )) + } + }; + + if !field.attrs.is_empty() { + return Err(Error::new(field.span(), "field attributes isn't allowed")); + } + + Ok(()) +} diff --git a/crates/macro/src/parsed_type.rs b/crates/macro/src/parsed_type.rs index 1175c34..e42cd73 100644 --- a/crates/macro/src/parsed_type.rs +++ b/crates/macro/src/parsed_type.rs @@ -14,11 +14,15 @@ * limitations under the License. */ +use crate::wasm_type::WasmType; + use quote::quote; +use serde::Serialize; +use serde::Deserialize; use syn::parse::Error; use syn::spanned::Spanned; -#[derive(PartialEq)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub(crate) enum ParsedType { Empty, I8, @@ -160,31 +164,106 @@ impl ParsedType { } } -pub trait PrologGenerator { - fn generate_fn_prolog(&self) -> proc_macro2::TokenStream; +pub(crate) trait ArgumentsGenerator { + fn generate_arguments(&self) -> Vec; } -pub trait EpilogGenerator { +pub(crate) trait PrologGenerator { + fn generate_fn_prolog( + &self, + generated_arg_id: usize, + supplied_arg_start_id: usize, + ) -> proc_macro2::TokenStream; +} + +pub(crate) trait EpilogGenerator { fn generate_fn_epilog(&self) -> proc_macro2::TokenStream; } -impl PrologGenerator for ParsedType { - fn generate_fn_prolog(&self) -> proc_macro2::TokenStream { +impl ArgumentsGenerator for ParsedType { + fn generate_arguments(&self) -> Vec { + // TODO: investigate possible issues in conversion between signed and unsigned types match self { + ParsedType::Empty => vec![], + ParsedType::I8 => vec![WasmType::I32], + ParsedType::I16 => vec![WasmType::I32], + ParsedType::I32 => vec![WasmType::I32], + ParsedType::I64 => vec![WasmType::I64], + ParsedType::U8 => vec![WasmType::I32], + ParsedType::U16 => vec![WasmType::I32], + ParsedType::U32 => vec![WasmType::I32], + ParsedType::U64 => vec![WasmType::I64], + ParsedType::F32 => vec![WasmType::F32], + ParsedType::F64 => vec![WasmType::F64], + ParsedType::Boolean => vec![WasmType::I32], + ParsedType::Utf8String => vec![WasmType::I32, WasmType::I32], + ParsedType::ByteVector => vec![WasmType::I32, WasmType::I32], + ParsedType::Record(_) => vec![WasmType::I32, WasmType::I32], + } + } +} + +impl PrologGenerator for ParsedType { + fn generate_fn_prolog( + &self, + generated_ard_id: usize, + supplied_arg_start_id: usize, + ) -> proc_macro2::TokenStream { + match self { + ParsedType::Empty => unimplemented!(), + ParsedType::I8 => quote! { + let converted_arg_#generated_ard_id = arg_#supplied_arg_start_id as i8; + }, + ParsedType::I16 => quote! { + let converted_arg_#generated_ard_id = arg_#supplied_arg_start_id as i16; + }, + ParsedType::I32 => quote! { + let converted_arg_#generated_ard_id = arg_#supplied_arg_start_id as i32; + }, + ParsedType::I64 => quote! { + let converted_arg_#generated_ard_id = arg_#supplied_arg_start_id as i64; + }, + ParsedType::U8 => quote! { + let converted_arg_#generated_ard_id = arg_#supplied_arg_start_id as u8; + }, + ParsedType::U16 => quote! { + let converted_arg_#generated_ard_id = arg_#supplied_arg_start_id as u16; + }, + ParsedType::U32 => quote! { + let converted_arg_#generated_ard_id = arg_#supplied_arg_start_id as u32; + }, + ParsedType::U64 => quote! { + let converted_arg_#generated_ard_id = arg_#supplied_arg_start_id as u64; + }, + ParsedType::F32 => quote! { + let converted_arg_#generated_ard_id = arg_#supplied_arg_start_id as f32; + }, + ParsedType::F64 => quote! { + let converted_arg_#generated_ard_id = arg_#supplied_arg_start_id as f64; + }, + ParsedType::Boolean => quote! { + let converted_arg_#generated_ard_id = arg_#supplied_arg_start_id as bool; + }, ParsedType::Utf8String => quote! { - let arg = memory::read_request_from_mem(ptr, len); - // TODO: it should be changed to more accurate check - let arg = String::from_utf8(arg).unwrap(); + let converted_arg_#generated_ard_id = String::from_raw_parts( + arg_#supplied_arg_start_id, + arg_#(supplied_arg_start_id+1), + arg_#(supplied_arg_start_id+1) + ); }, ParsedType::ByteVector => quote! { - let arg = memory::read_request_from_mem(ptr, len); + let converted_arg_#generated_ard_id = Vec::from_raw_parts( + arg_#supplied_arg_start_id, + arg_#(supplied_arg_start_id+1), + arg_#(supplied_arg_start_id+1) + ); }, - ParsedType::Empty => quote! { - // it is needed to delete memory occupied by the input argument - // this way does it without any additional imports of the export allocator module - let arg = memory::read_request_from_mem(ptr, len); + ParsedType::Record(record_name) => quote! { + let converted_arg_#generated_ard_id = __fce_generated_converter_#record_name( + arg_#supplied_arg_start_id, + arg_#(supplied_arg_start_id+1) + ); }, - _ => unimplemented!(), } } } @@ -192,18 +271,55 @@ impl PrologGenerator for ParsedType { impl EpilogGenerator for ParsedType { fn generate_fn_epilog(&self) -> proc_macro2::TokenStream { match self { + ParsedType::Empty => quote! {}, + ParsedType::I8 => quote! { + return result; + }, + ParsedType::I16 => quote! { + return result; + }, + ParsedType::I32 => quote! { + return result; + }, + ParsedType::I64 => quote! { + return result; + }, + ParsedType::U8 => quote! { + return result; + }, + ParsedType::U16 => quote! { + return result; + }, + ParsedType::U32 => quote! { + return result; + }, + ParsedType::U64 => quote! { + return result; + }, + ParsedType::F32 => quote! { + return result; + }, + ParsedType::F64 => quote! { + return result; + }, + ParsedType::Boolean => quote! { + return result; + }, ParsedType::Utf8String => quote! { - memory::write_response_to_mem( - result.as_bytes() - ) - .expect("Putting result string to memory has failed") + fluence::set_result_ptr(result.as_ptr() as _); + fluence::set_result_size(result.len() as _); + std::mem::forget(result); }, ParsedType::ByteVector => quote! { - memory::write_response_to_mem(&result[..]) - .expect("Putting result vector to memory has failed") + fluence::set_result_ptr(result.as_ptr() as _); + fluence::set_result_size(result.len() as _); + std::mem::forget(result); + }, + ParsedType::Record(_) => quote! { + fluence::set_result_ptr(result.as_ptr() as _); + fluence::set_result_size(result.len() as _); + std::mem::forget(result); }, - ParsedType::Empty => quote! {}, - _ => unimplemented!(), } } } diff --git a/crates/macro/src/token_stream_generator.rs b/crates/macro/src/token_stream_generator.rs index 45b5a74..31d15c3 100644 --- a/crates/macro/src/token_stream_generator.rs +++ b/crates/macro/src/token_stream_generator.rs @@ -22,6 +22,8 @@ use crate::fce_ast_types::FCEAst; use proc_macro2::TokenStream; +const GENERATED_FUNCS_PREFIX: &str = "__fce_generated_func_"; + pub(crate) trait TokenStreamGenerator { fn generate_token_stream(self) -> syn::Result; } diff --git a/crates/macro/src/token_stream_generator/fn_generator.rs b/crates/macro/src/token_stream_generator/fn_generator.rs index 6c067da..66efd32 100644 --- a/crates/macro/src/token_stream_generator/fn_generator.rs +++ b/crates/macro/src/token_stream_generator/fn_generator.rs @@ -15,12 +15,42 @@ */ use crate::fce_ast_types; +use crate::parsed_type::ArgumentsGenerator; +use crate::parsed_type::EpilogGenerator; +use crate::parsed_type::PrologGenerator; +use super::GENERATED_FUNCS_PREFIX; use super::TokenStreamGenerator; use proc_macro2::TokenStream; +use quote::quote; impl TokenStreamGenerator for fce_ast_types::AstFunctionItem { fn generate_token_stream(self) -> syn::Result { - unimplemented!() + let func_name = self.name; + let prefix = "__fce_generated_func_"; + + let embedded_tokens = quote! { + #[cfg_attr( + target_arch = "wasm32", + export_name = #func_name + )] + #[doc(hidden)] + #[allow(clippy::all)] + pub extern "C" fn #prefix_#func_name(#(#raw_args)*) #ret_type { + #prolog + + #ret_expression #func_name(#(#args)*); + + #epilog + } + + #[cfg(target_arch = "wasm32")] + #[allow(clippy::all)] + #[doc(hidden)] + #[link_section = #section_name] + pub static #generated_global_name: [u8; #size] = { #data }; + }; + + Ok(embedded_tokens) } } diff --git a/crates/macro/src/wasm_type.rs b/crates/macro/src/wasm_type.rs new file mode 100644 index 0000000..7c37fa0 --- /dev/null +++ b/crates/macro/src/wasm_type.rs @@ -0,0 +1,15 @@ +use proc_macro2::TokenStream; +use quote::TokenStreamExt; + +pub(crate) enum WasmType { + I32, + I64, + F32, + F64, +} + +impl quote::ToTokens for WasmType { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(self.clone()); + } +}