support records

This commit is contained in:
vms 2020-07-26 13:15:09 +03:00
parent 79a5896015
commit e589d52c72
10 changed files with 254 additions and 21 deletions

View File

@ -34,8 +34,8 @@ pub struct AstFunctionSignature {
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AstRecordField {
// fields of tuple structs haven't got name
pub field_name: Option<String>,
pub field_type: ParsedType,
pub name: Option<String>,
pub ty: ParsedType,
}
#[derive(Clone, Serialize, Deserialize)]

View File

@ -40,7 +40,7 @@ mod wasm_type;
pub use fce_ast_types::*;
pub use fce_macro_impl::fce;
pub use parsed_type::ParsedType;
pub use token_stream_generator::GENERATED_FUNC_PREFIX;
pub use token_stream_generator::GENERATED_WRAPPER_FUNC_PREFIX;
pub use token_stream_generator::GENERATED_SECTION_PREFIX;
pub use token_stream_generator::GENERATED_GLOBAL_PREFIX;
pub use wasm_type::WasmType;

View File

@ -40,8 +40,8 @@ impl ParseMacroInput for syn::ItemStruct {
let field_name = field.ident.as_ref().map(|ident| ident.to_string());
let field_type = ParsedType::from_type(&field.ty)?;
Ok(AstRecordField {
field_name,
field_type,
name: field_name,
ty: field_type,
})
})
.collect::<Result<Vec<_>>>()?;

View File

@ -52,6 +52,7 @@ pub enum ParsedType {
impl ParsedType {
pub fn from_type(input_type: &syn::Type) -> syn::Result<Self> {
use quote::ToTokens;
// parses generic param T in Vec<T> to string representation
fn parse_vec_bracket(args: &syn::PathArguments) -> syn::Result<String> {
// checks that T is angle bracketed
@ -121,12 +122,7 @@ impl ParsedType {
// argument can be given in full path form: ::std::string::String
// that why the last one used
.last()
.ok_or_else(|| {
Error::new(
path.span(),
"The invocation handler should have a non-empty input argument type",
)
})?;
.ok_or_else(|| Error::new(path.span(), "Type should be specified"))?;
match type_segment.ident.to_string().as_str() {
"i8" => Ok(ParsedType::I8),
@ -151,10 +147,11 @@ impl ParsedType {
},
Err(e) => Err(e),
},
type_name => Err(Error::new(
_ if !type_segment.arguments.is_empty() => Err(Error::new(
type_segment.span(),
format!("{} is unsupported", type_name),
"type with lifetimes or generics aren't allowed".to_string(),
)),
_ => Ok(ParsedType::Record(path.into_token_stream().to_string())),
}
}

View File

@ -14,8 +14,9 @@
* limitations under the License.
*/
use crate::new_ident;
use super::ParsedType;
use crate::new_ident;
use crate::token_stream_generator::GENERATED_RECORD_SERIALIZER_PREFIX;
use quote::quote;
@ -96,6 +97,14 @@ fn generate_epilog(ty: &Option<ParsedType>) -> proc_macro2::TokenStream {
Some(ty) if !ty.is_integral_type() => quote! {
return result as _;
},
Some(ParsedType::Record(record_name)) => {
let serializer =
crate::new_ident!(GENERATED_RECORD_SERIALIZER_PREFIX.to_string() + record_name);
quote! {
let result_ptr = #serializer(result);
fluence::internal::set_result_ptr(result_ptr as _);
}
}
Some(ty) if ty.is_integral_type() => quote! {
fluence::internal::set_result_ptr(result.as_ptr() as _);
fluence::internal::set_result_size(result.len() as _);

View File

@ -16,6 +16,7 @@
use super::ParsedType;
use super::FnArgGlueCodeGenerator;
use crate::token_stream_generator::GENERATED_RECORD_DESERIALIZER_PREFIX;
use crate::new_ident;
use crate::wasm_type::WasmType;
@ -104,9 +105,14 @@ fn generate_type_prolog(
ParsedType::ByteVector => quote! {
let #generated_arg_id = Vec::from_raw_parts(#ptr as _, #size as _, #size as _);
},
ParsedType::Record(record_name) => quote! {
let #generated_arg_id = __fce_generated_converter_#record_name(#ptr, #size);
},
ParsedType::Record(record_name) => {
let deserializer = crate::new_ident!(
GENERATED_RECORD_DESERIALIZER_PREFIX.to_string() + record_name
);
quote! {
let #generated_arg_id = #deserializer(#ptr, #size);
}
}
_ => panic!(
"perhaps new type's been added to ParsedType, and this match became incomplete"
),

View File

@ -22,7 +22,9 @@ use crate::fce_ast_types::FCEAst;
use proc_macro2::TokenStream;
pub const GENERATED_FUNC_PREFIX: &str = "__fce_generated_func_";
pub const GENERATED_WRAPPER_FUNC_PREFIX: &str = "__fce_generated_wrapper_func_";
pub const GENERATED_RECORD_SERIALIZER_PREFIX: &str = "__fce_generated_record_serializer_";
pub const GENERATED_RECORD_DESERIALIZER_PREFIX: &str = "__fce_generated_record_deserializer_";
pub const GENERATED_SECTION_PREFIX: &str = "__fce_generated_section__";
pub const GENERATED_GLOBAL_PREFIX: &str = "__fce_generated_static_global_";

View File

@ -37,7 +37,7 @@ impl quote::ToTokens for fce_ast_types::AstFunctionItem {
);
let signature = &self.signature;
let func_name = new_ident!(GENERATED_FUNC_PREFIX.to_string() + &signature.name);
let func_name = new_ident!(GENERATED_WRAPPER_FUNC_PREFIX.to_string() + &signature.name);
let original_func_ident = new_ident!(signature.name);
let export_func_name = &signature.name;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
use super::GENERATED_FUNC_PREFIX;
use super::GENERATED_WRAPPER_FUNC_PREFIX;
use crate::fce_ast_types;
use crate::new_ident;
@ -89,7 +89,7 @@ fn generate_extern_section_items(extern_item: &fce_ast_types::AstExternModItem)
}
fn generate_import_name(import_name: &str) -> syn::Ident {
crate::new_ident!(format!("{}_{}", GENERATED_FUNC_PREFIX, import_name))
crate::new_ident!(format!("{}_{}", GENERATED_WRAPPER_FUNC_PREFIX, import_name))
}
fn generate_wrapper_functions(extern_item: &fce_ast_types::AstExternModItem) -> TokenStream {

View File

@ -14,7 +14,12 @@
* limitations under the License.
*/
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) {
@ -29,9 +34,16 @@ impl quote::ToTokens for fce_ast_types::AstRecordItem {
section_name
);
let serializer = generate_serializer(self);
let deserializer = generate_deserializer(self);
let glue_code = quote::quote! {
#original
#serializer
#deserializer
#[cfg(target_arch = "wasm32")]
#[doc(hidden)]
#[allow(clippy::all)]
@ -42,3 +54,210 @@ impl quote::ToTokens for fce_ast_types::AstRecordItem {
tokens.extend(glue_code);
}
}
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 {
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);
}
quote::quote! {
#[cfg(target_arch = "wasm32")]
#[doc(hidden)]
#[allow(clippy::all)]
pub(crate) fn #serializer_fn_name(record: #ty) -> i32 {
let mut raw_record = Vec::new();
#serializer
let raw_record_ptr = raw_record.as_ptr();
std::mem::forget(raw_record);
raw_record_ptr as _
}
}
}
fn generate_deserializer(record: &fce_ast_types::AstRecordItem) -> proc_macro2::TokenStream {
let ret_type = new_ident!(record.name);
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::<Vec<_>>();
quote::quote! {
#ret_type {
#(#field_names: #field_values),*
}
}
}
Some(_) => {
quote::quote! {
#ret_type (
#(#field_values),*
)
}
}
_ => quote::quote! {},
};
quote::quote! {
#[cfg(target_arch = "wasm32")]
#[doc(hidden)]
#[allow(clippy::all)]
unsafe fn #deserializer_fn_name(offset: i32, size: i32) -> #ret_type {
let raw_record: Vec<u64> = Vec::from_raw_parts(offset as _, size as _, size as _);
#deserializer
#type_constructor
}
}
}