use core::iter;
use crate::{
generator,
generator::solidity::{
solidity_call_type_ident,
solidity_selector,
solidity_selector_id,
},
GenerateCode,
};
use derive_more::From;
use ir::{
Callable,
CallableWithSelector,
Constructor,
HexLiteral as _,
Message,
};
use proc_macro2::{
Ident,
TokenStream as TokenStream2,
};
use quote::{
format_ident,
quote,
quote_spanned,
};
use syn::{
spanned::Spanned as _,
LitInt,
};
pub struct MessageDispatchable<'a> {
message: CallableWithSelector<'a, Message>,
id: TokenStream2,
}
pub struct ConstructorDispatchable<'a> {
constructor: CallableWithSelector<'a, Constructor>,
id: LitInt,
}
#[derive(From)]
pub struct Dispatch<'a> {
contract: &'a ir::Contract,
}
impl_as_ref_for_generator!(Dispatch);
impl GenerateCode for Dispatch<'_> {
fn generate_code(&self) -> TokenStream2 {
let messages = self.compose_messages_with_ids();
let constructors = self.compose_constructors_with_ids();
let contract_dispatchable_constructor_infos =
self.generate_dispatchable_constructor_infos();
let contract_dispatchable_messages_infos =
self.generate_dispatchable_message_infos();
let constructor_decoder_type =
self.generate_constructor_decoder_type(&constructors);
let message_decoder_type = self.generate_message_decoder_type(&messages);
let entry_points = self.generate_entry_points(&constructors, &messages);
let solidity_call_info = self.generate_solidity_call_info();
quote! {
#contract_dispatchable_constructor_infos
#contract_dispatchable_messages_infos
#constructor_decoder_type
#message_decoder_type
#solidity_call_info
#[cfg(not(any(test, feature = "std", feature = "ink-as-dependency")))]
mod __do_not_access__ {
use super::*;
#entry_points
}
}
}
}
impl Dispatch<'_> {
fn query_wildcard_message(&self) -> Option<usize> {
self.contract
.module()
.impls()
.flat_map(|item_impl| item_impl.iter_messages())
.position(|item| item.has_wildcard_selector())
}
fn query_wildcard_constructor(&self) -> Option<usize> {
self.contract
.module()
.impls()
.flat_map(|item_impl| item_impl.iter_constructors())
.position(|item| item.has_wildcard_selector())
}
fn compose_messages_with_ids(&self) -> Vec<MessageDispatchable> {
let storage_ident = self.contract.module().storage().ident();
self.contract
.module()
.impls()
.flat_map(|item_impl| {
iter::repeat(item_impl.trait_path()).zip(item_impl.iter_messages())
})
.flat_map(|(trait_path, message)| {
let mut message_dispatchables = Vec::new();
if self.contract.config().abi().is_ink() {
let span = message.span();
let id = if let Some(trait_path) = trait_path {
let local_id = message.local_id().hex_padded_suffixed();
quote_spanned!(span=>
{
::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<#local_id>>::SELECTOR
)
}
)
} else {
let id = message
.composed_selector()
.into_be_u32()
.hex_padded_suffixed();
quote_spanned!(span=>
#id
)
};
message_dispatchables.push(MessageDispatchable { message, id });
}
if self.contract.config().abi().is_solidity() {
let id = solidity_selector_id(&message);
message_dispatchables.push(MessageDispatchable { message, id });
}
message_dispatchables
})
.collect::<Vec<_>>()
}
fn compose_constructors_with_ids(&self) -> Vec<ConstructorDispatchable> {
self.contract
.module()
.impls()
.flat_map(|item_impl| item_impl.iter_constructors())
.map(|constructor| {
let id = constructor
.composed_selector()
.into_be_u32()
.hex_padded_suffixed();
ConstructorDispatchable { constructor, id }
})
.collect::<Vec<_>>()
}
fn generate_dispatchable_constructor_infos(&self) -> TokenStream2 {
let span = self.contract.module().storage().span();
let storage_ident = self.contract.module().storage().ident();
let constructor_infos = self
.contract
.module()
.impls()
.flat_map(|item_impl| item_impl.iter_constructors())
.map(|constructor| {
let constructor_span = constructor.span();
let constructor_ident = constructor.ident();
let payable = constructor.is_payable();
let selector_id = constructor.composed_selector().into_be_u32().hex_padded_suffixed();
let selector_bytes = constructor.composed_selector().hex_lits();
let cfg_attrs = constructor.get_cfg_attrs(constructor_span);
let output_type = constructor.output().map(quote::ToTokens::to_token_stream)
.unwrap_or_else(|| quote! { () });
let input_bindings = generator::input_bindings(constructor.inputs());
let input_tuple_type = generator::input_types_tuple(constructor.inputs());
let input_tuple_bindings = generator::input_bindings_tuple(constructor.inputs());
let constructor_return_type = quote_spanned!(constructor_span=>
<::ink::reflect::ConstructorOutputValue<#output_type>
as ::ink::reflect::ConstructorOutput<#storage_ident>>
);
quote_spanned!(constructor_span =>
#( #cfg_attrs )*
impl ::ink::reflect::DispatchableConstructorInfo<#selector_id> for #storage_ident {
type Input = #input_tuple_type;
type Output = #output_type;
type Storage = #storage_ident;
type Error = #constructor_return_type::Error;
const IS_RESULT: ::core::primitive::bool = #constructor_return_type::IS_RESULT;
const CALLABLE: fn(Self::Input) -> Self::Output = |#input_tuple_bindings| {
#storage_ident::#constructor_ident(#( #input_bindings ),* )
};
const PAYABLE: ::core::primitive::bool = #payable;
const SELECTOR: [::core::primitive::u8; 4usize] = [ #( #selector_bytes ),* ];
const LABEL: &'static ::core::primitive::str = ::core::stringify!(#constructor_ident);
}
)
});
quote_spanned!(span=>
#( #constructor_infos )*
)
}
fn generate_dispatchable_message_infos(&self) -> TokenStream2 {
let span = self.contract.module().storage().span();
let abi = self.contract.config().abi();
let inherent_message_infos = self
.contract
.module()
.impls()
.filter(|item_impl| item_impl.trait_path().is_none())
.flat_map(|item_impl| item_impl.iter_messages())
.flat_map(|message| {
let mut message_infos = Vec::new();
if abi.is_ink() {
message_infos
.push(self.dispatchable_inherent_message_infos(&message));
}
if abi.is_solidity() {
message_infos.push(self.solidity_message_info(&message, None));
}
message_infos
});
let trait_message_infos = self
.contract
.module()
.impls()
.filter_map(|item_impl| {
item_impl
.trait_path()
.map(|trait_path| {
let trait_ident = item_impl.trait_ident().expect(
"must have an ink! trait identifier if it is an ink! trait implementation"
);
iter::repeat((trait_ident, trait_path)).zip(item_impl.iter_messages())
})
})
.flatten()
.flat_map(|((trait_ident, trait_path), message)| {
let mut message_infos = Vec::new();
if abi.is_ink() {
message_infos.push(self.dispatchable_trait_message_infos(&message, trait_ident, trait_path));
}
if abi.is_solidity() {
message_infos.push(self.solidity_message_info(&message, Some((trait_ident, trait_path))));
}
message_infos
});
quote_spanned!(span=>
#( #inherent_message_infos )*
#( #trait_message_infos )*
)
}
fn dispatchable_inherent_message_infos(
&self,
message: &CallableWithSelector<Message>,
) -> TokenStream2 {
let storage_ident = self.contract.module().storage().ident();
let message_span = message.span();
let message_ident = message.ident();
let payable = message.is_payable();
let mutates = message.receiver().is_ref_mut();
let cfg_attrs = message.get_cfg_attrs(message_span);
let output_tuple_type = message
.output()
.map(quote::ToTokens::to_token_stream)
.unwrap_or_else(|| quote! { () });
let input_bindings = generator::input_bindings(message.inputs());
let input_tuple_type = generator::input_types_tuple(message.inputs());
let input_tuple_bindings = generator::input_bindings_tuple(message.inputs());
let selector_id = message
.composed_selector()
.into_be_u32()
.hex_padded_suffixed();
let selector_bytes = message.composed_selector().hex_lits();
#[cfg(feature = "std")]
let return_type = quote! { () };
#[cfg(not(feature = "std"))]
let return_type = quote! { ! };
quote_spanned!(message_span=>
#( #cfg_attrs )*
impl ::ink::reflect::DispatchableMessageInfo<#selector_id> for #storage_ident {
type Input = #input_tuple_type;
type Output = #output_tuple_type;
type Storage = #storage_ident;
const CALLABLE: fn(&mut Self::Storage, Self::Input) -> Self::Output =
|storage, #input_tuple_bindings| {
#storage_ident::#message_ident( storage #( , #input_bindings )* )
};
const DECODE: fn(&mut &[::core::primitive::u8]) -> ::core::result::Result<Self::Input, ::ink::env::DispatchError> =
|input| {
<Self::Input as ::ink::scale::Decode>::decode(input)
.map_err(|_| ::ink::env::DispatchError::InvalidParameters)
};
const RETURN: fn(::ink::env::ReturnFlags, Self::Output) -> #return_type =
|flags, output| {
::ink::env::return_value::<::ink::MessageResult::<Self::Output>>(
flags,
&::ink::MessageResult::Ok(output),
)
};
const SELECTOR: [::core::primitive::u8; 4usize] = [ #( #selector_bytes ),* ];
const PAYABLE: ::core::primitive::bool = #payable;
const MUTATES: ::core::primitive::bool = #mutates;
const LABEL: &'static ::core::primitive::str = ::core::stringify!(#message_ident);
const ENCODING: ::ink::reflect::Encoding = ::ink::reflect::Encoding::Scale;
}
)
}
fn dispatchable_trait_message_infos(
&self,
message: &CallableWithSelector<Message>,
trait_ident: &Ident,
trait_path: &syn::Path,
) -> TokenStream2 {
let storage_ident = self.contract.module().storage().ident();
let message_span = message.span();
let message_ident = message.ident();
let mutates = message.receiver().is_ref_mut();
let local_id = message.local_id().hex_padded_suffixed();
let payable = quote! {{
<<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::env::ContractEnv>::Env>
as #trait_path>::__ink_TraitInfo
as ::ink::reflect::TraitMessageInfo<#local_id>>::PAYABLE
}};
let selector = quote! {{
<<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::env::ContractEnv>::Env>
as #trait_path>::__ink_TraitInfo
as ::ink::reflect::TraitMessageInfo<#local_id>>::SELECTOR
}};
let selector_id = quote! {{
::core::primitive::u32::from_be_bytes(#selector)
}};
let output_tuple_type = message
.output()
.map(quote::ToTokens::to_token_stream)
.unwrap_or_else(|| quote! { () });
let input_bindings = generator::input_bindings(message.inputs());
let input_tuple_type = generator::input_types_tuple(message.inputs());
let input_tuple_bindings = generator::input_bindings_tuple(message.inputs());
let label = format!("{trait_ident}::{message_ident}");
let cfg_attrs = message.get_cfg_attrs(message_span);
#[cfg(feature = "std")]
let return_type = quote! { () };
#[cfg(not(feature = "std"))]
let return_type = quote! { ! };
quote_spanned!(message_span=>
#( #cfg_attrs )*
impl ::ink::reflect::DispatchableMessageInfo<#selector_id> for #storage_ident {
type Input = #input_tuple_type;
type Output = #output_tuple_type;
type Storage = #storage_ident;
const CALLABLE: fn(&mut Self::Storage, Self::Input) -> Self::Output =
|storage, #input_tuple_bindings| {
<#storage_ident as #trait_path>::#message_ident( storage #( , #input_bindings )* )
};
const DECODE: fn(&mut &[::core::primitive::u8]) -> ::core::result::Result<Self::Input, ::ink::env::DispatchError> =
|input| {
<Self::Input as ::ink::scale::Decode>::decode(input)
.map_err(|_| ::ink::env::DispatchError::InvalidParameters)
};
const RETURN: fn(::ink::env::ReturnFlags, Self::Output) -> #return_type =
|flags, output| {
::ink::env::return_value::<::ink::MessageResult::<Self::Output>>(
flags,
&::ink::MessageResult::Ok(output),
)
};
const SELECTOR: [::core::primitive::u8; 4usize] = #selector;
const PAYABLE: ::core::primitive::bool = #payable;
const MUTATES: ::core::primitive::bool = #mutates;
const LABEL: &'static ::core::primitive::str = #label;
const ENCODING: ::ink::reflect::Encoding = ::ink::reflect::Encoding::Scale;
}
)
}
fn solidity_message_info(
&self,
message: &CallableWithSelector<Message>,
trait_info: Option<(&Ident, &syn::Path)>,
) -> TokenStream2 {
let storage_ident = self.contract.module().storage().ident();
let message_span = message.span();
let message_ident = message.ident();
let payable = message.is_payable();
let mutates = message.receiver().is_ref_mut();
let cfg_attrs = message.get_cfg_attrs(message_span);
let output_tuple_type = message
.output()
.map(quote::ToTokens::to_token_stream)
.unwrap_or_else(|| quote! { () });
let input_bindings = generator::input_bindings(message.inputs());
let input_tuple_type = generator::input_types_tuple(message.inputs());
let input_tuple_bindings = generator::input_bindings_tuple(message.inputs());
let selector_id = solidity_selector_id(message);
let selector_bytes = solidity_selector(message);
let (call_prefix, label) = match trait_info {
None => {
(
quote! {
#storage_ident
},
message_ident.to_string(),
)
}
Some((trait_ident, trait_path)) => {
(
quote! {
<#storage_ident as #trait_path>
},
format!("{trait_ident}::{message_ident}"),
)
}
};
#[cfg(feature = "std")]
let return_type = quote! { () };
#[cfg(not(feature = "std"))]
let return_type = quote! { ! };
quote_spanned!(message_span=>
#( #cfg_attrs )*
impl ::ink::reflect::DispatchableMessageInfo<#selector_id> for #storage_ident {
type Input = #input_tuple_type;
type Output = #output_tuple_type;
type Storage = #storage_ident;
const CALLABLE: fn(&mut Self::Storage, Self::Input) -> Self::Output =
|storage, #input_tuple_bindings| {
#call_prefix::#message_ident( storage #( , #input_bindings )* )
};
const DECODE: fn(&mut &[::core::primitive::u8]) -> ::core::result::Result<Self::Input, ::ink::env::DispatchError> =
|input| {
<Self::Input as ::ink::SolDecode>::decode(input)
.map_err(|_| ::ink::env::DispatchError::InvalidParameters)
};
const RETURN: fn(::ink::env::ReturnFlags, Self::Output) -> #return_type =
|flags, output| {
::ink::env::return_value_solidity::<Self::Output>(
flags,
&output,
)
};
const SELECTOR: [::core::primitive::u8; 4usize] = #selector_bytes;
const PAYABLE: ::core::primitive::bool = #payable;
const MUTATES: ::core::primitive::bool = #mutates;
const LABEL: &'static ::core::primitive::str = #label;
const ENCODING: ::ink::reflect::Encoding = ::ink::reflect::Encoding::Solidity;
}
)
}
fn generate_entry_points(
&self,
constructors: &[ConstructorDispatchable],
messages: &[MessageDispatchable],
) -> TokenStream2 {
let span = self.contract.module().storage().span();
let storage_ident = self.contract.module().storage().ident();
let any_constructor_accept_payment =
self.any_constructor_accepts_payment(constructors);
let any_message_accepts_payment = self.any_message_accepts_payment(messages);
let fn_call: syn::ItemFn = syn::parse_quote! {
#[cfg(target_arch = "riscv64")]
#[::ink::polkavm_export(abi = ::ink::polkavm_derive::default_abi)]
pub extern "C" fn call() {
internal_call()
}
};
let fn_deploy: syn::ItemFn = syn::parse_quote! {
#[cfg(target_arch = "riscv64")]
#[::ink::polkavm_export(abi = ::ink::polkavm_derive::default_abi)]
pub extern "C" fn deploy() {
internal_deploy()
}
};
quote_spanned!(span=>
#[allow(dead_code)] #[allow(clippy::nonminimal_bool)]
#[cfg(target_arch = "riscv64")]
fn internal_deploy() {
if !#any_constructor_accept_payment {
::ink::codegen::deny_payment()
.unwrap_or_else(|error| ::core::panic!("{}", error))
}
let dispatchable = match ::ink::env::decode_input::<
<#storage_ident as ::ink::reflect::ContractConstructorDecoder>::Type,
>() {
::core::result::Result::Ok(decoded_dispatchable) => {
decoded_dispatchable
}
::core::result::Result::Err(_decoding_error) => {
let error = ::ink::ConstructorResult::Err(::ink::LangError::CouldNotReadInput);
::ink::env::return_value::<::ink::ConstructorResult<()>>(
::ink::env::ReturnFlags::REVERT,
&error,
);
}
};
<<#storage_ident as ::ink::reflect::ContractConstructorDecoder>::Type
as ::ink::reflect::ExecuteDispatchable>::execute_dispatchable(dispatchable)
.unwrap_or_else(|error| {
::core::panic!("dispatching ink! constructor failed: {}", error)
})
}
#[allow(dead_code)] #[allow(clippy::nonminimal_bool)]
#[cfg(target_arch = "riscv64")]
fn internal_call() {
if !#any_message_accepts_payment {
::ink::codegen::deny_payment()
.unwrap_or_else(|error| ::core::panic!("{}", error))
}
let dispatchable = match ::ink::env::decode_input::<
<#storage_ident as ::ink::reflect::ContractMessageDecoder>::Type,
>() {
::core::result::Result::Ok(decoded_dispatchable) => {
decoded_dispatchable
}
::core::result::Result::Err(_decoding_error) => {
let error = ::ink::MessageResult::Err(::ink::LangError::CouldNotReadInput);
::ink::env::return_value::<::ink::MessageResult<()>>(
::ink::env::ReturnFlags::REVERT,
&error,
);
}
};
<<#storage_ident as ::ink::reflect::ContractMessageDecoder>::Type
as ::ink::reflect::ExecuteDispatchable>::execute_dispatchable(dispatchable)
.unwrap_or_else(|error| {
::core::panic!("dispatching ink! message failed: {}", error)
})
}
#fn_call
#fn_deploy
)
}
fn generate_constructor_decoder_type(
&self,
constructors: &[ConstructorDispatchable],
) -> TokenStream2 {
fn expand_constructor_input(
span: proc_macro2::Span,
storage_ident: &syn::Ident,
constructor_id: LitInt,
) -> TokenStream2 {
quote_spanned!(span=>
<#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #constructor_id >>::Input
)
}
fn constructor_variant_ident(n: usize) -> syn::Ident {
quote::format_ident!("Constructor{}", n)
}
let span = self.contract.module().storage().span();
let storage_ident = self.contract.module().storage().ident();
let constructors_variants =
constructors.iter().enumerate().map(|(index, item)| {
let constructor_span = item.constructor.span();
let constructor_ident = constructor_variant_ident(index);
let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
let constructor_input = expand_constructor_input(
constructor_span,
storage_ident,
item.id.clone(),
);
quote_spanned!(constructor_span=>
#( #cfg_attrs )*
#constructor_ident(#constructor_input)
)
});
let constructor_selector=
constructors.iter().enumerate().map(|(index, item)| {
let constructor_span = item.constructor.span();
let const_ident = format_ident!("CONSTRUCTOR_{}", index);
let id = item.id.clone();
let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
quote_spanned!(span=>
#( #cfg_attrs )*
const #const_ident: [::core::primitive::u8; 4usize] = <#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::SELECTOR;
)
});
let constructor_match = constructors.iter().enumerate().map(|(index, item)| {
let constructor_span = item.constructor.span();
let constructor_ident = constructor_variant_ident(index);
let const_ident = format_ident!("CONSTRUCTOR_{}", index);
let constructor_input = expand_constructor_input(
constructor_span,
storage_ident,
item.id.clone(),
);
let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
quote_spanned!(constructor_span=>
#( #cfg_attrs )*
#const_ident => {
::core::result::Result::Ok(Self::#constructor_ident(
<#constructor_input as ::ink::scale::Decode>::decode(input)
.map_err(|_| ::ink::env::DispatchError::InvalidParameters)?
))
}
)
});
let possibly_wildcard_selector_constructor = match self
.query_wildcard_constructor()
{
Some(wildcard_index) => {
let item = &constructors[wildcard_index];
let constructor_span = item.constructor.span();
let constructor_ident = constructor_variant_ident(wildcard_index);
let constructor_input = expand_constructor_input(
constructor_span,
storage_ident,
item.id.clone(),
);
quote! {
::core::result::Result::Ok(Self::#constructor_ident(
<#constructor_input as ::ink::scale::Decode>::decode(input)
.map_err(|_| ::ink::env::DispatchError::InvalidParameters)?
))
}
}
None => {
quote! {
::core::result::Result::Err(::ink::env::DispatchError::UnknownSelector)
}
}
};
let constructor_execute = constructors.iter().enumerate().map(|(index, item)| {
let constructor_span = item.constructor.span();
let constructor_ident = constructor_variant_ident(index);
let id = item.id.clone();
let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
let constructor_callable = quote_spanned!(constructor_span=>
<#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::CALLABLE
);
let constructor_output = quote_spanned!(constructor_span=>
<#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::Output
);
let deny_payment = quote_spanned!(constructor_span=>
!<#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::PAYABLE
);
let constructor_value = quote_spanned!(constructor_span=>
<::ink::reflect::ConstructorOutputValue<#constructor_output>
as ::ink::reflect::ConstructorOutput::<#storage_ident>>
);
let constructor_accept_payment_assignment =
self.any_constructor_accepts_payment(constructors);
quote_spanned!(constructor_span=>
#( #cfg_attrs )*
Self::#constructor_ident(input) => {
if #constructor_accept_payment_assignment && #deny_payment {
::ink::codegen::deny_payment()?;
}
let result: #constructor_output = #constructor_callable(input);
let output_value = ::ink::reflect::ConstructorOutputValue::new(result);
let output_result = #constructor_value::as_result(&output_value);
if let ::core::result::Result::Ok(contract) = output_result.as_ref() {
::ink::env::set_contract_storage::<::ink::primitives::Key, #storage_ident>(
&<#storage_ident as ::ink::storage::traits::StorageKey>::KEY,
contract,
);
}
let mut flag = ::ink::env::ReturnFlags::empty();
if output_result.is_err() {
flag = ::ink::env::ReturnFlags::REVERT;
}
::ink::env::return_value::<
::ink::ConstructorResult<
::core::result::Result<(), &#constructor_value::Error>
>,
>(
flag,
&::ink::ConstructorResult::Ok(output_result.map(|_| ())),
);
#[cfg(feature = "std")]
return ::core::result::Result::Ok(());
#[cfg(not(feature = "std"))]
#[cfg_attr(not(feature = "std"), allow(unreachable_code))]
{
::core::unreachable!("either `return_value` or the `return` before will already have returned");
}
}
)
});
quote_spanned!(span=>
const _: () = {
#[allow(non_camel_case_types)]
pub enum __ink_ConstructorDecoder {
#( #constructors_variants ),*
}
impl ::ink::env::DecodeDispatch for __ink_ConstructorDecoder {
fn decode_dispatch(input: &mut &[::core::primitive::u8])
-> ::core::result::Result<Self, ::ink::env::DispatchError> {
#(
#constructor_selector
)*
match <[::core::primitive::u8; 4usize] as ::ink::scale::Decode>::decode(input)
.map_err(|_| ::ink::env::DispatchError::InvalidSelector)?
{
#( #constructor_match , )*
_invalid => #possibly_wildcard_selector_constructor
}
}
}
impl ::ink::reflect::ExecuteDispatchable for __ink_ConstructorDecoder {
#[allow(clippy::nonminimal_bool, dead_code)]
fn execute_dispatchable(self) -> ::core::result::Result<(), ::ink::env::DispatchError> {
match self {
#( #constructor_execute ),*
}
}
}
impl ::ink::reflect::ContractConstructorDecoder for #storage_ident {
type Type = __ink_ConstructorDecoder;
}
};
)
}
fn generate_message_decoder_type(
&self,
messages: &[MessageDispatchable],
) -> TokenStream2 {
fn expand_message_input(
span: proc_macro2::Span,
storage_ident: &syn::Ident,
message_id: TokenStream2,
) -> TokenStream2 {
quote_spanned!(span=>
<#storage_ident as ::ink::reflect::DispatchableMessageInfo< #message_id >>::Input
)
}
fn message_variant_ident(n: usize) -> syn::Ident {
quote::format_ident!("Message{}", n)
}
let span = self.contract.module().storage().span();
let storage_ident = self.contract.module().storage().ident();
let message_variants = messages.iter().enumerate().map(|(index, item)| {
let message_span = item.message.span();
let message_ident = message_variant_ident(index);
let cfg_attrs = item.message.get_cfg_attrs(message_span);
let message_input =
expand_message_input(message_span, storage_ident, item.id.clone());
quote_spanned!(message_span=>
#( #cfg_attrs )*
#message_ident(#message_input)
)
});
let message_selector = messages
.iter()
.enumerate()
.map(|(index, item)| {
let message_span = item.message.span();
let const_ident = format_ident!("MESSAGE_{}", index);
let cfg_attrs = item.message.get_cfg_attrs(message_span);
let id = item.id.clone();
quote_spanned!(span=>
#( #cfg_attrs )*
const #const_ident: [::core::primitive::u8; 4usize] = <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::SELECTOR;
)
});
let message_match = messages
.iter()
.enumerate()
.map(|(index, item)| {
let message_ident = message_variant_ident(index);
let const_ident = format_ident!("MESSAGE_{}", index);
let message_span = item.message.span();
let cfg_attrs = item.message.get_cfg_attrs(message_span);
let id = item.id.clone();
quote_spanned!(message_span=>
#( #cfg_attrs )*
#const_ident => {
::core::result::Result::Ok(Self::#message_ident(
<#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::DECODE(input)?
))
}
)
});
let possibly_wildcard_selector_message = match self.query_wildcard_message() {
Some(wildcard_index) => {
let item = messages
.get(wildcard_index)
.expect("unable to get wildcard index");
let message_span = item.message.span();
let message_ident = message_variant_ident(wildcard_index);
let message_input =
expand_message_input(message_span, storage_ident, item.id.clone());
quote! {
::core::result::Result::Ok(Self::#message_ident(
<#message_input as ::ink::scale::Decode>::decode(input)
.map_err(|_| ::ink::env::DispatchError::InvalidParameters)?
))
}
}
None => {
quote! {
::core::result::Result::Err(::ink::env::DispatchError::UnknownSelector)
}
}
};
let message_execute = messages
.iter()
.enumerate()
.map(|(index, item)| {
let message_span = item.message.span();
let message_ident = message_variant_ident(index);
let id = item.id.clone();
let cfg_attrs = item.message.get_cfg_attrs(message_span);
let message_callable = quote_spanned!(message_span=>
<#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::CALLABLE
);
let message_return = quote_spanned!(message_span=>
<#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::RETURN
);
let message_output = quote_spanned!(message_span=>
<#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::Output
);
let deny_payment = quote_spanned!(message_span=>
!<#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::PAYABLE
);
let mutates_storage = quote_spanned!(message_span=>
<#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::MUTATES
);
let any_message_accepts_payment =
self.any_message_accepts_payment(messages);
quote_spanned!(message_span=>
#( #cfg_attrs )*
Self::#message_ident(input) => {
if #any_message_accepts_payment && #deny_payment {
::ink::codegen::deny_payment()?;
}
let result: #message_output = #message_callable(&mut contract, input);
let is_reverted = ::ink::is_result_type!(#message_output)
&& ::ink::is_result_err!(result);
let mut flag = ::ink::env::ReturnFlags::REVERT;
if !is_reverted {
flag = ::ink::env::ReturnFlags::empty();
push_contract(contract, #mutates_storage);
}
#message_return(flag, result);
#[cfg(feature = "std")]
return ::core::result::Result::Ok(());
#[cfg(not(feature = "std"))]
#[cfg_attr(not(feature = "std"), allow(unreachable_code))]
{
::core::unreachable!("either `return_value` or the `return` before will already have returned");
}
}
)
});
quote_spanned!(span=>
const _: () = {
#[allow(non_camel_case_types)]
pub enum __ink_MessageDecoder {
#( #message_variants ),*
}
impl ::ink::env::DecodeDispatch for __ink_MessageDecoder {
fn decode_dispatch(input: &mut &[::core::primitive::u8])
-> ::core::result::Result<Self, ::ink::env::DispatchError> {
#(
#message_selector
)*
match <[::core::primitive::u8; 4usize] as ::ink::scale::Decode>::decode(input)
.map_err(|_| ::ink::env::DispatchError::InvalidSelector)?
{
#( #message_match , )*
_invalid => #possibly_wildcard_selector_message
}
}
}
fn push_contract(contract: ::core::mem::ManuallyDrop<#storage_ident>, mutates: bool) {
if mutates {
::ink::env::set_contract_storage::<::ink::primitives::Key, #storage_ident>(
&<#storage_ident as ::ink::storage::traits::StorageKey>::KEY,
&contract,
);
}
}
impl ::ink::reflect::ExecuteDispatchable for __ink_MessageDecoder {
#[allow(clippy::nonminimal_bool, clippy::let_unit_value, dead_code)]
fn execute_dispatchable(
self
) -> ::core::result::Result<(), ::ink::env::DispatchError> {
let key = <#storage_ident as ::ink::storage::traits::StorageKey>::KEY;
let mut contract: ::core::mem::ManuallyDrop<#storage_ident> =
::core::mem::ManuallyDrop::new(
match ::ink::env::get_contract_storage(&key) {
::core::result::Result::Ok(::core::option::Option::Some(value)) => value,
::core::result::Result::Ok(::core::option::Option::None) => {
::core::panic!("storage entry was empty")
},
::core::result::Result::Err(_) => {
::core::panic!("could not properly decode storage entry")
},
}
);
match self {
#( #message_execute ),*
}
}
}
impl ::ink::reflect::ContractMessageDecoder for #storage_ident {
type Type = __ink_MessageDecoder;
}
};
)
}
fn any_message_accepts_payment(
&self,
messages: &[MessageDispatchable],
) -> TokenStream2 {
let span = self.contract.module().storage().span();
let storage_ident = self.contract.module().storage().ident();
let message_is_payable = messages
.iter()
.enumerate()
.map(|(index, item)| {
let message_span = item.message.span();
let cfg_attrs = item.message.get_cfg_attrs(message_span);
let id = item.id.clone();
let ident = quote::format_ident!("message_{}", index);
quote_spanned!(message_span=>
{
let #ident = false;
#( #cfg_attrs )*
let #ident = <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::PAYABLE;
#ident
}
)
});
quote_spanned!(span=>
{
false #( || #message_is_payable )*
}
)
}
fn any_constructor_accepts_payment(
&self,
constructors: &[ConstructorDispatchable],
) -> TokenStream2 {
let span = self.contract.module().storage().span();
let storage_ident = self.contract.module().storage().ident();
let constructor_is_payable = constructors.iter().enumerate().map(|(index, item)| {
let constructor_span = item.constructor.span();
let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
let id = item.id.clone();
let ident = quote::format_ident!("constructor_{}", index);
quote_spanned!(constructor_span=>
{
let #ident = false;
#( #cfg_attrs )*
let #ident = <#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::PAYABLE;
#ident
}
)
});
quote_spanned!(span=>
{
false #( || #constructor_is_payable )*
}
)
}
fn generate_solidity_call_info(&self) -> TokenStream2 {
if self.contract.config().abi().is_solidity() {
let span = self.contract.module().span();
let call_tys = self
.contract
.module()
.impls()
.flat_map(|item_impl| item_impl.iter_messages())
.map(|message| {
let span = message.span();
let ident = message.ident();
let ident_str = ident.to_string();
let call_type_ident = solidity_call_type_ident(&message);
let sig_param_tys = message.inputs().map(|input| {
let ty = &*input.ty;
let span = input.span();
quote_spanned!(span=>
<#ty as ::ink::SolDecode>::SOL_NAME
)
});
let input_types_len = generator::input_types(message.inputs()).len();
let sig_arg_fmt_params = (0..input_types_len).map(|_| "{}").collect::<Vec<_>>().join(",");
let sig_fmt_lit = format!("{{}}({})", sig_arg_fmt_params);
quote_spanned!(span=>
pub struct #call_type_ident;
const _: () = {
impl #call_type_ident {
pub const SIGNATURE: &'static ::core::primitive::str = ::ink::codegen::const_format!(#sig_fmt_lit, #ident_str #(,#sig_param_tys)*);
pub const SELECTOR: [::core::primitive::u8; 4usize] = ::ink::codegen::sol_selector_bytes(Self::SIGNATURE);
pub const SELECTOR_ID: ::core::primitive::u32 = ::core::primitive::u32::from_be_bytes(Self::SELECTOR);
}
};
)
});
quote_spanned!(span=>
#[allow(non_camel_case_types)]
mod __ink_sol_interop__ {
#[allow(unused)]
use super::*;
#(#call_tys)*
}
)
} else {
quote!()
}
}
}