use core::iter;
use crate::GenerateCode;
use derive_more::From;
use heck::ToLowerCamelCase as _;
use ir::{
Callable as _,
HexLiteral,
};
use proc_macro2::TokenStream as TokenStream2;
use quote::{
format_ident,
quote,
quote_spanned,
ToTokens,
};
use syn::spanned::Spanned as _;
#[derive(From)]
pub struct ItemImpls<'a> {
contract: &'a ir::Contract,
}
impl_as_ref_for_generator!(ItemImpls);
impl GenerateCode for ItemImpls<'_> {
fn generate_code(&self) -> TokenStream2 {
let item_impls = self
.contract
.module()
.impls()
.map(|item_impl| self.generate_item_impl(item_impl));
let inout_guards = self.generate_input_output_guards();
let trait_message_property_guards = self.generate_trait_message_property_guards();
quote! {
const _: () = {
use ::ink::codegen::{Env as _, StaticEnv as _};
#( #item_impls )*
#inout_guards
#trait_message_property_guards
};
}
}
}
impl ItemImpls<'_> {
fn generate_trait_message_property_guards(&self) -> TokenStream2 {
let storage_span = self.contract.module().storage().span();
let storage_ident = self.contract.module().storage().ident();
let trait_message_guards = self
.contract
.module()
.impls()
.filter_map(|item_impl| item_impl.trait_path().map(|trait_path| {
iter::repeat(trait_path).zip(item_impl.iter_messages())
}))
.flatten()
.map(|(trait_path, message)| {
let message_span = message.span();
let message_local_id = message.local_id().hex_padded_suffixed();
let message_guard_payable = message.is_payable().then(|| {
quote_spanned!(message_span=>
const _: ::ink::codegen::TraitMessagePayable<{
<<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::env::ContractEnv>::Env>
as #trait_path>::__ink_TraitInfo
as ::ink::reflect::TraitMessageInfo<#message_local_id>>::PAYABLE
}> = ::ink::codegen::TraitMessagePayable::<true>;
)
});
let message_guard_selector = message.user_provided_selector().map(|selector| {
let given_selector = selector.into_be_u32().hex_padded_suffixed();
quote_spanned!(message_span=>
const _: ::ink::codegen::TraitMessageSelector<{
::core::primitive::u32::from_be_bytes(
<<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::env::ContractEnv>::Env>
as #trait_path>::__ink_TraitInfo
as ::ink::reflect::TraitMessageInfo<#message_local_id>>::SELECTOR
)
}> = ::ink::codegen::TraitMessageSelector::<#given_selector>;
)
});
quote_spanned!(message_span=>
#message_guard_payable
#message_guard_selector
)
});
quote_spanned!(storage_span=>
#( #trait_message_guards )*
)
}
fn generate_input_output_guards(&self) -> TokenStream2 {
let storage_span = self.contract.module().storage().span();
let constructor_input_guards = self
.contract
.module()
.impls()
.flat_map(|item_impl| item_impl.iter_constructors())
.map(|constructor| {
let constructor_span = constructor.span();
let constructor_inputs = constructor.inputs().map(|input| {
let span = input.span();
let input_type = &*input.ty;
quote_spanned!(span=>
::ink::codegen::utils::consume_type::<
::ink::codegen::DispatchInput<#input_type>
>();
)
});
quote_spanned!(constructor_span=>
#( #constructor_inputs )*
)
});
let message_inout_guards = self
.contract
.module()
.impls()
.flat_map(|item_impl| item_impl.iter_messages())
.map(|message| {
let message_span = message.span();
let message_inputs = message.inputs().map(|input| {
let span = input.span();
let input_type = &*input.ty;
quote_spanned!(span=>
::ink::codegen::utils::consume_type::<
::ink::codegen::DispatchInput<#input_type>
>();
)
});
let message_output = message.output().map(|output_type| {
let span = output_type.span();
quote_spanned!(span=>
::ink::codegen::utils::consume_type::<
::ink::codegen::DispatchOutput<#output_type>
>();
)
});
quote_spanned!(message_span=>
#( #message_inputs )*
#message_output
)
});
quote_spanned!(storage_span=>
const _: () = {
#( #constructor_input_guards )*
#( #message_inout_guards )*
};
)
}
fn generate_trait_message(message: &ir::Message) -> TokenStream2 {
let span = message.span();
let attrs = message.attrs();
let vis = message.visibility();
let receiver = message.receiver();
let ident = message.ident();
let output_ident =
format_ident!("{}Output", ident.to_string().to_lower_camel_case());
let inputs = message.inputs();
let output = message
.output()
.cloned()
.unwrap_or_else(|| syn::parse_quote! { () });
let statements = message.statements();
let cfg_attrs = message.get_cfg_attrs(span);
quote_spanned!(span =>
#( #cfg_attrs )*
type #output_ident = #output;
#( #attrs )*
#vis fn #ident(#receiver #( , #inputs )* ) -> Self::#output_ident {
#( #statements )*
}
)
}
fn generate_trait_item_impl(item_impl: &ir::ItemImpl) -> TokenStream2 {
assert!(item_impl.trait_path().is_some());
let span = item_impl.span();
let attrs = item_impl.attrs();
let messages = item_impl
.iter_messages()
.map(|cws| Self::generate_trait_message(cws.callable()));
let trait_path = item_impl
.trait_path()
.expect("encountered missing trait path for trait impl block");
let self_type = item_impl.self_type();
quote_spanned!(span =>
#( #attrs )*
impl #trait_path for #self_type {
type __ink_TraitInfo = <::ink::reflect::TraitDefinitionRegistry<Environment>
as #trait_path>::__ink_TraitInfo;
#( #messages )*
}
)
}
fn generate_inherent_constructor(constructor: &ir::Constructor) -> TokenStream2 {
let span = constructor.span();
let attrs = constructor.attrs();
let vis = constructor.visibility();
let ident = constructor.ident();
let inputs = constructor.inputs();
let statements = constructor.statements();
let output = constructor.output();
quote_spanned!(span =>
#( #attrs )*
#[cfg(not(target_os = "dragonfly"))]
#vis fn #ident( #( #inputs ),* ) -> #output {
#( #statements )*
}
)
}
fn generate_inherent_message(message: &ir::Message) -> TokenStream2 {
let span = message.span();
let attrs = message.attrs();
let vis = message.visibility();
let receiver = message.receiver();
let ident = message.ident();
let inputs = message.inputs();
let output_arrow = message.output().map(|_| quote! { -> });
let output = message.output();
let statements = message.statements();
quote_spanned!(span =>
#( #attrs )*
#vis fn #ident(#receiver #( , #inputs )* ) #output_arrow #output {
#( #statements )*
}
)
}
fn generate_inherent_item_impl(item_impl: &ir::ItemImpl) -> TokenStream2 {
assert!(item_impl.trait_path().is_none());
let span = item_impl.span();
let attrs = item_impl.attrs();
let messages = item_impl
.iter_messages()
.map(|cws| Self::generate_inherent_message(cws.callable()));
let constructors = item_impl
.iter_constructors()
.map(|cws| Self::generate_inherent_constructor(cws.callable()));
let other_items = item_impl
.items()
.iter()
.filter_map(ir::ImplItem::filter_map_other_item)
.map(ToTokens::to_token_stream);
let self_type = item_impl.self_type();
quote_spanned!(span =>
#( #attrs )*
impl #self_type {
#( #constructors )*
#( #messages )*
#( #other_items )*
}
)
}
fn generate_item_impl_self_ty_guard(&self, item_impl: &ir::ItemImpl) -> TokenStream2 {
let self_ty = item_impl.self_type();
let span = self_ty.span();
let storage_ident = self.contract.module().storage().ident();
quote_spanned!(span =>
const _: ::ink::codegen::utils::IsSameType<#storage_ident> =
::ink::codegen::utils::IsSameType::<#self_ty>::new();
)
}
fn generate_item_impl(&self, item_impl: &ir::ItemImpl) -> TokenStream2 {
let self_ty_guard = self.generate_item_impl_self_ty_guard(item_impl);
let impl_block = match item_impl.trait_path() {
Some(_) => Self::generate_trait_item_impl(item_impl),
None => Self::generate_inherent_item_impl(item_impl),
};
quote! {
#self_ty_guard
#impl_block
}
}
}