diff --git a/crates/macro/src/fce_ast_types.rs b/crates/macro/src/fce_ast_types.rs index 1da1b4d..e42f0fb 100644 --- a/crates/macro/src/fce_ast_types.rs +++ b/crates/macro/src/fce_ast_types.rs @@ -21,7 +21,7 @@ use serde::Deserialize; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub(crate) struct AstFunctionItem { - pub(crate) name: String, + pub(crate) rust_name: String, pub(crate) input_types: Vec, // fce supports only one return value now, // waiting for adding multi-value support in Wasmer. @@ -33,11 +33,18 @@ pub(crate) struct AstRecordItem { pub(crate) fields: Vec, } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub(crate) struct AstExternFnItem { + pub(crate) link_name: Option, + // only imports are possible here + pub(crate) function: AstFunctionItem, +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub(crate) struct AstExternModItem { pub(crate) namespace: String, // only imports are possible here - pub(crate) imports: Vec, + pub(crate) imports: Vec, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/crates/macro/src/parse_macro_input/item_fn.rs b/crates/macro/src/parse_macro_input/item_fn.rs index 05bda63..03b6d4a 100644 --- a/crates/macro/src/parse_macro_input/item_fn.rs +++ b/crates/macro/src/parse_macro_input/item_fn.rs @@ -44,7 +44,7 @@ pub(super) fn parse_function( let output_type = ParsedType::from_return_type(&output)?; let ast_function_item = fce_ast_types::AstFunctionItem { - name: function_sig.ident.to_string(), + rust_name: function_sig.ident.to_string(), input_types, output_type, }; diff --git a/crates/macro/src/parse_macro_input/item_foreign_mod.rs b/crates/macro/src/parse_macro_input/item_foreign_mod.rs index 1fe95aa..44d3cc9 100644 --- a/crates/macro/src/parse_macro_input/item_foreign_mod.rs +++ b/crates/macro/src/parse_macro_input/item_foreign_mod.rs @@ -80,13 +80,13 @@ impl ParseMacroInput for syn::ItemForeignMod { } } -fn parse_raw_foreign_item(raw_item: syn::ForeignItem) -> Result { +fn parse_raw_foreign_item(raw_item: syn::ForeignItem) -> Result { let function_item = match raw_item { syn::ForeignItem::Fn(function_item) => function_item, _ => { return Err(Error::new( raw_item.span(), - "#[fce] could be upplied only to a function, struct ot extern block", + "#[fce] could be applied only to a function, struct ot extern block", )) } }; @@ -102,13 +102,13 @@ fn parse_raw_foreign_item(raw_item: syn::ForeignItem) -> Result Option { diff --git a/crates/macro/src/parsed_type.rs b/crates/macro/src/parsed_type.rs index bad5e69..bfbd7e9 100644 --- a/crates/macro/src/parsed_type.rs +++ b/crates/macro/src/parsed_type.rs @@ -14,9 +14,10 @@ * limitations under the License. */ -use crate::wasm_type::WasmType; +mod glue_code_generator; + +pub(crate) use glue_code_generator::GlueCodeGenerator; -use quote::quote; use serde::Serialize; use serde::Deserialize; use syn::parse::Error; @@ -163,190 +164,3 @@ impl ParsedType { } } } - -// TODO: replace String with Ident -pub(crate) trait MacroPartsGenerator { - fn generate_arguments(&self) -> Vec; - - fn generate_return_expression(&self) -> proc_macro2::TokenStream; - - fn generate_return_type(&self) -> String; - - fn generate_fn_prolog( - &self, - generated_arg_id: usize, - supplied_arg_start_id: usize, - ) -> proc_macro2::TokenStream; - - fn generate_fn_epilog(&self) -> proc_macro2::TokenStream; -} - -impl MacroPartsGenerator for ParsedType { - fn generate_arguments(&self) -> Vec { - // 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], - } - } - - fn generate_return_expression(&self) -> proc_macro2::TokenStream { - match self { - ParsedType::Empty => quote! {}, - ParsedType::Utf8String => quote! {}, - ParsedType::ByteVector => quote! {}, - ParsedType::Record(_) => quote! {}, - _ => quote! { - let result = - }, - } - } - - fn generate_return_type(&self) -> String { - match self { - ParsedType::I8 => "-> i32", - ParsedType::I16 => "-> i32", - ParsedType::I32 => "-> i32", - ParsedType::I64 => "-> i64", - ParsedType::U8 => "-> i32", - ParsedType::U16 => "-> i32", - ParsedType::U32 => "-> i32", - ParsedType::U64 => "-> i64", - ParsedType::F32 => "-> f32", - ParsedType::F64 => "-> f64", - ParsedType::Boolean => "-> i32", - _ => "", - } - .to_string() - } - - 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 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 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::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) - ); - }, - } - } - - fn generate_fn_epilog(&self) -> proc_macro2::TokenStream { - match self { - ParsedType::Empty => quote! {}, - ParsedType::I8 => quote! { - return result as _; - }, - ParsedType::I16 => quote! { - return result as _; - }, - ParsedType::I32 => quote! { - return result as _; - }, - ParsedType::I64 => quote! { - return result as _; - }, - ParsedType::U8 => quote! { - return result as _; - }, - ParsedType::U16 => quote! { - return result as _; - }, - ParsedType::U32 => quote! { - return result as _; - }, - ParsedType::U64 => quote! { - return result as _; - }, - ParsedType::F32 => quote! { - return result as _; - }, - ParsedType::F64 => quote! { - return result as _; - }, - ParsedType::Boolean => quote! { - return result as _; - }, - ParsedType::Utf8String => quote! { - fluence::set_result_ptr(result.as_ptr() as _); - fluence::set_result_size(result.len() as _); - std::mem::forget(result); - }, - ParsedType::ByteVector => quote! { - 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); - }, - } - } -} diff --git a/crates/macro/src/parsed_type/glue_code_generator.rs b/crates/macro/src/parsed_type/glue_code_generator.rs new file mode 100644 index 0000000..e6a1f90 --- /dev/null +++ b/crates/macro/src/parsed_type/glue_code_generator.rs @@ -0,0 +1,207 @@ +/* + * Copyright 2018 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::wasm_type::WasmType; +use super::ParsedType; + +use quote::quote; + +pub(crate) trait GlueCodeGenerator { + fn generate_arguments(&self) -> Vec; + + fn generate_return_expression(&self) -> proc_macro2::TokenStream; + + // TODO: replace String with Ident + fn generate_return_type(&self) -> String; + + fn generate_fn_prolog( + &self, + generated_arg_id: usize, + supplied_arg_start_id: usize, + ) -> proc_macro2::TokenStream; + + fn generate_fn_epilog(&self) -> proc_macro2::TokenStream; +} + +impl GlueCodeGenerator for ParsedType { + fn generate_arguments(&self) -> Vec { + // 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], + } + } + + fn generate_return_expression(&self) -> proc_macro2::TokenStream { + match self { + ParsedType::Empty => quote! {}, + ParsedType::Utf8String => quote! {}, + ParsedType::ByteVector => quote! {}, + ParsedType::Record(_) => quote! {}, + _ => quote! { + let result = + }, + } + } + + fn generate_return_type(&self) -> String { + match self { + ParsedType::I8 => "-> i32", + ParsedType::I16 => "-> i32", + ParsedType::I32 => "-> i32", + ParsedType::I64 => "-> i64", + ParsedType::U8 => "-> i32", + ParsedType::U16 => "-> i32", + ParsedType::U32 => "-> i32", + ParsedType::U64 => "-> i64", + ParsedType::F32 => "-> f32", + ParsedType::F64 => "-> f64", + ParsedType::Boolean => "-> i32", + _ => "", + } + .to_string() + } + + 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 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 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::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) + ); + }, + } + } + + fn generate_fn_epilog(&self) -> proc_macro2::TokenStream { + match self { + ParsedType::Empty => quote! {}, + ParsedType::I8 => quote! { + return result as _; + }, + ParsedType::I16 => quote! { + return result as _; + }, + ParsedType::I32 => quote! { + return result as _; + }, + ParsedType::I64 => quote! { + return result as _; + }, + ParsedType::U8 => quote! { + return result as _; + }, + ParsedType::U16 => quote! { + return result as _; + }, + ParsedType::U32 => quote! { + return result as _; + }, + ParsedType::U64 => quote! { + return result as _; + }, + ParsedType::F32 => quote! { + return result as _; + }, + ParsedType::F64 => quote! { + return result as _; + }, + ParsedType::Boolean => quote! { + return result as _; + }, + ParsedType::Utf8String => quote! { + fluence::set_result_ptr(result.as_ptr() as _); + fluence::set_result_size(result.len() as _); + std::mem::forget(result); + }, + ParsedType::ByteVector => quote! { + 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); + }, + } + } +} diff --git a/crates/macro/src/token_stream_generator/fn_generator.rs b/crates/macro/src/token_stream_generator/fn_generator.rs index b8e7071..24a34f9 100644 --- a/crates/macro/src/token_stream_generator/fn_generator.rs +++ b/crates/macro/src/token_stream_generator/fn_generator.rs @@ -14,12 +14,12 @@ * limitations under the License. */ -use crate::fce_ast_types; -use crate::parsed_type::MacroPartsGenerator; use super::GENERATED_FUNCS_PREFIX; use super::GENERATED_SECTION_NAME; use super::GENERATED_SECTION_PREFIX; use super::TokenStreamGenerator; +use crate::fce_ast_types; +use crate::parsed_type::GlueCodeGenerator; use proc_macro2::TokenStream; use quote::quote; @@ -29,7 +29,7 @@ impl TokenStreamGenerator for fce_ast_types::AstFunctionItem { fn generate_token_stream(self) -> syn::Result { let data = serde_json::to_string(&self).unwrap(); let data_size = data.len(); - let func_name = self.name; + let func_name = self.rust_name; let prefix = GENERATED_FUNCS_PREFIX; let section_name = GENERATED_SECTION_NAME; @@ -67,17 +67,19 @@ impl TokenStreamGenerator for fce_ast_types::AstFunctionItem { )] #[doc(hidden)] #[allow(clippy::all)] - pub extern "C" fn #prefix#func_name(#(#raw_args)*) #return_type { + pub extern "C" unsafe fn #prefix#func_name(#(#raw_args)*) #return_type { #prolog + // calling the original function with converted args #return_expression #func_name(#(#args)*); + // return value conversation from Rust type to a Wasm type #epilog } #[cfg(target_arch = "wasm32")] - #[allow(clippy::all)] #[doc(hidden)] + #[allow(clippy::all)] #[link_section = #section_name] pub static #func_name#section_prefix#generated_global_name: [u8; #data_size] = { #data }; }; diff --git a/crates/macro/src/token_stream_generator/foreign_mod_generator.rs b/crates/macro/src/token_stream_generator/foreign_mod_generator.rs index e24ef52..bc6bc53 100644 --- a/crates/macro/src/token_stream_generator/foreign_mod_generator.rs +++ b/crates/macro/src/token_stream_generator/foreign_mod_generator.rs @@ -14,13 +14,63 @@ * limitations under the License. */ -use crate::fce_ast_types; use super::TokenStreamGenerator; +use super::GENERATED_FUNCS_PREFIX; +use super::GENERATED_SECTION_NAME; +use super::GENERATED_SECTION_PREFIX; +use crate::fce_ast_types; use proc_macro2::TokenStream; +use quote::quote; impl TokenStreamGenerator for fce_ast_types::AstExternModItem { fn generate_token_stream(self) -> syn::Result { - unimplemented!() + let data = serde_json::to_string(&self).unwrap(); + let data_size = data.len(); + + let prefix = GENERATED_FUNCS_PREFIX; + let section_name = GENERATED_SECTION_NAME; + let section_prefix = GENERATED_SECTION_PREFIX; + let generated_global_name = uuid::Uuid::new_v4().to_string(); + let visibility = "pub"; + + let wasm_import_module_name = self.namespace; + + let glue_code = quote! { + #[link(wasm_import_module = #wasm_import_module_name)] + #[cfg(target_arch = "wasm32")] + extern "C" { + fn #import_name(#(#raw_args),*) #import_ret_type; + } + + #[cfg(target_arch = "wasm32")] + unsafe fn #glue_import_name(#(#args),*) #glue_ret_type { + #prolog + + #import_name(); + } + + #[cfg_attr(target_arch = "wasm32")] + #[doc(hidden)] + #[allow(clippy::all)] + #visibility unsafe fn #prefix#func_name(#(#args)*) #return_type { + // arg conversations from Rust types to Wasm types + #prolog + + // calling the original function with converted args + #return_expression #func_name(#(#raw_args)*); + + // return value conversation from Wasm type to a Rust type + #epilog + } + + #[cfg(target_arch = "wasm32")] + #[doc(hidden)] + #[allow(clippy::all)] + #[link_section = #section_name] + pub static #func_name#section_prefix#generated_global_name: [u8; #data_size] = { #data }; + }; + + Ok(glue_code) } }