Unverified Commit 0270add0 authored by Robin Freyler's avatar Robin Freyler Committed by GitHub
Browse files

Refactor and modernize dispatch codegen (#933)

* implement Display for ink_lang::Error

* re-export HexLiteral trait from ink_lang_ir crate

* use big-letters for hex-encoded message and constructor identifiers in codegen

* improve panic messages in dispatch codegen for call and deploy fns

* improve macro hygiene in dispatch codegen

* add ir::Selector::into_be_u32 method

* improve and modernize dispatch selector namespace codegen
parent b9eb9bec
Pipeline #158429 passed with stages
in 26 minutes and 4 seconds
......@@ -18,7 +18,10 @@ use crate::{
GenerateCodeUsing as _,
};
use derive_more::From;
use ir::Callable as _;
use ir::{
Callable as _,
HexLiteral as _,
};
use proc_macro2::{
Ident,
TokenStream as TokenStream2,
......@@ -98,8 +101,9 @@ impl Dispatch<'_> {
fn deploy() {
<#storage_ident as ::ink_lang::DispatchUsingMode>::dispatch_using_mode(
::ink_lang::DispatchMode::Instantiate,
)
.expect("failed to dispatch the constructor")
).unwrap_or_else(|error| {
::core::panic!("dispatching constructor failed: {}", error)
})
}
#[cfg(not(test))]
......@@ -111,8 +115,9 @@ impl Dispatch<'_> {
}
<#storage_ident as ::ink_lang::DispatchUsingMode>::dispatch_using_mode(
::ink_lang::DispatchMode::Call,
)
.expect("failed to dispatch the call")
).unwrap_or_else(|error| {
::core::panic!("dispatching message failed: {}", error)
})
}
}
}
......@@ -125,7 +130,7 @@ impl Dispatch<'_> {
#[allow(unused_parens)]
fn dispatch_using_mode(
mode: ::ink_lang::DispatchMode
) -> core::result::Result<(), ::ink_lang::DispatchError> {
) -> ::core::result::Result<(), ::ink_lang::DispatchError> {
match mode {
::ink_lang::DispatchMode::Instantiate => {
<<#storage_ident as ::ink_lang::ConstructorDispatcher>::Type as ::ink_lang::Execute>::execute(
......@@ -148,8 +153,8 @@ impl Dispatch<'_> {
/// Returns the generated ink! namespace identifier for the given callable kind.
fn dispatch_trait_impl_namespace(kind: ir::CallableKind) -> Ident {
match kind {
ir::CallableKind::Constructor => format_ident!("__ink_Constr"),
ir::CallableKind::Message => format_ident!("__ink_Msg"),
ir::CallableKind::Constructor => format_ident!("__ink_ConstructorInfo"),
ir::CallableKind::Message => format_ident!("__ink_MessageInfo"),
}
}
......@@ -162,31 +167,27 @@ impl Dispatch<'_> {
let constructor_namespace =
Self::dispatch_trait_impl_namespace(ir::CallableKind::Constructor);
quote! {
// Namespace for messages.
// Selector namespace for ink! messages of the root smart contract.
//
// # Note
//
// The `S` parameter is going to refer to array types `[(); N]`
// where `N` is the unique identifier of the associated message
// selector.
// - We have separate namespaces for ink! messages and constructors to
// allow for overlapping selectors between them.
// - The `ID` const parameter uniquely identifies one of the ink! messages
// implemented by the root smart contract.
#[doc(hidden)]
pub struct #message_namespace<S> {
// We need to wrap inner because of Rust's orphan rules.
marker: core::marker::PhantomData<fn() -> S>,
}
pub struct #message_namespace<const ID: u32> {}
// Namespace for constructors.
// Selector namespace for ink! constructors of the root smart contract.
//
// # Note
//
// The `S` parameter is going to refer to array types `[(); N]`
// where `N` is the unique identifier of the associated constructor
// selector.
// - We have separate namespaces for ink! messages and constructors to
// allow for overlapping selectors between them.
// - The `ID` const parameter uniquely identifies one of the ink! constructors
// implemented by the root smart contract.
#[doc(hidden)]
pub struct #constructor_namespace<S> {
// We need to wrap inner because of Rust's orphan rules.
marker: core::marker::PhantomData<fn() -> S>,
}
pub struct #constructor_namespace<const ID: u32> {}
}
}
......@@ -201,7 +202,10 @@ impl Dispatch<'_> {
let callable = cws.callable();
let callable_span = callable.span();
let selector = cws.composed_selector();
let (selector_bytes, selector_id) = (selector.as_bytes(), selector.unique_id());
let (selector_bytes, selector_id) = (
selector.hex_lits(),
selector.into_be_u32().hex_padded_suffixed(),
);
let input_types = callable
.inputs()
.map(|pat_type| &pat_type.ty)
......@@ -217,19 +221,19 @@ impl Dispatch<'_> {
quote! { #( #input_types )* }
};
let fn_input_impl = quote_spanned!(callable.inputs_span() =>
impl ::ink_lang::FnInput for #namespace<[(); #selector_id]> {
impl ::ink_lang::FnInput for #namespace::<#selector_id> {
type Input = #input_types_tuple;
}
);
let fn_selector_impl = quote_spanned!(callable_span =>
impl ::ink_lang::FnSelector for #namespace<[(); #selector_id]> {
impl ::ink_lang::FnSelector for #namespace::<#selector_id> {
const SELECTOR: ::ink_env::call::Selector = ::ink_env::call::Selector::new([
#( #selector_bytes ),*
]);
}
);
let fn_state_impl = quote_spanned!(callable_span =>
impl ::ink_lang::FnState for #namespace<[(); #selector_id]> {
impl ::ink_lang::FnState for #namespace::<#selector_id> {
type State = #storage_ident;
}
);
......@@ -301,7 +305,7 @@ impl Dispatch<'_> {
let message = cws.callable();
let message_span = message.span();
let selector = cws.composed_selector();
let selector_id = selector.unique_id();
let selector_id = selector.into_be_u32().hex_padded_suffixed();
let output_tokens = message
.output()
.map(quote::ToTokens::to_token_stream)
......@@ -311,7 +315,7 @@ impl Dispatch<'_> {
let message_ident = message.ident();
let namespace = Self::dispatch_trait_impl_namespace(ir::CallableKind::Message);
let fn_output_impl = quote_spanned!(message.output().span() =>
impl ::ink_lang::FnOutput for #namespace<[(); #selector_id]> {
impl ::ink_lang::FnOutput for #namespace::<#selector_id> {
#[allow(unused_parens)]
type Output = #output_tokens;
}
......@@ -333,7 +337,7 @@ impl Dispatch<'_> {
)
});
let message_impl = quote_spanned!(message_span =>
impl ::ink_lang::#message_trait_ident for #namespace<[(); #selector_id]> {
impl ::ink_lang::#message_trait_ident for #namespace::<#selector_id> {
const CALLABLE: fn(
&#mut_token <Self as ::ink_lang::FnState>::State,
<Self as ::ink_lang::FnInput>::Input
......@@ -357,7 +361,7 @@ impl Dispatch<'_> {
let constructor = cws.callable();
let constructor_span = constructor.span();
let selector = cws.composed_selector();
let selector_id = selector.unique_id();
let selector_id = selector.into_be_u32().hex_padded_suffixed();
let storage_ident = self.contract.module().storage().ident();
let constructor_ident = constructor.ident();
let namespace =
......@@ -371,7 +375,7 @@ impl Dispatch<'_> {
)
});
let constructor_impl = quote_spanned!(constructor_span =>
impl ::ink_lang::Constructor for #namespace<[(); #selector_id]> {
impl ::ink_lang::Constructor for #namespace::<#selector_id> {
const CALLABLE: fn(
<Self as ::ink_lang::FnInput>::Input
) -> <Self as ::ink_lang::FnState>::State = |#inputs_as_tuple_or_wildcard| {
......@@ -424,7 +428,7 @@ impl Dispatch<'_> {
ir::CallableKind::Constructor => "Constructor",
};
quote::format_ident!(
"__ink_{}_0x{:02x}{:02x}{:02x}{:02x}",
"__ink_{}_0x{:02X}{:02X}{:02X}{:02X}",
prefix,
selector_bytes[0],
selector_bytes[1],
......@@ -446,12 +450,12 @@ impl Dispatch<'_> {
where
C: ir::Callable,
{
let selector_bytes = cws.composed_selector().as_bytes().to_owned();
let selector_bytes = cws.composed_selector().hex_lits();
let variant_ident = self.generate_dispatch_variant_ident(cws);
let variant_types = cws.callable().inputs().map(|arg| &arg.ty);
quote! {
[ #( #selector_bytes ),* ] => {
Ok(Self::#variant_ident(
::core::result::Result::Ok(Self::#variant_ident(
#(
<#variant_types as ::scale::Decode>::decode(input)?
),*
......@@ -524,7 +528,7 @@ impl Dispatch<'_> {
(None, quote! { MessageRef }, quote! { execute_message })
}
};
let selector_id = cws.composed_selector().unique_id();
let selector_id = cws.composed_selector().into_be_u32().hex_padded_suffixed();
let namespace = Self::dispatch_trait_impl_namespace(ir::CallableKind::Message);
// If all ink! messages deny payment we can move the payment check to before
// the message dispatch which is more efficient.
......@@ -535,11 +539,11 @@ impl Dispatch<'_> {
.is_dynamic_storage_allocator_enabled();
quote! {
Self::#ident(#(#arg_pats),*) => {
::ink_lang::#exec_fn::<<#storage_ident as ::ink_lang::ContractEnv>::Env, #namespace<[(); #selector_id]>, _>(
::ink_lang::#exec_fn::<<#storage_ident as ::ink_lang::ContractEnv>::Env, #namespace::<#selector_id>, _>(
::ink_lang::AcceptsPayments(#accepts_payments),
::ink_lang::EnablesDynamicStorageAllocator(#is_dynamic_storage_allocation_enabled),
move |state: &#mut_mod #storage_ident| {
<#namespace<[(); #selector_id]> as ::ink_lang::#msg_trait>::CALLABLE(
<#namespace::<#selector_id> as ::ink_lang::#msg_trait>::CALLABLE(
state, #arg_inputs
)
}
......@@ -584,9 +588,13 @@ impl Dispatch<'_> {
impl ::scale::Decode for __ink_MessageDispatchEnum {
fn decode<I: ::scale::Input>(input: &mut I) -> ::core::result::Result<Self, ::scale::Error> {
match <[u8; 4] as ::scale::Decode>::decode(input)? {
match <[::core::primitive::u8; 4usize] as ::scale::Decode>::decode(input)? {
#( #decode_message )*
_invalid => Err(::scale::Error::from("encountered unknown ink! message selector"))
_invalid => ::core::result::Result::Err(
<::scale::Error as ::core::convert::From<&'static ::core::primitive::str>>::from(
"encountered unknown ink! message selector"
)
)
}
}
}
......@@ -620,7 +628,7 @@ impl Dispatch<'_> {
} else {
quote! { ( #(#arg_pats),* ) }
};
let selector_id = cws.composed_selector().unique_id();
let selector_id = cws.composed_selector().into_be_u32().hex_padded_suffixed();
let namespace =
Self::dispatch_trait_impl_namespace(ir::CallableKind::Constructor);
let is_dynamic_storage_allocation_enabled = self
......@@ -629,10 +637,10 @@ impl Dispatch<'_> {
.is_dynamic_storage_allocator_enabled();
quote! {
Self::#ident(#(#arg_pats),*) => {
::ink_lang::execute_constructor::<#namespace<[(); #selector_id]>, _>(
::ink_lang::execute_constructor::<#namespace::<#selector_id>, _>(
::ink_lang::EnablesDynamicStorageAllocator(#is_dynamic_storage_allocation_enabled),
move || {
<#namespace<[(); #selector_id]> as ::ink_lang::Constructor>::CALLABLE(
<#namespace::<#selector_id> as ::ink_lang::Constructor>::CALLABLE(
#arg_inputs
)
}
......@@ -677,9 +685,13 @@ impl Dispatch<'_> {
impl ::scale::Decode for __ink_ConstructorDispatchEnum {
fn decode<I: ::scale::Input>(input: &mut I) -> ::core::result::Result<Self, ::scale::Error> {
match <[u8; 4] as ::scale::Decode>::decode(input)? {
match <[::core::primitive::u8; 4usize] as ::scale::Decode>::decode(input)? {
#( #decode_message )*
_invalid => Err(::scale::Error::from("encountered unknown ink! constructor selector"))
_invalid => ::core::result::Result::Err(
<::scale::Error as ::core::convert::From<&'static ::core::primitive::str>>::from(
"encountered unknown ink! constructor selector"
)
)
}
}
}
......
......@@ -45,7 +45,12 @@ impl Selector {
/// Returns a unique identifier as `usize`.
pub fn unique_id(self) -> usize {
u32::from_le_bytes(self.bytes) as usize
self.into_be_u32() as usize
}
/// Returns the big-endian `u32` representation of the selector bytes.
pub fn into_be_u32(self) -> u32 {
u32::from_be_bytes(self.bytes)
}
/// Returns the 4 bytes that make up the selector as hex encoded bytes.
......
......@@ -34,37 +34,40 @@ mod ast;
mod ir;
mod literal;
pub use self::ir::{
Callable,
CallableKind,
CallableWithSelector,
ChainExtension,
ChainExtensionMethod,
Config,
Constructor,
Contract,
Event,
ExtensionId,
ImplItem,
InkItem,
InkTest,
InkTrait,
InkTraitConstructor,
InkTraitItem,
InkTraitMessage,
InputsIter,
Item,
ItemImpl,
ItemMod,
IterConstructors,
IterEvents,
IterInkTraitItems,
IterItemImpls,
IterMessages,
Message,
Namespace,
Receiver,
Selector,
Storage,
Visibility,
pub use self::{
ir::{
Callable,
CallableKind,
CallableWithSelector,
ChainExtension,
ChainExtensionMethod,
Config,
Constructor,
Contract,
Event,
ExtensionId,
ImplItem,
InkItem,
InkTest,
InkTrait,
InkTraitConstructor,
InkTraitItem,
InkTraitMessage,
InputsIter,
Item,
ItemImpl,
ItemMod,
IterConstructors,
IterEvents,
IterInkTraitItems,
IterItemImpls,
IterMessages,
Message,
Namespace,
Receiver,
Selector,
Storage,
Visibility,
},
literal::HexLiteral,
};
......@@ -12,22 +12,32 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use derive_more::Display;
/// A dispatch result.
#[doc(hidden)]
pub type DispatchResult = core::result::Result<(), DispatchError>;
/// A dispatch error.
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, Display)]
#[doc(hidden)]
pub enum DispatchError {
#[display(fmt = "unknown selector")]
UnknownSelector,
#[display(fmt = "unknown constructor selector")]
UnknownInstantiateSelector,
#[display(fmt = "unknown message selector")]
UnknownCallSelector,
#[display(fmt = "unable to decoded input parameter bytes")]
InvalidParameters,
#[display(fmt = "unable to decoded input parameter bytes for constructor")]
InvalidInstantiateParameters,
#[display(fmt = "unable to decoded input parameter bytes for message")]
InvalidCallParameters,
#[display(fmt = "could not read input parameters")]
CouldNotReadInput,
#[display(fmt = "paid an unpayable message")]
PaidUnpayableMessage,
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment