Merge pull request #6 from fluencelabs/array_support

Array support
This commit is contained in:
vms 2020-09-21 13:34:53 +03:00 committed by GitHub
commit 5ac0c45507
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 385 additions and 91 deletions

View File

@ -9,6 +9,7 @@ readme = "Readme.md"
keywords = ["fluence", "sdk", "webassembly"]
categories = ["api-bindings", "wasm"]
license = "Apache-2.0"
edition = "2018"
[package.metadata.docs.rs] # https://docs.rs/about
all-features = true
@ -19,6 +20,7 @@ path = "src/lib.rs"
[dependencies]
fluence-sdk-macro = { path = "crates/macro", version = "=0.2.3" }
fluence-sdk-main = { path = "crates/main", version = "=0.2.3" }
safe-transmute = "0.11.0"
[features]
# Print some internal logs by log_utf8_string

View File

@ -20,17 +20,21 @@ mod fn_prolog;
mod foreign_mod_arg;
mod foreign_mod_epilog;
mod foreign_mod_prolog;
mod vector_utils;
pub(crate) use fn_arg::*;
pub(crate) use fn_epilog::*;
pub(crate) use fn_prolog::*;
pub(crate) use foreign_mod_prolog::*;
pub(crate) use foreign_mod_epilog::*;
pub(crate) use vector_utils::*;
use serde::Serialize;
use serde::Deserialize;
use syn::parse::Error;
use syn::spanned::Spanned;
use proc_macro2::TokenStream;
use serde::export::Formatter;
/// An internal representation of supported Rust types.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@ -47,15 +51,16 @@ pub enum ParsedType {
F64,
Boolean,
Utf8String,
ByteVector,
Vector(Box<ParsedType>),
Record(String), // short type name
}
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> {
// parse generic param T in Vec<T> to syn::Type
fn parse_vec_bracket(args: &syn::PathArguments) -> syn::Result<&syn::Type> {
// checks that T is angle bracketed
let generic_arg = match args {
syn::PathArguments::AngleBracketed(args) => Ok(args),
@ -73,41 +78,13 @@ impl ParsedType {
})?;
// converts T to syn::Type
let arg_type = match arg {
match arg {
syn::GenericArgument::Type(ty) => Ok(ty),
_ => Err(Error::new(
arg.span(),
"Unsuitable type in Vec brackets - only Vec<u8> is supported",
)),
}?;
// converts T to syn::path
let arg_path = match arg_type {
syn::Type::Path(path) => Ok(&path.path),
_ => Err(Error::new(
arg_type.span(),
"Unsuitable type in Vec brackets - only Vec<u8> is supported",
)),
}?;
// There could be cases like Vec<some_crate::some_module::u8>
// that why this segments count check is needed
if arg_path.segments.len() != 1 {
return Err(Error::new(
arg_path.span(),
"Unsuitable type in Vec brackets - only Vec<u8> is supported",
));
}
// converts T to String
let arg_segment = arg_path.segments.first().ok_or_else(|| {
Error::new(
arg_path.span(),
"Unsuitable type in Vec brackets - only Vec<u8> is supported",
)
})?;
Ok(arg_segment.ident.to_string())
}
let path = match input_type {
@ -138,16 +115,12 @@ impl ParsedType {
"f64" => Ok(ParsedType::F64),
"bool" => Ok(ParsedType::Boolean),
"String" => Ok(ParsedType::Utf8String),
"Vec" => match parse_vec_bracket(&type_segment.arguments) {
Ok(value) => match value.as_str() {
"u8" => Ok(ParsedType::ByteVector),
_ => Err(Error::new(
value.span(),
"Unsuitable type in Vec brackets - only Vec<u8> is supported",
)),
},
Err(e) => Err(e),
},
"Vec" => {
let vec_type = parse_vec_bracket(&type_segment.arguments)?;
let parsed_type = ParsedType::from_type(vec_type)?;
Ok(ParsedType::Vector(Box::new(parsed_type)))
}
_ if !type_segment.arguments.is_empty() => Err(Error::new(
type_segment.span(),
"type with lifetimes or generics aren't allowed".to_string(),
@ -175,7 +148,30 @@ impl ParsedType {
}
}
pub fn to_token_stream(&self) -> proc_macro2::TokenStream {
pub fn is_complex_type(&self) -> bool {
match self {
ParsedType::Boolean
| ParsedType::I8
| ParsedType::I16
| ParsedType::I32
| ParsedType::I64
| ParsedType::U8
| ParsedType::U16
| ParsedType::U32
| ParsedType::U64
| ParsedType::F32
| ParsedType::F64 => false,
ParsedType::Utf8String | ParsedType::Vector(_) | ParsedType::Record(_) => true,
}
}
}
impl quote::ToTokens for ParsedType {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend(self.to_token_stream());
}
fn to_token_stream(&self) -> proc_macro2::TokenStream {
use quote::quote;
match self {
@ -191,28 +187,37 @@ impl ParsedType {
ParsedType::F64 => quote! { f64 },
ParsedType::Boolean => quote! { bool },
ParsedType::Utf8String => quote! { String },
ParsedType::ByteVector => quote! { Vec<u8> },
ParsedType::Vector(ty) => {
let quoted_type = ty.to_token_stream();
quote! { Vec<#quoted_type> }
}
ParsedType::Record(name) => {
let ty = crate::new_ident!(name);
quote! { #ty }
}
}
}
}
pub fn is_complex_type(&self) -> bool {
impl std::fmt::Display for ParsedType {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
ParsedType::Boolean
| ParsedType::I8
| ParsedType::I16
| ParsedType::I32
| ParsedType::I64
| ParsedType::U8
| ParsedType::U16
| ParsedType::U32
| ParsedType::U64
| ParsedType::F32
| ParsedType::F64 => false,
ParsedType::Utf8String | ParsedType::ByteVector | ParsedType::Record(_) => true,
}
ParsedType::Boolean => f.write_str("bool"),
ParsedType::I8 => f.write_str("i8"),
ParsedType::I16 => f.write_str("i16"),
ParsedType::I32 => f.write_str("i32"),
ParsedType::I64 => f.write_str("i64"),
ParsedType::U8 => f.write_str("u8"),
ParsedType::U16 => f.write_str("u16"),
ParsedType::U32 => f.write_str("u32"),
ParsedType::U64 => f.write_str("u64"),
ParsedType::F32 => f.write_str("f32"),
ParsedType::F64 => f.write_str("u64"),
ParsedType::Utf8String => f.write_str("String"),
ParsedType::Vector(_) => f.write_str("Vec"),
ParsedType::Record(record_name) => f.write_str(&record_name),
}?;
Ok(())
}
}

View File

@ -37,7 +37,7 @@ impl FnArgGlueCodeGenerator for (String, ParsedType) {
ParsedType::Record(_) => vec![RustType::U32],
ParsedType::F32 => vec![RustType::F32],
ParsedType::F64 => vec![RustType::F64],
ParsedType::Utf8String | ParsedType::ByteVector => vec![RustType::U32, RustType::U32],
ParsedType::Utf8String | ParsedType::Vector(_) => vec![RustType::U32, RustType::U32],
}
}
}

View File

@ -66,7 +66,7 @@ fn generate_fn_return_type(ty: &Option<ParsedType>) -> proc_macro2::TokenStream
Some(ParsedType::F64) => Some("f64"),
None
| Some(ParsedType::Utf8String)
| Some(ParsedType::ByteVector)
| Some(ParsedType::Vector(_))
| Some(ParsedType::Record(_)) => None,
};
@ -91,22 +91,33 @@ fn generate_return_expression(ty: &Option<ParsedType>) -> proc_macro2::TokenStre
fn generate_epilog(ty: &Option<ParsedType>) -> proc_macro2::TokenStream {
match ty {
None => quote!(),
Some(ty) if !ty.is_complex_type() => quote! {
return result as _;
},
Some(ParsedType::Record(_)) => {
quote! {
let result_ptr = result.__fce_generated_serialize();
fluence::internal::set_result_ptr(result_ptr as _);
}
}
Some(ty) if ty.is_complex_type() => quote! {
Some(ParsedType::Utf8String) => quote! {
fluence::internal::set_result_ptr(result.as_ptr() as _);
fluence::internal::set_result_size(result.len() as _);
std::mem::forget(result);
},
_ => {
panic!("perhaps new type's been added to ParsedType, and this match became incomplete")
Some(ParsedType::Vector(ty)) => {
let generated_serializer_name = format!("__fce_generated_vec_serializer");
let generated_serializer_ident = new_ident!(generated_serializer_name);
let vector_serializer =
super::vector_utils::generate_vector_serializer(ty, &generated_serializer_name);
quote! {
#vector_serializer
let result = #generated_serializer_ident(result);
fluence::internal::set_result_ptr(result.as_ptr() as _);
fluence::internal::set_result_size(result.len() as _);
std::mem::forget(result);
}
}
Some(_) => quote! {
return result as _;
},
}
}

View File

@ -107,9 +107,20 @@ fn generate_type_prolog(
ParsedType::Utf8String => quote! {
let #generated_arg_id = String::from_raw_parts(#ptr as _, #size as _ , #size as _);
},
ParsedType::ByteVector => quote! {
let #generated_arg_id = Vec::from_raw_parts(#ptr as _, #size as _, #size as _);
},
ParsedType::Vector(ty) => {
let generated_deserializer_name =
format!("__fce_generated_vec_deserializer_{}", supplied_arg_start_id);
let generated_deserializer_ident = new_ident!(generated_deserializer_name);
let vector_deserializer = super::vector_utils::generate_vector_deserializer(
ty,
&generated_deserializer_name,
);
quote! {
#vector_deserializer
let #generated_arg_id = #generated_deserializer_ident(#ptr as _, #size as _);
}
}
ParsedType::Record(record_name) => {
let record_ident = new_ident!(record_name);
quote! {

View File

@ -28,14 +28,12 @@ impl ForeignModArgGlueCodeGenerator for ParsedType {
let arg = crate::new_ident!(format!("arg_{}", arg_start_id));
match self {
ParsedType::Utf8String | ParsedType::ByteVector => {
ParsedType::Vector(_) | ParsedType::Utf8String => {
quote! { #arg.as_ptr() as _, #arg.len() as _ }
}
ParsedType::Record(_) => {
quote! {
#arg.__fce_generated_serialize() as _
}
}
ParsedType::Record(_) => quote! {
#arg.__fce_generated_serialize() as _
},
ParsedType::Boolean => quote! { #arg as _ },
_ => quote! { #arg },
}

View File

@ -29,6 +29,8 @@ pub(crate) trait ForeignModEpilogGlueCodeGenerator {
impl ForeignModEpilogGlueCodeGenerator for Option<ParsedType> {
fn generate_wrapper_return_type(&self) -> proc_macro2::TokenStream {
use quote::ToTokens;
match self {
Some(ty) => {
let ty = ty.to_token_stream();
@ -54,13 +56,22 @@ impl ForeignModEpilogGlueCodeGenerator for Option<ParsedType> {
fluence::internal::get_result_size() as _
)
},
Some(ParsedType::ByteVector) => quote! {
Vec::from_raw_parts(
fluence::internal::get_result_ptr() as _,
fluence::internal::get_result_size() as _,
fluence::internal::get_result_size() as _
)
},
Some(ParsedType::Vector(ty)) => {
let generated_deserializer_name = String::from("__fce_generated_vec_deserializer");
let generated_deserializer_ident = new_ident!(generated_deserializer_name);
let vector_deserializer = super::vector_utils::generate_vector_deserializer(
ty,
&generated_deserializer_name,
);
quote! {
#vector_deserializer
#generated_deserializer_ident(
fluence::internal::get_result_ptr() as _,
fluence::internal::get_result_size() as _,
)
}
}
Some(ParsedType::Record(record_name)) => {
let record_ident = new_ident!(record_name);

View File

@ -60,6 +60,7 @@ pub(crate) trait ForeignModPrologGlueCodeGenerator {
impl ForeignModPrologGlueCodeGenerator for Vec<(String, ParsedType)> {
fn generate_wrapper_prolog(&self) -> WrapperDescriptor {
use crate::parsed_type::foreign_mod_arg::ForeignModArgGlueCodeGenerator;
use quote::ToTokens;
let arg_types: Vec<proc_macro2::TokenStream> = self
.iter()
@ -70,14 +71,30 @@ impl ForeignModPrologGlueCodeGenerator for Vec<(String, ParsedType)> {
.iter()
.enumerate()
.fold((Vec::new(), proc_macro2::TokenStream::new(), proc_macro2::TokenStream::new()), |(mut arg_names, mut arg_transforms, mut arg_drops), (id, (_, ty))| {
let arg_ident = new_ident!(format!("arg_{}", id));
let arg_name = format!("arg_{}", id);
let arg_ident = new_ident!(arg_name);
arg_names.push(arg_ident.clone());
match ty {
ParsedType::ByteVector | ParsedType::Utf8String => {
ParsedType::Utf8String => {
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); });
},
ParsedType::Vector(ty) => {
let generated_serializer_name = format!("__fce_generated_vec_serializer_{}", arg_name);
let generated_serializer_ident = new_ident!(generated_serializer_name);
let vector_serializer = super::vector_utils::generate_vector_serializer(ty, &generated_serializer_name);
let arg_transform = quote::quote! {
#vector_serializer
let #arg_ident = #generated_serializer_ident(#arg_ident);
let mut #arg_ident = std::mem::ManuallyDrop::new(#arg_ident);
};
arg_transforms.extend(arg_transform);
arg_drops.extend(quote::quote! { std::mem::ManuallyDrop::drop(&mut #arg_ident); });
}
_ => {}
}

View File

@ -0,0 +1,211 @@
/*
* 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 super::ParsedType;
use quote::quote;
pub(crate) fn generate_vector_serializer(
value_ty: &ParsedType,
arg_name: &str,
) -> proc_macro2::TokenStream {
let values_serializer = match value_ty {
ParsedType::Boolean => {
quote! {
fluence::internal::transmute_vec::<i32, u8>(arg).unwrap()
}
}
ParsedType::I8
| ParsedType::I16
| ParsedType::I32
| ParsedType::I64
| ParsedType::U8
| ParsedType::U16
| ParsedType::U32
| ParsedType::U64 => {
quote! {
fluence::internal::transmute_vec::<#value_ty, u8>(arg).unwrap()
}
}
ParsedType::F32 => {
quote! {
let mut result: Vec<u32> = Vec::with_capacity(arg.len());
for value in arg {
result.push(value.to_bits());
}
fluence::internal::transmute_vec::<u32, u8>(result).unwrap()
}
}
ParsedType::F64 => {
quote! {
let mut result: Vec<u64> = Vec::with_capacity(arg.len());
for value in arg {
result.push(value.to_bits());
}
fluence::internal::transmute_vec::<u64, u8>(result).unwrap()
}
}
ParsedType::Utf8String => {
quote! {
let mut result: Vec<u32> = Vec::with_capacity(arg.len());
for value in arg {
result.push(value.as_ptr() as _);
result.push(value.len() as _);
}
fluence::internal::transmute_vec::<u32, u8>(result).unwrap()
}
}
ParsedType::Vector(ty) => {
let serializer_name = format!("{}_{}", arg_name, ty);
let inner_vector_serializer = generate_vector_serializer(&*ty, &serializer_name);
let serializer_ident = crate::new_ident!(serializer_name);
quote! {
#inner_vector_serializer
let mut result: Vec<u32> = Vec::with_capacity(2 * arg.len());
for value in arg {
let value = std::mem::ManuallyDrop::new(#serializer_ident(value));
result.push(value.as_ptr() as _);
result.push(value.len() as _);
}
fluence::internal::transmute_vec::<u32, u8>(result).unwrap()
}
}
ParsedType::Record(_) => {
quote! {
let mut result: Vec<u32> = Vec::with_capacity(arg.len());
for value in arg {
result.push(value.__fce_generated_serialize() as _);
}
fluence::internal::transmute_vec::<u32, u8>(result).unwrap()
}
}
};
let arg = crate::new_ident!(arg_name);
quote! {
unsafe fn #arg(arg: Vec<#value_ty>) -> Vec<u8> {
if arg.is_empty() {
return vec![];
}
#values_serializer
}
}
}
pub(crate) fn generate_vector_deserializer(
value_ty: &ParsedType,
arg_name: &str,
) -> proc_macro2::TokenStream {
let arg = crate::new_ident!(arg_name);
let values_deserializer = match value_ty {
ParsedType::Boolean => {
quote! {
fluence::internal::transmute_vec::<u8, i32>(arg).unwrap()
}
}
ParsedType::F32 => {
quote! {
let mut arg = fluence::internal::transmute_vec::<u8, u32>(arg).unwrap();
arg.into_iter().map(f32::from_bits).collect::<Vec<_>>()
}
}
ParsedType::F64 => {
quote! {
let mut arg = fluence::internal::transmute_vec::<u8, u64>(arg).unwrap();
arg.into_iter().map(f64::from_bits).collect::<Vec<_>>()
}
}
ParsedType::Utf8String => {
quote! {
let mut arg = fluence::internal::transmute_vec::<u8, u32>(arg).unwrap();
let mut arg = arg.into_iter();
let mut result = Vec::with_capacity(arg.len() / 2);
while let Some(offset) = arg.next() {
let size = arg.next().unwrap();
let value = String::from_raw_parts(offset as _, size as _, size as _);
result.push(value);
}
result
}
}
ParsedType::Vector(ty) => {
let deserializer_name = format!("{}_{}", arg_name, ty);
let inner_vector_deserializer = generate_vector_deserializer(&*ty, &deserializer_name);
let deserializer_ident = crate::new_ident!(deserializer_name);
quote! {
#inner_vector_deserializer
let mut arg = fluence::internal::transmute_vec::<u8, u32>(arg).unwrap();
let mut result = Vec::with_capacity(arg.len());
let mut arg = arg.into_iter();
while let Some(offset) = arg.next() {
let size = arg.next().unwrap();
let value = #deserializer_ident(offset, size);
result.push(value);
}
result
}
}
ParsedType::Record(record_name) => {
let record_name_ident = crate::new_ident!(record_name);
quote! {
let arg = fluence::internal::transmute_vec::<u8, u32>(arg).unwrap();
let mut result = Vec::with_capacity(arg.len());
for offset in arg {
let value = #record_name_ident::__fce_generated_deserialize(offset as _);
result.push(value);
}
result
}
}
v => {
quote! {
fluence::internal::transmute_vec::<u8, #v>(arg).unwrap()
}
}
};
quote! {
unsafe fn #arg(offset: u32, size: u32) -> Vec<#value_ty> {
let arg: Vec<u8> = Vec::from_raw_parts(offset as _, size as _, size as _);
if arg.is_empty() {
return vec![];
}
#values_deserializer
}
}
}

View File

@ -103,13 +103,22 @@ impl RecordDeserializerGlueCodeGenerator for fce_ast_types::AstRecordItem {
let #field = unsafe { String::from_raw_parts(raw_record[#ptr_id] as _, raw_record[#size_id] as _, raw_record[#size_id] as _) };
}
}
ParsedType::ByteVector => {
ParsedType::Vector(ty) => {
let ptr_id = value_id;
let size_id = value_id + 1;
value_id += 1;
let generated_deserializer_name =
String::from("__fce_generated_vec_deserializer");
let generated_deserializer_ident = new_ident!(generated_deserializer_name);
let vector_deserializer = crate::parsed_type::generate_vector_deserializer(
ty,
&generated_deserializer_name,
);
quote! {
let #field = unsafe { Vec::from_raw_parts(raw_record[#ptr_id] as _, raw_record[#size_id] as _, raw_record[#size_id] as _) };
#vector_deserializer
let #field = unsafe { #generated_deserializer_ident(raw_record[#ptr_id] as _, raw_record[#size_id] as _) };
}
}
ParsedType::Record(record_name) => {

View File

@ -37,13 +37,34 @@ impl RecordSerializerGlueCodeGenerator for fce_ast_types::AstRecordItem {
raw_record.push(#field_ident.to_bits());
}
}
ParsedType::Utf8String | ParsedType::ByteVector => {
ParsedType::Utf8String => {
quote! {
raw_record.push(#field_ident.as_ptr() as _);
raw_record.push(#field_ident.len() as _);
std::mem::forget(#field_ident);
}
}
ParsedType::Vector(ty) => {
let generated_serializer_name = format!(
"__fce_generated_vec_serializer_{}_{}",
field.name.as_ref().unwrap(),
id
);
let generated_serializer_ident = new_ident!(generated_serializer_name);
let vector_serializer = crate::parsed_type::generate_vector_serializer(
ty,
&generated_serializer_name,
);
let serialized_field_ident = new_ident!(format!("serialized_arg_{}", id));
quote::quote! {
#vector_serializer
let #serialized_field_ident = unsafe { #generated_serializer_ident(#field_ident) };
raw_record.push(#serialized_field_ident.as_ptr() as _);
raw_record.push(#serialized_field_ident.len() as _);
std::mem::forget(#serialized_field_ident);
}
}
ParsedType::Record(_) => {
quote! {
raw_record.push(#field_ident.__fce_generated_serialize() as _);

View File

@ -54,7 +54,7 @@ pub fn get_record_size<'a>(
for field in fields {
let params_count = match field {
ParsedType::ByteVector | ParsedType::Utf8String => 2,
ParsedType::Vector(_) | ParsedType::Utf8String => 2,
_ => 1,
};

View File

@ -67,9 +67,6 @@
)]
#![warn(rust_2018_idioms)]
extern crate fluence_sdk_macro;
extern crate fluence_sdk_main;
pub use fluence_sdk_macro::fce;
pub use fluence_sdk_main::CallParameters;
#[cfg(target_arch = "wasm32")]
@ -84,4 +81,5 @@ pub mod internal {
pub use fluence_sdk_main::get_result_size;
pub use fluence_sdk_main::set_result_ptr;
pub use fluence_sdk_main::set_result_size;
pub use safe_transmute::transmute_vec;
}