Skip to content

Commit

Permalink
feat: make dynamic_link macro which adds validate interface automatic…
Browse files Browse the repository at this point in the history
…ally
  • Loading branch information
loloicci committed Sep 29, 2022
1 parent cde75b5 commit f1952a9
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 6 deletions.
2 changes: 1 addition & 1 deletion packages/derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ proc-macro = true
default = []

[dependencies]
syn = { version = "1.0", features = ["full"] }
syn = { version = "1.0", features = ["full", "parsing"] }
quote = "1.0"
proc-macro2 = "1.0"
proc-macro-error = { version = "1", default-features = false }
Expand Down
51 changes: 47 additions & 4 deletions packages/derive/src/dynamic_link.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{
AttributeArgs, Ident, ItemTrait, Lit, Meta, NestedMeta, Signature, TraitItem, TypeParamBound,
AttributeArgs, Ident, ItemTrait, Lit, Meta, NestedMeta, Signature, TraitItem, TypeParamBound, ReturnType, parse_quote
};

use crate::utils::{abort_by, collect_available_arg_types, has_return_value, make_typed_return};
Expand Down Expand Up @@ -86,9 +86,16 @@ pub fn generate_import_contract_declaration(
contract_struct_id,
&signatures,
);

let mut new_trait_def = trait_def.clone();
let method_validate_interface: TraitItem = parse_quote! {
fn validate_interface(&self, deps: cosmwasm_std::Deps) -> cosmwasm_std::StdResult<()>;
};
new_trait_def.items.push(method_validate_interface);

if does_use_user_defined_mock {
quote! {
#trait_def
#new_trait_def

#[cfg(target_arch = "wasm32")]
#extern_block
Expand All @@ -98,7 +105,7 @@ pub fn generate_import_contract_declaration(
}
} else {
quote! {
#trait_def
#new_trait_def

#extern_block

Expand Down Expand Up @@ -145,6 +152,30 @@ fn generate_extern_block(module_name: &str, methods: &[&Signature]) -> TokenStre
}
}

fn generate_validate_interface_method(methods: &[&Signature]) -> TokenStream {
let interfaces = methods.iter().map(|sig| {
let name = sig.ident.to_string();
// -1 for &self and +1 for arg `env`, so equals to len()
let input_len = sig.inputs.len();
let result_len = match sig.output {
ReturnType::Default => 0_usize,
ReturnType::Type(..) => 1_usize,
};
quote! {
wasmer_types::ExportType::new(#name, ([wasmer_types::Type::I32; #input_len], [wasmer_types::Type::I32; #result_len]).into())
}
});
quote! {
fn validate_interface(&self, deps: cosmwasm_std::Deps) -> cosmwasm_std::StdResult<()> {
let address = self.get_address();
let expected_interface: Vec<wasmer_types::ExportType<wasmer_types::FunctionType>> = vec![
#(#interfaces,)*
];
deps.api.validate_dynamic_link_interface(&address, &expected_interface)
}
}
}

fn generate_implements(
module_name: &str,
trait_id: &Ident,
Expand All @@ -154,9 +185,11 @@ fn generate_implements(
let impl_funcs = methods
.iter()
.map(|sig| generate_serialization_func(module_name, sig));
let impl_validate_interface = generate_validate_interface_method(methods);
quote! {
impl #trait_id for #struct_id {
#(#impl_funcs)*
#impl_validate_interface
}
}
}
Expand Down Expand Up @@ -223,7 +256,7 @@ fn make_call_function_and_return(
#[cfg(test)]
mod tests {
use super::*;
use syn::{parse_quote, ItemTrait, Signature};
use syn::{ItemTrait, Signature};

#[test]
fn make_call_function_and_return_works() {
Expand Down Expand Up @@ -474,6 +507,16 @@ mod tests {
cosmwasm_std::from_slice(&vec_result).unwrap()
}
}

fn validate_interface(&self, deps: cosmwasm_std::Deps) -> cosmwasm_std::StdResult<()> {
let address = self.get_address();
let expected_interface: Vec<wasmer_types::ExportType<wasmer_types::FunctionType>> = vec![
wasmer_types::ExportType::new("foo", ([wasmer_types::Type::I32; 3usize], [wasmer_types::Type::I32; 1usize]).into()),
wasmer_types::ExportType::new("bar", ([wasmer_types::Type::I32; 1usize], [wasmer_types::Type::I32; 0usize]).into()),
wasmer_types::ExportType::new("foobar", ([wasmer_types::Type::I32; 3usize], [wasmer_types::Type::I32; 1usize]).into()),
];
deps.api.validate_dynamic_link_interface(&address, &expected_interface)
}
}
};
assert_eq!(expected.to_string(), result_code);
Expand Down
12 changes: 11 additions & 1 deletion packages/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ pub fn callable_point(_attr: TokenStream, item: TokenStream) -> TokenStream {

/// This macro implements functions to call dynamic linked function for attributed trait.
///
/// To use this macro, the contract must declare the import
/// `wasmer-types = { version = "1.0.2", features = ["enable-serde"] }`
/// in Cargo.toml
///
/// This macro must take an attribute specifying a struct to implement the traits for.
/// The trait must have `cosmwasm_std::Contract` as a supertrait and each
/// methods of the trait must have `&self` receiver as its first argument.
Expand All @@ -138,7 +142,7 @@ pub fn callable_point(_attr: TokenStream, item: TokenStream) -> TokenStream {
/// example usage:
///
/// ```
/// use cosmwasm_std::{Addr, Contract, dynamic_link};
/// use cosmwasm_std::{Addr, Contract, Deps, StdResult, dynamic_link};
///
/// #[derive(Contract)]
/// struct ContractStruct {
Expand All @@ -157,6 +161,12 @@ pub fn callable_point(_attr: TokenStream, item: TokenStream) -> TokenStream {
/// fn callable_point_on_another_contract(&self, x: i32) -> i32 {
/// 42
/// }
///
/// // validate_interface is auto generated function from `dynamic_link` macro.
/// // this function must be defined in the mock.
/// fn validate_interface(&self, dep: Deps) -> StdResult<()> {
/// Ok(())
/// }
/// }
/// ```
#[proc_macro_error]
Expand Down

0 comments on commit f1952a9

Please sign in to comment.