diff --git a/crates/wit/src/token_stream_generator/record_generator.rs b/crates/wit/src/token_stream_generator/record_generator.rs index 6bc5d95..d846d1c 100644 --- a/crates/wit/src/token_stream_generator/record_generator.rs +++ b/crates/wit/src/token_stream_generator/record_generator.rs @@ -14,12 +14,17 @@ * limitations under the License. */ +mod record_serializer; +mod record_deserializer; + +use record_serializer::*; +use record_deserializer::*; + use super::GENERATED_RECORD_SERIALIZER_PREFIX; use super::GENERATED_RECORD_DESERIALIZER_PREFIX; use crate::new_ident; use crate::fce_ast_types; -use crate::ParsedType; impl quote::ToTokens for fce_ast_types::AstRecordItem { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { @@ -34,15 +39,21 @@ impl quote::ToTokens for fce_ast_types::AstRecordItem { section_name ); - let serializer = generate_serializer(self); - let deserializer = generate_deserializer(self); + let serializer_fn = generate_serializer_fn(self); + let deserializer_fn = generate_deserializer_fn(self); let glue_code = quote::quote! { #original - #serializer + #[cfg(target_arch = "wasm32")] + #[doc(hidden)] + #[allow(clippy::all)] + #serializer_fn - #deserializer + #[cfg(target_arch = "wasm32")] + #[doc(hidden)] + #[allow(clippy::all)] + #deserializer_fn #[cfg(target_arch = "wasm32")] #[doc(hidden)] @@ -55,60 +66,17 @@ impl quote::ToTokens for fce_ast_types::AstRecordItem { } } -fn field_ident(field: &fce_ast_types::AstRecordField, id: usize) -> proc_macro2::TokenStream { - match &field.name { - Some(name) => { - let name = new_ident!(name); - quote::quote! { record.#name } - } - None => { - let id = new_ident!(format!("{}", id)); - quote::quote! { record.#id } - } - } -} - -fn generate_serializer(record: &fce_ast_types::AstRecordItem) -> proc_macro2::TokenStream { +fn generate_serializer_fn(record: &fce_ast_types::AstRecordItem) -> proc_macro2::TokenStream { let serializer_fn_name = new_ident!(GENERATED_RECORD_SERIALIZER_PREFIX.to_string() + &record.name); - let ty = new_ident!(record.name); - let mut serializer = proc_macro2::TokenStream::new(); - for (id, field) in record.fields.iter().enumerate() { - let field_ident = field_ident(field, id); - - let field_serialization = match &field.ty { - ParsedType::F64 => { - quote::quote! { - raw_record.push(#field_ident.to_bits()); - } - } - ParsedType::Utf8String | ParsedType::ByteVector => { - quote::quote! { - raw_record.push(#field_ident.as_ptr() as _); - raw_record.push(#field_ident.len() as _); - } - } - ParsedType::Record(record_name) => { - let serializer_name = - new_ident!(GENERATED_RECORD_SERIALIZER_PREFIX.to_string() + record_name); - quote::quote! { - raw_record.push(#serializer_name(#field_ident) as _); - } - } - _ => quote::quote! { - raw_record.push(#field_ident as u64); - }, - }; - - serializer.extend(field_serialization); - } + let RecordSerializerDescriptor { + serializer, + record_type, + } = record.generate_serializer(&record.name); quote::quote! { - #[cfg(target_arch = "wasm32")] - #[doc(hidden)] - #[allow(clippy::all)] - pub(crate) fn #serializer_fn_name(record: #ty) -> i32 { + pub(crate) fn #serializer_fn_name(record: #record_type) -> i32 { let mut raw_record = Vec::new(); #serializer @@ -121,138 +89,18 @@ fn generate_serializer(record: &fce_ast_types::AstRecordItem) -> proc_macro2::To } } -fn generate_deserializer(record: &fce_ast_types::AstRecordItem) -> proc_macro2::TokenStream { - let ret_type = new_ident!(record.name); +fn generate_deserializer_fn(record: &fce_ast_types::AstRecordItem) -> proc_macro2::TokenStream { let deserializer_fn_name = new_ident!(GENERATED_RECORD_DESERIALIZER_PREFIX.to_string() + &record.name); - let mut field_values = Vec::with_capacity(record.fields.len()); - let mut deserializer = proc_macro2::TokenStream::new(); - let mut value_id: usize = 0; - - for (id, ast_field) in record.fields.iter().enumerate() { - let field = new_ident!(format!("field_{}", id)); - let field_d = match &ast_field.ty { - ParsedType::Boolean => { - quote::quote! { - let #field = raw_record[#value_id] as bool; - } - } - ParsedType::I8 => { - quote::quote! { - let #field = raw_record[#value_id] as i8; - } - } - ParsedType::I16 => { - quote::quote! { - let #field = raw_record[#value_id] as i16; - } - } - ParsedType::I32 => { - quote::quote! { - let #field = raw_record[#value_id] as i32; - } - } - ParsedType::I64 => { - quote::quote! { - let #field = raw_record[#value_id] as i64; - } - } - ParsedType::U8 => { - quote::quote! { - let #field = raw_record[#value_id] as u8; - } - } - ParsedType::U16 => { - quote::quote! { - let #field = raw_record[#value_id] as u16; - } - } - ParsedType::U32 => { - quote::quote! { - let #field = raw_record[#value_id] as u32; - } - } - ParsedType::U64 => { - quote::quote! { - let #field = raw_record[#value_id] as u64; - } - } - ParsedType::F32 => { - quote::quote! { - let #field = raw_record[#value_id] as f32; - } - } - ParsedType::F64 => { - quote::quote! { - let #field = f64::from_bits(raw_record[#value_id as _]); - } - } - ParsedType::Utf8String => { - let ptr_id = value_id; - let size_id = value_id + 1; - value_id += 1; - - quote::quote! { - let #field = unsafe { String::from_raw_parts(raw_record[#ptr_id] as _, raw_record[#size_id] as _, raw_record[#size_id] as _) }; - } - } - ParsedType::ByteVector => { - let ptr_id = value_id; - let size_id = value_id + 1; - value_id += 1; - - quote::quote! { - let #field = unsafe { Vec::from_raw_parts(raw_record[#ptr_id] as _, raw_record[#size_id] as _, raw_record[#size_id] as _) }; - } - } - ParsedType::Record(record_name) => { - let ptr_id = value_id; - let size_id = value_id + 1; - value_id += 1; - let deserializer_name = - new_ident!(GENERATED_RECORD_DESERIALIZER_PREFIX.to_string() + record_name); - - quote::quote! { - let #field = #deserializer_name(raw_record[#ptr_id] as _, raw_record[#size_id] as _); - } - } - }; - - field_values.push(field); - deserializer.extend(field_d); - value_id += 1; - } - - let type_constructor = match record.fields.first() { - Some(ast_field) if ast_field.name.is_some() => { - let field_names = record - .fields - .iter() - .map(|field| new_ident!(field.name.clone().expect("all fields should have name"))) - .collect::>(); - - quote::quote! { - #ret_type { - #(#field_names: #field_values),* - } - } - } - Some(_) => { - quote::quote! { - #ret_type ( - #(#field_values),* - ) - } - } - _ => quote::quote! {}, - }; + let RecordDeserializerDescriptor { + deserializer, + type_constructor, + return_type, + } = record.generate_deserializer(&record.name); quote::quote! { - #[cfg(target_arch = "wasm32")] - #[doc(hidden)] - #[allow(clippy::all)] - unsafe fn #deserializer_fn_name(offset: i32, size: i32) -> #ret_type { + unsafe fn #deserializer_fn_name(offset: i32, size: i32) -> #return_type { let raw_record: Vec = Vec::from_raw_parts(offset as _, size as _, size as _); #deserializer diff --git a/crates/wit/src/token_stream_generator/record_generator/record_deserializer.rs b/crates/wit/src/token_stream_generator/record_generator/record_deserializer.rs new file mode 100644 index 0000000..be38c3b --- /dev/null +++ b/crates/wit/src/token_stream_generator/record_generator/record_deserializer.rs @@ -0,0 +1,169 @@ +/* + * 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::new_ident; +use crate::parsed_type::ParsedType; +use crate::fce_ast_types; +use crate::token_stream_generator::GENERATED_RECORD_DESERIALIZER_PREFIX; + +use quote::quote; + +pub(super) struct RecordDeserializerDescriptor { + pub(super) deserializer: proc_macro2::TokenStream, + pub(super) type_constructor: proc_macro2::TokenStream, + pub(super) return_type: syn::Ident, +} + +/// This trait could be used to generate various parts of a record serializer func. +pub(super) trait RecordDeserializerGlueCodeGenerator { + fn generate_deserializer(&self, record_name: &str) -> RecordDeserializerDescriptor; +} + +impl RecordDeserializerGlueCodeGenerator for fce_ast_types::AstRecordItem { + fn generate_deserializer(&self, record_name: &str) -> RecordDeserializerDescriptor { + let return_type = new_ident!(record_name); + + let mut field_values = Vec::with_capacity(self.fields.len()); + let mut deserializer = proc_macro2::TokenStream::new(); + let mut value_id: usize = 0; + + for (id, ast_field) in self.fields.iter().enumerate() { + let field = new_ident!(format!("field_{}", id)); + let field_d = match &ast_field.ty { + ParsedType::Boolean => { + quote! { + let #field = raw_record[#value_id] as bool; + } + } + ParsedType::I8 => { + quote! { + let #field = raw_record[#value_id] as i8; + } + } + ParsedType::I16 => { + quote! { + let #field = raw_record[#value_id] as i16; + } + } + ParsedType::I32 => { + quote! { + let #field = raw_record[#value_id] as i32; + } + } + ParsedType::I64 => { + quote! { + let #field = raw_record[#value_id] as i64; + } + } + ParsedType::U8 => { + quote! { + let #field = raw_record[#value_id] as u8; + } + } + ParsedType::U16 => { + quote! { + let #field = raw_record[#value_id] as u16; + } + } + ParsedType::U32 => { + quote! { + let #field = raw_record[#value_id] as u32; + } + } + ParsedType::U64 => { + quote! { + let #field = raw_record[#value_id] as u64; + } + } + ParsedType::F32 => { + quote! { + let #field = raw_record[#value_id] as f32; + } + } + ParsedType::F64 => { + quote! { + let #field = f64::from_bits(raw_record[#value_id as _]); + } + } + ParsedType::Utf8String => { + let ptr_id = value_id; + let size_id = value_id + 1; + value_id += 1; + + quote! { + let #field = unsafe { String::from_raw_parts(raw_record[#ptr_id] as _, raw_record[#size_id] as _, raw_record[#size_id] as _) }; + } + } + ParsedType::ByteVector => { + let ptr_id = value_id; + let size_id = value_id + 1; + value_id += 1; + + quote! { + let #field = unsafe { Vec::from_raw_parts(raw_record[#ptr_id] as _, raw_record[#size_id] as _, raw_record[#size_id] as _) }; + } + } + ParsedType::Record(record_name) => { + let ptr_id = value_id; + let size_id = value_id + 1; + value_id += 1; + let deserializer_name = + new_ident!(GENERATED_RECORD_DESERIALIZER_PREFIX.to_string() + record_name); + + quote! { + let #field = #deserializer_name(raw_record[#ptr_id] as _, raw_record[#size_id] as _); + } + } + }; + + field_values.push(field); + deserializer.extend(field_d); + value_id += 1; + } + + let type_constructor = match self.fields.first() { + Some(ast_field) if ast_field.name.is_some() => { + let field_names = self + .fields + .iter() + .map(|field| { + new_ident!(field.name.clone().expect("all fields should have name")) + }) + .collect::>(); + + quote! { + #return_type { + #(#field_names: #field_values),* + } + } + } + Some(_) => { + quote! { + #return_type ( + #(#field_values),* + ) + } + } + _ => quote! {}, + }; + + RecordDeserializerDescriptor { + deserializer, + type_constructor, + return_type, + } + } +} diff --git a/crates/wit/src/token_stream_generator/record_generator/record_serializer.rs b/crates/wit/src/token_stream_generator/record_generator/record_serializer.rs new file mode 100644 index 0000000..88dcc3a --- /dev/null +++ b/crates/wit/src/token_stream_generator/record_generator/record_serializer.rs @@ -0,0 +1,86 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::new_ident; +use crate::parsed_type::ParsedType; +use crate::fce_ast_types; +use crate::token_stream_generator::GENERATED_RECORD_SERIALIZER_PREFIX; + +use quote::quote; + +pub(super) struct RecordSerializerDescriptor { + pub(super) serializer: proc_macro2::TokenStream, + pub(super) record_type: syn::Ident, +} + +/// This trait could be used to generate various parts of a record serializer func. +pub(super) trait RecordSerializerGlueCodeGenerator { + fn generate_serializer(&self, record_name: &str) -> RecordSerializerDescriptor; +} + +impl RecordSerializerGlueCodeGenerator for fce_ast_types::AstRecordItem { + fn generate_serializer(&self, record_name: &str) -> RecordSerializerDescriptor { + let mut serializer = proc_macro2::TokenStream::new(); + for (id, field) in self.fields.iter().enumerate() { + let field_ident = field_ident(field, id); + + let field_serialization = match &field.ty { + ParsedType::F64 => { + quote! { + raw_record.push(#field_ident.to_bits()); + } + } + ParsedType::Utf8String | ParsedType::ByteVector => { + quote! { + raw_record.push(#field_ident.as_ptr() as _); + raw_record.push(#field_ident.len() as _); + } + } + ParsedType::Record(record_name) => { + let serializer_name = + new_ident!(GENERATED_RECORD_SERIALIZER_PREFIX.to_string() + &record_name); + quote! { + raw_record.push(#serializer_name(#field_ident) as _); + } + } + _ => quote! { + raw_record.push(#field_ident as u64); + }, + }; + + serializer.extend(field_serialization); + } + let record_type = new_ident!(record_name); + + RecordSerializerDescriptor { + serializer, + record_type, + } + } +} + +fn field_ident(field: &fce_ast_types::AstRecordField, id: usize) -> proc_macro2::TokenStream { + match &field.name { + Some(name) => { + let name = new_ident!(name); + quote! { record.#name } + } + None => { + let id = new_ident!(format!("{}", id)); + quote! { record.#id } + } + } +}