some improvements

This commit is contained in:
vms 2021-04-05 01:09:55 +03:00
parent 440c5992c1
commit 7b8d72e84c
12 changed files with 179 additions and 88 deletions

View File

@ -17,6 +17,7 @@
mod item_fn; mod item_fn;
mod item_foreign_mod; mod item_foreign_mod;
mod item_record; mod item_record;
mod utils;
use crate::fce_ast_types::FCEAst; use crate::fce_ast_types::FCEAst;

View File

@ -20,7 +20,6 @@ use crate::ParsedType;
use crate::fce_ast_types::FCEAst; use crate::fce_ast_types::FCEAst;
use crate::fce_ast_types::AstFunctionItem; use crate::fce_ast_types::AstFunctionItem;
use crate::fce_ast_types::AstFuncArgument; use crate::fce_ast_types::AstFuncArgument;
use crate::parsed_type::passing_style_of;
use crate::syn_error; use crate::syn_error;
use syn::Result; use syn::Result;
@ -148,23 +147,10 @@ fn check_parsed_functions<'a>(
/// Vec<Vec<&Vec<String>>> => true /// Vec<Vec<&Vec<String>>> => true
/// &Vec<String> => false /// &Vec<String> => false
fn contains_inner_ref(ty: &ParsedType) -> bool { fn contains_inner_ref(ty: &ParsedType) -> bool {
fn contains_inner_ref_impl(ty: &ParsedType) -> bool { use super::utils::contain_inner_ref;
use crate::parsed_type::PassingStyle;
match ty {
ParsedType::Vector(ty, passing_style) => match passing_style {
PassingStyle::ByValue => contains_inner_ref_impl(ty),
PassingStyle::ByRef | PassingStyle::ByMutRef => true,
},
_ => match passing_style_of(ty) {
PassingStyle::ByValue => false,
PassingStyle::ByRef | PassingStyle::ByMutRef => true,
},
}
}
match ty { match ty {
ParsedType::Vector(ty, _) => contains_inner_ref_impl(ty), ParsedType::Vector(ty, _) => contain_inner_ref(ty),
// Structs are checked while parsing // Structs are checked while parsing
_ => false, _ => false,
} }

View File

@ -28,65 +28,105 @@ const WASM_IMPORT_MODULE_DIRECTIVE_NAME: &str = "wasm_import_module";
impl ParseMacroInput for syn::ItemForeignMod { impl ParseMacroInput for syn::ItemForeignMod {
fn parse_macro_input(self) -> Result<FCEAst> { fn parse_macro_input(self) -> Result<FCEAst> {
match &self.abi.name { check_foreign_section(&self)?;
Some(name) if name.value() != "C" => {
return syn_error!(self.span(), "only 'C' abi is allowed") let wasm_import_module: Option<String> = parse_wasm_import_module(&self);
} let namespace = try_extract_namespace(wasm_import_module, &self)?;
_ => {}
let imports = extract_import_functions(&self)?;
check_imports(imports.iter().zip(self.items.iter().map(|i| i.span())))?;
let extern_mod_item = fce_ast_types::AstExternModItem {
namespace,
imports,
original: Some(self),
}; };
Ok(FCEAst::ExternMod(extern_mod_item))
}
}
let self_span = self.span(); fn check_foreign_section(foreign_mod: &syn::ItemForeignMod) -> Result<()> {
match &foreign_mod.abi.name {
Some(name) if name.value() != "C" => {
syn_error!(foreign_mod.span(), "only 'C' abi is allowed")
}
_ => Ok(()),
}
}
let imports = self /// Tries to find and parse wasm module name from
.items /// #[link(wasm_import_module = "host")]
.iter() fn parse_wasm_import_module(foreign_mod: &syn::ItemForeignMod) -> Option<String> {
.cloned() foreign_mod
.map(parse_raw_foreign_item) .attrs
.collect::<Result<_>>()?; .iter()
.filter_map(|attr| attr.parse_meta().ok())
.filter(|meta| meta.path().is_ident(LINK_DIRECTIVE_NAME))
.filter_map(|meta| {
let pair = match meta {
syn::Meta::List(mut meta_list) if meta_list.nested.len() == 1 => {
meta_list.nested.pop().unwrap()
}
_ => return None,
};
Some(pair.into_tuple().0)
})
.filter_map(|nested| match nested {
syn::NestedMeta::Meta(meta) => Some(meta),
_ => None,
})
.filter(|meta| meta.path().is_ident(WASM_IMPORT_MODULE_DIRECTIVE_NAME))
.map(extract_value)
.collect()
}
// try to find and parse wasm module name from fn try_extract_namespace(
// #[link(wasm_import_module = "host")] attr: Option<String>,
let wasm_import_module: Option<String> = self foreign_mod: &syn::ItemForeignMod,
.attrs ) -> Result<String> {
.iter() match attr {
.filter_map(|attr| attr.parse_meta().ok()) Some(namespace) if namespace.is_empty() => syn_error!(
.filter(|meta| meta.path().is_ident(LINK_DIRECTIVE_NAME)) foreign_mod.span(),
.filter_map(|meta| { "import module name should be defined by 'wasm_import_module' directive"
let pair = match meta { ),
syn::Meta::List(mut meta_list) if meta_list.nested.len() == 1 => { Some(namespace) => Ok(namespace),
meta_list.nested.pop().unwrap() None => syn_error!(
} foreign_mod.span(),
_ => return None, "import module name should be defined by 'wasm_import_module' directive"
}; ),
Some(pair.into_tuple().0) }
}) }
.filter_map(|nested| match nested {
syn::NestedMeta::Meta(meta) => Some(meta),
_ => None,
})
.filter(|meta| meta.path().is_ident(WASM_IMPORT_MODULE_DIRECTIVE_NAME))
.map(extract_value)
.collect();
match wasm_import_module { fn extract_import_functions(
Some(namespace) if namespace.is_empty() => syn_error!( foreign_mod: &syn::ItemForeignMod,
self_span, ) -> Result<Vec<fce_ast_types::AstExternFnItem>> {
"import module name should be defined by 'wasm_import_module' directive" foreign_mod
), .items
Some(namespace) => { .iter()
let extern_mod_item = fce_ast_types::AstExternModItem { .cloned()
namespace, .map(parse_raw_foreign_item)
imports, .collect::<Result<_>>()
original: Some(self), }
};
Ok(FCEAst::ExternMod(extern_mod_item)) /// This function checks whether these imports contains inner references. In this case glue
/// code couldn't be generated.
fn check_imports<'i>(
extern_fns: impl ExactSizeIterator<Item = (&'i fce_ast_types::AstExternFnItem, proc_macro2::Span)>,
) -> Result<()> {
use super::utils::contain_inner_ref;
for (extern_fn, span) in extern_fns {
if let Some(output_type) = &extern_fn.signature.output_type {
if contain_inner_ref(output_type) {
return crate::syn_error!(
span,
"import function can't return a value with references"
);
} }
None => syn_error!(
self_span,
"import module name should be defined by 'wasm_import_module' directive"
),
} }
} }
Ok(())
} }
fn parse_raw_foreign_item(raw_item: syn::ForeignItem) -> Result<fce_ast_types::AstExternFnItem> { fn parse_raw_foreign_item(raw_item: syn::ForeignItem) -> Result<fce_ast_types::AstExternFnItem> {

View File

@ -0,0 +1,33 @@
/*
* 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::ParsedType;
use crate::parsed_type::PassingStyle;
use crate::parsed_type::passing_style_of;
/// Checks whether a type contains a reference in one of types.
pub(super) fn contain_inner_ref(ty: &ParsedType) -> bool {
let passing_style = passing_style_of(ty);
match passing_style {
PassingStyle::ByValue => {}
PassingStyle::ByRef | PassingStyle::ByMutRef => return true,
};
match ty {
ParsedType::Vector(ty, _) => contain_inner_ref(ty),
_ => false,
}
}

View File

@ -57,7 +57,7 @@ pub enum ParsedType {
Record(String, PassingStyle), // short type name Record(String, PassingStyle), // short type name
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum PassingStyle { pub enum PassingStyle {
ByValue, ByValue,
ByRef, ByRef,

View File

@ -116,11 +116,14 @@ fn generate_epilog(ty: &Option<ParsedType>) -> proc_macro2::TokenStream {
fluence::internal::set_result_size(result.len() as _); fluence::internal::set_result_size(result.len() as _);
} }
} }
Some(ParsedType::Vector(ty, _)) => { Some(ParsedType::Vector(ty, passing_style)) => {
let generated_serializer_name = "__fce_generated_vec_serializer"; let generated_serializer_name = "__fce_generated_vec_serializer";
let generated_serializer_ident = new_ident!(generated_serializer_name); let generated_serializer_ident = new_ident!(generated_serializer_name);
let vector_serializer = let vector_serializer = super::vector_utils::generate_vector_serializer(
super::vector_utils::generate_vector_serializer(ty, generated_serializer_name); ty,
*passing_style,
generated_serializer_name,
);
quote! { quote! {
#vector_serializer #vector_serializer

View File

@ -81,11 +81,11 @@ impl ForeignModPrologGlueCodeGenerator for Vec<AstFuncArgument> {
arg_transforms.extend(quote::quote! { let mut #arg_ident = std::mem::ManuallyDrop::new(#arg_ident); }); arg_transforms.extend(quote::quote! { let mut #arg_ident = std::mem::ManuallyDrop::new(#arg_ident); });
arg_drops.extend(quote::quote! { std::mem::ManuallyDrop::drop(&mut #arg_ident); }); arg_drops.extend(quote::quote! { std::mem::ManuallyDrop::drop(&mut #arg_ident); });
}, },
ParsedType::Vector(ty, _) => { ParsedType::Vector(ty, passing_style) => {
let generated_serializer_name = format!("__fce_generated_vec_serializer_{}", arg_name).replace("&<>", "_"); let generated_serializer_name = format!("__fce_generated_vec_serializer_{}", arg_name).replace("&<>", "_");
let generated_serializer_ident = new_ident!(generated_serializer_name); let generated_serializer_ident = new_ident!(generated_serializer_name);
let vector_serializer = super::vector_utils::generate_vector_serializer(ty, &generated_serializer_name); let vector_serializer = super::vector_utils::generate_vector_serializer(ty, *passing_style, &generated_serializer_name);
let arg_transform = quote::quote! { let arg_transform = quote::quote! {
#vector_serializer #vector_serializer

View File

@ -15,21 +15,13 @@
*/ */
use super::ParsedType; use super::ParsedType;
use super::PassingStyle;
use quote::quote; use quote::quote;
pub(crate) fn generate_vector_serializer( pub(crate) fn generate_vector_serializer(
value_ty: &ParsedType, value_ty: &ParsedType,
arg_name: &str, vec_passing_style: PassingStyle,
) -> proc_macro2::TokenStream {
let serializer_func = generate_vector_serializer_impl(value_ty, arg_name);
quote! {
#serializer_func
}
}
fn generate_vector_serializer_impl(
value_ty: &ParsedType,
arg_name: &str, arg_name: &str,
) -> proc_macro2::TokenStream { ) -> proc_macro2::TokenStream {
let values_serializer = match value_ty { let values_serializer = match value_ty {
@ -90,12 +82,13 @@ fn generate_vector_serializer_impl(
(result.as_ptr() as _, (4 * result.len()) as _) (result.as_ptr() as _, (4 * result.len()) as _)
} }
} }
ParsedType::Vector(ty, _) => { ParsedType::Vector(ty, passing_style) => {
let serializer_name = format!("{}_{}", arg_name, ty) let serializer_name = format!("{}_{}", arg_name, ty)
.replace("<", "_") .replace("<", "_")
.replace(">", "_") .replace(">", "_")
.replace("&", "_"); .replace("&", "_");
let inner_vector_serializer = generate_vector_serializer(&*ty, &serializer_name); let inner_vector_serializer =
generate_vector_serializer(&*ty, *passing_style, &serializer_name);
let serializer_ident = crate::new_ident!(serializer_name); let serializer_ident = crate::new_ident!(serializer_name);
quote! { quote! {
@ -128,7 +121,7 @@ fn generate_vector_serializer_impl(
let arg = crate::new_ident!(arg_name); let arg = crate::new_ident!(arg_name);
quote! { quote! {
unsafe fn #arg(arg: Vec<#value_ty>) -> (u32, u32) { unsafe fn #arg(arg: #vec_passing_style Vec<#value_ty>) -> (u32, u32) {
#values_serializer #values_serializer
} }
} }

View File

@ -44,7 +44,7 @@ impl RecordSerializerGlueCodeGenerator for fce_ast_types::AstRecordItem {
std::mem::forget(#field_ident); std::mem::forget(#field_ident);
} }
} }
ParsedType::Vector(ty, _) => { ParsedType::Vector(ty, passing_style) => {
let generated_serializer_name = format!( let generated_serializer_name = format!(
"__fce_generated_vec_serializer_{}_{}", "__fce_generated_vec_serializer_{}_{}",
field.name.as_ref().unwrap(), field.name.as_ref().unwrap(),
@ -54,6 +54,7 @@ impl RecordSerializerGlueCodeGenerator for fce_ast_types::AstRecordItem {
let generated_serializer_ident = new_ident!(generated_serializer_name); let generated_serializer_ident = new_ident!(generated_serializer_name);
let vector_serializer = crate::parsed_type::generate_vector_serializer( let vector_serializer = crate::parsed_type::generate_vector_serializer(
ty, ty,
*passing_style,
&generated_serializer_name, &generated_serializer_name,
); );
let serialized_field_ident = new_ident!(format!("serialized_arg_{}", id)); let serialized_field_ident = new_ident!(format!("serialized_arg_{}", id));

View File

@ -0,0 +1,27 @@
#![allow(improper_ctypes)]
use fluence::fce;
pub fn main() {}
#[fce]
#[link(wasm_import_module = "arrays_passing_effector")]
extern "C" {
#[fce]
pub fn func_1() -> &String;
#[fce]
pub fn func_2() -> &Vec<Vec<Vec<Vec<u8>>>>;
#[fce]
pub fn func_3() -> Vec<&Vec<Vec<Vec<u8>>>>;
#[fce]
pub fn func_4() -> Vec<Vec<&Vec<Vec<u8>>>>;
#[fce]
pub fn func_5() -> Vec<Vec<Vec<&Vec<u8>>>>;
#[fce]
pub fn func_6() -> Vec<Vec<Vec<Vec<&u8>>>>;
}

View File

@ -0,0 +1,6 @@
error: import function can't return a value with references
--> $DIR/arrays_out_inner_refs.rs:10:5
|
10 | / #[fce]
11 | | pub fn func_1() -> &String;
| |_______________________________^

View File

@ -8,6 +8,7 @@ fn test() {
tests.pass("tests/export_functions/ref_basic_types.rs"); tests.pass("tests/export_functions/ref_basic_types.rs");
tests.compile_fail("tests/export_functions/improper_types.rs"); tests.compile_fail("tests/export_functions/improper_types.rs");
tests.compile_fail("tests/import_functions/arrays_out_inner_refs.rs");
tests.pass("tests/import_functions/arrays.rs"); tests.pass("tests/import_functions/arrays.rs");
tests.pass("tests/import_functions/ref_arrays.rs"); tests.pass("tests/import_functions/ref_arrays.rs");
tests.pass("tests/import_functions/basic_types.rs"); tests.pass("tests/import_functions/basic_types.rs");