mirror of
https://github.com/fluencelabs/marine-rs-sdk-test
synced 2024-12-04 15:20:18 +00:00
update macro crate
This commit is contained in:
parent
aa73411663
commit
79d7e181ee
@ -21,4 +21,5 @@ proc-macro = true
|
||||
syn = { version = '1.0.33', features = ['full'] }
|
||||
quote = "1.0.7"
|
||||
proc-macro2 = "1.0.18"
|
||||
|
||||
fluence-sdk-main = { path = "../main", version = "=0.1.11" }
|
||||
|
41
crates/macro/src/fce_ast_types.rs
Normal file
41
crates/macro/src/fce_ast_types.rs
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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::parsed_type::ParsedType;
|
||||
|
||||
pub(crate) struct AstFunctionItem {
|
||||
pub(crate) name: String,
|
||||
pub(crate) input_types: Vec<ParsedType>,
|
||||
// fce supports only one return value now,
|
||||
// waiting for adding multi-value support in Wasmer.
|
||||
pub(crate) output_type: ParsedType,
|
||||
}
|
||||
|
||||
pub(crate) struct AstRecordItem {
|
||||
pub(crate) fields: Vec<ParsedType>,
|
||||
}
|
||||
|
||||
pub(crate) struct AstExternModItem {
|
||||
pub(crate) namespace: String,
|
||||
// only imports are possible here
|
||||
pub(crate) imports: Vec<AstFunctionItem>,
|
||||
}
|
||||
|
||||
pub(crate) enum FCEAst {
|
||||
Function(AstFunctionItem),
|
||||
Record(AstRecordItem),
|
||||
ExternMod(AstExternModItem),
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018 Fluence Labs Limited
|
||||
* 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.
|
||||
@ -13,197 +13,32 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
//! This module defines an `invocation_handler` attribute procedural macro. It can be used to
|
||||
//! simplify the signature of the main module invocation handler:
|
||||
//!
|
||||
//! ```
|
||||
//! use fluence::sdk::*;
|
||||
//!
|
||||
//! #[invocation_handler]
|
||||
//! fn greeting(name: String) -> String {
|
||||
//! format!("Hello from Fluence to {}", name)
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! To use this macro with a function `f` certain conditions must be met:
|
||||
//! 1. `f` shouldn't have more than one input argument.
|
||||
//! 2. `f` shouldn't be `unsafe`, `const`, generic, have custom ABI linkage or variadic param.
|
||||
//! 3. The type of `f` input (if it presents) and output parameters should be one from
|
||||
//! {String, Vec<u8>} set.
|
||||
//! 4. `f` shouldn't have the name `invoke`.
|
||||
//!
|
||||
//! For troubleshooting and macros debugging [cargo expand](https://github.com/dtolnay/cargo-expand)
|
||||
//! can be used.
|
||||
//!
|
||||
//! Internally this macro creates a new function `invoke` that converts a raw argument to the
|
||||
//! appropriate format, calls `f` and then writes `f` result via `memory::write_response_to_mem` to
|
||||
//! module memory. So to use this crate apart from `fluence` `fluence_sdk_main` has to be imported.
|
||||
//!
|
||||
//! The macro also has the `init_fn` and `side_modules` attributes. The first one that can be used
|
||||
//! for specifying initialization function name. This function is called only once at the first
|
||||
//! call of the invoke function. It can be used like this:
|
||||
//!
|
||||
//! ```
|
||||
//! use fluence::sdk::*;
|
||||
//! use log::info;
|
||||
//!
|
||||
//! fn init() {
|
||||
//! logger::WasmLogger::init_with_level(log::Level::Info).is_ok()
|
||||
//! }
|
||||
//!
|
||||
//! #[invocation_handler(init_fn = init)]
|
||||
//! fn greeting(name: String) -> String {
|
||||
//! info!("{} has been successfully greeted", name);
|
||||
//! format!("Hello from Fluence to {}", name)
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The second macro could be used for generate API to connect with side modules like SQlite and
|
||||
//! Redis. It can be used like this:
|
||||
//! ```
|
||||
//! use fluence::sdk::*;
|
||||
//!
|
||||
//! #[invocation_handler(side_modules = (sqlite, redis))]
|
||||
//! fn greeting(name: String) -> String {
|
||||
//! sqlite::call("SELECT * from users");
|
||||
//! sqlite::call("GET user");
|
||||
//! format!("Hello from Fluence to {}", name)
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! Please find more examples [here](https://github.com/fluencelabs/tutorials).
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/fluence-sdk-macro/0.1.11")]
|
||||
#![deny(
|
||||
dead_code,
|
||||
// dead_code,
|
||||
nonstandard_style,
|
||||
unused_imports,
|
||||
unused_mut,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![recursion_limit = "128"]
|
||||
#![recursion_limit = "1024"]
|
||||
|
||||
mod macro_attr_parser;
|
||||
mod macro_input_parser;
|
||||
mod fce_ast_types;
|
||||
mod parsed_type;
|
||||
mod macro_impl;
|
||||
mod parse_macro_input;
|
||||
mod token_stream_generator;
|
||||
|
||||
use crate::macro_attr_parser::{generate_side_modules_glue_code, HandlerAttrs};
|
||||
use crate::macro_input_parser::{InputTypeGenerator, ParsedType, ReturnTypeGenerator};
|
||||
use macro_impl::fce_impl;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{parse::Error, parse_macro_input, ItemFn, Visibility};
|
||||
|
||||
fn invoke_handler_impl(
|
||||
attr: proc_macro2::TokenStream,
|
||||
fn_item: ItemFn,
|
||||
) -> syn::Result<proc_macro2::TokenStream> {
|
||||
let ItemFn {
|
||||
vis,
|
||||
sig,
|
||||
..
|
||||
} = &fn_item;
|
||||
|
||||
match vis {
|
||||
Visibility::Public(_) => {},
|
||||
_ => return Err(Error::new(
|
||||
vis.span(),
|
||||
"The #[faas_export] could be applied only to public functions",
|
||||
))
|
||||
}
|
||||
|
||||
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 attrs = syn::parse2::<HandlerAttrs>(attr)?;
|
||||
let raw_init_fn_name = attrs.init_fn_name();
|
||||
let raw_side_modules_list = attrs.side_modules();
|
||||
|
||||
let resulted_invoke = match raw_init_fn_name {
|
||||
Some(init_fn_name) => {
|
||||
let init_fn_name = syn::parse_str::<syn::Ident>(init_fn_name)?;
|
||||
quote! {
|
||||
#fn_item
|
||||
|
||||
static mut __FLUENCE_SDK_IS_INITED_d28374a960b570e5db00dfe7a0c7b93: bool = false;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn invoke(ptr: *mut u8, len: usize) -> std::ptr::NonNull<u8> {
|
||||
if !__FLUENCE_SDK_IS_INITED_d28374a960b570e5db00dfe7a0c7b93 {
|
||||
#init_fn_name();
|
||||
unsafe { __FLUENCE_SDK_IS_INITED_d28374a960b570e5db00dfe7a0c7b93 = true; }
|
||||
}
|
||||
|
||||
#prolog
|
||||
|
||||
#epilog
|
||||
}
|
||||
}
|
||||
},
|
||||
None => quote! {
|
||||
#fn_item
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn invoke(ptr: *mut u8, len: usize) -> std::ptr::NonNull<u8> {
|
||||
#prolog
|
||||
|
||||
#epilog
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
match raw_side_modules_list {
|
||||
Some(side_modules) => {
|
||||
let side_modules_glue_code = generate_side_modules_glue_code(side_modules)?;
|
||||
Ok(quote! {
|
||||
#side_modules_glue_code
|
||||
#resulted_invoke
|
||||
})
|
||||
},
|
||||
_ => Ok(resulted_invoke),
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn faas_export(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let fn_item = parse_macro_input!(input as ItemFn);
|
||||
match invoke_handler_impl(attr.into(), fn_item) {
|
||||
pub fn fce(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
// into converts proc_macro::TokenStream to proc_macro2::TokenStream
|
||||
match fce_impl(input.into()) {
|
||||
Ok(v) => v,
|
||||
// converts syn:error to proc_macro2::TokenStream
|
||||
Err(e) => e.to_compile_error(),
|
||||
|
@ -1,208 +0,0 @@
|
||||
/*
|
||||
* 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 quote::quote;
|
||||
use syn::export::TokenStream2;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
|
||||
pub struct HandlerAttrs {
|
||||
handler_attrs: Vec<HandlerAttr>,
|
||||
}
|
||||
|
||||
pub enum HandlerAttr {
|
||||
InitFnName(String),
|
||||
SideModules(Vec<String>),
|
||||
}
|
||||
|
||||
impl HandlerAttrs {
|
||||
pub fn init_fn_name(&self) -> Option<&str> {
|
||||
self.handler_attrs
|
||||
.iter()
|
||||
.filter_map(|attr| match attr {
|
||||
HandlerAttr::InitFnName(name) => Some(&name[..]),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
pub fn side_modules(&self) -> Option<&Vec<String>> {
|
||||
self.handler_attrs
|
||||
.iter()
|
||||
.filter_map(|attr| match attr {
|
||||
HandlerAttr::SideModules(modules) => Some(modules),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for HandlerAttrs {
|
||||
fn default() -> Self {
|
||||
HandlerAttrs {
|
||||
handler_attrs: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for HandlerAttrs {
|
||||
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||
let mut attrs = HandlerAttrs::default();
|
||||
if input.is_empty() {
|
||||
return Ok(attrs);
|
||||
}
|
||||
|
||||
let attr_opts =
|
||||
syn::punctuated::Punctuated::<HandlerAttr, syn::token::Comma>::parse_terminated(input)?;
|
||||
attrs.handler_attrs = attr_opts.into_iter().collect();
|
||||
|
||||
Ok(attrs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for HandlerAttr {
|
||||
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||
// trying to parse the `init_fn`/`side_modules`/... tokens
|
||||
let attr_name = input.step(|cursor| match cursor.ident() {
|
||||
Some((ident, rem)) => Ok((ident, rem)),
|
||||
None => Err(cursor.error("Expected a valid ident")),
|
||||
})?;
|
||||
|
||||
match attr_name.to_string().as_str() {
|
||||
"init_fn" => {
|
||||
// trying to parse `=`
|
||||
input.parse::<::syn::token::Eq>()?;
|
||||
|
||||
// trying to parse a init function name
|
||||
match input.parse::<syn::Ident>() {
|
||||
Ok(init_fn_name) => Ok(HandlerAttr::InitFnName(init_fn_name.to_string())),
|
||||
Err(_) => Err(syn::Error::new(
|
||||
attr_name.span(),
|
||||
"Expected a function name",
|
||||
)),
|
||||
}
|
||||
},
|
||||
|
||||
"side_modules" => {
|
||||
// trying to parse `=`
|
||||
input.parse::<::syn::token::Eq>()?;
|
||||
|
||||
// check for parens
|
||||
let raw_side_modules_list = match syn::group::parse_parens(&input) {
|
||||
Ok(parens) => parens.content,
|
||||
_ => {
|
||||
match input.parse::<syn::Ident>() {
|
||||
Ok(module_name) => return Ok(HandlerAttr::SideModules(vec![module_name.to_string()])),
|
||||
Err(_) => return Err(syn::Error::new(
|
||||
attr_name.span(),
|
||||
"Expected a module name name",
|
||||
)),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let raw_side_modules_opts =
|
||||
syn::punctuated::Punctuated::<syn::Ident, syn::token::Comma>::parse_terminated(
|
||||
&raw_side_modules_list,
|
||||
)?;
|
||||
|
||||
let side_modules = raw_side_modules_opts
|
||||
.iter()
|
||||
.map(|c| c.to_string())
|
||||
.collect();
|
||||
|
||||
Ok(HandlerAttr::SideModules(side_modules))
|
||||
},
|
||||
|
||||
_ => Err(syn::Error::new(
|
||||
attr_name.span(),
|
||||
"Expected a `side_modules` or `init_fn` tokens in invocation_handler macros attributes",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_side_modules_glue_code(side_modules_list: &[String]) -> syn::Result<TokenStream2> {
|
||||
let mut modules_glue_code = quote!();
|
||||
|
||||
for module_name in side_modules_list {
|
||||
let module_name_ident = syn::parse_str::<syn::Ident>(&module_name)?;
|
||||
|
||||
modules_glue_code = quote! {
|
||||
pub mod #module_name_ident {
|
||||
#[link(wasm_import_module = #module_name)]
|
||||
extern "C" {
|
||||
// Allocate chunk of module memory, and return a pointer to that region
|
||||
pub fn allocate(size: usize) -> i32;
|
||||
|
||||
// Deallocate chunk of module memory after it's not used anymore
|
||||
pub fn deallocate(ptr: i32, size: usize);
|
||||
|
||||
// Call module's invocation handler with data specified by pointer and size
|
||||
pub fn invoke(ptr: i32, size: usize) -> i32;
|
||||
|
||||
// Read 1 byte from ptr location of module memory
|
||||
pub fn load(ptr: i32) -> u8;
|
||||
|
||||
// Put 1 byte at ptr location in module memory
|
||||
pub fn store(ptr: *mut i32, byte: u8);
|
||||
}
|
||||
|
||||
// Execute query on module
|
||||
pub fn call(request: &[u8]) -> Vec<u8> {
|
||||
unsafe {
|
||||
// Allocate memory for the query in module
|
||||
let query_ptr = allocate(request.len());
|
||||
|
||||
// Store query in module's memory
|
||||
for (i, byte) in request.iter().enumerate() {
|
||||
let ptr = query_ptr + i as i32;
|
||||
store(ptr as *mut i32, *byte);
|
||||
}
|
||||
|
||||
// Execute the query, and get pointer to the result
|
||||
let response_ptr = invoke(query_ptr, request.len());
|
||||
|
||||
// First 4 bytes at result_ptr location encode result size, read that first
|
||||
let mut response_size: usize = 0;
|
||||
for byte_id in 0..3 {
|
||||
let ptr = response_ptr + byte_id as i32;
|
||||
let b = load(ptr) as usize;
|
||||
response_size = response_size + (b << (8 * byte_id));
|
||||
}
|
||||
// Now we know exact size of the query execution result
|
||||
|
||||
// Read query execution result byte-by-byte
|
||||
let mut response_bytes = vec![0; response_size as usize];
|
||||
for byte_id in 0..response_size {
|
||||
let ptr = response_ptr + (byte_id + 4) as i32;
|
||||
let b = load(ptr);
|
||||
response_bytes[byte_id as usize] = b;
|
||||
}
|
||||
|
||||
// Deallocate response
|
||||
deallocate(response_ptr, response_size + 4);
|
||||
|
||||
response_bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#modules_glue_code
|
||||
}
|
||||
}
|
||||
|
||||
Ok(modules_glue_code)
|
||||
}
|
77
crates/macro/src/macro_impl.rs
Normal file
77
crates/macro/src/macro_impl.rs
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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::parse_macro_input::ParseMacroInput;
|
||||
use crate::token_stream_generator::TokenStreamGenerator;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use syn::Result;
|
||||
|
||||
pub(super) fn fce_impl(tokens: TokenStream) -> Result<TokenStream> {
|
||||
let item = syn::parse2::<syn::Item>(tokens)?;
|
||||
let fce_ast_item = item.parse_macro_input()?;
|
||||
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)
|
||||
*/
|
||||
}
|
41
crates/macro/src/parse_macro_input.rs
Normal file
41
crates/macro/src/parse_macro_input.rs
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
mod item_fn;
|
||||
mod item_foreign_mod;
|
||||
mod item_record;
|
||||
|
||||
use crate::fce_ast_types::FCEAst;
|
||||
|
||||
pub(crate) trait ParseMacroInput {
|
||||
fn parse_macro_input(self) -> syn::Result<FCEAst>;
|
||||
}
|
||||
|
||||
impl ParseMacroInput for syn::Item {
|
||||
fn parse_macro_input(self) -> syn::Result<FCEAst> {
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
match self {
|
||||
syn::Item::Fn(function) => function.parse_macro_input(),
|
||||
syn::Item::ForeignMod(extern_mod) => extern_mod.parse_macro_input(),
|
||||
syn::Item::Struct(item_struct) => item_struct.parse_macro_input(),
|
||||
_ => Err(syn::Error::new(
|
||||
self.span(),
|
||||
"At now, #[fce] could be applied only to a function, extern block or struct",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
109
crates/macro/src/parse_macro_input/item_fn.rs
Normal file
109
crates/macro/src/parse_macro_input/item_fn.rs
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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::ParseMacroInput;
|
||||
use crate::fce_ast_types;
|
||||
use crate::fce_ast_types::FCEAst;
|
||||
|
||||
use syn::Result;
|
||||
|
||||
impl ParseMacroInput for syn::ItemFn {
|
||||
fn parse_macro_input(self) -> Result<FCEAst> {
|
||||
parse_function(self.sig, self.vis).map(|f| FCEAst::Function(f))
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn parse_function(
|
||||
function_sig: syn::Signature,
|
||||
function_vis: syn::Visibility,
|
||||
) -> Result<fce_ast_types::AstFunctionItem> {
|
||||
use crate::parsed_type::ParsedType;
|
||||
|
||||
check_func(&function_sig, function_vis)?;
|
||||
|
||||
let syn::Signature { inputs, output, .. } = function_sig;
|
||||
|
||||
let input_types = inputs
|
||||
.iter()
|
||||
.map(ParsedType::from_fn_arg)
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
let output_type = ParsedType::from_return_type(&output)?;
|
||||
|
||||
let ast_function_item = fce_ast_types::AstFunctionItem {
|
||||
name: function_sig.ident.to_string(),
|
||||
input_types,
|
||||
output_type,
|
||||
};
|
||||
|
||||
Ok(ast_function_item)
|
||||
}
|
||||
|
||||
/// Check whether the #[fce] macro could be applied to a function.
|
||||
fn check_func(function_sig: &syn::Signature, function_vis: syn::Visibility) -> Result<()> {
|
||||
use syn::Error;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
let syn::Signature {
|
||||
constness,
|
||||
unsafety,
|
||||
abi,
|
||||
variadic,
|
||||
generics,
|
||||
..
|
||||
} = function_sig;
|
||||
|
||||
if let Some(constness) = constness {
|
||||
return Err(Error::new(
|
||||
constness.span,
|
||||
"FCE export function shouldn't be constant",
|
||||
));
|
||||
}
|
||||
if let Some(unsafety) = unsafety {
|
||||
return Err(Error::new(
|
||||
unsafety.span,
|
||||
"FCE export function shouldn't be unsafe",
|
||||
));
|
||||
}
|
||||
if let Some(abi) = abi {
|
||||
return Err(Error::new(
|
||||
abi.extern_token.span,
|
||||
"FCE export function shouldn't have any custom linkage",
|
||||
));
|
||||
}
|
||||
if !generics.params.is_empty() || generics.where_clause.is_some() {
|
||||
return Err(Error::new(
|
||||
function_sig.span(),
|
||||
"FCE export function shouldn't use template parameters",
|
||||
));
|
||||
}
|
||||
if let Some(_) = variadic {
|
||||
return Err(Error::new(
|
||||
variadic.span(),
|
||||
"FCE export function shouldn't use variadic interface",
|
||||
));
|
||||
}
|
||||
|
||||
// TODO: check for a lifetime
|
||||
|
||||
match function_vis {
|
||||
syn::Visibility::Public(_) => Ok(()),
|
||||
_ => Err(Error::new(
|
||||
variadic.span(),
|
||||
"FCE export function should be public",
|
||||
)),
|
||||
}
|
||||
}
|
117
crates/macro/src/parse_macro_input/item_foreign_mod.rs
Normal file
117
crates/macro/src/parse_macro_input/item_foreign_mod.rs
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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::ParseMacroInput;
|
||||
use crate::fce_ast_types;
|
||||
use crate::fce_ast_types::FCEAst;
|
||||
|
||||
use syn::Error;
|
||||
use syn::Result;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
const LINK_DIRECTIVE_NAME: &str = "link";
|
||||
const LINK_NAME_DIRECTIVE_NAME: &str = "link_name";
|
||||
const WASM_IMPORT_MODULE_DIRECTIVE_NAME: &str = "wasm_import_module";
|
||||
|
||||
impl ParseMacroInput for syn::ItemForeignMod {
|
||||
fn parse_macro_input(self) -> Result<FCEAst> {
|
||||
match self.abi.name {
|
||||
Some(ref name) if name.value() != "C".to_string() => {
|
||||
return Err(Error::new(self.span(), "only 'C' abi is allowed"))
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
let self_span = self.span();
|
||||
|
||||
let imports = self
|
||||
.items
|
||||
.into_iter()
|
||||
.map(parse_raw_foreign_item)
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
// try to find and parse wasm module name from
|
||||
// #[link(wasm_import_module = "host")]
|
||||
let wasm_import_module: Option<String> = self
|
||||
.attrs
|
||||
.into_iter()
|
||||
.filter_map(|attr| attr.parse_meta().ok())
|
||||
.filter(|meta| meta.path().is_ident(LINK_DIRECTIVE_NAME))
|
||||
.filter_map(|meta| match meta {
|
||||
syn::Meta::List(meta_list) => Some(meta_list),
|
||||
_ => None,
|
||||
})
|
||||
.filter_map(|meta_list| match meta_list.nested.first().unwrap() {
|
||||
syn::NestedMeta::Meta(meta) => Some(meta.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.filter(|meta| meta.path().is_ident(WASM_IMPORT_MODULE_DIRECTIVE_NAME))
|
||||
.map(extract_value)
|
||||
.collect();
|
||||
|
||||
match wasm_import_module {
|
||||
Some(namespace) => {
|
||||
let extern_mod_item = fce_ast_types::AstExternModItem { namespace, imports };
|
||||
Ok(FCEAst::ExternMod(extern_mod_item))
|
||||
}
|
||||
None => Err(Error::new(
|
||||
self_span,
|
||||
"import module name should be defined by 'wasm_import_module' directive",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_raw_foreign_item(raw_item: syn::ForeignItem) -> Result<fce_ast_types::AstFunctionItem> {
|
||||
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",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// parse the link_name attribute
|
||||
// #[link_name = "put"]
|
||||
// fn ipfs_put(ptr: i32, size: i32);
|
||||
let link_name: Option<String> = function_item
|
||||
.attrs
|
||||
.iter()
|
||||
.filter_map(|attr| attr.parse_meta().ok())
|
||||
.filter(|meta| meta.path().is_ident(LINK_NAME_DIRECTIVE_NAME))
|
||||
.map(extract_value)
|
||||
.collect();
|
||||
|
||||
let mut function_item = super::item_fn::parse_function(function_item.sig, function_item.vis)?;
|
||||
|
||||
if let Some(link_name) = link_name {
|
||||
function_item.name = link_name;
|
||||
}
|
||||
|
||||
Ok(function_item)
|
||||
}
|
||||
|
||||
fn extract_value(nested_meta: syn::Meta) -> Option<String> {
|
||||
match nested_meta {
|
||||
syn::Meta::NameValue(name_value) => match name_value.lit {
|
||||
syn::Lit::Str(str) => Some(str.value()),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
25
crates/macro/src/parse_macro_input/item_record.rs
Normal file
25
crates/macro/src/parse_macro_input/item_record.rs
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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::ParseMacroInput;
|
||||
use crate::fce_ast_types::FCEAst;
|
||||
|
||||
use syn::Result;
|
||||
|
||||
impl ParseMacroInput for syn::ItemStruct {
|
||||
fn parse_macro_input(self) -> Result<FCEAst> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
@ -15,13 +15,26 @@
|
||||
*/
|
||||
|
||||
use quote::quote;
|
||||
use syn::{parse::Error, spanned::Spanned};
|
||||
use syn::parse::Error;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum ParsedType {
|
||||
pub(crate) enum ParsedType {
|
||||
Empty,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
F32,
|
||||
F64,
|
||||
Boolean,
|
||||
Utf8String,
|
||||
ByteVector,
|
||||
Empty,
|
||||
Record(String),
|
||||
}
|
||||
|
||||
impl ParsedType {
|
||||
@ -103,6 +116,17 @@ impl ParsedType {
|
||||
})?;
|
||||
|
||||
match type_segment.ident.to_string().as_str() {
|
||||
"i8" => Ok(ParsedType::I8),
|
||||
"i16" => Ok(ParsedType::I16),
|
||||
"i32" => Ok(ParsedType::I32),
|
||||
"i64" => Ok(ParsedType::I64),
|
||||
"u8" => Ok(ParsedType::U8),
|
||||
"u16" => Ok(ParsedType::U16),
|
||||
"u32" => Ok(ParsedType::U32),
|
||||
"u64" => Ok(ParsedType::U64),
|
||||
"f32" => Ok(ParsedType::F32),
|
||||
"f64" => Ok(ParsedType::F32),
|
||||
"bool" => Ok(ParsedType::Boolean),
|
||||
"String" => Ok(ParsedType::Utf8String),
|
||||
"Vec" => match parse_vec_bracket(&type_segment.arguments) {
|
||||
Ok(value) => match value.as_str() {
|
||||
@ -114,9 +138,9 @@ impl ParsedType {
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
_ => Err(Error::new(
|
||||
type_name => Err(Error::new(
|
||||
type_segment.span(),
|
||||
"Only String and Vec<u8> input types are supported (also, it is possible not to specify the input argument)",
|
||||
format!("{} is unsupported", type_name),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -136,15 +160,15 @@ impl ParsedType {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InputTypeGenerator {
|
||||
pub trait PrologGenerator {
|
||||
fn generate_fn_prolog(&self) -> proc_macro2::TokenStream;
|
||||
}
|
||||
|
||||
pub trait ReturnTypeGenerator {
|
||||
pub trait EpilogGenerator {
|
||||
fn generate_fn_epilog(&self) -> proc_macro2::TokenStream;
|
||||
}
|
||||
|
||||
impl InputTypeGenerator for ParsedType {
|
||||
impl PrologGenerator for ParsedType {
|
||||
fn generate_fn_prolog(&self) -> proc_macro2::TokenStream {
|
||||
match self {
|
||||
ParsedType::Utf8String => quote! {
|
||||
@ -160,11 +184,12 @@ impl InputTypeGenerator for ParsedType {
|
||||
// this way does it without any additional imports of the export allocator module
|
||||
let arg = memory::read_request_from_mem(ptr, len);
|
||||
},
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ReturnTypeGenerator for ParsedType {
|
||||
impl EpilogGenerator for ParsedType {
|
||||
fn generate_fn_epilog(&self) -> proc_macro2::TokenStream {
|
||||
match self {
|
||||
ParsedType::Utf8String => quote! {
|
||||
@ -178,6 +203,7 @@ impl ReturnTypeGenerator for ParsedType {
|
||||
.expect("Putting result vector to memory has failed")
|
||||
},
|
||||
ParsedType::Empty => quote! {},
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
37
crates/macro/src/token_stream_generator.rs
Normal file
37
crates/macro/src/token_stream_generator.rs
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
mod fn_generator;
|
||||
mod foreign_mod_generator;
|
||||
mod record_generator;
|
||||
|
||||
use crate::fce_ast_types::FCEAst;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
pub(crate) trait TokenStreamGenerator {
|
||||
fn generate_token_stream(self) -> syn::Result<TokenStream>;
|
||||
}
|
||||
|
||||
impl TokenStreamGenerator for FCEAst {
|
||||
fn generate_token_stream(self) -> syn::Result<TokenStream> {
|
||||
match self {
|
||||
FCEAst::Function(ast_function) => ast_function.generate_token_stream(),
|
||||
FCEAst::ExternMod(ast_extern) => ast_extern.generate_token_stream(),
|
||||
FCEAst::Record(ast_record) => ast_record.generate_token_stream(),
|
||||
}
|
||||
}
|
||||
}
|
26
crates/macro/src/token_stream_generator/fn_generator.rs
Normal file
26
crates/macro/src/token_stream_generator/fn_generator.rs
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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::fce_ast_types;
|
||||
use super::TokenStreamGenerator;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
impl TokenStreamGenerator for fce_ast_types::AstFunctionItem {
|
||||
fn generate_token_stream(self) -> syn::Result<TokenStream> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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::fce_ast_types;
|
||||
use super::TokenStreamGenerator;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
impl TokenStreamGenerator for fce_ast_types::AstExternModItem {
|
||||
fn generate_token_stream(self) -> syn::Result<TokenStream> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
26
crates/macro/src/token_stream_generator/record_generator.rs
Normal file
26
crates/macro/src/token_stream_generator/record_generator.rs
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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::fce_ast_types;
|
||||
use super::TokenStreamGenerator;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
impl TokenStreamGenerator for fce_ast_types::AstRecordItem {
|
||||
fn generate_token_stream(self) -> syn::Result<TokenStream> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user