update function generation

This commit is contained in:
vms 2020-07-02 22:46:54 +03:00
parent 9839e54383
commit 2c68bb7a2c
9 changed files with 263 additions and 72 deletions

View File

@ -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" }

View File

@ -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<ParsedType>,
@ -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<ParsedType>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) struct AstExternModItem {
pub(crate) namespace: String,
// only imports are possible here
pub(crate) imports: Vec<AstFunctionItem>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) enum FCEAst {
Function(AstFunctionItem),
Record(AstRecordItem),

View File

@ -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;

View File

@ -26,52 +26,6 @@ pub(super) fn fce_impl(tokens: TokenStream) -> Result<TokenStream> {
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<u8> {
#prolog
#epilog
}
};
Ok(resulted_invoke)
*/
*/
}

View File

@ -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<FCEAst> {
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::<Result<Vec<_>>>()?;
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(())
}

View File

@ -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<WasmType>;
}
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<WasmType> {
// 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!(),
}
}
}

View File

@ -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<TokenStream>;
}

View File

@ -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<TokenStream> {
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)
}
}

View File

@ -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());
}
}